为什么我的系统上没有 rootfs 文件系统?

Geo*_*lly 6 linux kernel mount root-filesystem

Linux内核文件要求:

Rootfs 是 ramfs(或 tmpfs,如果已启用)的一个特殊实例,它始终存在于 2.6 系统中。你不能卸载 rootfs ...

在我测试的所有 linux 系统上(内核 > 2.6 和 afaik 正常引导程序,例如 ubuntu 12.04),mount不显示rootfs条目。

但是,在使用外部存档启动时使用buildroot映像.cpio,它是存在的。

在什么情况下有rootfs条目mount

sou*_*edi 5

  1. 在旧系统上,mount可能不同意/proc/mounts
  2. 大多数情况下,您不会rootfs在 中看到/proc/mounts,但它仍处于安装状态。
  3. 我们能证明 rootfs 仍然挂载吗?

1. 在旧系统上,mount可能不同意/proc/mounts

man mount说:“程序mountumount传统上在文件中维护了当前挂载的文件系统列表/etc/mtab。”

旧方法实际上不适用于根文件系统。根文件系统可能已由内核挂载,而不是由mount. 因此/in 中的条目/etc/mtab可能非常人为设计,并且不一定与内核的当前挂载列表同步。

我还没有确定,但实际上我认为任何使用旧方案的系统都不会初始化mtab以显示带有rootfs. (理论上,是否mount显示rootfs取决于首先安装该mtab文件的软件)。

man mount 继续说:“仍然支持真正的 mtab 文件,但在当前的 Linux 系统上,最好将其设置为指向 /proc/mounts 的符号链接,因为在用户空间中维护的常规 mtab 文件不能可靠地与命名空间、容器和其他高级 Linux 一起使用特征。”

mtab 在 Debian 7 和 Ubuntu 15.04 中被转换为符号链接。

1.1 来源

Debian 报告#494001 - “debian-installer: /etc/mtab 必须是 /proc/mounts 与 linux >= 2.6.26 的符号链接”

#494001 在 sysvinit-2.88dsf-14 中得到解决。请参阅2011 年 12 月 14 日的结束消息。该更改包含在2013 年 5 月 4 日发布的Debian 7 "Wheezy" 中 。(它使用 sysvinit-2.88dsf-41)。

Ubuntu 将此更改推迟到sysvinit_2.88dsf-53.2ubuntu1。该更改日志页面显示更改输入“生动”,这是 Ubuntu 15.04 的代号

2. 大部分时间你看不到rootfsin /proc/mounts,但它仍然是mounted

从 Linux v4.17 开始,这个内核文档仍然是最新的。rootfs 始终存在,并且永远无法卸载。但大多数时候你在 /proc/mounts 中看不到它。

如果您启动到 initramfs shell,您可以看到 rootfs。如果您的 initramfs 是dracut,就像在 Fedora Linux 中一样,您可以通过将选项添加rd.break到内核​​命令行来实现。(例如在 GRUB 引导加载程序中)。

switch_root:/# grep rootfs /proc/mounts
rootfs / rootfs rw 0 0
Run Code Online (Sandbox Code Playgroud)

当 dracut 将系统切换到真正的根文件系统时,您将无法再在 /proc/mounts 中看到 rootfs。dracut 可以使用switch_rootsystemd来做到这一点。这两者都遵循相同的操作顺序,在链接的内核文档中建议。

在其他一些帖子中,人们可以在切换出 initramfs 后在 /proc/mounts 中看到 rootfs。例如在 Debian 7 上:“我如何才能找到“rootfs” ”。我认为这一定是因为内核在 Debian 7 中的内核版本和我当前的内核 v4.17 之间的某个时刻改变了它显示 /proc/mounts 的方式。从进一步的搜索,我认为根文件系统显示在Ubuntu 14.04,但与Ubuntu内核4.4.0-28,通用在Ubuntu 16.04中。

即使我不使用 initramfs,而是让内核挂载根文件系统,我也无法在 /proc/mounts 中看到 rootfs。这是有道理的,因为内核代码似乎也遵循相同的操作顺序。

