作者:Neil Brown 请查阅 MAINTAINERS 文件以了解向何处发送问题。

Overlay 文件系统

本文档描述了一种在 Linux 中提供 overlay-filesystem 功能(有时称为 union-filesystems)的新方法的原型。overlay-filesystem 尝试呈现一个文件系统,该文件系统是将一个文件系统叠加在另一个文件系统之上的结果。

Overlay 对象

overlay 文件系统方法是“混合”的,因为出现在文件系统中的对象并不总是看起来属于该文件系统。在许多情况下,在联合中访问的对象与从原始文件系统访问相应对象没有区别。从 stat(2) 返回的“st_dev”字段中可以明显看出这一点。

虽然目录将报告来自 overlay-filesystem 的 st_dev,但非目录对象可能会报告来自提供该对象的较低文件系统或较高文件系统的 st_dev。同样,只有与 st_dev 结合使用时,st_ino 才是唯一的,并且这两者都可能在非目录对象的生命周期内发生变化。许多应用程序和工具会忽略这些值,因此不会受到影响。

在所有 overlay 层都在同一底层文件系统的特殊情况下,所有对象都将报告来自 overlay 文件系统的 st_dev 以及来自底层文件系统的 st_ino。这将使 overlay 挂载更符合文件系统扫描器的要求,并且 overlay 对象可以与原始文件系统中的相应对象区分开来。

在 64 位系统上,即使所有 overlay 层都不在同一底层文件系统上,也可以通过“xino”功能实现相同的合规行为。“xino”功能通过真实对象的 st_ino 和底层 fsid 号组成唯一的对象标识符。“xino”功能使用 inode 号的高位作为 fsid,因为底层文件系统很少使用 inode 号的高位。如果底层 inode 号溢出到 xino 高位,overlay 文件系统将回退到该 inode 的非 xino 行为。

可以使用“-o xino=on”overlay 挂载选项启用“xino”功能。如果所有底层文件系统都支持 NFS 文件句柄,则 overlay 文件系统对象的 st_ino 值不仅是唯一的,而且在文件系统的整个生命周期中都是持久的。“-o xino=auto”overlay 挂载选项仅在满足持久性 st_ino 要求时才启用“xino”功能。

下表总结了在不同 overlay 配置中可以预期的情况。

Inode 属性

配置

持久性 st_ino

统一的 st_dev

st_ino == d_ino

d_ino == i_ino [*]

目录

!目录

目录

!目录

目录

!目录

目录

!目录

所有层在同一文件系统上

层不在同一文件系统上,xino=off

xino=on/auto

xino=on/auto,ino 溢出

[*] nfsd v3 readdirplus 验证 d_ino == i_ino。 i_ino 通过多个 /proc 文件公开,例如 /proc/locks 和 inotify 文件描述符的 /proc/self/fdinfo/<fd>。

Upper 和 Lower

overlay 文件系统组合了两个文件系统 - 一个“upper”文件系统和一个“lower”文件系统。当一个名称同时存在于两个文件系统中时,“upper”文件系统中的对象是可见的,而“lower”文件系统中的对象要么被隐藏,要么(在目录的情况下)与“upper”对象合并。

更准确的说法是 upper 和 lower “目录树”,而不是“文件系统”,因为两个目录树都可能位于同一文件系统中,并且 upper 或 lower 都不需要提供文件系统的根目录。

Linux 支持的各种文件系统都可以作为 lower 文件系统,但并非所有 Linux 可以挂载的文件系统都具有 OverlayFS 工作所需的特性。lower 文件系统不需要是可写的。lower 文件系统甚至可以是另一个 overlayfs。upper 文件系统通常是可写的,如果是可写的,则必须支持创建 trusted.* 和/或 user.* 扩展属性,并且必须在 readdir 响应中提供有效的 d_type,因此 NFS 不适用。

两个只读文件系统的只读 overlay 可以使用任何文件系统类型。

目录

Overlay 主要涉及目录。如果给定的名称同时出现在 upper 和 lower 文件系统中,并且在其中一个文件中指向非目录,则 lower 对象将被隐藏 - 该名称仅指向 upper 对象。

