/proc/self/mountinfo
给出安装点列表。我怀疑它们是按照最近的排序的已创建最后安装(mount --move
对它们没有影响)。
那么是否可以检查安装是否由于最近移动或创建的安装而变得不可访问?
man proc
显示 的第一个字段/proc/self/mountinfo
是“安装 ID”。但它没有解释检查给定路径上最上面的挂载 ID 的任何方法。
statvfs()
( /usr/bin/stat -f
) 可以检索其他内容,即“文件系统 ID”。我在每个文件系统中看到明显唯一的值...虽然它对我在这里提出的问题没有帮助,但它也足以解决我原来的问题...如果还有一种方法来关联文件系统 ID包含安装路径和所有安装选项。 (statvfs()
返回“挂载标志”,但不返回特定于文件系统的选项,与 不同/proc/self/mountinfo
)。
对此的第二个兴趣是我报告了不完整的超载检测作为缺乏df
。
我认为FSINFO_ATTR_MOUNT_INFO会很好地回答这个问题。然而,该补丁尚未被当前内核接受。
答案1
看来您应该能够通过查看 .txt 文件中的“父 ID”字段来计算出安装树的所有详细信息mountinfo
。
考虑两个安装座/dir/sub
和/dir
。如果 的父挂载/dir/sub
不是/dir
,而是/
,则/dir/sub
必须由 屏蔽/dir
。
或者,如果您的路径有两个安装/
,一个将是另一个的父安装,而子安装将是最上面的(可访问的)安装。
如果你有两个或者更多安装在完全相同的路径上,这些安装中最上面的安装不是任何其他安装的父级。
所以试试这个:
查看你的坐骑路径 P。如果 P 处有一个坐骑是你的坐骑 M 的子代,那么你的坐骑 M 就会被隐藏(停止)。
查看您的挂载路径 P。如果 P 处有一个挂载是您的挂载 M 的父挂载,则将 M 设置为该父挂载并重复 2。
查看路径 P 的最长子前缀 PRE,其上至少有一个挂载点。查找您的坐骑 M 的父坐骑 PAR。如果您的父坐骑 PAR 不存在,则您的坐骑 M 被隐藏(停止)。
递归地:如果您的父挂载 PAR 被隐藏,那么您的原始挂载也会被隐藏。
注意:路径/
没有更小的前缀。如果到达步骤 2 并且路径 P 等于/
,那么您就知道坐骑 M 没有隐藏。停止。
答案2
在实现了 Sourcejedi 的算法之后,我遇到了一个主要障碍,因为它给了我误报,即声明一个可见的挂载点被隐藏。让我们获取这组挂载点及其路径、ID 和父 ID:
- ID:2,“/”
- ID:3,“/a/b”,ParentID:2
- ID:4,“/a/b/c”,ParentID:3
- ID:5,“/a/b/c/e/f”,ParentID:4
- ID:40,“/a/b/c”,ParentID:4
- ID:5,“/a”,ParentID:2
- ID:50,“/a/b/c/e/f”,ParentID:5
仅 ID:2“/”、ID:5、“/a”和 ID:50“/a/b/c/e/f”的挂载点应该可见。在我实施 Sourcejedi 算法的第 3 步中,我得到了挂载点 ID:50 的误报。我思考了很长时间,这种情况下如何正确得到PRE和PAR,假设PRE是在“VFS视图”上计算的,即PRE是一个可能与多个挂载点相关的挂载路径。此外,有一个 PRE 可能仅引用来自不同层次结构的安装点,并且此组合可能会导致步骤 3 出错。
考虑到 Linux 内核的 VFS 通过从根开始沿着 VFS 节点行进来将路径解析为 inode”向下“因此,做同样的事情来确定挂载点是否隐藏可能是一个更安全的选择(而不是通过去弄清楚它)向上层次结构,从有问题的安装点开始)。缺点是,我们(或多或少)需要确定整个挂载点树的过度挂载,然后才能安全地确定特定挂载点是否确实被过度挂载/隐藏。
我的实施(Golang)如下并成功通过上面的测试用例;它的工作原理基本上如下:
- 从挂载路径“/”处的挂载点开始:
- 检查“就地”过度安装,其中当前安装点的子级之一的路径 P(child(MP)) 与当前安装点的路径 P(MP) 相同。
- 是的:将当前挂载点的所有子挂载点(与其子挂载点递归)标记为隐藏,除了超越当前挂载点的子挂载点。然后递归检查overmount的子挂载点,然后再做。
- 不:继续
- 通过比较路径来检查子级是否超过其他子级:ISPREFIX(P(ith-child(MP)),P(jth-child(MP))):
- 是的:递归地将 jth-child(MP) 及其所有(孙)子级标记为隐藏(超载)。
- 不:继续。
- 对于所有未隐藏(超载)的子项(MP):使用此算法递归检查它们。
- 检查“就地”过度安装,其中当前安装点的子级之一的路径 P(child(MP)) 与当前安装点的路径 P(MP) 相同。
链接到的实现有一些温和的优化,例如根据挂载点 ID 构建挂载点的链接树,并按挂载路径的长度(!)对子挂载点进行排序。例如,这允许简化对子挂载点超过其父挂载点的检查,只需查看第一个子挂载点(如果有)。它还将子挂载点集中的 O² 前缀超载搜索减半,不可否认,鉴于 O² 并没有获得太多好处...
答案3
如果您有权访问chroot
,则有一个 hack适用于最新的内核。至少它可以在 Linux v4.17 上运行。我认为它对 shell 不友好,但 python 还可以。
chroot
(请注意,如果您有权访问用户命名空间,则可以获得以下功能。使用unshare -rm --propagation slave
或等效项。)
# mount --bind / /mnt
# mount --make-slave /mnt # don't propagate submounts back to /
# mount --bind / /mnt
现在我们发现了超载,让我们调查一下。
# python3
...
>>> import os
>>> os.chdir("/")
>>> os.system("grep mnt proc/self/mountinfo")
231 73 253:0 / /mnt rw,relatime shared:1 - ext4 /dev/mapper/alan_dell_2016-fedora rw,seclabel
352 231 253:0 / /mnt rw,relatime shared:281 - ext4 /dev/mapper/alan_dell_2016-fedora rw,seclabel
0
>>> os.chroot("/mnt")
>>> os.system("cat proc/self/mountinfo")
352 231 253:0 / / rw,relatime shared:281 - ext4 /dev/mapper/alan_dell_2016-fedora rw,seclabel
0
此结果表明 上挂载了一个可访问的文件系统/mnt
,它是挂载 ID 为 352 的文件系统,等等。