隐藏 rootfs 的操作是chroot.

switch_root:/# cd /sysroot
switch_root:/sysroot# mount --bind /proc proc
switch_root:/sysroot# grep rootfs proc/mounts
rootfs / rootfs rw 0 0

switch_root:/sysroot# chroot .
sh-4.4# cat proc/mounts
/dev/sda3 / ext4 ro,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
Run Code Online (Sandbox Code Playgroud)

3.能证明rootfs还在挂载吗?

众所周知,chroot当您以特权用户身份运行时,可以逃避一个简单的问题。如果switch_root没有做多chroot,我们可以扭转,并再次看到根文件系统。

sh-4.4# python3
...
>>> import os
>>> os.system('mount --bind / /mnt')
>>> os.system('cat proc/mounts')
/dev/sda3 / ext4 ro,relatime 0 0
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
/dev/sda3 /mnt ext4 ro,relatime 0 0
>>> os.chroot('/mnt')
>>>
>>> # now the root, "/", is the old "/mnt"...
>>> # but the current directory, ".", is outside the root :-)
>>>
>>> os.system('cat proc/mounts')
/dev/sda3 / ext4 ro,relatime 0 0
>>> os.chdir('..')
>>> os.system('bash')
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
shell-init: error retrieving current directory: getcwd: cannot access parent directories: No such file or directory
bash-4.4# chroot .
sh-4.4# grep rootfs proc/mounts
rootfs / rootfs rw 0 0
Run Code Online (Sandbox Code Playgroud)

然而,switch_root这种技术不能逆转整个序列。完整的序列确实

  1. 将当前工作目录(如/proc/self/cwd)更改为新文件系统的挂载点:

    cd /newmount
    
    Run Code Online (Sandbox Code Playgroud)
  2. 移动新的文件系统,即更改其挂载点,使其直接位于根目录的顶部。

    mount --move . /
    
    Run Code Online (Sandbox Code Playgroud)
  3. 更改当前根目录(如/proc/self/root)以匹配当前工作目录。

    chroot .
    
    Run Code Online (Sandbox Code Playgroud)

在上面的 chroot 转义中,我们能够从ext4文件系统的根目录返回到rootfsusing ..,因为ext4文件系统挂载在rootfs. 当ext4文件系统挂载在rootfs的目录时,escape 方法不起作用。

我能够找到rootfs使用不同的方法。(至少一位重要的内核开发人员认为这是 Linux 中的一个错误)。

http://archive.today/2018.07.22-161140/https://lore.kernel.org/lkml/20141007133339.GH7996@ZenIV.linux.org.uk/

/* CURSED.c - DO NOT RUN THIS PROGRAM INSIDE YOUR MAIN MOUNT NAMESPACE */

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>     /* open() */
#include <sys/mount.h>
#include <sched.h>     /* setns() */
#include <sys/statfs.h>

int main() {
        int fd = open("/proc/self/ns/mnt", O_RDONLY);

        /* "umount -l /" - lazy unmount everything we can see */
        umount2("/", MNT_DETACH);

        /* reset root, by re-entering our mount namespace */
        setns(fd, CLONE_NEWNS);

        /* "stat -f /" - inspect the root */
        struct statfs fs;
        statfs("/", &fs);
}
Run Code Online (Sandbox Code Playgroud)

在 Linux 4.17.3-200.fc28.x86_64 上测试:

$ make CURSED
cc CURSED.c -o CURSED
$ sudo unshare -m strace ./CURSED
...
openat(AT_FDCWD, "/proc/self/ns/mnt", O_RDONLY) = 3
umount2("/", MNT_DETACH)                = 0
setns(3, CLONE_NEWNS)                   = 0
statfs("/", {f_type=RAMFS_MAGIC, f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={val=[0, 0]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID}) = 0
                    ^
                    ^ result: rootfs uses ramfs code on this system
Run Code Online (Sandbox Code Playgroud)

(我还确认该文件系统按预期是空的,并且是可写的)。