如果 upper 和 lower 对象都是目录,则会形成一个合并的目录。

挂载时,作为挂载选项“lowerdir”和“upperdir”给出的两个目录将合并到一个合并的目录中

mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
workdir=/work /merged

“workdir”需要是与 upperdir 位于同一文件系统上的空目录。

然后,每当在这样一个合并的目录中请求查找时,都会在每个实际目录中执行查找,并将组合结果缓存在属于 overlay 文件系统的 dentry 中。如果两个实际查找都找到目录,则会存储这两个目录并创建一个合并的目录,否则只会存储一个:如果 upper 存在,则存储 upper,否则存储 lower。

仅合并目录中的名称列表。其他内容(如元数据和扩展属性)仅报告 upper 目录的内容。lower 目录的这些属性将被隐藏。

whiteout 和 opaque 目录

为了支持 rm 和 rmdir 而无需更改 lower 文件系统,overlay 文件系统需要在 upper 文件系统中记录已删除的文件。这是使用 whiteout 和 opaque 目录完成的(非目录始终是不透明的)。

whiteout 创建为设备号为 0/0 的字符设备或具有 xattr “trusted.overlay.whiteout” 的零大小的常规文件。

如果在合并目录的 upper 层中找到 whiteout,则 lower 层中的任何匹配名称都将被忽略,并且 whiteout 本身也会被隐藏。

通过将 xattr “trusted.overlay.opaque” 设置为 “y” 来使目录变得 opaque。如果 upper 文件系统包含 opaque 目录,则 lower 文件系统中任何具有相同名称的目录都将被忽略。

opaque 目录不应包含任何 whiteout,因为它们没有任何作用。包含具有 xattr “trusted.overlay.whiteout” 的常规文件的合并目录还应通过将 xattr “trusted.overlay.opaque” 设置为合并目录本身的 “x” 来标记。这是为了避免在常见情况下 readdir 期间检查所有条目的“trusted.overlay.whiteout”的开销。

readdir

当对合并的目录发出“readdir”请求时,会读取 upper 和 lower 目录,并以显而易见的方式合并名称列表(首先读取 upper,然后读取 lower - 不会重新添加已存在的条目)。此合并的名称列表缓存在“struct file”中,因此只要文件保持打开状态,该列表就会保留。如果目录被两个进程同时打开和读取,则它们将各自拥有单独的缓存。seekdir 到目录的开头(偏移量 0)后跟 readdir 将导致缓存被丢弃并重建。

这意味着在读取目录时,合并目录的更改不会显示。许多程序不太可能注意到这一点。

seek 偏移量在读取目录时按顺序分配。因此,如果

  • 读取目录的一部分

  • 记住一个偏移量并关闭目录

  • 稍后重新打开目录

  • seek 到记住的偏移量

则文件名列表中的旧位置和新位置之间可能几乎没有关联,特别是如果目录中的任何内容发生了更改。

对未合并的目录的 Readdir 仅由底层目录(upper 或 lower)处理。

重命名目录

当重命名位于 lower 层或已合并的目录(即,该目录不是一开始就在 upper 层上创建的)时,overlayfs 可以通过两种不同的方式处理它

  1. 返回 EXDEV 错误:尝试跨文件系统边界移动文件或目录时,rename(2) 会返回此错误。因此,应用程序通常会准备好处理此错误(例如,mv(1) 会递归复制目录树)。这是默认行为。

  2. 如果启用了“redirect_dir”功能,则该目录将被复制到 upper 层(但内容不会)。然后,将“trusted.overlay.redirect”扩展属性设置为从 overlay 根目录到原始位置的路径。最后,该目录被移动到新位置。

有几种方法可以调整“redirect_dir”功能。

内核配置选项

  • OVERLAY_FS_REDIRECT_DIR

    如果启用了此选项,则默认情况下启用 redirect_dir。

  • OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW

    如果启用了此选项,则默认情况下始终跟踪重定向。启用此选项会导致安全性较低的配置。只有在担心与具有 redirect_dir 功能的内核向后兼容性,并且即使关闭也跟踪重定向时,才启用此选项。

