什麼是綁定掛載?
什麼是“綁定掛載”?我怎麼做一個?到底有什麼好處呢?
我被告知要使用綁定掛載來做某事,但我不明白它是什麼或如何使用它。
什麼是綁定掛載?
綁定掛載是目錄樹的替代視圖。傳統上,掛載將儲存設備的視圖創建為目錄樹。綁定掛載取而代之的是採用現有的目錄樹並將其複製到不同的點。綁定掛載中的目錄和文件與原始相同。一側的任何修改都會立即反映在另一側,因為兩個視圖顯示相同的數據。
例如,在發出 Linux 命令後——
mount --bind /some/where /else/where
目錄
/some/where
和/else/where
具有相同的內容,即/some/where
. (如果/else/where
不為空,則其之前的內容現在被隱藏。)與硬連結或符號連結不同,綁定掛載不會影響文件系統上儲存的內容。它是實時系統的屬性。
如何創建綁定掛載?
綁定文件
bindfs
文件系統是一個創建目錄樹視圖的 FUSE 文件系統。例如,命令bindfs /some/where /else/where
創建
/else/where
一個掛載點,在該掛載點下可以/some/where
看到 的內容。由於 bindfs 是一個單獨的文件系統,這些文件在應用程序
/some/where/foo
中/else/where/foo
表現為不同的文件(bindfs 文件系統有其自己的st_dev
價值)。一側的任何更改都會“神奇地”反映在另一側,但文件相同的事實只有在知道 bindfs 的操作方式時才會顯現出來。
/some/where
Bindfs不知道掛載點,所以如果/else/where
. 在下面掛載或解除安裝文件系統/some/where
顯示/else/where
為相應目錄的更改。Bindfs 可以更改某些文件元數據:它可以顯示文件的虛假權限和所有權。有關詳細資訊,請參閱手冊,並參閱下面的範例。
bindfs 文件系統可以作為非 root 使用者掛載,您只需要掛載 FUSE 文件系統的權限。根據您的分配,這可能需要在
fuse
組中或允許所有使用者使用。要解除安裝 FUSE 文件系統,請使用fusermount -u
代替umount
,例如fusermount -u /else/where
空文件
FreeBSD 提供了
nullfs
創建文件系統的替代視圖的文件系統。以下兩個命令是等價的:mount -t nullfs /some/where /else/where mount_nullfs /some/where /else/where
發出任一命令後,成為可見
/else/where
內容的掛載點。/some/where
由於 nullfs 是一個單獨的文件系統,因此文件
/some/where/foo
和/else/where/foo
應用程序顯示為不同的文件(nullfs 文件系統有其自己的st_dev
價值)。一側的任何更改都會“神奇地”反映在另一側,但文件相同的事實只有在知道 nullfs 的操作方式時才會顯現出來。與 FUSE bindfs 不同,它在目錄樹的級別上起作用,FreeBSD 的 nullfs 在核心中的作用更深,所以下面的掛載點是
/else/where
不可見的:只有與./some/where``/else/where
nullfs 文件系統可能在其他 BSD 變體(OS X、OpenBSD、NetBSD)下可用,但它不編譯為預設系統的一部分。
Linux 綁定掛載
在 Linux 下,綁定掛載可作為核心功能使用。您可以使用
mount
命令創建一個,通過傳遞--bind
命令行選項或bind
掛載選項。以下兩個命令是等價的:mount --bind /some/where /else/where mount -o bind /some/where /else/where
這裡,“設備”
/some/where
不是像磁碟文件系統那樣的磁碟分區,而是現有目錄。像往常一樣,掛載點/else/where
必須是現有目錄。請注意,無論哪種方式都沒有指定文件系統類型:進行綁定掛載不涉及文件系統驅動程序,它從原始掛載複製核心資料結構。
mount --bind
還支持將非目錄掛載到非目錄上:/some/where
可以是正常文件(在這種情況下也/else/where
需要是正常文件)。Linux 綁定掛載與原始掛載幾乎沒有區別。該命令
df -T /else/where
顯示相同的設備和相同的文件系統類型df -T /some/where
。這些文件/some/where/foo
和/else/where/foo
是無法區分的,就好像它們是硬連結一樣。可以解除安裝/some/where
,在這種情況下/else/where
保持安裝狀態。對於較舊的核心(我不知道確切的時間,我認為直到大約 3.x),綁定掛載與原始核心真正無法區分。最近的核心確實跟踪綁定掛載並通過 <code/proc/ PID /mountinfo 公開資訊,這允許
findmnt
指示綁定掛載。您可以將綁定掛載條目放入
/etc/fstab
. 只需在選項中包含bind
(或rbind
等)以及您想要的任何其他選項。“設備”是現有的樹。文件系統列可以包含none
或bind
(它被忽略,但使用文件系統名稱會令人困惑)。例如:/some/where /readonly/view none bind,ro
如果 下有掛載點
/some/where
,則其內容在 下不可見/else/where
。取而代之的是bind
,您可以使用rbind
,也可以在下面複製掛載點/some/where
。例如,如果/some/where/mnt
是一個掛載點,那麼mount --rbind /some/where /else/where
相當於
mount --bind /some/where /else/where mount --bind /some/where/mnt /else/where/mnt
此外,Linux 允許將掛載聲明為shared、slave、private或unbindable。這會影響該掛載操作是否反映在複製掛載點的綁定掛載下。有關更多詳細資訊,請參閱核心文件。
Linux 還提供了一種移動掛載的方法:在哪裡
--bind
複製,--move
移動一個掛載點。在兩個綁定掛載的目錄中可以有不同的掛載選項。但是有一個怪癖:進行綁定掛載和設置掛載選項不能原子地完成,它們必須是兩個連續的操作。(較舊的核心不允許這樣做。)例如,以下命令會創建一個只讀視圖,但有一小段時間視窗
/else/where
是讀寫的:mount --bind /some/where /else/where mount -o remount,ro,bind /else/where
我無法讓綁定坐騎工作!
如果您的系統不支持 FUSE,實現相同效果的經典技巧是執行 NFS 伺服器,使其導出您要公開的文件(允許訪問
localhost
)並將它們安裝在同一台機器上。這在記憶體和性能方面有很大的成本,因此綁定掛載在可用的情況下具有明顯的優勢(由於 FUSE,這在大多數 Unix 變體上都有)。案例
只讀視圖
出於安全原因或僅作為安全層以確保您不會意外修改它,創建文件系統的只讀視圖可能很有用。
使用 bindfs:
bindfs -r /some/where /mnt/readonly
使用 Linux,簡單的方法:
mount --bind /some/where /mnt/readonly mount -o remount,ro,bind /mnt/readonly
這留下了一段很短的時間間隔,在此期間
/mnt/readonly
是讀寫的。如果這是一個安全問題,請首先在只有 root 可以訪問的目錄中創建綁定掛載,使其成為只讀,然後將其移動到公共掛載點。在下面的程式碼片段中,請注意/root/private
(掛載點上方的目錄)是私有的,這一點很重要;原始權限/root/private/mnt
無關緊要,因為它們隱藏在掛載點後面。mkdir -p /root/private/mnt chmod 700 /root/private mount --bind /some/where /root/private/mnt mount -o remount,ro,bind /root/private/mnt mount --move /root/private/mnt /mnt/readonly
重新映射使用者和組
文件系統通過數字 ID 記錄使用者和組。有時您最終會得到多個系統,這些系統將不同的使用者 ID 分配給同一個人。這不是網路訪問的問題,但是當您在磁碟上將數據從一個系統傳送到另一個系統時,它會使使用者 ID 毫無意義。假設您在 Alice 的使用者 ID 為 1000 而 Bob 的使用者 ID 為 1001 的系統上創建了一個使用多使用者文件系統(例如 ext4、btrfs、zfs、UFS 等)的磁碟,並且您想讓該磁碟在Alice 的使用者 ID 為 1001 而 Bob 的使用者 ID 為 1000 的系統。如果直接掛載磁碟,Alice 的文件將顯示為 Bob 所有(因為使用者 ID 為 1001),Bob 的文件將顯示為 Alice 所有(因為使用者 ID 為 1000)。
您可以使用 bindfs 重新映射使用者 ID。首先將磁碟分區掛載到一個私有目錄中,只有 root 可以訪問它。然後在公共區域創建一個 bindfs 視圖,使用使用者 ID 和組 ID 重新映射,交換 Alice 和 Bob 的使用者 ID 和組 ID。
mkdir -p /root/private/alice_disk /media/alice_disk chmod 700 /root/private mount /dev/sdb1 /root/private/alice_disk bindfs --map=1000/1001:1001/1000:@1000/1001:@1001/1000 /root/private/alice_disk /media/alice_disk
請參閱如何允許訪問未啟動系統的使用者主文件夾中的文件?並mount –bind 其他使用者作為我自己的另一個例子。
安裝在監獄或容器中
chroot jail或容器在系統目錄樹的子樹中執行一個程序。這對於執行一個訪問受限的程序很有用,例如執行一個網路伺服器,它只能訪問它自己的文件和它所服務的文件,但不能訪問儲存在同一台電腦上的其他數據)。chroot 的一個限制是程序僅限於一個子樹:它不能訪問獨立的子樹。綁定安裝允許將其他子樹嫁接到該主樹上。這使得它們成為 Linux 下最實際使用容器的基礎。
例如,假設一台機器執行的服務
/usr/sbin/somethingd
應該只能訪問/var/lib/something
. 包含這兩個文件的最小目錄樹是根目錄。如何限制服務?一種可能性是/usr/sbin/somethingd
在/var/lib/something
./var/lib/something
但這很麻煩(每當升級文件時都需要更新硬連結),並且如果並且/usr
位於不同的文件系統上則不起作用。更好的解決方案是創建一個臨時根並使用 mounts 填充它:mkdir /run/something cd /run/something mkdir -p etc/something lib usr/lib usr/sbin var/lib/something mount --bind /etc/something etc/something mount --bind /lib lib mount --bind /usr/lib usr/lib mount --bind /usr/sbin usr/sbin mount --bind /var/lib/something var/lib/something mount -o remount,ro,bind etc/something mount -o remount,ro,bind lib mount -o remount,ro,bind usr/lib mount -o remount,ro,bind usr/sbin chroot . /usr/sbin/somethingd &
Linux 的掛載命名空間概括了 chroot。綁定掛載是如何以靈活的方式填充命名空間。有關範例,請參閱讓程序讀取相同文件名的不同文件。
執行不同的發行版
chroot 的另一個用途是在目錄中安裝不同的發行版並從中執行程序,即使它們需要硬編碼路徑中的文件,這些文件在基本系統上不存在或具有不同的內容。這可能很有用,例如,在不支持混合包的 64 位系統上安裝 32 位發行版,安裝舊版本的發行版或其他發行版以測試兼容性,安裝新版本進行測試最新功能,同時保持穩定的基本系統等。請參閱如何在 64 位 Debian/Ubuntu 上執行 32 位程序?以 Debian/Ubuntu 為例。
假設您在 目錄下安裝了發行版的最新軟體包
/f/unstable
,您通過使用 切換到該目錄來執行程序chroot /f/unstable
。要從此安裝中使用主目錄,請將它們綁定到 chroot 中:mount --bind /home /f/unstable/home
程序schroot會自動執行此操作。
訪問隱藏在掛載點後面的文件
當您在目錄上掛載文件系統時,這會隱藏目錄後面的內容。在解除安裝該目錄之前,該目錄中的文件將無法訪問。因為 BSD nullfs 和 Linux 綁定掛載在比掛載基礎架構更低的級別上執行,所以文件系統的 nullfs 掛載或綁定掛載會暴露隱藏在原始子掛載後面的目錄。
例如,假設您有一個 tmpfs 文件系統安裝在
/tmp
. 如果在/tmp
創建 tmpfs 文件系統時有文件,這些文件可能仍然存在,實際上無法訪問,但會佔用磁碟空間。跑mount --bind / /mnt
(Linux) 或
mount -t nullfs / /mnt
(FreeBSD) 在
/mnt
. 該目錄/mnt/tmp
是來自根文件系統的目錄。不同路徑的 NFS 導出
一些 NFS 伺服器(例如 NFSv4 之前的 Linux 核心 NFS 伺服器)在導出目錄時總是通告實際的目錄位置。也就是說,當客戶端請求時
server:/requested/location
,伺服器會為該位置的樹提供服務/requested/location
。有時希望允許客戶端請求/request/location
但實際上在/actual/location
. 如果您的 NFS 伺服器不支持為備用位置提供服務,您可以為預期的請求創建綁定掛載,例如/requested/location *.localdomain(rw,async)
in
/etc/exports
和以下 in/etc/fstab
:/actual/location /requested/location bind bind
符號連結的替代品
有時您想製作符號連結以使文件
/some/where/is/my/file
出現在 下/else/where
,但使用file
擴展符號連結並拒絕的應用程序/some/where/is/my/file
。綁定掛載可以解決此問題:綁定掛載/some/where/is/my
到/else/where/is/my
,然後realpath
將報告/else/where/is/my/file
為 under/else/where
,而不是 under/some/where
。綁定安裝的副作用
遞歸目錄遍歷
如果您使用綁定掛載,則需要處理遞歸遍歷文件系統樹的應用程序,例如備份和索引(例如建構定位數據庫)。
通常,應該從遞歸目錄遍歷中排除綁定掛載,以便每個目錄樹只在原始位置遍歷一次。如果可能,使用 bindfs 和 nullfs 將遍歷工具配置為忽略這些文件系統類型。無法辨識 Linux 綁定掛載:新位置與原始位置相同。對於 Linux 綁定掛載,或者使用只能排除路徑而不是文件系統類型的工具,您需要排除綁定掛載的掛載點。
在文件系統邊界處停止的遍歷(例如
find -xdev
,rsync -x
,du -x
, …)將在遇到 bindfs 或 nullfs 掛載點時自動停止,因為該掛載點是不同的文件系統。對於 Linux 綁定掛載,情況稍微複雜一些:只有綁定掛載嫁接不同的文件系統時才存在文件系統邊界,而不是嫁接同一文件系統的另一部分。超越綁定坐騎
綁定掛載提供不同位置的目錄樹視圖。它們公開相同的文件,可能具有不同的掛載選項和(使用 bindfs)不同的所有權和權限。呈現目錄樹的更改視圖的文件系統稱為覆蓋文件系統或可堆疊文件系統。還有許多其他覆蓋文件系統可以執行更高級的轉換。這裡有幾個常見的。如果此處未涵蓋您所需的案例,請檢查FUSE 文件系統的儲存庫。
- logsfs — 記錄所有文件系統訪問以用於調試或監視目的(配置文件語法,是否可以找出給定文件創建的程序或腳本?,列出程序訪問的文件)
過濾可見文件
clamfs — 讀取文件時通過病毒掃描程序執行文件
filterfs — 隱藏文件系統的一部分
rofs — 只讀視圖。類似於
bindfs -r
,只是更輕量級。聯合掛載——在單個目錄下顯示多個文件系統(稱為分支
tree1
):如果包含foo
和tree2
包含,bar
則它們的聯合視圖同時包含foo
和bar
。新文件被寫入特定分支,或寫入根據更複雜規則選擇的分支。這個概念有幾種實現方式,包括:修改文件名和元數據
- ciopfs — 不區分大小寫的文件名(可用於掛載 Windows 文件系統)
- convmvfs — 在字元集之間轉換文件名(範例)
- posixovl — 將 Unix 文件名和其他元數據(權限、所有權等)儲存在更受限制的文件系統上,例如 VFAT(範例)
查看更改的文件內容
- avfs — 對於每個存檔文件,顯示一個包含存檔內容的目錄(例如,更多範例)。還有許多FUSE 文件系統將特定的檔案公開為目錄。
- fuseflt — 在讀取文件時通過管道執行文件,例如重新編碼文本文件或媒體文件(範例)
- lzopfs — 壓縮文件的透明解壓
- mp3fs — 讀取 FLAC 文件時將其轉碼為 MP3(範例)
- scriptfs — 執行腳本以提供內容(一種本地 CGI)(範例)
修改內容儲存方式
- chironfs — 將文件複製到多個底層儲存(目錄樹級別的 RAID-1)
- copyfs — 保留文件所有版本的副本
- encfs — 加密文件
- pcachefs — 用於慢速遠端文件系統的磁碟記憶體層
- simplecowfs — 通過提供的視圖將更改儲存在記憶體中,保持原始文件不變
- wayback — 保留文件所有版本的副本