LWN: Btrfs inode号引起的麻烦!
共 2623字,需浏览 6分钟
·
2021-09-06 01:56
关注了就能看到更多这么棒的文章哦~
The Btrfs inode-number epic (part 1: the problem)
By Jonathan Corbet
August 20, 2021
DeepL assisted translation
https://lwn.net/Articles/866582/
类 Unix 系统(以及它的用户)往往希望所有的文件系统的行为方式都能保持一致。但是这些用户也经常会对新的文件系统感兴趣,而这些时髦的文件系统通常提供了 Unix 文件系统模型的开发者以前从未设想过的功能。随着时间的推移,这已经导致了一些有意思的互不兼容的行为差异。Btrfs 当然就是一个例子。它提供了许多在其他系统中很少见的功能,而其中一些功能与传统上对文件系统的看法不太一致。最近,Neil Brown 一直在努力解决一个与 Btrfs 处理 inode 号有关的特定问题,这就是一个让大家感到很混乱的行为。
Btrfs 的关键功能之一是 subvolume,这本质上是在一个存储卷中维护的一个独立文件系统。snapshot 则是 subvolume 中的一种常用形式,用来在一个给定的时间点上抓拍另一个 subvolume 当前状态并生成副本,而底层的数据中一直没有改变过的部分则会保持共享,不浪费空间。subvolume 还有其他的应用场景,而且往往得到了大量的使用。Btrfs 文件系统中可以包含成千上万的 subvolume。
Btrfs subvolume 带来了一些有趣的独特之处。它们可以互相独立 mount 进来,就像是完全互不相干的多个文件系统一样,但它们也可以作为文件系统层次结构的一部分,就是从 root 目录的角度来看的。因此,我们可以 mount subvolume,但如果 mount 了更高层级的目录的话,subvolume 也可以不用 mount 就能直接被访问到。想象一下,如果 /dev/sda1 包含一个 Btrfs 文件系统,并被 mount 到/butter,那么我们可以用以下命令来创建一对 subvolume:
> cd /butter
> btrfs subvolume create subv1
> btrfs subvolume create subv2
现在,/butter 的根目录将包含两个子目录(subv1 和 subv2):
> tree /butter
/butter
├── subv1
└── subv2
2 directories, 0 files
它们在大多数时候就像是一个目录一样,但是,由于它们实际上是 subvolume,所以其实会有一些区别。比如说,不能将文件从一个目录中 rename 移动到另一个目录中去。而有相应权限的用户现在可以把 subv1 或 subv2(或两者同时)mount 进来,当作独立的文件系统来使用。但是,只要/butter 仍然是被 mount 的状态,那么两个 subvolume 都是可见的,就好像它们是同一个文件系统内的一部分一样。这种行为会带来一些有趣的后果,我们下面会看到。
Btrfs 在内部使用了一个 subvolume ID 号来标识不同的 subvolume,但是用户空间没法直接看到这个数字。而是通过文件系统为每个 subvolume 分配了一个独立的设备号(就是常见的 major/minor 这种形式),这个可以通过 stat() 等系统调用查看到。不过,如果 subvolume 没有明确 mount 进来的话,这个数字就不会显示在 /proc/self/mountinfo 这个文件中,从而一些软件查看系统中有哪些文件系统的时候会得到不同的结论。[更新:正如 Brown 私下告诉我们的,即使 subvolume 被明确 mount 上来,这里也不会显示那些数字编号。] 在一个 subvolume 内的文件上调用 stat() 的话就会返回一个设备号,而这个设备号并不存在于 mountinfo 文件中,这种情况有时会让那些不了解这个内情的应用程序处理时出现问题。
情况还可以更糟。由于 Btrfs 对每个 subvolume 都有一个内部的唯一 ID,它觉得没有必要在这些 subvolume 之间确保 inode 编号不会重复。结果呢,某个进程从 root 路径进入一个 Btrfs 文件系统的时候,很可能会看到多个具有相同 inode 号的文件。类似 find 这样的工具就会使用 inode 号来跟踪他们已经遍历过的文件,从而检测出发生了文件系统循环问题。对于本地 mount 的 Btrfs 文件系统,基本还是能够按预期来进行的,因为即使不同 subvolume 上的两个文件可能有相同的 inode 号,它们还是会有不同的设备号,因此能判断出来两者是不同的。
然而,内核的 NFS daemon 守护程序则会碰到更难处理的情况。它不能向 NFS 客户端展示所有这些生成的设备号,因为那将需要所有的 subvolume(记得吗,系统中可能会有有成千上万的 subvolume)都要在客户端这一侧显示为完全独立的 mount 点。所以一个通过 NFS export 出来的 Btrfs 文件系统在所有 subvolume 显示出来的都是相同的设备号(也就是 root 文件节点的设备号)。这在大多数情况下是不会出问题的,但这样一来我们就无法在一个 NFS mount 过来的带有 subvolume 的 Btrfs 文件系统上使用 find 这类工具了。这个完全统一的设备号会使得它无法区分不同 subvolume 上具有相同 inode 号的文件,导致 find 中止查找,并报出发现了文件系统循环的错误。于是我们会时不时看到有用户抱怨,以及希望能用什么方法来改善这种情况。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~