Linux

Linux:如何為分層綁定掛載保留只讀模式

  • February 11, 2022

讓我們為這個問題提供一些背景資訊:

  1. /foo/bar目錄處於讀寫模式
  2. /bar綁定掛載指向/foo/bar
  3. /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/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成為只讀的正確方法是什麼?

aviro的回答完全涵蓋了解釋部分: 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)

TL;博士

預設情況下,/bar/foo/bar共享掛載。這意味著在您將某些內容綁定到文件夾中後/foo/bar,它會傳播到/bar,而且您將某些內容安裝到文件夾中時/bar,它也會傳播回/foo/bar

從手冊頁mount(8)

…共享掛載提供了創建該掛載的鏡像的能力,以便任何鏡像中的掛載和解除安裝傳播到另一個鏡像。…

由於您綁定 mount /bar/bazon /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

由於您現在從 original 掛載/foo/bar,因此它不會被傳播回那裡。

選項 #2

你需要做/bar一個奴隸/foo/bar

同樣,從手冊頁mount(8)

…從掛載接收來自其主掛載的傳播,但反之則不然。…

所以不要執行:

mount -o bind /foo/bar /bar

跑:

mount --make-rslave -o bind /foo/bar /bar

這是唯一的區別。然後您可以將 mount 綁定/foo/bar/baz為只讀(此時/bar/baz仍然是讀/寫),然後將 mount 綁定/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. 安裝 ID 146/foo/bar是安裝在/bar.
  2. 它的父 ID 是65,即根分區 ( /)。

另外,請注意/foo/bar安裝/barshared

(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/baz2)ro

2. 146 /foo/bar安裝上/bar作為**(1)**的結果。

  • 結果, 183/foo/bar/baz再次安裝在頂部/bar/baz( **2)**上。rw

重要的注意事項是掛載選項(例如,ro)不會與掛載一起傳播。/foo/bar/baz被傳播到/bar/baz,但沒有ro選項。

所以 now/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( 2)rw
      • 200 上面的/foo/bar/baz安裝是由於**(3)**。/bar/baz``ro

2. 202 /foo/bar/baz是原來的文件夾/

  • 結果182/foo/bar/baz安裝在頂部/foo/bar/baz( 2)ro
    • 由於**(3)的結果,203/foo/bar/baz重新**安裝。/foo/bar/baz``rw

現在/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大師/bar您可以從/foo/barto傳播 mounts /bar,但 mounts on/bar不會被傳播回 to /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,因此由於( 2)/foo/bar/baz仍被掛載。/foo/bar/baz``ro

所以 now在和/foo/bar/baz上都以只讀方式掛載。/foo/bar/baz``/bar/baz

引用自:https://unix.stackexchange.com/questions/689878