为什么挂载不尊重绑定挂载的只读选项?

Str*_*Bad 38 linux mount readonly bind-mount

在我的 Arch Linux 系统(Linux Kernel 3.14.2)上,绑定挂载不遵守只读选项

# mkdir test
# mount --bind -o ro test/ /mnt
# touch /mnt/foo
Run Code Online (Sandbox Code Playgroud)

创建文件/mnt/foo。相关条目/proc/mounts

/dev/sda2 /mnt ext4 rw,noatime,data=ordered 0 0
Run Code Online (Sandbox Code Playgroud)

安装选项不符合我要求的选择,但这样做同时匹配绑定的读/写性能和安装使用的选项最初安装/dev/sda2/

/dev/sda2 / ext4 rw,noatime,data=ordered 0 0
Run Code Online (Sandbox Code Playgroud)

但是,如果我重新安装挂载,则它尊重只读选项

# mount --bind -o remount,ro test/ /mnt
# touch /mnt/bar
touch: cannot touch ‘/mnt/bar’: Read-only file system
Run Code Online (Sandbox Code Playgroud)

和相关条目 /proc/mounts/

/dev/sda2 /mnt ext4 ro,relatime,data=ordered 0 0
Run Code Online (Sandbox Code Playgroud)

看起来像我所期望的(尽管实际上我希望看​​到test目录的完整路径)。on/proc/mounts/的原始挂载入口也没有变化,保持读/写/dev/sda2//

/dev/sda2 / ext4 rw,noatime,data=ordered 0 0
Run Code Online (Sandbox Code Playgroud)

这种行为和解决方法至少从2008 年就已为人所知,并记录在mount

请注意,文件系统挂载选项将与原始挂载点上的相同,并且无法通过将 -o 选项与 --bind/--rbind 一起传递来更改。可以通过单独的重新安装命令更改安装选项

并非所有发行版的行为都相同。Arch 似乎默默地不尊重这些选项,而当绑定挂载未以只读方式挂载时,Debian 会生成警告

mount: warning: /mnt seems to be mounted read-write.
Run Code Online (Sandbox Code Playgroud)

有报道称这种行为在 Debian Lenny 和 Squeeze 中被“修复”,尽管它似乎不是一个通用的修复,也不能在 Debian Wheezy 中工作。使绑定安装尊重初始安装上的只读选项有什么困难?

V13*_*V13 25

绑定坐骑只是……嗯……绑定坐骑。即它不是一个新的坐骑。它只是“链接”/“公开”/“考虑”一个子目录作为新的挂载点。因此它不能改变挂载参数。这就是您收到投诉的原因:

# mount /mnt/1/lala /mnt/2 -o bind,ro
mount: warning: /mnt/2 seems to be mounted read-write.
Run Code Online (Sandbox Code Playgroud)

但是正如您所说,正常的绑定安装有效:

# mount /mnt/1/lala /mnt/2 -o bind
Run Code Online (Sandbox Code Playgroud)

然后 ro 重新安装也有效:

# mount /mnt/1/lala /mnt/2 -o bind,remount,ro 
Run Code Online (Sandbox Code Playgroud)

但是,发生的情况是您正在更改整个安装,而不仅仅是这个绑定安装。如果您查看 /proc/mounts,您会看到绑定安装和原始安装都更改为只读:

/dev/loop0 /mnt/1 ext2 ro,relatime,errors=continue,user_xattr,acl 0 0
/dev/loop0 /mnt/2 ext2 ro,relatime,errors=continue,user_xattr,acl 0 0
Run Code Online (Sandbox Code Playgroud)

因此,您所做的就像将初始挂载更改为只读挂载,然后进行绑定挂载,这当然是只读的。

更新 2016-07-20:

