tl-wr802n v1: OpenWrt系统中的failsafe模式

这篇主要是为了说明tl-wr802n v1如何实现failsafe模式,如何进行factory reset的。当然这时必然汲及到OpenWrt所使用的文件系统:SquashFS, JFFS2和OverlayFS。我们再看一下官方固件的名称:openwrt-15.05-ar71xx-generic-tl-wr841n-v9-squashfs-factory.bin,这里面有一个关键写squashfs, 说明根文件系统为SquashFS, 这个是给NOR Flash使用的文件系统。我们知道Flash有分NOR flash和NAND flash, tl-wr802n v1使用的flash为w25q32(4M)或者w25q128(16M), 是一种接口为SPI的NOR flash;而Netgear wndr4300 v1所使用的flash为NAND flash, 它所使用的根文件系统为ubi, 所以,它的固件名称为openwrt-15.05-ar71xx-nand-wndr4300-ubi-factory.img, 名称中同时表明它使用的flash为NAND flash。

 

这里需要强调的是SquashFS是只读的文件系统,这就意味着在这个文件系统中存放的文件都是不能修改,删除的。我们再来粗略看一下它的文件结构:

hzak@B85PRO:/data/openwrt/OpenWrt-ImageBuilder-15.05-ar71xx-generic.Linux-x86_64/build_dir/target-mips_34kc_uClibc-0.9.33.2/root-ar71xx$ tree -L 2
.
├── bin
│   ├── ash -> busybox
│   ├── board_detect
│   ├── busybox
│   ├── cat -> busybox
│   ├── chgrp -> busybox
│   ├── chmod -> busybox
│   ├── chown -> busybox
│   ├── config_generate
│   ├── cp -> busybox
│   ├── date -> busybox
│   ├── dd -> busybox
│   ├── df -> busybox
│   ├── dmesg -> busybox
│   ├── echo -> busybox
│   ├── egrep -> busybox
│   ├── false -> busybox
│   ├── fgrep -> busybox
│   ├── fsync -> busybox
│   ├── grep -> busybox
│   ├── gunzip -> busybox
│   ├── gzip -> busybox
│   ├── ipcalc.sh
│   ├── kill -> busybox
│   ├── ln -> busybox
│   ├── lock -> busybox
│   ├── login.sh
│   ├── ls -> busybox
│   ├── mkdir -> busybox
│   ├── mknod -> busybox
│   ├── mktemp -> busybox
│   ├── mount -> busybox
│   ├── mv -> busybox
│   ├── netmsg -> busybox
│   ├── netstat -> busybox
│   ├── nice -> busybox
│   ├── opkg
│   ├── pidof -> busybox
│   ├── ping -> busybox
│   ├── ping6 -> busybox
│   ├── ps -> busybox
│   ├── pwd -> busybox
│   ├── rm -> busybox
│   ├── rmdir -> busybox
│   ├── sed -> busybox
│   ├── sh -> busybox
│   ├── sleep -> busybox
│   ├── sync -> busybox
│   ├── tar -> busybox
│   ├── touch -> busybox
│   ├── true -> busybox
│   ├── ubus
│   ├── umount -> busybox
│   ├── uname -> busybox
│   ├── vi -> busybox
│   └── zcat -> busybox
├── dev
├── etc
│   ├── banner
│   ├── banner.failsafe
│   ├── config
│   ├── crontabs
│   ├── device_info
│   ├── diag.sh
│   ├── dnsmasq.conf
│   ├── dropbear
│   ├── firewall.user
│   ├── fstab -> /tmp/fstab
│   ├── group
│   ├── hosts
│   ├── hotplug.d
│   ├── hotplug.json
│   ├── hotplug-preinit.json
│   ├── init.d
│   ├── inittab
│   ├── modules-boot.d
│   ├── modules.d
│   ├── mtab -> /proc/mounts
│   ├── openwrt_release
│   ├── openwrt_version
│   ├── opkg
│   ├── opkg.conf
│   ├── passwd
│   ├── ppp
│   ├── preinit
│   ├── profile
│   ├── protocols
│   ├── rc.button
│   ├── rc.common
│   ├── rc.d
│   ├── rc.local
│   ├── resolv.conf -> /tmp/resolv.conf
│   ├── services
│   ├── shadow
│   ├── shells
│   ├── sysctl.conf
│   ├── sysupgrade.conf
│   ├── TZ -> /tmp/TZ
│   └── uci-defaults
├── init
├── lib
│   ├── ar71xx.sh
│   ├── config
│   ├── firmware
│   ├── functions
│   ├── functions.sh
│   ├── ld-uClibc-0.9.33.2.so
│   ├── ld-uClibc.so.0 -> ld-uClibc-0.9.33.2.so
│   ├── libblobmsg_json.so
│   ├── libcrypt-0.9.33.2.so
│   ├── libcrypt.so.0 -> libcrypt-0.9.33.2.so
│   ├── libc.so.0 -> libuClibc-0.9.33.2.so
│   ├── libdl-0.9.33.2.so
│   ├── libdl.so.0 -> libdl-0.9.33.2.so
│   ├── libfstools.so
│   ├── libgcc_s.so.1
│   ├── libjson_script.so
│   ├── libm-0.9.33.2.so
│   ├── libm.so.0 -> libm-0.9.33.2.so
│   ├── libsetlbf.so
│   ├── libubox.so
│   ├── libubus.so
│   ├── libuci.so
│   ├── libuClibc-0.9.33.2.so
│   ├── libutil-0.9.33.2.so
│   ├── libutil.so.0 -> libutil-0.9.33.2.so
│   ├── libvalidate.so
│   ├── modules
│   ├── netifd
│   ├── network
│   ├── preinit
│   ├── uboot-envtools.sh
│   ├── uci
│   ├── upgrade
│   └── wifi
├── mnt
├── overlay
├── proc
├── rom
│   └── note
├── root
├── sbin
│   ├── askfirst
│   ├── devmem -> ../bin/busybox
│   ├── devstatus
│   ├── firstboot
│   ├── fw3
│   ├── halt -> ../bin/busybox
│   ├── hotplug-call
│   ├── hwclock -> ../bin/busybox
│   ├── ifconfig -> ../bin/busybox
│   ├── ifdown -> ifup
│   ├── ifstatus
│   ├── ifup
│   ├── init
│   ├── jffs2mark -> jffs2reset
│   ├── jffs2reset
│   ├── kmodloader
│   ├── led.sh
│   ├── logd
│   ├── logread
│   ├── luci-reload
│   ├── mkswap -> ../bin/busybox
│   ├── mount_root
│   ├── mtd
│   ├── netifd
│   ├── pivot_root -> ../bin/busybox
│   ├── poweroff -> ../bin/busybox
│   ├── procd
│   ├── reboot -> ../bin/busybox
│   ├── reload_config
│   ├── route -> ../bin/busybox
│   ├── rpcd
│   ├── snapshot
│   ├── snapshot_tool
│   ├── start-stop-daemon -> ../bin/busybox
│   ├── swconfig
│   ├── switch_root -> ../bin/busybox
│   ├── sysctl -> ../bin/busybox
│   ├── sysupgrade
│   ├── ubusd
│   ├── uci
│   ├── udevtrigger
│   ├── udhcpc -> ../bin/busybox
│   ├── validate_data
│   ├── vconfig -> ../bin/busybox
│   ├── wget2nand
│   └── wifi
├── sys
├── tmp
├── usr
│   ├── bin
│   ├── lib
│   ├── sbin
│   └── share
├── var -> /tmp
└── www
    ├── cgi-bin
    ├── index.html
    └── luci-static

