1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 本文目标
简析 rootfs 挂载的流程,分析 ext4 rootfs 文件系统会被挂载成【只读】模式的几个简单场景。
3. 分析背景
本文分析基于 linux-4.14.132
上游代码。
运行环境为:Ubuntu 16.04.4 LTS + QEMU arm vexpress-a9
rootfs 基于 ubuntu-base-16.04-core-armhf.tar.gz
制作。
4. 问题场景分析
编译内核,基于 ubuntu-base-16.04-core-armhf.tar.gz
制作 ext4 格式的 rootfs,然后通过 QEMU 模拟 arm vexpress-a9 板型启动系统。登录系统后,发现无法创建任何文件,通过 mount 命令检查,发现根目录以只读(RO)方式挂载,这就难怪了。
那到底是什么原因,导致根目录以只读方式挂载呢?最常见的一种情形是,我们在内核命令行指定了 ro
参数。检查内核命令行参数,如下:
Kernel command line: console=ttyAMA0 root=/dev/mmcblk0 rw rootfstype=ext4 init=/linuxrc
显然,我们告诉内核要以 rw
方式挂载根文件系统,也即用户根目录应该是可读写的,但事实并非如此。继续查找内核日志,发现如下提示信息:
EXT4-fs (mmcblk0): Filesystem with huge files cannot be mounted RDWR without CONFIG_LBDAF
这个信息告诉我们,打开了 ext4 的大文件(2TB)支持特性时,如果没有开启内核配置项 CONFIG_LBDAF
,则无法以【可读写】方式挂载 ext4 文件系统。挂载ext4 rootfs时,触发上述内核日志的代码简要流程如下:
prepare_namespace()
mount_root()
mount_block_root("/dev/root", root_mountflags)
do_mount_root(name, p, flags, root_mount_data)
sys_mount(name, "/root", fs, flags, data)
...
ext4_mount(type, flags, name, data)
mount_bdev(..., ext4_fill_super)
s = sget(fs_type, ...)
ext4_fill_super(s, ...)
if (silent && ext4_feature_set_ok(sb, sb_rdonly(sb)))
/* 开启了 huge file 特性的 ext4 文件系统,要进行可读写挂载, 必须开启 CONFIG_LBDAF */
if (ext4_has_feature_huge_file(sb)) {
if (sizeof(blkcnt_t) < sizeof(u64)) {
ext4_msg(sb, KERN_ERR, "Filesystem with huge files "
"cannot be mounted RDWR without "
"CONFIG_LBDAF");
return 0;
}
}
至此,我们找到了为什么在内核命令行指定 rw
参数后,ext4 rootfs 仍然会被挂载为【只读】模式的原因。修正的办法很简单,开启配置项 CONFIG_LBDAF
,然后重新编译内核并运行。再次运行新编译的内核,我们看到,根文件系统已经按照我们的意愿,以【可读写】模式挂载;登录系统后,也能够正常创建文件了。
EXT4-fs (mmcblk0): mounted filesystem with ordered data mode. Opts: (null)
VFS: Mounted root (ext4 filesystem) on device 179:0.
$ mount
/dev/mmcblk0 on / type ext4 (rw,relatime,data=ordered)
$ mkdir test
$ ls
test
当然,开启内核 CONFIG_LBDAF
配置项,并非上述问题的唯一修正方法,我们也可以在用 mkfs
制作根文件系统时,用 -O ^huge_file
选项,关闭 ext4 fs对huge file的支持;或者在制作好 rootfs 后,通过 tune2fs -O ^huge_file
对 rootfs 进行调整。
5. 后记
对我们问题场景的更多细节感兴趣的童鞋,可以参考社区ext4 huge file内核补丁。
这是一个相关链接:LWN