让我们给出这个问题的一些背景:
- 有
/foo/bar
目录处于读写模式 - 有一个
/bar
绑定挂载点指向/foo/bar
- 其中
/foo/bar
有bar
必须处于只读模式的目录(/foo/bar/baz
和/bar/baz
)
为了使其/foo/bar/baz
成为只读,我做了另一个绑定:
$ sudo mount -o bind,ro /foo/bar/baz /foo/bar/baz
$ sudo touch /foo/bar/baz/test
touch: cannot touch '/foo/bar/baz/test': Read-only file system
$ mount | grep bar
/dev/vda1 on /bar type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /foo/bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
PS 有/foo/bar/baz
and/bar/baz
但后者不是只读的。
但/bar/baz
可写:
$ sudo touch /bar/baz/test
$ echo $?
0
尝试进行另一个绑定:
$ sudo mount -o bind,ro /bar/baz /bar/baz
$ mount | grep bar
/dev/vda1 on /bar type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /foo/bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /foo/bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /foo/bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
真正让我困惑的是:
为什么现在有3个相同的坐骑
/bar/baz
?没有,一次绑定后我得到了三个:/dev/vda1 on /bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/vda1 on /bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/vda1 on /bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
两种是可读写的,一种是只读的。哪一个优先?似乎是只读的:
$ sudo touch /bar/baz/test touch: cannot touch '/bar/baz/test': Read-only file system
但它不是最后一张,只读的在中间。
为什么现在有 3 个挂载点
/foo/bar/baz
,以前只有只读挂载点,现在我有 3 个:/dev/vda1 on /foo/bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/vda1 on /foo/bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota) /dev/vda1 on /foo/bar/baz type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
一个只读,两个读写。什么优先?结果不是只读的,因为它是可写的:
$ sudo touch /foo/bar/baz/test $ echo $? 0
为什么会发生这种情况?以及如何避免呢?同时使两者成为只读的/foo/bar/baz
正确方法是什么?/bar/baz
答案1
长话短说
默认情况下,/bar
并且/foo/bar
是共享坐骑。这意味着在您将某些内容绑定到 中的文件夹上后/foo/bar
,它会传播到/bar
,但是还当您在 中的文件夹中安装某些内容时/bar
,它将被传播回/foo/bar
.
从手册页mount(8)
:
...共享安装提供了创建该安装的镜像的能力,以便任何镜像中的安装和卸载都会传播到另一个镜像。 ...
由于您将 mount 绑定/bar/baz
到/bar/baz
,因此它会传播回/foo/bar/baz
(as rw
)。
有两种选择:
选项1
而不是运行:
mount -o bind,ro /bar/baz /bar/baz
跑步:
mount -o bind,ro /foo/bar/baz /bar/baz
由于您现在从原始位置安装/foo/bar
,因此它不会传播回那里。
选项#2
你需要做/bar
一个奴隶的/foo/bar
。
再次,来自以下手册页mount(8)
:
...从挂载接收来自其主挂载的传播,但反之则不然。 ...
所以不要运行:
mount -o bind /foo/bar /bar
跑步:
mount --make-rslave -o bind /foo/bar /bar
这是唯一的区别。然后您可以将挂载绑定/foo/bar/baz
为只读(此时/bar/baz
仍然是读/写),然后将挂载绑定/bar/baz
为只读,而不会干扰您之前在 上创建的只读绑定挂载/foo/bar/baz
。
解释问题
如果我们重新创建您的步骤,我们将看到以下内容:
(1)mount -o bind /foo/bar /bar
您可以在 中看到结果/proc/self/mountinfo
。
为了了解不同的领域,您需要阅读以下手册页proc(5)
:
/proc/[pid]/mountinfo (since Linux 2.6.26)
This file contains information about mount points. It contains lines of the form:
36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
(1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11)
The numbers in parentheses are labels for the descriptions below:
(1) mount ID: unique identifier of the mount (may be reused after umount(2)).
(2) parent ID: ID of parent mount (or of self for the top of the mount tree).
现在让我们检查一下/proc/self/mountinfo
:
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
# grep ^65 /proc/self/mountinfo
65 0 8:1 / / rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
- 安装 ID146,是
/foo/bar
安装在 上的/bar
。 - 它的父 ID 是65,这是根分区 (
/
)。
另外,请注意/foo/bar
安装在/bar
as共享。
(2)mount -o bind,ro /foo/bar/baz /foo/bar/baz
- 这将创建从 /foo/bar/baz 到 /foo/bar/baz 的只读绑定挂载。
- 它还传播到
/bar
这意味着/foo/bar/baz
也安装在 上/bar/baz
。 - 但正如我所说,该
ro
选项没有传播,所以/bar/baz
仍然是rw
。
您可以在以下位置看到它/proc/self/mountinfo
:
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
182 65 8:1 /foo/bar/baz /foo/bar/baz ro,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
183 146 8:1 /foo/bar/baz /bar/baz rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
1. 182 /foo/bar/baz
安装在/foo/bar/baz
顶部ro
顶部(2)
2. 146 /foo/bar
安装在/bar
作为结果(1)。
- 183
/foo/bar/baz
/bar/baz
然后再次安装在as之上rw
,(2)。
需要注意的是,挂载选项(例如ro
)不会随挂载一起传播。/foo/bar/baz
被传播到/bar/baz
,但没有ro
选项。
因此,现在/foo/bar/baz
安装一次/foo/bar/baz
(作为只读),一次安装/bar/baz
(作为读/写,作为传播的结果/foo/bar
)。
(3)mount -o bind,ro /bar/baz /bar/baz
- 这将创建从 /bar/baz 到 /bar/baz 的只读绑定安装。
- 它还传播到
/foo/bar
这意味着/bar/baz
也安装在 上/foo/bar/baz
。 - 但同样,该
ro
选项不会传播,因此/foo/bar/baz
现在是读写的。
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
182 202 8:1 /foo/bar/baz /foo/bar/baz ro,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
183 201 8:1 /foo/bar/baz /bar/baz rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
200 183 8:1 /foo/bar/baz /bar/baz ro,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
203 182 8:1 /foo/bar/baz /foo/bar/baz rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
202 65 8:1 /foo/bar/baz /foo/bar/baz rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
201 146 8:1 /foo/bar/baz /bar/baz rw,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
1. 146 /foo/bar
安装在/bar
作为结果(1)。
- 201
/foo/bar/baz
安装在/bar/baz
.- 183
/foo/bar/baz
/bar/baz
然后再次安装在as之上rw
,(2)- 200
/foo/bar/baz
已安装的再次在上面/bar/baz
是ro
由于(3)。
- 200
- 183
2. 第202章 /foo/bar/baz
是 上的原始文件夹/
。
- 182
/foo/bar/baz
安装在/foo/bar/baz
顶部ro
顶部(2)- 203
/foo/bar/baz
已安装再次作为/foo/bar/baz
rw
(3)。
- 203
现在/foo/bar/baz
安装一次/bar/baz
(作为只读),一次安装/foo/bar/baz
(作为读/写)。
解决方案
(1)mount --make-rslave -o bind /foo/bar /bar
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
现在/foo/bar
是掌握of /bar
:您可以将挂载从/foo/bar
to传播/bar
,但 on 上的挂载/bar
不会传播回/foo/bar
。
(2)mount -o bind,ro /foo/bar/baz /foo/bar/baz
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
182 65 8:1 /foo/bar/baz /foo/bar/baz ro,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
183 146 8:1 /foo/bar/baz /bar/baz rw,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
和我们之前看到的类似,只不过现在/foo/bar
是 的主人/bar
,而且/foo/bar/baz
是 的主人/bar/baz
。
(3)mount -o bind,ro /bar/baz /bar/baz
# grep bar /proc/self/mountinfo
146 65 8:1 /foo/bar /bar rw,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
182 65 8:1 /foo/bar/baz /foo/bar/baz ro,relatime shared:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
183 146 8:1 /foo/bar/baz /bar/baz rw,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
200 183 8:1 /foo/bar/baz /bar/baz ro,relatime master:1 - xfs /dev/sda1 rw,attr2,inode64,logbsize=256k,sunit=512,swidth=512,noquota
这是主要区别:现在只创建了新的坐骑,数量200。
其余的没有改变。此安装没有传播回/foo/bar
,因此/foo/bar/baz
仍然安装在/foo/bar/baz
asro
仍然(2)。
所以现在在和/foo/bar/baz
上都以只读方式安装。/foo/bar/baz
/bar/baz
答案2
解释部分完美地涵盖在阿维罗的回答:https://unix.stackexchange.com/a/689950/513617
我找到了一个很好的解决方案,不需要添加任何额外的读写安装。这是remount
一个选择。
mount -o bind /foo/bar /bar
mount -o bind,ro /foo/bar/baz /foo/bar/baz
- 然后采用现有的读写传播
/bar/baz
绑定remount
:
mount -o bind,ro,remount /foo/bar/baz /bar/baz
之后你只会得到这些:
$ mount | grep bar
/dev/vda1 on /bar type xfs (rw,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /foo/bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)
/dev/vda1 on /bar/baz type xfs (ro,relatime,seclabel,attr2,inode64,logbufs=8,logbsize=32k,noquota)