模块选项(也可以通过 /sys/module/overlay/parameters/ 更改)

  • “redirect_dir=BOOL”

    请参阅上面的 OVERLAY_FS_REDIRECT_DIR 内核配置选项。

  • “redirect_always_follow=BOOL”

    请参阅上面的 OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW 内核配置选项。

  • “redirect_max=NUM”

    绝对重定向中的最大字节数(默认为 256)。

挂载选项

  • “redirect_dir=on”

    启用重定向。

  • “redirect_dir=follow”

    不会创建重定向,但会跟踪重定向。

  • “redirect_dir=nofollow”

    不会创建重定向,也不会跟踪重定向。

  • “redirect_dir=off”

    如果在内核/模块配置中启用了“redirect_always_follow”,则此“off”转换为“follow”,否则转换为“nofollow”。

启用 NFS 导出功能后,每个复制到 upper 层的目录都由 lower inode 的文件句柄索引,upper 目录的文件句柄存储在索引条目的 “trusted.overlay.upper” 扩展属性中。在查找合并的目录时,如果 upper 目录与索引中存储的文件句柄不匹配,则表明多个 upper 目录可能重定向到同一个 lower 目录。在这种情况下,查找返回错误并警告可能存在不一致。

由于 lower 层重定向无法通过索引验证,因此在没有 upper 层的 overlay 文件系统上启用 NFS 导出支持需要关闭重定向跟踪(例如,“redirect_dir=nofollow”)。

非目录

不是目录的对象(文件、符号链接、设备特殊文件等)以适合的方式从 upper 或 lower 文件系统呈现。当以需要写访问权限的方式访问 lower 文件系统中的文件时,例如打开以进行写访问、更改某些元数据等,该文件首先从 lower 文件系统复制到 upper 文件系统 (copy_up)。请注意,创建硬链接也需要 copy_up,当然,创建符号链接不需要。

copy_up 可能是不必要的,例如,如果文件以读写方式打开但数据未被修改。

copy_up 过程首先确保包含目录存在于 upper 文件系统中 - 根据需要创建它及其任何父目录。然后,它使用相同的元数据(所有者、模式、mtime、符号链接目标等)创建对象,如果对象是一个文件,则将数据从 lower 文件系统复制到 upper 文件系统。最后,任何扩展属性都会被复制到 upper 层。

copy_up 完成后,overlay 文件系统只是提供对 upper 文件系统中新创建的文件的直接访问 - overlay 文件系统几乎不会注意到对该文件的未来操作(尽管对文件名称的操作(例如重命名或取消链接)当然会被注意到并处理)。

权限模型

overlay 文件系统会存储在访问 lower 或 upper 文件系统时将使用的凭据。

在旧的挂载 API 中,调用 mount(2) 的任务的凭据会被存储。在新的挂载 API 中,通过 fsconfig(2) 的 FSCONFIG_CMD_CREATE 命令创建超级块的任务的凭据会被存储。

从内核 v6.15 开始,可以使用 “override_creds” 挂载选项,该选项将导致记录调用任务的凭据。请注意,“override_creds” 仅在使用新的挂载 API 时才有意义,因为旧的挂载 API 将设置选项和超级块创建合并到单个 mount(2) 系统调用中。

overlay 文件系统中的权限检查遵循以下原则

  1. 权限检查在 copy up 前后应返回相同的结果

  2. 创建 overlay 挂载的任务不得获得额外的权限

  3. 与直接访问底层 lower 或 upper 文件系统相比,任务[*] 可能通过 overlay 获得额外的权限

这是通过对每次访问执行两次权限检查来实现的

  1. 检查当前任务是否基于本地 DAC(所有者、组、模式和 posix acl)以及 MAC 检查被允许访问

  2. 检查存储的凭据是否允许基于底层文件系统权限对 lower 或 upper 层执行实际操作,再次包括 MAC 检查

检查 (a) 确保一致性 (1),因为所有者、组、模式和 posix acl 都会被复制到 upper 层。另一方面,这可能会导致服务器强制执行的权限(例如,NFS 使用的权限)被忽略 (3)。

检查 (b) 确保没有任务获得存储的凭据不具有的底层层的权限 (2)。这也意味着可以创建不满足一致性规则 (1) 的设置;但是,通常,存储的凭据将具有执行所有操作的足够权限。

