02

一段已租借的历史,变成一个可写的内核视图。

OverlayFS 连接层栈(LayerStack)存储与执行中的工作区。它在会话的挂载命名空间内,把固定的不可变 lower 层与该会话的私有可写状态组合起来。

第02节·覆盖层挂载

存储为层提供电源。命名空间拥有视图。

LayerStack does not expose a writable checkout. The runtime takes one pinned lease, adds fresh per-session writable directories, and asks the kernel to attach exactly one merged workspace inside the holder's mount namespace.

这座桥

两个输入在一个内核对象处相遇。

层栈提供稳定的下部路径,而租约则使它们保持活动状态。命名空间运行时提供 holder、用户和挂载命名空间文件描述符以及预先存在的工作区根。一次性 runner 加入这些命名空间并在那里构建覆盖。

所有权交接

在此切换期间,不会复制或具体化任何基础树。

层栈租约

[L42、S41、B1]

immutable · newest-first · pinned

一发runner

setns(用户,mnt)

configure · attach · mask · exit

已挂载工作区

/workspace

私有写·共享读

挂载结构

一根可见的根,四种不同的角色。

upperdir 和 lowerdirs 参与合并视图。 workdir 则不然:它是私有内核暂存,并且必须保持同一文件系统上 upperdir 的同级。

OverlayFS优先

最高可见源优先

1个会话
/workspace合并根
upper/ws-17rw

私人写入+whiteout

L0000421 · 最新已发布三角洲
S0000412 · 压缩的不可变历史
B000001-base3 · 最古老·共享基地
work/ws-17内核划痕 · 不可见
工作区根
工作负载将预先存在的挂载点视为合并的文件系统。
upperdir
会话-私有可写树。新文件、复制和删除元数据都位于此处。
Workdir
OverlayFS upperdir 旁边的暂存空间。它支持内核操作,但不属于可见堆栈的一部分。
Lowerdirs
已租借 不可变的层按最新优先顺序排列,共享基址最后。
原始挂载 API

配置分离。贴一次。

运行时直接调用Linux new-挂载API。它不会调用 挂载(8) binary, assemble a legacy option string, or attach a half-built filesystem to the daemon's namespace.

mount_overlay()
  1. 01
    fsopen

    打开一个新的覆盖文件系统上下文。

  2. 02
    fsconfig·lowerdir+

    附加每个 已租借 较低路径,最新的在前。

  3. 03
    fsconfig · userxattr

    启用用户命名空间兼容的覆盖元数据。

  4. 04
    fsconfig·上层/工作

    附上会话的真实可写路径。

  5. 05
    fsconfig_create

    要求内核完成文件系统配置。

  6. 06
    fsmount

    创建分离的读写挂载对象。

  7. 07
    move_mount

    将该对象附加到真实的工作区根。

Lowerdirs 已固定

每个 已租借 下层目录在不遵循其最终符号链接的情况下打开,并通过 /proc/self/fd/N 小路。重命名其原始路径无法重定向此挂载版本。

可写路径保持真实

upperdir、workdir 和工作区根经过验证并保持打开状态,但配置了真实路径,因为通用内核不接受对这些角色进行相同的 FD 路径处理。

Ordering + copy-on-write

数组顺序成为文件系统的真相。

发布前面加上层。租约将 manifest 命令复制到其路径中。工作区和 runner 消息保留该数组。最后,第一个 lowerdir+ 呼叫获得最高优先级。管道稍后不会排序或重新导出顺序。

跨越四个边界的一个不变量
  1. 01

    Manifest

    前置最新的 L*

  2. 02

    租约

    复制有序路径

  3. 03

    runner 请求

    序列化不变

  4. 04

    核心

    第一个 lowerdir+ 获胜

读取

first visible path wins

首先检查 upperdir,然后 已租借 从最新到最旧检查层。

第一次突变

lower file → copy-up

在应用写入之前,OverlayFS 可以将完整的下层文件复制到 upperdir 中。

删除

lower path → whiteout

删除标记隐藏不可变的较低路径,而不更改共享历史记录。

写时复制 不是写入时字节差异。 对仅较低文件的小编辑可能需要首先将整个文件复制到 upperdir 中。

捕获交接

upperdir 既是写入目标又是更改记录。

捕获不会复制合并后的工作区。它遍历私有 upperdir,保留 OverlayFS 删除和不透明语义,并准备候选的不可变增量。工作区协调决定该候选者是否可以成为共享历史。

内核状态成为发布候选状态
  1. 01

    停顿写道

    会话闸门停止围绕捕获的生命周期竞赛。

  2. 02

    行走upperdir

    读取创建的文件、复制的条目、whiteout 和不透明目录。

  3. 03

    阶段三角洲

    在 manifest 外部构建私有不可变负载和路径元数据。

  4. 04

    不可触摸

    冲突检测和合并决定发布是否可以提交。

覆盖元数据是语义的。 删除 whiteout 或不透明目录标记可以恢复旧的较低层路径。捕获必须翻译这些标记,而不是将它们视为一次性划痕。

命名空间拥有的生命周期

该守护进程协调挂载。它从未拥有挂载。

The one-shot runner joins only the holder's user and mount namespaces. After mounting and applying configured path masks, it deliberately gives up its userspace teardown guard. Kernel namespace references keep the overlay alive after the runner exits.

  1. 01

    Daemon

    生成 holder 并开放命名空间 FD

  2. 02

    runner

    加入 holder 用户 +挂载命名空间

  3. 03

    核心

    附加覆盖层;掩码配置的主机路径

  4. 04

    Holder

    命名空间固定挂载直至拆卸

存储路径被屏蔽

附加覆盖层后,现有配置的路径(例如运行时存储根)会在工作负载命名空间内接收一个小型、只读、无执行的 tmpfs 覆盖。

拆除遵循所有权

普通会话销毁杀死holder。当其隔离的挂载命名空间死亡时,内核会丢弃覆盖;然后,运行时删除会话临时树。

限制+失败语义

在附加之前拒绝歧义。保留其后的内核错误。

验证限制路径形状和目录标识。它不承诺每个 Linux 内核都接受每个层链;功能支持和实际堆栈深度仍然是内核的限制。

Linux 内核边界
挂载系统调用仅限 Linux;非 Linux 版本不提供真正的覆盖工作区。
非空租约
至少需要一个较低的层。工作区根和每个较低的层必须已存在。
目录完整性
根路径、下路径、上路径和工作路径作为目录打开,而不遵循最终的符号链接。
路径卫生
挂载输入在配置前拒绝逗号、冒号、反斜杠、控制空格和 NUL。
内核大小的堆栈
运行时未设置合成下层上限;内核拒绝不支持的链。
没有隐藏的后备方案
系统调用失败保留其操作上下文,而不是默默地更改挂载语义。

系统调用之前

输入无效

未创建挂载上下文。会话设置因验证详细信息而失败。

附着前

内核构建失败

拥有的文件描述符关闭;从未移动到根的分离的挂载将被丢弃。

附着后

命名空间生命周期

holder命名空间拥有实时挂载;普通的拆卸依赖于名称空间死亡,而不是惰性分离。

在线压缩重新挂载是有意保留的例外:它会暂存第二个 OverlayFS,通过预先打开的 FD 移动挂载点,并严格卸载,以便 EBUSY 保持可见,而不是被延迟分离隐藏。