43 directories, 157 files

可以看到,这里面包含了/etc文件夹,这个文件夹中的文件属于配置文件,是经常会被修改的。

我们再来看一下系统开机的时候,preinit的流程应该是这样的:

-> /etc/preinit::preinit_essential
-> /etc/preinit::preinit_main
    -> /lib/preinit/02_default_set_state
    -> /lib/preinit/03_preinit_do_ar71xx.sh
    -> /lib/preinit/05_set_iface_mac_ar71xx
    -> /lib/preinit/05_set_preinit_iface_ar71xx
    -> /lib/preinit/10_indicate_failsafe
    -> /lib/preinit/10_indicate_preinit
    -> /lib/preinit/10_sysinfo
    -> /lib/preinit/30_failsafe_wait
    -> /lib/preinit/40_run_failsafe_hook
    -> /lib/preinit/50_indicate_regular_preinit
    -> /lib/preinit/70_initramfs_test
    -> /lib/preinit/80_mount_root
    -> /lib/preinit/81_load_ath10k_board_bin
    -> /lib/preinit/82_patch_ath10
    -> /lib/preinit/99_10_failsafe_login
    -> /lib/preinit/99_10_run_init

其中,我们重点看一下/lib/preinit/80_mount_root:

root@OpenWrt-wr802n-v1:/lib/preinit# cat 80_mount_root 
#!/bin/sh
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2010 Vertical Communications