演示此模型的另一种方法是在以下两者之间建立联系

mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,... /merged

cp -a /lower /upper
mount --bind /upper /merged

生成的访问权限应相同。区别在于复制的时间(按需 vs. 预先)。

多个 lower 层

现在可以使用冒号 (“:”) 作为目录名称之间的分隔符来给出多个 lower 层。例如

mount -t overlay overlay -olowerdir=/lower1:/lower2:/lower3 /merged

如示例所示,可以省略 “upperdir=” 和 “workdir=”。在这种情况下,overlay 将是只读的。

指定的 lower 目录将从最右边的目录开始,从左到右堆叠。在上面的示例中,lower1 将位于顶部,lower2 位于中间,lower3 位于底层。

注意:包含冒号的目录名称可以通过使用单个反斜杠转义冒号来作为 lower 层提供。例如

mount -t overlay overlay -olowerdir=/a\:lower\:\:dir /merged

从内核版本 v6.8 开始,也可以使用新的挂载 API 中的 “lowerdir+” 挂载选项和 fsconfig 系统调用将包含冒号的目录名称配置为 lower 层。例如

fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/a:lower::dir", 0);

在后一种情况下,lower 层目录名称中的冒号将在 /proc/self/mountinfo 中显示时转义为八进制字符 (072)。

仅元数据复制 up

启用“metacopy”功能后,当执行像 chown/chmod 这样的特定于元数据的操作时,overlayfs 将仅复制 up 元数据(而不是整个文件)。处于此状态的 upper 文件标有 “trusted.overlayfs.metacopy” xattr,表明 upper 文件不包含数据。当文件打开以进行 WRITE 操作时,数据将在稍后复制 up。在复制 up lower 文件的数据后,将从 upper 文件中删除 “trusted.overlayfs.metacopy” xattr。

换句话说,这是延迟的数据复制 up 操作,并且仅在需要实际修改数据时才复制 up 数据。

有多种方法可以启用/禁用此功能。可以设置/取消设置配置选项 CONFIG_OVERLAY_FS_METACOPY,以默认启用/禁用此功能。或者,可以使用模块参数 metacopy=on/off 在模块加载时启用/禁用它。最后,还有一个每个挂载选项 metacopy=on/off 来启用/禁用每个挂载的此功能。

不要将 metacopy=on 与不受信任的 upper/lower 目录一起使用。否则,攻击者可能会创建一个带有适当的 REDIRECT 和 METACOPY xattrs 的手工制作的文件,并获得对 REDIRECT 指向的 lower 上的文件的访问权限。这在本地系统上是不可能的,因为设置 “trusted.” xattrs 需要 CAP_SYS_ADMIN。但是,这对于来自笔式驱动器等不受信任的层应该是可能的。

注意:redirect_dir={off|nofollow|follow[*]} 和 nfs_export=on 挂载选项与 metacopy=on 冲突,并将导致错误。

[*] 只有在给定 upperdir=... 的情况下,redirect_dir=follow 才与 metacopy=on 冲突。

仅数据 lower 层

启用 “metacopy” 功能后,overlayfs 常规文件可以是来自最多三个不同层的信息的组合

  1. 来自 upper 层中的文件的元数据

  2. 来自 lower 层中的文件的 st_ino 和 st_dev 对象标识符

  3. 来自另一个 lower 层(更下方)中的文件的数据

“lower 数据”文件可以位于任何 lower 层上,但不能位于最顶层的 lower 层上。

在最顶层的 lower 层下方,可以使用双冒号 (“::”) 分隔符将任意数量的最底层的 lower 层定义为 “仅数据” lower 层。不允许普通 lower 层位于仅数据层下方,因此不允许在双冒号 (“::”) 分隔符的右侧使用单冒号分隔符。

例如

mount -t overlay overlay -olowerdir=/l1:/l2:/l3::/do1::/do2 /merged

“仅数据” lower 层中的文件路径在合并的 overlayfs 目录中不可见,并且 “仅数据” lower 层中的文件的元数据和 st_ino/st_dev 在 overlayfs inode 中不可见。

