为什么如果挂载点的 inode 发生变化,Linux 绑定挂载就会消失?

为什么如果挂载点的 inode 发生变化,Linux 绑定挂载就会消失?

总结一下:如果您在新的挂载命名空间之上绑定挂载了一个文件,但随后父命名空间中的 inode 发生了/tmp/a变化,则该绑定挂载会在子命名空间中消失。我试图理解为什么。/tmp/b/tmp/b

mount(8) 不会公开绑定挂载单个文件(仅目录)的功能,因此重现此操作需要一个额外的可执行文件来发出必要的 mount(2) 系统调用。下面是一个简单的例子(参考如下bmount):

#include <sys/mount.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    if (argc != 3) {
        printf("requires exactly 2 args\n");
        return 1;
    }

    int err = mount(argv[1], argv[2], "none", MS_BIND, NULL);
    if (err == 0) {
        return 0;
    } else {
        printf("mount error (%d): %s\n", errno, strerror(errno));
        return 1;
    }
}

设置测试用例:

# echo a > /tmp/a; echo b > /tmp/b; echo c > /tmp/c;
# ls -ldi /tmp/a /tmp/b /tmp/c
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a                                                               
11403422 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/c

现在,在一个单独的 shell 中:

# unshare -m /bin/bash
# bmount /tmp/a /tmp/b
# ls -ldi /tmp/a /tmp/b /tmp/c
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/c
# cat /tmp/b
a
# grep "\/tmp\/" /proc/self/mounts
[redacted] /tmp/b ext4 rw,relatime,errors=remount-ro,data=ordered 0 0

在原始外壳中:

# mv /tmp/c /tmp/b
# ls -ldi /tmp/a /tmp/b /tmp/c
ls: cannot access '/tmp/c': No such file or directory                                                               
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a                                                               
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b

unshare外壳中:

# ls -ldi /tmp/a /tmp/b /tmp/c
ls: cannot access '/tmp/c': No such file or directory
11403315 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/a
11403452 -rw-r--r-- 1 root root 2 Jan 19 13:34 /tmp/b
# cat /tmp/b
c
# grep "\/tmp\/" /proc/self/mounts
#

绑定挂载已悄然消失,底层文件系统的/tmp/b文件现在在命名空间内可见。

我找到了一个lwn.net 文章这里描述了语义上的变化:在 2013 年之前,挂载点上mv的命令将失败并显示,但行为已更改,以便它会成功,然后挂载将被删除。相关的内核提交似乎是rename(2)EBUSY8ed936b5671

我的问题是:

  1. 为什么在任何 inode 更改时都会删除挂载?它是否只是挂载系统的一个实现细节,其中挂载点由 dentry 而不是简单的路径来标识?
  2. 有没有一种方法可以使绑定安装变得不那么“脆弱”,因为它们不能被名称空间之外的文件系统操作覆盖或删除?

与实践相关的一种情况是ip-netns(8);ip netns exec通过绑定安装/etc/netns/${NAMESPACE}/resolv.conf/etc/resolv.conf.如果/etc/resolv.confresolvconf(8) 或 systemd-resolved 更改了inode ,则更新后的内容/etc/resolv.conf对于命名空间内运行的进程将是可见的。

答案1

这就是安装传播。 Linux 默认情况下不启用它,但 systemd 会启用它。如果您不希望安装和卸载传播到新的命名空间,您可以mount --make-rprivate /在其中运行。。旁白:这不是挂载传播。

为什么在任何 inode 更改时都会删除挂载?它是否只是挂载系统的一个实现细节,其中挂载点由 dentry 而不是简单的路径来标识?

我想说,你可以期望rm b; mv c b和之间唯一的不同mv c b是,不可能b在任何时候观察到不存在。我将其描述为一个经过精心设计或维护的功能...我不确定历史上的多用户 Unix 系统在多大程度上是这样,但它确实被依赖于例如支持软件更新在正在运行的系统上。

我……能想到正是为您所谓的“inode 更改”而实现的另一项特定功能- 这是不情愿地完成的,并且是特定于文件系统的。

相关内容