do_mount_root() {
	mount_root
	boot_run_hook preinit_mount_root
	[ -f /sysupgrade.tgz ] && {
		echo "- config restore -"
		cd /
		tar xzf /sysupgrade.tgz
	}
}

[ "$INITRAMFS" = "1" ] || boot_hook_add preinit_main do_mount_root

在执行do_mount_root()时,会去执行mount_root, mount_root(/sbin/mount_root)为binary可执行文件。

[2016-01-28 20:57:33]

mount_root相关的源代码在这里:http://git.openwrt.org/?p=project/fstools.git;a=summary

先看一下http://git.openwrt.org/?p=project/fstools.git;a=blob_plain;f=mount_root.c;hb=96415afecef35766332067f4205ef3b2c7561d21

/*
 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <sys/mount.h>
#include <stdio.h>
#include <stdlib.h>

#include <libubox/ulog.h>

#include "libfstools/libfstools.h"
#include "libfstools/volume.h"

/*
 * Called in the early (PREINIT) stage, when we immediately need some writable
 * filesystem.
 */
static int
start(int argc, char *argv[1])
{
	struct volume *root;
	struct volume *data = volume_find("rootfs_data");

	if (!getenv("PREINIT"))
		return -1;

	if (!data) {
		root = volume_find("rootfs");
		volume_init(root);
		ULOG_NOTE("mounting /dev/root\n");
		mount("/dev/root", "/", NULL, MS_NOATIME | MS_REMOUNT, 0);
	}

	/*
	 * Before trying to mount and use "rootfs_data" let's check if there is
	 * extroot configured. Following call will handle reading config from
	 * the "rootfs_data" on its own.
	 */
	extroot_prefix = "";
	if (!mount_extroot()) {
		ULOG_NOTE("switched to extroot\n");
		return 0;
	}

	/* There isn't extroot, so just try to mount "rootfs_data" */
	switch (volume_identify(data)) {
	case FS_NONE:
		ULOG_WARN("no usable overlay filesystem found, using tmpfs overlay\n");
		return ramoverlay();

	case FS_DEADCODE:
		/*
		 * Filesystem isn't ready yet and we are in the preinit, so we
		 * can't afford waiting for it. Use tmpfs for now and handle it
		 * properly in the "done" call.
		 */
		ULOG_NOTE("jffs2 not ready yet, using temporary tmpfs overlay\n");
		return ramoverlay();

	case FS_JFFS2:
	case FS_UBIFS:
		mount_overlay(data);
		break;

	case FS_SNAPSHOT:
		mount_snapshot(data);
		break;
	}

	return 0;
}

static int
stop(int argc, char *argv[1])
{
	if (!getenv("SHUTDOWN"))
		return -1;

	return 0;
}

/*
 * Called at the end of init, it can wait for filesystem if needed.
 */