仅当上面的 lower 层中的 “metacopy” 文件 “重定向” 到 “仅数据” lower 层中 “lower 数据” 文件的绝对路径时,“仅数据” lower 层中的文件数据才可能可见。

无需显式启用 “metacopy=on”,指定至少一个仅数据层足以启用将数据重定向到仅数据层。在这种情况下,其他形式的 metacopy 将被拒绝。注意:可以通过 “userxattr” 将此方式与仅数据层一起使用,在这种情况下,必须密切注意更改 “user.overlay.redirect” xattr 所需的权限,以防止滥用。

从内核版本 v6.8 开始,也可以使用新的挂载 API 中的 “datadir+” 挂载选项和 fsconfig 系统调用添加 “仅数据” lower 层。例如

fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l1", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l2", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "lowerdir+", "/l3", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do1", 0);
fsconfig(fs_fd, FSCONFIG_SET_STRING, "datadir+", "/do2", 0);

通过文件描述符指定层

从内核 v6.13 开始,overlayfs 支持通过文件描述符(而不是将它们指定为路径)来指定层。此功能可用于新的挂载 API 中带有 fsconfig 系统调用的 “datadir+”、“lowerdir+”、“upperdir” 和 “workdir+” 挂载选项

fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower1);
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower2);
fsconfig(fs_fd, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower3);
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data1);
fsconfig(fs_fd, FSCONFIG_SET_FD, "datadir+", NULL, fd_data2);
fsconfig(fs_fd, FSCONFIG_SET_FD, "workdir", NULL, fd_work);
fsconfig(fs_fd, FSCONFIG_SET_FD, "upperdir", NULL, fd_upper);

fs-verity 支持

在 lower 文件的元数据复制 up 期间,如果源文件启用了 fs-verity 并且启用了 overlay verity 支持,则 lower 文件的摘要将添加到 “trusted.overlay.metacopy” xattr。然后,这用于每次打开 metacopy 文件时验证 lower 文件的内容。

当使用包含 verity xattrs 的层时,这意味着 upper 层中的任何此类 metacopy 文件都保证与复制 up 时 lower 中的内容匹配。如果在任何时候(在挂载期间、重新挂载之后等)以任何方式替换或修改 lower 中的此类文件,则对 overlayfs 中相应文件的访问将导致 EIO 错误(由于 overlayfs 摘要检查,在打开时,或由于 fs-verity,在稍后的读取中),并且详细的错误会打印到内核日志中。有关 fs-verity 文件访问如何工作的更多详细信息,请参阅 Documentation/filesystems/fsverity.rst

Verity 可用作检测正在使用的 overlayfs 目录中意外更改的一般稳健性检查。但是,通过额外的注意,它也可以提供更强大的保证。例如,如果 upper 层是完全受信任的(通过使用 dm-verity 或类似的东西),则不受信任的 lower 层可用于为所有 metacopy 文件提供经过验证的文件内容。如果此外,不受信任的 lower 目录被指定为 “仅数据”,则它们只能提供此类文件内容,并且可以信任整个挂载与 upper 层匹配。

此功能由 “verity” 挂载选项控制,该选项支持以下值

  • “off”

    永远不会生成或使用 metacopy 摘要。如果未指定 verity 选项,则这是默认值。

  • “on”

    每当 metacopy 文件指定预期的摘要时,相应的数据文件必须与指定的摘要匹配。生成 metacopy 文件时,verity 摘要将基于源文件(如果它有摘要)设置在其中。

  • “require”

    与 “on” 相同,但此外,所有 metacopy 文件必须指定摘要(否则在打开时返回 EIO)。这意味着仅当数据文件启用了 fs-verity 时才使用元数据复制 up,否则使用完整的复制 up。

共享和复制层

多个 overlay 挂载之间可以共享 lower 层,这实际上是一种非常常见的做法。overlay 挂载可以使用与另一个 overlay 挂载相同的 lower 层路径,并且可以使用位于另一个 overlay lower 层路径下方或上方的 lower 层路径。

