Linux:如何保留分层绑定安装的只读模式

Linux:如何保留分层绑定安装的只读模式

让我们给出这个问题的一些背景:

  1. /foo/bar目录处于读写模式
  2. 有一个/bar绑定挂载点指向/foo/bar
  3. 其中/foo/barbar必须处于只读模式的目录(/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/bazand/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)

真正让我困惑的是:

  1. 为什么现在有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
    

    但它不是最后一张,只读的在中间。

  2. 为什么现在有 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

  1. 安装 ID146,是/foo/bar安装在 上的/bar
  2. 它的父 ID 是65,这是根分区 ( /)。

另外,请注意/foo/bar安装在/baras共享

(2)mount -o bind,ro /foo/bar/baz /foo/bar/baz

  1. 这将创建从 /foo/bar/baz 到 /foo/bar/baz 的只读绑定挂载。
  2. 它还传播到/bar这意味着/foo/bar/baz也安装在 上/bar/baz
  3. 但正如我所说,该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

  1. 这将创建从 /bar/baz 到 /bar/baz 的只读绑定安装。
  2. 它还传播到/foo/bar这意味着/bar/baz也安装在 上/foo/bar/baz
  3. 但同样,该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/bazro由于(3)

2. 第202章 /foo/bar/baz是 上的原始文件夹/

  • 182 /foo/bar/baz安装在/foo/bar/baz顶部ro顶部(2)
    • 203 /foo/bar/baz已安装再次作为/foo/bar/bazrw(3)

现在/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/barto传播/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/bazasro仍然(2)

所以现在在和/foo/bar/baz上都以只读方式安装。/foo/bar/baz/bar/baz

答案2

解释部分完美地涵盖在阿维罗的回答:https://unix.stackexchange.com/a/689950/513617

我找到了一个很好的解决方案,不需要添加任何额外的读写安装。这是remount一个选择。

  1. mount -o bind /foo/bar /bar
  2. mount -o bind,ro /foo/bar/baz /foo/bar/baz
  3. 然后采用现有的读写传播/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)

相关内容