文件系统挂载 API¶
概述¶
新挂载的创建现在将通过多步骤过程完成
创建文件系统上下文。
解析参数并将其附加到上下文。参数预期将从用户空间单独传递,但也可以处理传统的二进制参数。
验证并预处理上下文。
获取或创建一个超级块和可挂载根。
执行挂载。
返回附加到上下文的错误消息。
销毁上下文。
为支持此功能,file_system_type
结构体新增了两个字段
int (*init_fs_context)(struct fs_context *fc);
const struct fs_parameter_description *parameters;
第一个用于设置文件系统上下文的文件系统特定部分,包括额外空间;第二个指向参数描述,用于注册时验证和未来系统调用的查询。
请注意,安全初始化是在文件系统被调用之后完成的,以便可以首先调整命名空间。
文件系统上下文¶
超级块的创建和重新配置由文件系统上下文控制。这由 fs_context
结构体表示
struct fs_context {
const struct fs_context_operations *ops;
struct file_system_type *fs_type;
void *fs_private;
struct dentry *root;
struct user_namespace *user_ns;
struct net *net_ns;
const struct cred *cred;
char *source;
char *subtype;
void *security;
void *s_fs_info;
unsigned int sb_flags;
unsigned int sb_flags_mask;
unsigned int s_iflags;
enum fs_context_purpose purpose:8;
...
};
fs_context
字段如下
const struct fs_context_operations *ops这些是对文件系统上下文可以执行的操作(见下文)。这必须通过
->init_fs_context() file_system_type
操作设置。 struct file_system_type *fs_type指向正在构建或重新配置的文件系统的
file_system_type
的指针。这会保留对类型所有者的引用。 void *fs_private指向文件系统私有数据的指针。文件系统需要在此处存储它解析的任何选项。
struct dentry *root指向可挂载树根(并间接指向其超级块)的指针。这由
->get_tree()
操作填充。如果已设置,则还必须持有对root->d_sb
的活动引用。 struct user_namespace *user_ns struct net *net_ns这些是调用进程正在使用的命名空间的子集。它们保留对每个命名空间的引用。订阅的命名空间可能会被文件系统替换,以反映其他来源,例如自动挂载时的父挂载超级块。
const struct cred *cred挂载者的凭据。这会保留对凭据的引用。
char *source这指定了来源。它可能是一个块设备(例如
/dev/sda1
)或更不寻常的来源,例如 NFS 所需的“host:/path”。 char *subtype这是一个字符串,将添加到
/proc/mounts
中显示的类型以对其进行限定(由 FUSE 使用)。如果需要,文件系统可以设置此项。 void *securityLSM(Linux 安全模块)用于存放其超级块安全数据的位置。相关的安全操作在下面描述。
void *s_fs_info新超级块的提议
s_fs_info
,由sget_fc()
在超级块中设置。这可用于区分超级块。 unsigned int sb_flags unsigned int sb_flags_mask
super_block::s_flags
中哪些SB_*
标志位将被设置/清除。 unsigned int s_iflags当创建超级块时,这些将与
s->s_iflags
进行位或操作。 enum fs_context_purpose这表明了上下文的预期用途。可用值如下
FS_CONTEXT_FOR_MOUNT
,用于显式挂载的新超级块
FS_CONTEXT_FOR_SUBMOUNT
现有挂载的新自动子挂载
FS_CONTEXT_FOR_RECONFIGURE
更改现有挂载
挂载上下文通过调用 vfs_new_fs_context()
或 vfs_dup_fs_context()
创建,并使用 put_fs_context()
销毁。请注意,此结构体不进行引用计数。
VFS、安全和文件系统挂载选项通过 vfs_parse_mount_option()
单独设置。旧的 mount(2)
系统调用提供的作为数据页的选项可以使用 generic_parse_monolithic()
进行解析。
挂载时,文件系统可以从任何指针获取数据并将其附加到超级块(或任何其他地方),前提是它清除了挂载上下文中的指针。
文件系统也可以分配资源并将其与挂载上下文关联。例如,NFS 可能会关联适当的协议版本模块。
文件系统上下文操作¶
文件系统上下文指向一个操作表
struct fs_context_operations {
void (*free)(struct fs_context *fc);
int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
int (*parse_param)(struct fs_context *fc,
struct fs_parameter *param);
int (*parse_monolithic)(struct fs_context *fc, void *data);
int (*get_tree)(struct fs_context *fc);
int (*reconfigure)(struct fs_context *fc);
};
这些操作在挂载过程的各个阶段被调用,以管理文件系统上下文。它们如下:
void (*free)(struct fs_context *fc);当上下文被销毁时,调用此函数以清理文件系统上下文的文件系统特定部分。它应该知道上下文的部分内容可能已经被
->get_tree()
移除并置为 NULL。 int (*dup)(struct fs_context *fc, struct fs_context *src_fc);当文件系统上下文被复制以复制文件系统私有数据时调用。可能会返回错误以指示此操作失败。
警告
请注意,即使此操作失败,
put_fs_context()
也会紧随其后被调用,因此->dup()
必须使文件系统私有数据对于->free()
是安全的。 int (*parse_param)(struct fs_context *fc, struct fs_parameter *param);当参数被添加到文件系统上下文时调用。
param
指向键名和可能的数值对象。VFS 特定的选项将被剔除,并且上下文中的fc->sb_flags
会被更新。安全选项也将被剔除,并且fc->security
会被更新。参数可以使用
fs_parse()
和fs_lookup_param()
进行解析。请注意,来源被呈现为名为“source”的参数。如果成功,应返回 0;否则返回负错误码。
int (*parse_monolithic)(struct fs_context *fc, void *data);当调用
mount(2)
系统调用一次性传递整个数据页时调用。如果预期这只是一个由逗号分隔的“key[=val]”项列表,则可以将其设置为 NULL。返回值与
->parse_param()
相同。如果文件系统(例如 NFS)需要首先检查数据,然后发现它是标准的键值列表,则可以将其传递给
generic_parse_monolithic()
。 int (*get_tree)(struct fs_context *fc);调用此函数以获取或创建可挂载根和超级块,使用存储在文件系统上下文中的信息(重新配置通过不同的向量进行)。它可以将文件系统上下文中所需的任何资源分离出来,并将其转移到它创建的超级块。
成功时,它应将
fc->root
设置为可挂载根并返回 0。如果发生错误,则应返回负错误码。用户空间驱动的上下文的阶段将被设置为只允许在任何特定上下文上调用一次此函数。
int (*reconfigure)(struct fs_context *fc);调用此函数以使用存储在文件系统上下文中的信息来重新配置超级块。它可以将文件系统上下文中所需的任何资源分离出来,并将其转移到超级块。超级块可以从
fc->root->d_sb
中找到。成功时,它应返回 0。如果发生错误,则应返回负错误码。
注意
reconfigure
旨在替代remount_fs
。
文件系统上下文安全性¶
文件系统上下文包含一个安全指针,LSM 可以使用它为要挂载的超级块构建安全上下文。新的挂载代码为此目的使用了许多操作:
int security_fs_context_alloc(struct fs_context *fc, struct dentry *reference);调用此函数以初始化
fc->security
(预设为 NULL)并分配所需的任何资源。成功时应返回 0,失败时返回负错误码。如果上下文是为超级块重新配置(
FS_CONTEXT_FOR_RECONFIGURE
)而创建的,则reference
将非 NULL,在这种情况下它指示要重新配置的超级块的根目录项。在子挂载(FS_CONTEXT_FOR_SUBMOUNT
)的情况下,它也将非 NULL,在这种情况下它指示自动挂载点。 int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);调用此函数以初始化
fc->security
(预设为 NULL)并分配所需的任何资源。原始文件系统上下文由src_fc
指向,并可用作参考。成功时应返回 0,失败时返回负错误码。 void security_fs_context_free(struct fs_context *fc);调用此函数以清理附加到
fc->security
的任何内容。请注意,内容可能已在get_tree
期间转移到超级块,并且指针已清除。 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);为每个挂载参数调用此函数,包括来源。参数与
->parse_param()
方法的参数相同。它应返回 0 表示参数应传递给文件系统,返回 1 表示参数应被丢弃,或返回错误表示参数应被拒绝。
param
指向的值可以被修改(如果是字符串)或“窃取”(如果值指针被置为 NULL)。如果被“窃取”,则必须返回 1 以防止其传递给文件系统。 int security_fs_context_validate(struct fs_context *fc);在所有选项解析完毕后调用此函数,以整体验证集合并进行任何必要的分配,从而降低
security_sb_get_tree()
和security_sb_reconfigure()
失败的可能性。它应返回 0 或负错误码。在重新配置的情况下,目标超级块将通过
fc->root
访问。 int security_sb_get_tree(struct fs_context *fc);在挂载过程中调用此函数,以验证指定的超级块是否允许被挂载,并将安全数据传输到那里。它应返回 0 或负错误码。
void security_sb_reconfigure(struct fs_context *fc);调用此函数以将任何重新配置应用于 LSM 的上下文。它不得失败。错误检查和资源分配必须提前由参数解析和验证钩子完成。
int security_sb_mountpoint(struct fs_context *fc, struct path *mountpoint, unsigned int mnt_flags);在挂载过程中调用此函数,以验证附加到上下文的根目录项是否允许附加到指定的挂载点。成功时应返回 0,失败时返回负错误码。
VFS 文件系统上下文 API¶
有四个用于创建文件系统上下文的操作,以及一个用于销毁上下文的操作:
struct fs_context *fs_context_for_mount(struct file_system_type *fs_type, unsigned int sb_flags);分配一个文件系统上下文,用于设置新的挂载,无论是使用新的超级块还是共享现有超级块。这会设置超级块标志,初始化安全性,并调用
fs_type->init_fs_context()
来初始化文件系统私有数据。
fs_type
指定将管理上下文的文件系统类型,sb_flags
预设其中存储的超级块标志。 struct fs_context *fs_context_for_reconfigure( struct dentry *dentry, unsigned int sb_flags, unsigned int sb_flags_mask);分配一个文件系统上下文,用于重新配置现有超级块。
dentry
提供对要配置的超级块的引用。sb_flags
和sb_flags_mask
指示哪些超级块标志需要更改以及更改为哪个值。 struct fs_context *fs_context_for_submount( struct file_system_type *fs_type, struct dentry *reference);分配一个文件系统上下文,用于为自动挂载点或其他派生超级块创建新的挂载。
fs_type
指定将管理上下文的文件系统类型,引用目录项提供参数。命名空间也从引用目录项的超级块传播。请注意,参考目录项不需要与
fs_type
具有相同的文件系统类型。 struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc);复制文件系统上下文,复制所有记录的选项,并复制或额外引用其中持有的任何资源。当文件系统必须在现有挂载中获取挂载时(例如 NFS4 通过内部挂载目标服务器的根目录然后私有路径遍历到目标目录来实现),可以使用此功能。
新上下文中的用途继承自旧上下文。
void put_fs_context(struct fs_context *fc);销毁文件系统上下文,释放其持有的任何资源。这会调用
->free()
操作。创建文件系统上下文的任何人都可以调用此函数。警告
文件系统上下文不进行引用计数,因此这会导致无条件销毁。
在上述所有操作中,除了 put
操作外,返回值都是一个挂载上下文指针或一个负错误码。
对于其余操作,如果发生错误,将返回负错误码。
int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param);向文件系统上下文提供单个挂载参数。这包括来源/设备的规范,它被指定为“source”参数(如果文件系统支持,可以多次指定)。
param
指定参数键名和值。在传递给文件系统之前,会首先检查参数是否对应标准挂载标志(在这种情况下,它用于设置SB_xxx
标志并被消耗)或安全选项(在这种情况下,LSM 消耗它)。参数值是类型化的,可以是以下之一:
fs_value_is_flag
未赋值的参数
fs_value_is_string
值为字符串
fs_value_is_blob
值为二进制大对象
fs_value_is_filename
值为文件名* + dirfd
fs_value_is_file
值为一个打开的文件(
file*
)如果存在值,该值将存储在结构体的一个联合体中,位于
param->{string,blob,name,file}
之一。请注意,函数可能会“窃取”并清除指针,但随后负责处理该对象。 int vfs_parse_fs_string(struct fs_context *fc, const char *key, const char *value, size_t v_size);
vfs_parse_fs_param()
的一个包装器,它复制传递给它的值字符串。 int generic_parse_monolithic(struct fs_context *fc, void *data);解析
sys_mount()
数据页,假设其形式为由逗号分隔的key[=val]
选项文本列表。列表中的每个项都传递给vfs_mount_option()
。当->parse_monolithic()
方法为 NULL 时,这是默认行为。 int vfs_get_tree(struct fs_context *fc);获取或创建可挂载根和超级块,使用文件系统上下文中的参数来选择/配置超级块。这会调用
->get_tree()
方法。 struct vfsmount *vfs_create_mount(struct fs_context *fc);根据指定文件系统上下文中的参数创建挂载。请注意,这不会将挂载附加到任何地方。
超级块创建辅助函数¶
VFS 提供了许多辅助函数供文件系统用于创建或查找超级块。
struct super_block * sget_fc(struct fs_context *fc, int (*test)(struct super_block *sb, struct fs_context *fc), int (*set)(struct super_block *sb, struct fs_context *fc));这是核心例程。如果
test
非 NULL,它会使用测试函数搜索与fs_context
中持有的条件匹配的现有超级块。如果没有找到匹配项,则会创建一个新的超级块,并调用set
函数进行设置。在调用
set
函数之前,fc->s_fs_info
将被传输到sb->s_fs_info
——如果set
返回成功(即 0),fc->s_fs_info
将被清除。
以下辅助函数都封装了 sget_fc()
vfs_get_single_super
系统中只能存在一个这样的超级块。任何进一步尝试获取新超级块的操作都将获取此超级块(并且任何参数差异都将被忽略)。
vfs_get_keyed_super
这种类型的多个超级块可能存在,并且它们以其
s_fs_info
指针为键(例如,这可能指一个命名空间)。
vfs_get_independent_super
这种类型的多个独立超级块可能存在。此函数从不匹配现有的超级块,并且总是创建一个新的。
参数描述¶
参数使用 linux/fs_parser.h
中定义的结构体进行描述。有一个核心描述结构体将所有内容链接在一起:
struct fs_parameter_description {
const struct fs_parameter_spec *specs;
const struct fs_parameter_enum *enums;
};
例如
enum {
Opt_autocell,
Opt_bar,
Opt_dyn,
Opt_foo,
Opt_source,
};
static const struct fs_parameter_description afs_fs_parameters = {
.specs = afs_param_specs,
.enums = afs_param_enums,
};
成员如下:
const struct fs_parameter_specification *specs;参数规范表,以空条目终止,其中条目类型为:
struct fs_parameter_spec { const char *name; u8 opt; enum fs_parameter_type type:8; unsigned short flags; };“name”字段是一个字符串,用于精确匹配参数键(无通配符、模式且不区分大小写),“opt”是在成功匹配时
fs_parser()
函数将返回的值。“type”字段指示所需的值类型,并且必须是以下之一:
类型名称
预期值
结果在
fs_param_is_flag
无值
不适用
fs_param_is_bool
布尔值
result->boolean
fs_param_is_u32
32 位无符号整型
result->uint_32
fs_param_is_u32_octal
32 位八进制整型
result->uint_32
fs_param_is_u32_hex
32 位十六进制整型
result->uint_32
fs_param_is_s32
32 位有符号整型
result->int_32
fs_param_is_u64
64 位无符号整型
result->uint_64
fs_param_is_enum
枚举值名称
result->uint_32
fs_param_is_string
任意字符串
param->string
fs_param_is_blob
二进制大对象
fs_param_is_blockdev
块设备路径
需要查找
fs_param_is_path
路径
fs_param_is_fd
fs_param_is_path
文件描述符
fs_param_is_uid
result->int_32
用户 ID (u32)
result->uid
fs_param_is_gid
组 ID (u32)
result->gid
请注意,如果值为
fs_param_is_bool
类型,fs_parse()
将尝试将任何字符串值与“0”、“1”、“no”、“yes”、“false”、“true”进行匹配。每个参数也可以用“flags”限定:
fs_param_v_optional
值是可选的
fs_param_neg_with_no
如果键以“no”为前缀,则
result->negated
被设置
fs_param_neg_with_empty
如果值为“”,则
result->negated
被设置
fs_param_deprecated
该参数已废弃。
这些被一些方便的包装器封装:
宏
指定
fsparam_flag()
fsparam_flag_no()
fs_param_is_flag
fs_param_is_flag
,fs_param_neg_with_no
fsparam_bool()
fsparam_u32()
fs_param_is_bool
fsparam_u32oct()
fs_param_is_u32
fsparam_s32()
fs_param_is_u32_octal
fsparam_u64()
fs_param_is_s32
fsparam_enum()
fs_param_is_u64
fsparam_string()
fs_param_is_enum
fsparam_blob()
fs_param_is_string
fsparam_bdev()
fs_param_is_blob
fsparam_path()
块设备路径
fsparam_fd()
路径
fsparam_uid()
文件描述符
fsparam_gid()
用户 ID (u32)
所有这些都接受两个参数,即名称字符串和选项编号——例如:
组 ID (u32)
还提供了一个附加宏
__fsparam()
,它接受一对额外参数来指定不匹配上述任何宏的任何内容的类型和标志。static const struct fs_parameter_spec afs_param_specs[] = { fsparam_flag ("autocell", Opt_autocell), fsparam_flag ("dyn", Opt_dyn), fsparam_string ("source", Opt_source), fsparam_flag_no ("foo", Opt_foo), {} };枚举值名称到整数映射的表,以空条目终止。其类型为:
const struct fs_parameter_enum *enums;其中数组是 { 参数 ID, 名称 } 键控元素的无序列表,指示要映射到的值,例如:
struct fs_parameter_enum { u8 opt; char name[14]; u8 value; };如果遇到类型为
fs_param_is_enum
的参数,fs_parse()
将尝试在枚举表中查找该值,并将结果存储在解析结果中。static const struct fs_parameter_enum afs_param_enums[] = { { Opt_bar, "x", 1}, { Opt_bar, "y", 23}, { Opt_bar, "z", 42}, };解析器应由
file_system_type
结构体中的parser
指针指向,因为这将提供注册时的验证(如果CONFIG_VALIDATE_FS_PARSER=y
),并允许使用fsinfo()
系统调用从用户空间查询描述。
参数辅助函数¶
提供了许多辅助函数,以帮助文件系统或 LSM 处理给定参数。
在名称到整数映射表中按名称查找常量。该表是一个以下类型的元素数组:
int lookup_constant(const struct constant_table tbl[], const char *name, int not_found);如果找到匹配项,则返回相应的值。如果未找到匹配项,则返回
not_found
值。struct constant_table { const char *name; int value; };这会对参数描述执行一些验证检查。如果描述良好则返回 true,否则返回 false。如果验证失败,它将把错误记录到内核日志缓冲区。
bool fs_validate_description(const char *name, const struct fs_parameter_description *desc);这是参数的主要解释器。它使用参数描述按键名查找参数,并将其转换为选项编号(并返回)。
int fs_parse(struct fs_context *fc, const struct fs_parameter_description *desc, struct fs_parameter *param, struct fs_parse_result *result);如果成功,并且参数类型指示结果是布尔型、整数型、枚举型、uid 型或 gid 型,则此函数会转换该值,并将结果存储在
result->{boolean,int_32,uint_32,uint_64,uid,gid}
中。如果最初未找到匹配项,并且键以“no”为前缀且不存在值,则将尝试查找已移除前缀的键。如果这与类型设置了
fs_param_neg_with_no
标志的参数匹配,则将进行匹配,并且result->negated
将设置为 true。如果参数不匹配,将返回
-ENOPARAM
;如果参数匹配但值有误,将返回-EINVAL
;否则将返回参数的选项编号。此函数接受一个携带字符串或文件名类型的参数,并尝试对其进行路径查找。如果参数期望一个块设备,则会检查 inode 是否确实表示一个块设备。
int fs_lookup_param(struct fs_context *fc, struct fs_parameter *value, bool want_bdev, unsigned int flags, struct path *_path);如果成功则返回 0,并且
*_path
将被设置;否则返回负错误码。©内核开发社区。 | 由 Sphinx 5.3.0 和 Alabaster 0.7.16 提供支持 | 页面源文件