不允许使用已由另一个 overlay 挂载使用的 upper 层路径和/或 workdir 路径,并且可能会因 EBUSY 而失败。不允许使用部分重叠的路径,并且可能会因 EBUSY 而失败。如果从两个 overlayfs 挂载访问共享或重叠 upper 层和/或 workdir 路径的文件,则 overlay 的行为未定义,但不会导致崩溃或死锁。

允许使用 upper 层路径挂载 overlay,其中 upper 层路径之前由另一个已挂载的 overlay 与不同的 lower 层路径组合使用,除非启用了 “index” 或 “metacopy” 功能。

对于 “index” 功能,首次挂载时,lower 层根目录的 NFS 文件句柄以及 lower 文件系统的 UUID 会被编码并存储在 upper 层根目录上的 “trusted.overlay.origin” 扩展属性中。在后续挂载尝试中,lower 根目录文件句柄和 lower 文件系统 UUID 将与 upper 根目录中存储的原始文件句柄进行比较。如果验证 lower 根目录原始文件句柄失败,则挂载将因 ESTALE 而失败。如果 lower 文件系统不支持 NFS 导出、lower 文件系统没有有效的 UUID 或 upper 文件系统不支持扩展属性,则启用 “index” 的 overlayfs 挂载将因 EOPNOTSUPP 而失败。

对于 “metacopy” 功能,挂载时没有验证机制。因此,如果使用不同的 lower 文件系统集挂载相同的 upper 文件系统,则挂载可能会成功,但预计稍后会出现意外情况。因此,不要这样做。

复制 overlay 层到同一底层文件系统上或不同文件系统上的不同目录树,甚至复制到不同的机器,是一种非常常见的做法。对于 “index” 功能,尝试挂载复制的层将导致验证 lower 根文件句柄失败。

嵌套 overlayfs 挂载

可以使用存储在 overlayfs 挂载上的 lower 目录。对于常规文件,这不需要任何特殊的注意。但是,具有 overlayfs 属性的文件(例如 whiteout 或 “overlay.*” xattrs)将被底层 overlayfs 挂载解释并剥离。为了允许第二个 overlayfs 挂载查看属性,必须转义它们。

Overlayfs 特定 xattrs 通过使用特殊的 “overlay.overlay.” 前缀来转义。因此,lower 目录中带有 “trusted.overlay.overlay.metacopy” xattr 的文件将在 overlayfs 挂载中公开为带有 “trusted.overlay.metacopy” xattr 的常规文件。这可以通过多次重复前缀来嵌套,因为每个实例只删除一个前缀。

常规 whiteout 的 lower 目录将始终由 overlayfs 挂载处理,因此为了支持在 overlayfs 挂载中存储有效的 whiteout 文件,支持另一种形式的 whiteout。此形式是一个常规的、零大小的文件,其中 “overlay.whiteout” xattr 设置为 “overlay.opaque” xattr 设置为 “x” 的目录中(请参阅 whiteout 和 opaque 目录)。这些备用 whiteout 永远不会由 overlayfs 创建,但可以由生成 lower 层的用户空间工具(如容器)使用。可以使用标准 xattr 转义机制来转义这些备用 whiteout,以便正确地嵌套到任何深度。

非标准行为

当前版本的 overlayfs 可以充当大部分符合 POSIX 标准的文件系统。

以下是 overlayfs 当前无法处理的情况列表

  1. POSIX 规定更新读取的 st_atime。如果文件位于 lower 层上,则当前未完成此操作。

  2. 如果位于 lower 层上的文件以只读方式打开,然后使用 MAP_SHARED 进行内存映射,则对该文件的后续更改不会反映在内存映射中。

  3. 如果位于 lower 层上的文件正在执行,则打开该文件进行写入或截断该文件将不会因 ETXTBSY 而被拒绝。

以下选项允许 overlayfs 更像标准兼容的文件系统

redirect_dir

使用挂载选项或模块选项启用:“redirect_dir=on” 或使用内核配置选项 CONFIG_OVERLAY_FS_REDIRECT_DIR=y。

如果禁用此功能,则对 lower 或合并目录的 rename(2) 将因 EXDEV (“无效的跨设备链接”) 而失败。

index

通过挂载选项或模块选项“index=on”或内核配置选项 CONFIG_OVERLAY_FS_INDEX=y 启用。