static int
done(int argc, char *argv[1])
{
	struct volume *v = volume_find("rootfs_data");

	if (!v)
		return -1;

	switch (volume_identify(v)) {
	case FS_NONE:
	case FS_DEADCODE:
		return jffs2_switch(v);

	case FS_JFFS2:
	case FS_UBIFS:
		fs_state_set("/overlay", FS_STATE_READY);
		break;
	}

	return 0;
}

int main(int argc, char **argv)
{
	if (argc < 2)
		return start(argc, argv);
	if (!strcmp(argv[1], "ram"))
		return ramoverlay();
	if (!strcmp(argv[1], "stop"))
		return stop(argc, argv);
	if (!strcmp(argv[1], "done"))
		return done(argc, argv);
	return -1;
}
  • 第一次开机或者做factory reset:

main()->start()->volume_find()->volume_identify():FS_DEADCODE->ramoverlay()

-> /etc/rc.d/S90done (/etc/init.d/done) -> “mount_root done”

  • 之后的正常开机流程为:

main()->start()->volume_find()->volume_identify():FS_JFFS2->mount_overlay()

-> /etc/rc.d/S90done (/etc/init.d/done)

/lib/functions/preinit.sh中挂载文件系统相关的代码如下:

pivot() { # <new_root> <old_root>
        /bin/mount -o noatime,move /proc $1/proc && \
        pivot_root $1 $1$2 && {
                /bin/mount -o noatime,move $2/dev /dev
                /bin/mount -o noatime,move $2/tmp /tmp
                /bin/mount -o noatime,move $2/sys /sys 2>&-
                /bin/mount -o noatime,move $2/overlay /overlay 2>&-
                return 0
        }
}

fopivot() { # <rw_root> <ro_root> <dupe?>
        /bin/mount -o noatime,lowerdir=/,upperdir=$1 -t overlayfs "overlayfs:$1" /mnt
        pivot /mnt $2
}

ramoverlay() {
        mkdir -p /tmp/root
        /bin/mount -t tmpfs -o noatime,mode=0755 root /tmp/root
        fopivot /tmp/root /rom 1
}

再看一下参考文档https://wiki.openwrt.org/doc/techref/preinit_mount Mount Root Filesystem:

  1. 先将mtd分区中的rootfs分区(/dev/root)挂载到/rom上(使用SquashFS文件系统)
  2. 再将mtd分区中的rootfs_data分区(/dev/mtdblock3)挂载到/overlay上(使用JFFS2文件系统)
  3. 最后使用OverlayFS,以/rom作为lowerdir, /overlay/upper作为upperdir, /overlay/work作为workdir, 创建最终的根文件系统。

执行mount命令,看一下当前文件系统挂载的情况:

root@OpenWrt-wr802n-v1:~# mount
rootfs on / type rootfs (rw)
/dev/root on /rom type squashfs (ro,relatime)
proc on /proc type proc (rw,noatime)
sysfs on /sys type sysfs (rw,noatime)
tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noatime)
/dev/mtdblock3 on /overlay type jffs2 (rw,noatime)
overlayfs:/overlay on / type overlay (rw,noatime,lowerdir=/,upperdir=/overlay/upper,workdir=/overlay/work)
tmpfs on /dev type tmpfs (rw,relatime,size=512k,mode=755)
devpts on /dev/pts type devpts (rw,relatime,mode=600)
debugfs on /sys/kernel/debug type debugfs (rw,noatime)

这里涉及到几个问题:

  • /dev/root分区怎么来的
  • rootfs_data分区什么时候格式化的
  • JFFS2文件系统怎么来的,为什么要使用这种文件系统?
  • OverlayFS又是怎么回事,怎么创建根文件系统的?

 

所以failsafe模式其实就是只挂载了rootfs分区,并将这个rootfs分区作为根文件系统。

而factory reset也只需要将/overlay中的文件删除,或者是如/etc/rc.button/reset中的做法:执行jffs2reset -y && reboot

