使用 CLONE_NEWNS 标志克隆后挂载文件系统

Mount filesystem after clone with CLONE_NEWNS flag

我正在尝试实现以下场景:

  1. clone() 带有 CLONE_NEWNS 标志的主进程(这意味着新的挂载命名空间)
  2. mount() 子进程中的新文件系统
  3. 子进程已完成,所有在此进程中创建的文件系统都已卸载

但它没有像我预期的那样工作,我仍然在主进程中看到已挂载的文件系统。我做错了什么?

来源在这里https://github.com/dmitrievanthony/sprat/blob/master/src/container.c#L47

系统默认为 AWS Ubuntu、

ubuntu@ip-172-31-31-112:~/sprat$ uname -a
Linux ip-172-31-31-112 4.4.0-53-generic #74-Ubuntu SMP Fri Dec 2 15:59:10 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

简答: mount propagation 的类型似乎设置不正确。


说明

Linux 内核默认所有挂载为 MS_PRIVATE,但 systemd overrides this during early boot to MS_SHARED 为了 nspawn 的方便。 这可以通过查看 /proc/$PID/mountinfo 可选字段 来观察。 例如,可能会出现这样的情况:

$ cat /proc/self/mountinfo
  . . .
25 0 8:6 / / rw,relatime shared:1 - ext4 /dev/sda6 rw,errors=remount-ro,data=ordered
                         ^^^^^^
  . . .

注意上面带下划线的shared:1字段,表示/挂载点的当前传播类型是MS_SHARED对等组 ID 是 1(在我们的例子中我们根本不关心对等组 ID)。

当在 clone(2) 上使用 CLONE_NEWNS 标志时,会创建一个新的挂载命名空间,它被初始化为调用者挂载命名空间的副本。 新命名空间的新复制挂载点与调用方挂载命名空间中各自的原始挂载点加入同一个对等组。

父传播类型为MS_SHARED的新挂载点的传播类型也是MS_SHARED。因此,当您的 "contained" 处理 mount() 循环设备上的文件系统时,默认情况下会挂载 MS_SHARED。后来,它下面的所有挂载也被传播到 "main" 进程的命名空间,这就是 "main" 进程可以看到它们的原因。

为了满足您的要求(为了让 "main" 进程看不到 "contained" 进程的挂载点),您寻求的挂载传播类型是 MS_SLAVEMS_PRIVATE,具体取决于您是否希望 "contained" 进程的根挂载点分别接收来自其他对等方的传播事件。 显然,MS_PRIVATEMS_SLAVE 提供更好的隔离。

因此,在您的情况下,将 "contained" 进程的根安装点的传播类型更改为 MS_PRIVATEMS_SLAVE before 你挂载了其余的文件系统,所以挂载不会传播到 "main" 进程的命名空间。


密码

起初,当 "contained" 进程创建其根挂载点时,人们会尝试正确设置传播类型。

但是,我在 man 8 mount(引用)中注意到以下内容:

Note that the Linux kernel does not allow to change multiple propagation flags with a single mount(2) system call, and the flags cannot be mixed with other mount options.

Since util-linux 2.23 the mount command allows to use several propagation flags together and also together with other mount operations. This feature is EXPERIMENTAL. The propagation flags are applied by additional mount(2) system calls when the preceding mount operations were successful.

查看您的代码,"contained" 进程,在它 mount() 循环设备上的文件系统之后,它向它发出 chroot()。此时,您可以通过注入此 mount(2) 调用来设置其传播类型:

if (chroot(".") < 0) {
    // handle error
}

if (mount("/", "/", c->fstype, MS_PRIVATE, "") < 0) {
    // handle error
}

if (mkdir(...)) {
    // handle error
}

现在传播类型设置为 MS_PRIVATE,"contained" 进程在 / 下进行的所有后续挂载都不会被传播,因此在"main" 进程的命名空间,如您在 /proc/mounts/proc/$PID/mountinfo.

中所见

资源