如果禁用此功能,并且复制一个具有多个硬链接的文件,则将“破坏”该链接。更改不会传播到引用同一 inode 的其他名称。

xino

通过挂载选项“xino=auto”或“xino=on”,模块选项“xino_auto=on”或内核配置选项 CONFIG_OVERLAY_FS_XINO_AUTO=y 启用。通过对组成 overlay 的所有层使用相同的底层文件系统也可以隐式启用此选项。

如果禁用此功能或底层文件系统的 inode 号中没有足够的空闲位,则 overlayfs 将无法保证 stat(2) 返回的 st_ino 和 st_dev 的值以及 readdir(3) 返回的 d_ino 的值像在普通文件系统上一样。 例如,对于同一 overlay 文件系统中的两个对象,st_dev 的值可能不同,并且文件系统对象的 st_ino 值可能不是持久的,甚至可能在 overlay 文件系统挂载时发生更改,如上面的 Inode 属性 表中所总结的那样。

底层文件系统的更改

不允许在挂载的 overlay 文件系统的一部分时更改底层文件系统。如果底层文件系统发生更改,则 overlay 的行为是未定义的,但不会导致崩溃或死锁。

允许对上层树进行离线更改(overlay 未挂载时)。只有在未使用“metacopy”、“index”、“xino”和“redirect_dir”功能的情况下,才允许对下层树进行离线更改。如果修改了下层树,并且使用了这些功能中的任何一个,则 overlay 的行为是未定义的,但不会导致崩溃或死锁。

启用 overlay NFS 导出功能后,overlay 文件系统在底层下层的离线更改上的行为与禁用 NFS 导出时的行为不同。

在每次 copy_up 操作时,下层 inode 的 NFS 文件句柄以及下层文件系统的 UUID 会被编码并存储在上层 inode 的扩展属性“trusted.overlay.origin”中。

启用 NFS 导出功能后,查找合并的目录(该目录在查找路径或“trusted.overlay.redirect”扩展属性指向的路径处找到一个下层目录)将验证找到的下层目录文件句柄和下层文件系统 UUID 是否与 copy_up 时存储的原始文件句柄匹配。 如果找到的下层目录与存储的原始文件句柄不匹配,则该目录不会与上层目录合并。

NFS 导出

当底层文件系统支持 NFS 导出且启用了“nfs_export”功能时,可以将 overlay 文件系统导出到 NFS。

使用“nfs_export”功能,在 copy_up 任何下层对象时,会在索引目录下创建一个索引条目。索引条目的名称是 copy_up 原始文件句柄的十六进制表示。对于非目录对象,索引条目是上层 inode 的硬链接。对于目录对象,索引条目具有一个扩展属性“trusted.overlay.upper”,其中包含上层目录 inode 的编码文件句柄。

在编码来自 overlay 文件系统对象的文件句柄时,适用以下规则

  1. 对于非上层对象,编码来自下层 inode 的下层文件句柄

  2. 对于索引对象,编码来自 copy_up 原始对象的一个下层文件句柄

  3. 对于纯上层对象和现有的非索引上层对象,编码来自上层 inode 的上层文件句柄

编码的 overlay 文件句柄包括

  • 包括路径类型信息(例如,下层/上层)的标头

  • 底层文件系统的 UUID

  • 底层 inode 的底层文件系统编码

此编码格式与存储在扩展属性“trusted.overlay.origin”中的文件句柄的编码格式相同。

解码 overlay 文件句柄时,请按照以下步骤操作

  1. 按 UUID 和路径类型信息查找底层图层。

  2. 将底层文件系统文件句柄解码为底层 dentry。

  3. 对于下层文件句柄,按名称在索引目录中查找句柄。

  4. 如果在索引中找到一个 whiteout,则返回 ESTALE。 这表示在对其文件句柄进行编码后被删除的 overlay 对象。

  5. 对于非目录,从解码的底层 dentry、路径类型和索引 inode(如果找到)实例化一个断开连接的 overlay dentry。

  6. 对于目录,使用连接的底层解码的 dentry、路径类型和索引来查找连接的 overlay dentry。