jffs2reset的代码在这里:http://git.openwrt.org/?p=project/fstools.git;a=blob_plain;f=jffs2reset.c;hb=96415afecef35766332067f4205ef3b2c7561d21

static int jffs2_reset(struct volume *v, int reset)
{
	char *mp;

	mp = find_mount_point(v->blk, 1);
	if (mp) {
		ULOG_INFO("%s is mounted as %s, only erasing files\n", v->blk, mp);
		fs_state_set("/overlay", FS_STATE_PENDING);
		overlay_delete(mp, false);
		mount(mp, "/", NULL, MS_REMOUNT, 0);
	} else {
		ULOG_INFO("%s is not mounted\n", v->blk);
		return jffs2_mark(v);
	}

	if (reset) {
		sync();
		sleep(2);
		reboot(RB_AUTOBOOT);
		while (1)
			;
	}

	return 0;
}

static int jffs2_mark(struct volume *v)
{
	__u32 deadc0de = __cpu_to_be32(0xdeadc0de);
	size_t sz;
	int fd;

	fd = open(v->blk, O_WRONLY);
	ULOG_INFO("%s will be erased on next mount\n", v->blk);
	if (!fd) {
		ULOG_ERR("opening %s failed\n", v->blk);
		return -1;
	}

	sz = write(fd, &deadc0de, sizeof(deadc0de));
	close(fd);

	if (sz != 4) {
		ULOG_ERR("writing %s failed: %s\n", v->blk, strerror(errno));
		return -1;
	}

	return 0;
}

int main(int argc, char **argv)
{
	struct volume *v;
	int ch, yes = 0, reset = 0;
	while ((ch = getopt(argc, argv, "yr")) != -1) {
		switch(ch) {
		case 'y':
			yes = 1;
			break;
		case 'r':
			reset = 1;
			break;
		}

	}

	if (!yes && ask_user())
		return -1;

	/*
	 * TODO: Currently this only checks if kernel supports OverlayFS. We
	 * should check if there is a mount point using it with rootfs_data
	 * as upperdir.
	 */
	if (find_filesystem("overlay")) {
		ULOG_ERR("overlayfs not supported by kernel\n");
		return -1;
	}

	v = volume_find("rootfs_data");
	if (!v) {
		ULOG_ERR("MTD partition 'rootfs_data' not found\n");
		return -1;
	}

	if (!strcmp(*argv, "jffs2mark"))
		return jffs2_mark(v);
	return jffs2_reset(v, reset);
}

第一次开机时进入failsafe模式时,/dev/mtd3前4个字节为0xde 0xad 0xc0 0xde:

root@(none):/# hexdump -n 16 -v -e '16/1 "%02x ""\n"' /dev/mtd3
de ad c0 de ff ff ff ff ff ff ff ff ff ff ff ff

而执行mount_root时,会使用tmpfs做overlay:

root@(none):/# mount_root
[   89.120000] mount_root: jffs2 not ready yet, using temporary tmpfs overlay

第一开机时如果不进入failsafe模式,最开始也会使用tmpfs做overlay, 之后会去格式化/dev/mtdblock3成JFFS2文件系统,再使用这个分区做overlay (在执行/etc/rc.d/S90done时做的?):

Press the [f] key and hit [enter] to enter failsafe mode
Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
[    8.930000] mount_root: jffs2 not ready yet, using temporary tmpfs overlay
[    8.980000] procd: - early -
[    8.980000] procd: - watchdog -
...
[   19.650000] jffs2_scan_eraseblock(): End of filesystem marker found at 0x0
[   19.680000] jffs2_build_filesystem(): unlocking the mtd device... done.
[   19.680000] jffs2_build_filesystem(): erasing all blocks after the end marker...
[   21.870000] device eth0 entered promiscuous mode
...
  • 验证在挂载/dev/mtdblk3时由kernel做的格式化?:
