Gil*_*il' 464 filesystems mount bind-mount
什么是“绑定挂载”?我怎么做一个?到底有什么好处呢?
有人告诉我要为某些东西使用绑定安装,但我不明白它是什么或如何使用它。
Gil*_*il' 801
甲绑定安装是目录树的替换视图。传统上,挂载将存储设备的视图创建为目录树。绑定安装取而代之的是采用现有目录树并将其复制到不同的点。绑定挂载中的目录和文件与原始目录和文件相同。一侧的任何修改都会立即反映在另一侧,因为两个视图显示相同的数据。
例如,在发出 Linux 命令后——
mount --bind /some/where /else/where
Run Code Online (Sandbox Code Playgroud)
目录/some/where并/else/where具有相同的内容,即/some/where. (如果/else/where不为空,则其先前的内容现在已隐藏。)
与硬链接或符号链接不同,绑定挂载不会影响文件系统上存储的内容。这是实时系统的属性。
该bindfs文件系统是一个FUSE文件系统,它会创建一个目录树的视图。例如,命令
bindfs /some/where /else/where
Run Code Online (Sandbox Code Playgroud)
制作/else/where一个挂载点,在该挂载点下的内容/some/where可见。
由于 bindfs 是一个单独的文件系统,文件/some/where/foo和/else/where/foo对应用程序显示为不同的文件(bindfs 文件系统有其自己的st_dev值)。一侧的任何更改都会“神奇地”反映在另一侧,但文件相同的事实只有在知道 bindfs 的操作方式时才会明显。
Bindfs 不知道挂载点,所以如果 下有挂载点/some/where,它看起来只是 下的另一个目录/else/where。挂载或卸载下面的文件系统/some/where显示/else/where为相应目录的更改。
Bindfs 可以更改一些文件元数据:它可以显示文件的虚假权限和所有权。有关详细信息,请参阅手册,并参阅下面的示例。
bindfs 文件系统可以以非 root 用户身份挂载,您只需要挂载 FUSE 文件系统的权限。根据您的分布,这可能需要在fuse组中或允许所有用户使用。要卸载 FUSE 文件系统,请使用fusermount -u代替umount,例如
fusermount -u /else/where
Run Code Online (Sandbox Code Playgroud)
FreeBSD 提供了nullfs创建文件系统替代视图的文件系统。以下两个命令是等效的:
mount -t nullfs /some/where /else/where
mount_nullfs /some/where /else/where
Run Code Online (Sandbox Code Playgroud)
发出任一命令后,/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 下,绑定挂载可作为内核功能使用。您可以使用该mount命令创建一个,通过传递--bind命令行选项或bind挂载选项。以下两个命令是等效的:
mount --bind /some/where /else/where
mount -o bind /some/where /else/where
Run Code Online (Sandbox Code Playgroud)
在这里,“设备”/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
Run Code Online (Sandbox Code Playgroud)
如果下有挂载点,则在下/some/where看不到它们的内容/else/where。取而代之的是bind,您可以使用rbind,也可以复制下面的挂载点/some/where。例如,如果/some/where/mnt是挂载点,则
mount --rbind /some/where /else/where
Run Code Online (Sandbox Code Playgroud)
相当于
mount --bind /some/where /else/where
mount --bind /some/where/mnt /else/where/mnt
Run Code Online (Sandbox Code Playgroud)
此外,Linux 允许将挂载声明为shared、slave、private或unbindable。这会影响该挂载操作是否反映在复制挂载点的绑定挂载下。有关更多详细信息,请参阅内核文档。
Linux 还提供了一种移动挂载的方法:在那里--bind复制,--move移动一个挂载点。
在两个绑定挂载的目录中可以有不同的挂载选项。然而,有一个怪癖:进行绑定挂载和设置挂载选项不能自动完成,它们必须是两个连续的操作。(旧内核不允许这样做。)例如,以下命令创建只读视图,但有一小段时间窗口/else/where可读写:
mount --bind /some/where /else/where
mount -o remount,ro,bind /else/where
Run Code Online (Sandbox Code Playgroud)
如果您的系统不支持 FUSE,实现相同效果的一个经典技巧是运行 NFS 服务器,使其导出您想要公开的文件(允许访问localhost)并将它们安装在同一台机器上。这在内存和性能方面具有显着的开销,因此绑定挂载在可用的情况下具有明显的优势(由于 FUSE,这在大多数 Unix 变体上)。
创建文件系统的只读视图可能很有用,无论是出于安全原因还是作为安全层以确保您不会意外修改它。
使用绑定:
bindfs -r /some/where /mnt/readonly
Run Code Online (Sandbox Code Playgroud)
使用 Linux,简单的方法:
mount --bind /some/where /mnt/readonly
mount -o remount,ro,bind /mnt/readonly
Run Code Online (Sandbox Code Playgroud)
这留下了很短的时间间隔,在此期间/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
Run Code Online (Sandbox Code Playgroud)
文件系统通过数字 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
Run Code Online (Sandbox Code Playgroud)
请参阅如何允许访问未启动系统的用户主文件夹中的文件?和mount --bind 其他用户作为我自己的另一个例子。
一个chroot监牢或容器运行在系统目录树的子树的过程。这对于运行具有受限访问权限的程序非常有用,例如,运行网络服务器只能访问它自己的文件和它所服务的文件,而不能访问存储在同一台计算机上的其他数据)。chroot 的一个限制是程序被限制在一个子树中:它不能访问独立的子树。绑定坐骑允许将其他子树嫁接到该主树上。这使它们成为 Linux 下容器的最实际使用的基础。
例如,假设一台机器运行的服务/usr/sbin/somethingd应该只能访问/var/lib/something. 包含这两个文件的最小目录树是根目录。如何限制服务?一种可能性是/usr/sbin/somethingd在/var/lib/something. 但这很麻烦(每当文件升级时都需要更新硬链接),并且如果/var/lib/something和/usr位于不同的文件系统上则不起作用。更好的解决方案是创建一个临时根并使用挂载填充它:
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 &
Run Code Online (Sandbox Code Playgroud)
Linux 的挂载命名空间概括了 chroot。绑定挂载是命名空间以灵活方式填充的方式。有关示例,请参阅使进程读取具有相同文件名的不同文件。
chroots 的另一个用途是在目录中安装不同的发行版并从中运行程序,即使它们需要位于基本系统上不存在或具有不同内容的硬编码路径中的文件。这可能很有用,例如,在不支持混合包的 64 位系统上安装 32 位发行版、安装发行版的旧版本或其他发行版以测试兼容性、安装新发行版以进行测试最新功能,同时保持稳定的基本系统等。请参阅如何在 64 位 Debian/Ubuntu 上运行 32 位程序?以 Debian/Ubuntu 为例。
假设您在 目录下安装了您的发行版的最新软件包/f/unstable,您可以通过切换到该目录来运行程序chroot /f/unstable。要从此安装中提供主目录,请将它们绑定安装到 chroot 中:
mount --bind /home /f/unstable/home
Run Code Online (Sandbox Code Playgroud)
程序schroot会自动执行此操作。
当您在目录上挂载文件系统时,这会隐藏目录后面的内容。在卸载该目录之前,该目录中的文件将无法访问。由于 BSD nullfs 和 Linux 绑定挂载在比挂载基础结构更低的级别上运行,因此文件系统的 nullfs 挂载或绑定挂载暴露了隐藏在原始子挂载后面的目录。
例如,假设您有一个 tmpfs 文件系统安装在/tmp. 如果在/tmp创建 tmpfs 文件系统时有文件,这些文件可能仍然存在,实际上无法访问,但会占用磁盘空间。跑
mount --bind / /mnt
Run Code Online (Sandbox Code Playgroud)
(Linux) 或
mount -t nullfs / /mnt
Run Code Online (Sandbox Code Playgroud)
(FreeBSD) 在/mnt. 该目录/mnt/tmp是来自根文件系统的目录。
某些 NFS 服务器(例如 NFSv4 之前的 Linux 内核 NFS 服务器)在导出目录时总是通告实际目录位置。也就是说,当客户端请求时server:/requested/location,服务器会在该位置为树提供服务/requested/location。有时希望允许客户端请求/request/location但实际提供/actual/location. 如果您的 NFS 服务器不支持为备用位置提供服务,您可以为预期的请求创建一个绑定安装,例如
/requested/location *.localdomain(rw,async)
Run Code Online (Sandbox Code Playgroud)
in/etc/exports和以下内容/etc/fstab:
/actual/location /requested/location bind bind
Run Code Online (Sandbox Code Playgroud)
有时您希望创建符号链接以使文件/some/where/is/my/file出现在 下/else/where,但使用的应用程序file扩展符号链接并拒绝/some/where/is/my/file. 绑定挂载可以解决这个问题:bind-mount /some/where/is/myto /else/where/is/my,然后realpath将报告/else/where/is/my/file为 under /else/where,而不是 under /some/where。
如果使用绑定挂载,则需要处理递归遍历文件系统树的应用程序,例如备份和索引(例如构建定位数据库)。
通常,应该从递归目录遍历中排除绑定挂载,以便每个目录树仅在原始位置遍历一次。如果可能,使用 bindfs 和 nullfs,将遍历工具配置为忽略这些文件系统类型。无法识别 Linux 绑定挂载:新位置与原始位置相同。对于 Linux 绑定挂载,或者使用只能排除路径而不是文件系统类型的工具,您需要排除绑定挂载的挂载点。
当遇到 bindfs 或 nullfs 挂载点时,停止在文件系统边界(例如find -xdev, rsync -x, du -x, ...)的遍历将自动停止,因为该挂载点是不同的文件系统。对于 Linux 绑定挂载,情况稍微复杂一些:只有当绑定挂载嫁接不同的文件系统时才存在文件系统边界,如果它嫁接同一文件系统的另一部分,则没有。
绑定挂载提供了不同位置的目录树视图。它们公开相同的文件,可能具有不同的挂载选项和(使用 bindfs)不同的所有权和权限。显示目录树更改视图的文件系统称为覆盖文件系统或可堆叠文件系统。还有许多其他覆盖文件系统可以执行更高级的转换。这里有一些常见的。如果此处未涵盖您想要的用例,请检查FUSE 文件系统的存储库。
clamfs — 读取文件时通过病毒扫描程序运行文件
filterfs — 隐藏文件系统的一部分
rofs — 只读视图。与 类似bindfs -r,只是轻了一点。
联合挂载-在单个目录下呈现多个文件系统(称为分支):如果tree1包含foo和tree2包含,bar则它们的联合视图包含foo和bar。新文件将写入特定分支,或写入根据更复杂规则选择的分支。这个概念有几种实现方式,包括:
| 归档时间: |
|
| 查看次数: |
377943 次 |
| 最近记录: |