解码非目录文件句柄可能会返回断开连接的 dentry。 copy_up 该断开连接的 dentry 将创建一个没有上层别名的上层索引条目。

当 overlay 文件系统具有多个下层时,中间层目录可能具有指向较低目录的“重定向”。 由于中间层“重定向”未建立索引,因此无法使用从“重定向”原始目录编码的较低文件句柄来查找中间层或上层目录。 同样,无法使用从“重定向”原始目录的后代编码的较低文件句柄来重建连接的 overlay 路径。 为了减轻无法从较低文件句柄解码目录的情况,这些目录会在编码时复制到上层,并编码为上层文件句柄。 在没有上层的 overlay 文件系统上,无法使用此缓解措施,并且此设置中的 NFS 导出需要关闭重定向跟随(例如,“redirect_dir=nofollow”)。

overlay 文件系统不支持非目录的可连接文件句柄,因此使用“subtree_check”exportfs 配置导出将导致通过 NFS 查找文件失败。

启用 NFS 导出功能后,所有目录索引条目都会在挂载时进行验证,以检查上层文件句柄是否已过时。在某些情况下,此验证可能会导致很大的开销。

注意:对于读写挂载,挂载选项 index=off,nfs_export=on 是冲突的,并且会导致错误。

注意:可以使用挂载选项 uuid=off 将文件句柄中底层文件系统的 UUID 替换为 null,并有效地禁用 UUID 检查。 如果复制了底层磁盘并且此副本的 UUID 发生更改,这将非常有用。 这仅适用于所有下层/上层/工作目录都在同一文件系统上的情况,否则它将回退到正常行为。

UUID 和 fsid

overlayfs 实例本身的 UUID 和 statfs(2) 报告的 fsid 由“uuid”挂载选项控制,该选项支持以下值

  • “null”

    overlayfs 的 UUID 为空。 fsid 取自最上层文件系统。

  • “off”

    overlayfs 的 UUID 为空。 fsid 取自最上层文件系统。底层图层的 UUID 将被忽略。

  • “on”

    生成 overlayfs 的 UUID 并用于报告唯一的 fsid。 UUID 存储在 xattr “trusted.overlay.uuid”中,使 overlayfs fsid 唯一且持久。此选项需要具有支持 xattr 的上层文件系统的 overlayfs。

  • “auto”:(默认)

    如果存在,则从 xattr “trusted.overlay.uuid”中获取 UUID。 首次挂载满足先决条件的新 overlay 文件系统时升级到 “uuid=on”。 对于从未与 “uuid=on” 挂载的现有 overlay 文件系统,降级到 “uuid=null”。

易失性挂载

这通过 “volatile” 挂载选项启用。 无法保证易失性挂载在崩溃后能幸存。 强烈建议仅当写入 overlay 的数据无需付出大量努力即可重新创建时才使用易失性挂载。

使用 “volatile” 选项挂载的优点是省略了对上层文件系统的所有形式的 sync 调用。

为了避免产生虚假的安全感,易失性挂载的 syncfs(和 fsync)语义与 VFS 的其余部分略有不同。 如果在上层目录的文件系统上发生任何写回错误(在发生易失性挂载之后),所有 sync 函数都将返回错误。 一旦达到此状态,文件系统将不会恢复,并且每个后续的 sync 调用都将返回错误,即使上层目录自上次 sync 调用以来没有遇到新的错误。

当 overlay 使用 “volatile” 选项挂载时,会创建目录 “$workdir/work/incompat/volatile”。 在下次挂载期间,overlay 会检查此目录,如果存在则拒绝挂载。 这强烈表明用户应丢弃上层和工作目录并创建一个新的。 在用户知道系统没有崩溃并且上层目录的内容完好无损的非常有限的情况下,可以删除 “volatile” 目录。

用户 xattr

“-o userxattr” 挂载选项强制 overlayfs 使用 “user.overlay.” xattr 命名空间,而不是 “trusted.overlay.”。 这对于非特权挂载 overlayfs 非常有用。

测试套件

David Howells 最初开发并由 Amir Goldstein 当前维护的测试套件位于

https://github.com/amir73il/unionmount-testsuite.git

以 root 身份运行

# cd unionmount-testsuite
# ./run --ov --verify