root@OpenWrt:~# dd if=/dev/zero of=mtd.bin bs=$((1024*1024)) count=4
4+0 records in
4+0 records out
root@OpenWrt:~# echo -e "\xde\xad\xc0\xde" | dd of=mtd.bin bs=1 count=4 conv=notrunc
4+0 records in
4+0 records out
root@OpenWrt:~# hexdump -s $((0x0)) -n 4 -v -e '4/1 "%02x ""\n"' mtd.bin 
de ad c0 de
root@OpenWrt:~# mkdir -pv tmp
root@OpenWrt:~# mount -t jffs2 -o loop mtd.bin ./tmp
mount: mounting /dev/loop0 on ./tmp failed: Invalid argument

嗯,看起来用假的mtd设备没法进行验证啊,只好在wr802n v1上再做几次factory reset了。

首先说明一下在OpenWrt(Linux)系统里, mtk分区对应的设备名称为/dev/mtd?或者是/dev/mtdblock?,它们之间的区别是,你往/dev/mtd?里写数据,没有变化(为什么?)。而如果你往/dev/mtdblock?中写数据,写的是什么,/dev/mtdblock?中保存的数据就是什么。

请看下面的这个操作,往/dev/mtd?设备中写数据(在failsafe模式下操作):

root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
19 85 20 03 00 00 00 0c
root@(none):/# echo -e '\xde\xad\xc0\xde' | dd of=/dev/mtdblock3 bs=1 count=4 co
nv=notrunc
4+0 records in
4+0 records out
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
de ad c0 de 00 00 00 0c

再看看这个,往/dev/mtdblock?设备中写入数据(在failsafe模式下操作):

root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
19 85 20 03 00 00 00 0c
root@(none):/# echo -e '\xde\xad\xc0\xde' | dd of=/dev/mtdblock3 bs=1 count=4 co
nv=notrunc
4+0 records in
4+0 records out
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
de ad c0 de 00 00 00 0c

往/dev/mtdblock3中写入0xdeadc0de之后,再去挂载会怎样:

root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
de ad c0 de 00 00 00 0c
root@(none):/# mount -t jffs2 /dev/mtdblock3 /overlay/
[  624.440000] jffs2_scan_eraseblock(): End of filesystem marker found at 0x0
[  624.450000] jffs2_build_filesystem(): unlocking the mtd device... done.
[  624.460000] jffs2_build_filesystem(): erasing all blocks after the end marker... done.
[  660.390000] jffs2: notice: (372) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan) and 0 of xref (0 dead, 0 orphan) found.
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
19 85 20 03 00 00 00 0c

