关注了就能看到更多这么棒的文章哦~
listmount() and statmount()
By Jonathan Corbet
November 10, 2023
ChatGPT translation
https://lwn.net/Articles/950569/
多年前,Unix 或 Linux 机器上挂载(mount)的文件系统很少,且比较固定。添加文件系统通常是一个罕见的事件,一般是在购买新驱动器的时候才会发生。相比之下,具有大量容器的现代系统中可能会有很多个 mount 好的文件系统,并且经常动态变化。正如讨论的那样,在2023 LSFMM+BPF峰会上,Linux 内核提供有关挂载文件系统的信息的机制没有跟上这一最新趋势,导致了系统管理上的问题。现在,由 Miklos Szeredi提出的两个新的系统调用似乎将为此提供一些迫切需要的缓解。
即使在没有容器的情况下,在典型的 Linux 系统上,mount 的文件系统列表也在增长,部分原因是内核提供的虚拟文件系统数量增加。例如,在编者的基本桌面系统中,/proc/self/mountinfo 列出了 34 个挂载点,其中很少分区是对应于实际存储设备的。随着这个虚拟文件内容变得越来越长,对于系统管理工具(以及人类)来说也更难对它进行处理了。因此引出了新增的系统调用 listmount()
和 statmount()
,它们提供了一种替代方法,而不用再查看 mountinfo
文件了。
然而,在实现这些系统调用之前,Szeredi 必须解决一个相关的问题。系统中的每个挂载都被分配了一个挂载 ID 来标识它;该 ID 可以从statx()
获取,这是在新系统调用中指明哪个挂载的直接方式。然而,如果卸载了文件系统,其 ID 将被内核重新使用以标识未来的新挂载,因此这是一个含义不唯一的标识符。明显的解决方案是不要再重用已经用过的挂载 ID,因为没有必要。
不幸的是,有一些用户空间程序假定挂载 ID 是一个 32 位的数量,尽管在 statx()
系统调用中被定义为 _u64
。Systemd 在LSFMM+BPF讨论期间被确定为其中一个例子程序. 使 32 位挂载 ID 在系统的整个生命周期内是唯一的,就意味着只允许有 40 亿个挂载,这在某些情况下显然是有限制的;它也可能被攻击者故意进行溢出攻击。因此,需要一个新的、更明确为 64 位的挂载 ID。这组补丁系列添加了这样的一个 ID,以及一个新的 statx()
标志(STATX_MNT_ID_UNIQUE),该标志会返回 unique ID 而不是 32 位 ID(当然,后者不能被删除)。为了避免混淆这两个 ID,唯一挂载(unique mount) ID 的最小值是 2^32。
在此基础上,就可以添加这两个系统调用了。第一个是这样的:
struct __mount_arg {
__u64 mnt_id;
__u64 request_mask;
};
int listmount(const struct __mount_arg *req, u64 *buf, size_t bufsize,
unsigned int flags);
调用 listmount()
就会返回在由 req->mnt_id
标识的挂载点下挂载的文件系统列表,其中该 ID 必须是唯一值(unique)类型的。得到的结果作为唯一挂载 ID 的数组返回到 buf
中,其长度为 bufsize
。通常,不可访问的挂载点(例如,可能在不同的挂载命名空间中挂载)将被省略;将 flags
设置为 LISTMOUNT_UNREACHABLE
会导致也把它们都列出来;此选项需要 CAP_SYS_ADMIN
权限。 LISTMOUNT_RECURSIVE
标志将导致 listmount()
对起始挂载点下的层次结构进行深度优先遍历,并列出在那里找到的所有挂载;否则,只返回直接子挂载(direct child mount)。返回值是返回的挂载 ID 的个数(或错误代码)。
req 结构的 request_mask
字段不会被 listmount()
使用,必须为零。
另一个调用 statmount() 会返回指定挂载的详细信息:
int statmount(const struct __mount_arg *req, struct statmnt *buf,
size_t bufsize, unsigned int flags);
在这个系统调用中, req->mnt_id
仍然像以前一样指示感兴趣的挂载点,而 req->request_mask
告诉内核请求哪些信息。 flags
值必须为零,而 buf
指向一个以此结构开始的缓冲区(长度为 bufsize):
struct statmount {
__u32 size; /* Total size, including strings */
__u32 __spare1;
__u64 mask; /* What results were written */
__u32 sb_dev_major; /* Device ID */
__u32 sb_dev_minor;
__u64 sb_magic; /* ..._SUPER_MAGIC */
__u32 sb_flags; /* MS_{RDONLY,SYNCHRONOUS,DIRSYNC,LAZYTIME} */
__u32 fs_type; /* [str] Filesystem type */
__u64 mnt_id; /* Unique ID of mount */
__u64 mnt_parent_id; /* Unique ID of parent (for root == mnt_id) */
__u32 mnt_id_old; /* Reused IDs used in proc/.../mountinfo */
__u32 mnt_parent_id_old;
__u64 mnt_attr; /* MOUNT_ATTR_... */
__u64 mnt_propagation; /* MS_{SHARED,SLAVE,PRIVATE,UNBINDABLE} */
__u64 mnt_peer_group; /* ID of shared peer group */
__u64 mnt_master; /* Mount receives propagation from this ID */
__u64 propagate_from; /* Propagation from in current namespace */
__u32 mnt_root; /* [str] Root of mount relative to root of fs */
__u32 mnt_point; /* [str] Mountpoint relative to current root */
__u64 __spare2[50];
char str[]; /* Variable size part containing strings */
};
内核不一定会填充此结构的所有字段;相反,它只提供了 req->request_mask
字段中指示的那些信息。可用的 request 值包括:
STMT_SB_BASIC :挂载的“基本”超级块数据,具体包括 sb_dev_major 、 sb_dev_minor 、 sb_magic 和 sb_flags 字段。
STMT_MNT_BASIC :更多基本数据:mnt_id、mnt_parent_id、mnt_id_old、mnt_parent_id_old、mnt_attr、mnt_propagation、mnt_peer_group 和 mnt_master。
STMT_PROPAGATE_FROM :填充 propagate_from 字段。 (有关挂载传播 mount propagation 的详细信息,请参见共享子树shared subtrees文档)。
生成字符串的请求处理方式有点不同。实际的字符串数据将写入结构后面的内存中(buf 必须要达到足以容纳该数据),并且字符串的开始偏移将存储在相关的结构字段中。返回字符串的请求包括:
STMT_FS_TYPE:在结构后存储文件系统类型的字符串表示形式,将字符串的偏移存储在 fs_type 中。
STMT_MNT_ROOT:在结构后存储根文件系统的路径,将字符串的偏移存储在 mnt_root 中。
STMT_MNT_POINT:在结构后存储挂载点的路径,将字符串的偏移存储在 mnt_point 中。
成功返回时,mask 字段将被设置为指示内核写入结构的其他字段中的哪些结果。
VFS 维护者 Christian Brauner 已经接受了此系列,可能会在 6.8 版本中合并。但他在其中做了一些更改: struct statmnt
被重命名为 struct statmount
,而 struct __mount_arg
变成了 struct mnt_id_req
。"库可以以任何形式暴露这些值,但我们也会有直接的使用者。我更希望这个结构没有下划线并且得到正式认可。" 尽管最终尚未合入 linux-next 中,但一旦 6.7 合并窗口关闭,它应该很可能会出现在 linux-next 中。
C 库提供的接口与此处显示的可能有所不同,这也并不奇怪。例如, mnt_id_req
结构被用于简化跨多个体系结构的兼容性,但用户空间库没有相同的担忧,可能不希望暴露该结构。在这些系统调用出现在发布的内核中之前,这样的细节不太可能被确定。然而,最终将会有一种更好、更容易获取有关已挂载的文件系统信息的方法。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~