以下内容适用于 4.5 内核,但不适用于 4.3 内核(这是错误的。请参阅下面的更新 #2):

内核有两个控制只读的标志:

  • MS_READONLY:指示是否安装为只读
  • MNT_READONLY:表示“用户”是否需要它只读

在 4.5 内核上,执行 amount -o bind,ro实际上可以解决问题。例如,这个:

# mkdir /tmp/test
# mkdir /tmp/test/a /tmp/test/b
# mount -t tmpfs none /tmp/test/a
# mkdir /tmp/test/a/d
# mount -o bind,ro /tmp/test/a/d /tmp/test/b
Run Code Online (Sandbox Code Playgroud)

将创建一个只读绑定安装/tmp/test/a/dto /tmp/test/b,它将在以下位置可见/proc/mounts

none /tmp/test/a tmpfs rw,relatime 0 0
none /tmp/test/b tmpfs ro,relatime 0 0
Run Code Online (Sandbox Code Playgroud)

在 中可以看到更详细的视图/proc/self/mountinfo,它考虑了用户视图(命名空间)。相关行将是这些:

363 74 0:49 / /tmp/test/a rw,relatime shared:273 - tmpfs none rw
368 74 0:49 /d /tmp/test/b ro,relatime shared:273 - tmpfs none rw
Run Code Online (Sandbox Code Playgroud)

在第二行的位置,您可以看到它同时表示ro( MNT_READONLY) 和rw( !MS_READONLY)。

最终结果是这样的:

# echo a > /tmp/test/a/d/f
# echo a > /tmp/test/b/f
-su: /tmp/test/b/f: Read-only file system
Run Code Online (Sandbox Code Playgroud)

更新 2016-07-20 #2:

进一步深入研究表明,行为实际上取决于 libmount 的版本,它是 util-linux 的一部分。此提交添加了对此的支持,并随 2.27 版一起发布:

提交 9ac77b8a78452eab0612523d27fee52159f5016a
添加一名作者 
日期:2015 年 8 月 17 日星期一 11:54:26 +0200

    libmount:添加对“bind,ro”的支持

    现在没有必要使用两个 mount(8) 调用来创建一个只读的
    山:

      挂载 /foo /bar -o 绑定
      挂载 /bar -o 重新挂载,ro,bind

    此补丁允许指定“bind,ro”并完成重新安装
    通过额外的 mount(2) 系统调用由 libmount 自动执行。它不是
    当然是原子的。

    签字人:Karel Zak 

这也提供了解决方法。在较旧和较新的安装上使用 strace 可以看到该行为:

老的:

mount("/tmp/test/a/d", "/tmp/test/b", 0x222e240, MS_MGC_VAL|MS_RDONLY|MS_BIND, NULL) = 0 <0.000681>
Run Code Online (Sandbox Code Playgroud)

新的:

mount("/tmp/test/a/d", "/tmp/test/b", 0x1a8ee90, MS_MGC_VAL|MS_RDONLY|MS_BIND, NULL) = 0 <0.011492>
mount("none", "/tmp/test/b", NULL, MS_RDONLY|MS_REMOUNT|MS_BIND, NULL) = 0 <0.006281>
Run Code Online (Sandbox Code Playgroud)

结论:

为了达到预期的结果,需要运行两个命令(正如@Thomas 已经说过的):

mount SRC DST -o bind
mount DST -o remount,ro,bind
Run Code Online (Sandbox Code Playgroud)

较新版本的 mount (util-linux >=2.27) 在运行时会自动执行此操作

mount SRC DST -o bind,ro
Run Code Online (Sandbox Code Playgroud)

  • `mount --bind /tmp/ /mnt/tmp/; mount -o remount,bind,ro /mnt/tmp/` ... 然后`touch /tmp/a` 没问题,但是`touch /mnt/tmp/b` 给出`touch: cannot touch '/mnt/tmp/ b':只读文件系统`。这适用于 Debian 3.13 和 kernel.org 3.14.2。所以它不只是改变整个坐骑。至少不是最近的内核。 (7认同)
  • 是的,但不是。IIRC 内核支持不同的*挂载点*(不是文件系统)有不同的选项。[Debian 曾经有一个补丁使 `mount -o bind,ro` 创建一个读写文件系统的只读视图](http://unix.stackexchange.com/questions/11122/make-all-files -under-a-directory-read-only-without-changes-permissions/11123#11123)(但它似乎不再存在于喘息中)。 (3认同)

Tho*_*mas 9

正确的解决方案实际上是将其安装两次。在命令行上:

mount -t none -o bind /source/dir /destination/dir
mount -t none -o bind,remount,ro /source/dir /destination/dir
Run Code Online (Sandbox Code Playgroud)

/etc/fstab

/source/dir            /destination/dir    none  bind            0 0
/source/dir            /destination/dir    none  remount,bind,ro 0 0
Run Code Online (Sandbox Code Playgroud)

手册 ( man mount) 是这样说的:

   The bind mounts.
          Since Linux 2.4.0 it is possible to remount part of the file hierarchy somewhere else. The call is
                 mount --bind olddir newdir
   [...]
          Note that the filesystem mount options will remain the same as those on the original mount point, and cannot be changed  by  passing  the  -o  option
          along with --bind/--rbind. The mount options can be changed by a separate remount command, for example:
          .
                 mount --bind olddir newdir
                 mount -o remount,ro newdir
          .
          Note  that  behavior  of  the remount operation depends on the /etc/mtab file. The first command stores the 'bind' flag to the /etc/mtab file and the
          second command reads the flag from the file.  If you have a system without the /etc/mtab file or if you explicitly define source and target  for  the
          remount command (then mount(8) does not read /etc/mtab), then you have to use bind flag (or option) for the remount command too. For example:
          .
                 mount --bind olddir newdir
                 mount -o remount,ro,bind olddir newdir
Run Code Online (Sandbox Code Playgroud)