OpenWrt中说这叫trick (https://wiki.openwrt.org/doc/techref/filesystems), 由kernel来完成JFFS2文件系统的格式化。

Explanations 1

Both SquashFS and JFFS2 are compressed filesystems using LZMA for the compression. SquashFS is a read only filesystem while JFFS2 is a writable filesystem with journaling and wear leveling.

Our job when writing the firmware is to put as much common functionality on SquashFS while not wasting space with unwanted features. Additional features can always be installed onto JFFS2 by the user. The use of mini_fo/overlayfs means that the filesystem is presented as one large writable filesystem to the user with no visible boundary between SquashFS and JFFS2 – files are simply copied to JFFS2 when they‘re written.

It‘s not all without side effects however.

The fact that we pack things so tightly in flash means that if the firmware ever changes, the size and location of the JFFS2 partition also changes, potentially wiping out a large chunk of JFFS2 data and corrupting the filesystem. To deal with this, we‘ve implemented a policy that after each reflash the JFFS2 data is reformatted. The trick to doing that is a special value, 0xdeadc0de; when this value appears in a JFFS2 partition, everything from that point to the end of the partition is wiped. So, hidden at the end of the firmware images, is the value 0xdeadcode, positioned such that it becomes the start of the JFFS2 partition.

The fact that we use a combination of compressed and partially read only filesystems also has an interesting effect on package management:

In particular, you need to be careful what packages you update. While OPKG is more than happy to install an updated package on JFFS2, it‘s unable to remove the original package from SquashFS; the end result is that you slowly start using more and more space until the JFFS2 partition is filled. The opkg util really has no idea how much space is available on the JFFS2 partition since it’s compressed, and so it will blindly keep going until the opkg system crashes – at that point you have so little space you probably can‘t even use opkg to remove anything.

Explanation 2

On many embedded targets that use NOR flash for the root filesystem, OpenWrt implements a clever trick to get the most out of the limited flash memory capacity while retaining flexibility for the end-user:

Basically, during the image creation, all of the rootfs contents is packed up in a SquashFS filesystem – a highly efficient filesystem with compression support. There‘s one important detail about it though: it is a read-only filesystem. To overcome this limitation OpenWrt uses the remaining portion of the NOR rootfs partition to store an additional read/write jffs2 filesystem which is “overlayed” on top of the rootfs (that is, allowing to read unchanged files from the SquashFS but storing all the modifications made to the jffs2 part).

This design has another important advantage for the end-user: even when the read/write partition is in total mess, he can always boot to the failsafe mode (which mounts only the squashfs part) and proceed from there.

在挂载了/dev/mtdblock3后,做jffs2reset会怎样(只是去删除文件)

root@(none):/# touch /overlay/abc
root@(none):/# ls -l /overlay/
-rw-r--r--    1 root     root             0 Jan  1 00:24 abc
root@(none):/# jffs2reset -y
[ 1470.580000] jffs2reset: /dev/mtdblock3 is mounted as /overlay, only erasing files
root@(none):/# ls -l /overlay/
root@(none):/#

在没的挂载的情况下,又会怎样:

root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
18 85 00 02 00 00 00 0c
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtd3
18 85 00 02 00 00 00 0c
root@(none):/# umount /overlay/
root@(none):/# jffs2reset -y
[ 1561.330000] jffs2reset: /dev/mtdblock3 is not mounted
[ 1561.340000] jffs2reset: /dev/mtdblock3 will be erased on next mount
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtdblock3
de ad c0 de 00 00 00 0c
root@(none):/# hexdump -s 0x00 -n 8 -v -e '8/1 "%02x ""\n"' /dev/mtd3
de ad c0 de 00 00 00 0c
root@(none):/# 

没错,会在下次挂载时进行擦除,并创建文件系统。

 

相关的文档请看这里:

  1. https://wiki.openwrt.org/doc/techref/filesystems
  2. https://wiki.openwrt.org/doc/techref/flash
  3. https://wiki.openwrt.org/doc/techref/preinit_mount
  4. https://en.wikipedia.org/wiki/Flash_memory#NOR_flash
  5. https://en.wikipedia.org/wiki/Flash_memory#NAND_flash

《tl-wr802n v1: OpenWrt系统中的failsafe模式》有5个想法

  1. 最近安装openwrt镜像后台看到以下console提示:运行状态。
    Press the [f] key and hit [enter] to enter failsafe mode
    Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
    label buffer too small 256 > 63
    label buffer too small 256 > 63
    Press the [f] key and hit [enter] to enter failsafe mode
    Press the [1], [2], [3] or [4] key and hit [enter] to select the debug level
    label buffer too small 256 > 63
    label buffer too small 256 > 63

    望大神指教,叩谢!
    环境斐讯N1+docker+ kanshudj/n1-openwrtgateway:r9.10.1
    我目标是在局域网内搭建自己DNS 路由器,智能家庭助理HASS需要域名装换,发布外网访问内网。
    目前只能手工指定映射端口,很慢反。

  2. 然后又pull了dnscrypt-proxy:latest
    有点不理解他们的配置原理,https://blog.csdn.net/weixin_34126215/article/details/89636100
    虽然是小白但还是怀着一颗永远上进的胸怀,还忘大神辑文指导,叩谢!

    1. 不好意思,我没有这样的设备,帮不了你。当前我手头上只有网件的wndr3700 v4

发表评论

电子邮件地址不会被公开。 必填项已用*标注