From cd140ce9f611a5e9d2a5989a282b75e55c71dab3 Mon Sep 17 00:00:00 2001 From: Hongbo Li Date: Thu, 30 May 2024 20:01:11 +0800 Subject: [PATCH 01/20] hostfs: convert hostfs to use the new mount API Convert the hostfs filesystem to the new internal mount API as the old one will be obsoleted and removed. This allows greater flexibility in communication of mount parameters between userspace, the VFS and the filesystem. See Documentation/filesystems/mount_api.txt for more information. Signed-off-by: Hongbo Li Link: https://lore.kernel.org/r/20240530120111.3794664-1-lihongbo22@huawei.com Signed-off-by: Christian Brauner --- fs/hostfs/hostfs_kern.c | 83 ++++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 21 deletions(-) diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index a73d27c4dd58..6ec3c368d7bf 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -16,11 +16,16 @@ #include #include #include +#include #include #include "hostfs.h" #include #include +struct hostfs_fs_info { + char *host_root_path; +}; + struct hostfs_inode_info { int fd; fmode_t mode; @@ -90,8 +95,10 @@ static char *__dentry_name(struct dentry *dentry, char *name) char *p = dentry_path_raw(dentry, name, PATH_MAX); char *root; size_t len; + struct hostfs_fs_info *fsi; - root = dentry->d_sb->s_fs_info; + fsi = dentry->d_sb->s_fs_info; + root = fsi->host_root_path; len = strlen(root); if (IS_ERR(p)) { __putname(name); @@ -196,8 +203,10 @@ static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) long long f_bavail; long long f_files; long long f_ffree; + struct hostfs_fs_info *fsi; - err = do_statfs(dentry->d_sb->s_fs_info, + fsi = dentry->d_sb->s_fs_info; + err = do_statfs(fsi->host_root_path, &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), &sf->f_namelen); @@ -245,7 +254,11 @@ static void hostfs_free_inode(struct inode *inode) static int hostfs_show_options(struct seq_file *seq, struct dentry *root) { - const char *root_path = root->d_sb->s_fs_info; + struct hostfs_fs_info *fsi; + const char *root_path; + + fsi = root->d_sb->s_fs_info; + root_path = fsi->host_root_path; size_t offset = strlen(root_ino) + 1; if (strlen(root_path) > offset) @@ -922,10 +935,11 @@ static const struct inode_operations hostfs_link_iops = { .get_link = hostfs_get_link, }; -static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) +static int hostfs_fill_super(struct super_block *sb, struct fs_context *fc) { + struct hostfs_fs_info *fsi = sb->s_fs_info; struct inode *root_inode; - char *host_root_path, *req_root = d; + char *host_root = fc->source; int err; sb->s_blocksize = 1024; @@ -939,15 +953,15 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) return err; /* NULL is printed as '(null)' by printf(): avoid that. */ - if (req_root == NULL) - req_root = ""; + if (fc->source == NULL) + host_root = ""; - sb->s_fs_info = host_root_path = - kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root); - if (host_root_path == NULL) + fsi->host_root_path = + kasprintf(GFP_KERNEL, "%s/%s", root_ino, host_root); + if (fsi->host_root_path == NULL) return -ENOMEM; - root_inode = hostfs_iget(sb, host_root_path); + root_inode = hostfs_iget(sb, fsi->host_root_path); if (IS_ERR(root_inode)) return PTR_ERR(root_inode); @@ -955,7 +969,7 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) char *name; iput(root_inode); - name = follow_link(host_root_path); + name = follow_link(fsi->host_root_path); if (IS_ERR(name)) return PTR_ERR(name); @@ -972,11 +986,38 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) return 0; } -static struct dentry *hostfs_read_sb(struct file_system_type *type, - int flags, const char *dev_name, - void *data) +static int hostfs_fc_get_tree(struct fs_context *fc) { - return mount_nodev(type, flags, data, hostfs_fill_sb_common); + return get_tree_nodev(fc, hostfs_fill_super); +} + +static void hostfs_fc_free(struct fs_context *fc) +{ + struct hostfs_fs_info *fsi = fc->s_fs_info; + + if (!fsi) + return; + + kfree(fsi->host_root_path); + kfree(fsi); +} + +static const struct fs_context_operations hostfs_context_ops = { + .get_tree = hostfs_fc_get_tree, + .free = hostfs_fc_free, +}; + +static int hostfs_init_fs_context(struct fs_context *fc) +{ + struct hostfs_fs_info *fsi; + + fsi = kzalloc(sizeof(*fsi), GFP_KERNEL); + if (!fsi) + return -ENOMEM; + + fc->s_fs_info = fsi; + fc->ops = &hostfs_context_ops; + return 0; } static void hostfs_kill_sb(struct super_block *s) @@ -986,11 +1027,11 @@ static void hostfs_kill_sb(struct super_block *s) } static struct file_system_type hostfs_type = { - .owner = THIS_MODULE, - .name = "hostfs", - .mount = hostfs_read_sb, - .kill_sb = hostfs_kill_sb, - .fs_flags = 0, + .owner = THIS_MODULE, + .name = "hostfs", + .init_fs_context = hostfs_init_fs_context, + .kill_sb = hostfs_kill_sb, + .fs_flags = 0, }; MODULE_ALIAS_FS("hostfs"); From 104eef133fd9c17e4dc28bf43f592a86f26d8a59 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 11 Jun 2024 12:58:41 -0700 Subject: [PATCH 02/20] hostfs: Add const qualifier to host_root in hostfs_fill_super() After the recent conversion to the new mount API, there is a warning when building hostfs (which may be upgraded to an error via CONFIG_WERROR=y): fs/hostfs/hostfs_kern.c: In function 'hostfs_fill_super': fs/hostfs/hostfs_kern.c:942:27: warning: initialization discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers] 942 | char *host_root = fc->source; | ^~ Add the 'const' qualifier, as host_root will not be modified after its assignment. Move the assignment to keep the existing reverse Christmas tree order intact. Fixes: cd140ce9f611 ("hostfs: convert hostfs to use the new mount API") Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20240611-hostfs-fix-mount-api-conversion-v1-1-ef75bbc77f44@kernel.org Acked-by: Richard Weinberger Signed-off-by: Christian Brauner --- fs/hostfs/hostfs_kern.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 6ec3c368d7bf..e44747bbfe87 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -938,8 +938,8 @@ static const struct inode_operations hostfs_link_iops = { static int hostfs_fill_super(struct super_block *sb, struct fs_context *fc) { struct hostfs_fs_info *fsi = sb->s_fs_info; + const char *host_root = fc->source; struct inode *root_inode; - char *host_root = fc->source; int err; sb->s_blocksize = 1024; From 9f111059e725f7ca79a136bfc734da3c8c1838b4 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:26:24 -0500 Subject: [PATCH 03/20] fs_parse: add uid & gid option option parsing helpers Multiple filesystems take uid and gid as options, and the code to create the ID from an integer and validate it is standard boilerplate that can be moved into common helper functions, so do that for consistency and less cut&paste. This also helps avoid the buggy pattern noted by Seth Jenkins at https://lore.kernel.org/lkml/CALxfFW4BXhEwxR0Q5LSkg-8Vb4r2MONKCcUCVioehXQKr35eHg@mail.gmail.com/ because uid/gid parsing will fail before any assignment in most filesystems. Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/de859d0a-feb9-473d-a5e2-c195a3d47abb@redhat.com Signed-off-by: Christian Brauner --- Documentation/filesystems/mount_api.rst | 9 +++++-- fs/fs_parser.c | 34 +++++++++++++++++++++++++ include/linux/fs_parser.h | 6 ++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/mount_api.rst b/Documentation/filesystems/mount_api.rst index 9aaf6ef75eb5..317934c9e8fc 100644 --- a/Documentation/filesystems/mount_api.rst +++ b/Documentation/filesystems/mount_api.rst @@ -645,6 +645,8 @@ The members are as follows: fs_param_is_blockdev Blockdev path * Needs lookup fs_param_is_path Path * Needs lookup fs_param_is_fd File descriptor result->int_32 + fs_param_is_uid User ID (u32) result->uid + fs_param_is_gid Group ID (u32) result->gid ======================= ======================= ===================== Note that if the value is of fs_param_is_bool type, fs_parse() will try @@ -678,6 +680,8 @@ The members are as follows: fsparam_bdev() fs_param_is_blockdev fsparam_path() fs_param_is_path fsparam_fd() fs_param_is_fd + fsparam_uid() fs_param_is_uid + fsparam_gid() fs_param_is_gid ======================= =============================================== all of which take two arguments, name string and option number - for @@ -784,8 +788,9 @@ process the parameters it is given. option number (which it returns). If successful, and if the parameter type indicates the result is a - boolean, integer or enum type, the value is converted by this function and - the result stored in result->{boolean,int_32,uint_32,uint_64}. + boolean, integer, enum, uid, or gid type, the value is converted by this + function and the result stored in + result->{boolean,int_32,uint_32,uint_64,uid,gid}. If a match isn't initially made, the key is prefixed with "no" and no value is present then an attempt will be made to look up the key with the diff --git a/fs/fs_parser.c b/fs/fs_parser.c index a4d6ca0b8971..24727ec34e5a 100644 --- a/fs/fs_parser.c +++ b/fs/fs_parser.c @@ -308,6 +308,40 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p, } EXPORT_SYMBOL(fs_param_is_fd); +int fs_param_is_uid(struct p_log *log, const struct fs_parameter_spec *p, + struct fs_parameter *param, struct fs_parse_result *result) +{ + kuid_t uid; + + if (fs_param_is_u32(log, p, param, result) != 0) + return fs_param_bad_value(log, param); + + uid = make_kuid(current_user_ns(), result->uint_32); + if (!uid_valid(uid)) + return inval_plog(log, "Invalid uid '%s'", param->string); + + result->uid = uid; + return 0; +} +EXPORT_SYMBOL(fs_param_is_uid); + +int fs_param_is_gid(struct p_log *log, const struct fs_parameter_spec *p, + struct fs_parameter *param, struct fs_parse_result *result) +{ + kgid_t gid; + + if (fs_param_is_u32(log, p, param, result) != 0) + return fs_param_bad_value(log, param); + + gid = make_kgid(current_user_ns(), result->uint_32); + if (!gid_valid(gid)) + return inval_plog(log, "Invalid gid '%s'", param->string); + + result->gid = gid; + return 0; +} +EXPORT_SYMBOL(fs_param_is_gid); + int fs_param_is_blockdev(struct p_log *log, const struct fs_parameter_spec *p, struct fs_parameter *param, struct fs_parse_result *result) { diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h index d3350979115f..6cf713a7e6c6 100644 --- a/include/linux/fs_parser.h +++ b/include/linux/fs_parser.h @@ -28,7 +28,7 @@ typedef int fs_param_type(struct p_log *, */ fs_param_type fs_param_is_bool, fs_param_is_u32, fs_param_is_s32, fs_param_is_u64, fs_param_is_enum, fs_param_is_string, fs_param_is_blob, fs_param_is_blockdev, - fs_param_is_path, fs_param_is_fd; + fs_param_is_path, fs_param_is_fd, fs_param_is_uid, fs_param_is_gid; /* * Specification of the type of value a parameter wants. @@ -57,6 +57,8 @@ struct fs_parse_result { int int_32; /* For spec_s32/spec_enum */ unsigned int uint_32; /* For spec_u32{,_octal,_hex}/spec_enum */ u64 uint_64; /* For spec_u64 */ + kuid_t uid; + kgid_t gid; }; }; @@ -131,6 +133,8 @@ static inline bool fs_validate_description(const char *name, #define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0, NULL) #define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0, NULL) #define fsparam_fd(NAME, OPT) __fsparam(fs_param_is_fd, NAME, OPT, 0, NULL) +#define fsparam_uid(NAME, OPT) __fsparam(fs_param_is_uid, NAME, OPT, 0, NULL) +#define fsparam_gid(NAME, OPT) __fsparam(fs_param_is_gid, NAME, OPT, 0, NULL) /* String parameter that allows empty argument */ #define fsparam_string_empty(NAME, OPT) \ From 748cddf13de55d9b1ab1988647d3b19d8d03b2e2 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:27:35 -0500 Subject: [PATCH 04/20] autofs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/faccdd51-07d6-413f-aa55-41bb0e7660df@redhat.com Acked-by: Ian Kent Signed-off-by: Christian Brauner --- fs/autofs/inode.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c index 1f5db6863663..cf792d4de4f1 100644 --- a/fs/autofs/inode.c +++ b/fs/autofs/inode.c @@ -126,7 +126,7 @@ enum { const struct fs_parameter_spec autofs_param_specs[] = { fsparam_flag ("direct", Opt_direct), fsparam_fd ("fd", Opt_fd), - fsparam_u32 ("gid", Opt_gid), + fsparam_gid ("gid", Opt_gid), fsparam_flag ("ignore", Opt_ignore), fsparam_flag ("indirect", Opt_indirect), fsparam_u32 ("maxproto", Opt_maxproto), @@ -134,7 +134,7 @@ const struct fs_parameter_spec autofs_param_specs[] = { fsparam_flag ("offset", Opt_offset), fsparam_u32 ("pgrp", Opt_pgrp), fsparam_flag ("strictexpire", Opt_strictexpire), - fsparam_u32 ("uid", Opt_uid), + fsparam_uid ("uid", Opt_uid), {} }; @@ -193,8 +193,6 @@ static int autofs_parse_param(struct fs_context *fc, struct fs_parameter *param) struct autofs_fs_context *ctx = fc->fs_private; struct autofs_sb_info *sbi = fc->s_fs_info; struct fs_parse_result result; - kuid_t uid; - kgid_t gid; int opt; opt = fs_parse(fc, autofs_param_specs, param, &result); @@ -205,16 +203,10 @@ static int autofs_parse_param(struct fs_context *fc, struct fs_parameter *param) case Opt_fd: return autofs_parse_fd(fc, sbi, param, &result); case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return invalfc(fc, "Invalid uid"); - ctx->uid = uid; + ctx->uid = result.uid; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return invalfc(fc, "Invalid gid"); - ctx->gid = gid; + ctx->gid = result.gid; break; case Opt_pgrp: ctx->pgrp = result.uint_32; From 49abee5991e18f14ec822ef53acd173ae58ff594 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:29:46 -0500 Subject: [PATCH 05/20] debugfs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/b2f44ee0-3cee-49eb-a416-f26a9306eb56@redhat.com Signed-off-by: Christian Brauner --- fs/debugfs/inode.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index dc51df0b118d..df80db408890 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -92,9 +92,9 @@ enum { }; static const struct fs_parameter_spec debugfs_param_specs[] = { - fsparam_u32 ("gid", Opt_gid), + fsparam_gid ("gid", Opt_gid), fsparam_u32oct ("mode", Opt_mode), - fsparam_u32 ("uid", Opt_uid), + fsparam_uid ("uid", Opt_uid), {} }; @@ -102,8 +102,6 @@ static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param { struct debugfs_fs_info *opts = fc->s_fs_info; struct fs_parse_result result; - kuid_t uid; - kgid_t gid; int opt; opt = fs_parse(fc, debugfs_param_specs, param, &result); @@ -112,16 +110,10 @@ static int debugfs_parse_param(struct fs_context *fc, struct fs_parameter *param switch (opt) { case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return invalf(fc, "Unknown uid"); - opts->uid = uid; + opts->uid = result.uid; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return invalf(fc, "Unknown gid"); - opts->gid = gid; + opts->gid = result.gid; break; case Opt_mode: opts->mode = result.uint_32 & S_IALLUGO; From dcffad38c7677a975ca36db34da4f085995ca50e Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:30:56 -0500 Subject: [PATCH 06/20] efivarfs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/ff7c415c-43d4-4a1e-a987-246c781f8850@redhat.com Signed-off-by: Christian Brauner --- fs/efivarfs/super.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index bb14462f6d99..a929f1b613be 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -275,8 +275,8 @@ enum { }; static const struct fs_parameter_spec efivarfs_parameters[] = { - fsparam_u32("uid", Opt_uid), - fsparam_u32("gid", Opt_gid), + fsparam_uid("uid", Opt_uid), + fsparam_gid("gid", Opt_gid), {}, }; @@ -293,14 +293,10 @@ static int efivarfs_parse_param(struct fs_context *fc, struct fs_parameter *para switch (opt) { case Opt_uid: - opts->uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(opts->uid)) - return -EINVAL; + opts->uid = result.uid; break; case Opt_gid: - opts->gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(opts->gid)) - return -EINVAL; + opts->gid = result.gid; break; default: return -EINVAL; From ffe1b94d7464bef15b6585f9e7f8192cd0668327 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:31:51 -0500 Subject: [PATCH 07/20] exfat: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/dda575de-11a7-4139-8a25-07957d311ed3@redhat.com Signed-off-by: Christian Brauner --- fs/exfat/super.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/exfat/super.c b/fs/exfat/super.c index 3d5ea2cfad66..a3c7173ef693 100644 --- a/fs/exfat/super.c +++ b/fs/exfat/super.c @@ -225,8 +225,8 @@ static const struct constant_table exfat_param_enums[] = { }; static const struct fs_parameter_spec exfat_parameters[] = { - fsparam_u32("uid", Opt_uid), - fsparam_u32("gid", Opt_gid), + fsparam_uid("uid", Opt_uid), + fsparam_gid("gid", Opt_gid), fsparam_u32oct("umask", Opt_umask), fsparam_u32oct("dmask", Opt_dmask), fsparam_u32oct("fmask", Opt_fmask), @@ -262,10 +262,10 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) switch (opt) { case Opt_uid: - opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); + opts->fs_uid = result.uid; break; case Opt_gid: - opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); + opts->fs_gid = result.gid; break; case Opt_umask: opts->fs_fmask = result.uint_32; From 6b5732b5ca4f8af2e733726a2b3deef2909b3819 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:32:45 -0500 Subject: [PATCH 08/20] ext4: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/a84be40d-5110-4dac-83b1-0ea8e043f0fd@redhat.com Signed-off-by: Christian Brauner --- fs/ext4/super.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c682fb927b64..0c614df3225d 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1721,8 +1721,8 @@ static const struct fs_parameter_spec ext4_param_specs[] = { fsparam_flag ("bsdgroups", Opt_grpid), fsparam_flag ("nogrpid", Opt_nogrpid), fsparam_flag ("sysvgroups", Opt_nogrpid), - fsparam_u32 ("resgid", Opt_resgid), - fsparam_u32 ("resuid", Opt_resuid), + fsparam_gid ("resgid", Opt_resgid), + fsparam_uid ("resuid", Opt_resuid), fsparam_u32 ("sb", Opt_sb), fsparam_enum ("errors", Opt_errors, ext4_param_errors), fsparam_flag ("nouid32", Opt_nouid32), @@ -2127,8 +2127,6 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param) struct fs_parse_result result; const struct mount_opts *m; int is_remount; - kuid_t uid; - kgid_t gid; int token; token = fs_parse(fc, ext4_param_specs, param, &result); @@ -2270,23 +2268,11 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param) ctx->spec |= EXT4_SPEC_s_stripe; return 0; case Opt_resuid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) { - ext4_msg(NULL, KERN_ERR, "Invalid uid value %d", - result.uint_32); - return -EINVAL; - } - ctx->s_resuid = uid; + ctx->s_resuid = result.uid; ctx->spec |= EXT4_SPEC_s_resuid; return 0; case Opt_resgid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) { - ext4_msg(NULL, KERN_ERR, "Invalid gid value %d", - result.uint_32); - return -EINVAL; - } - ctx->s_resgid = gid; + ctx->s_resgid = result.gid; ctx->spec |= EXT4_SPEC_s_resgid; return 0; case Opt_journal_dev: From eefc132477226c54796cd5dafc1ddf1850b8ba90 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:35:12 -0500 Subject: [PATCH 09/20] hugetlbfs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/6cb0dcfd-f837-463f-b5ec-0e7d2608b837@redhat.com Signed-off-by: Christian Brauner --- fs/hugetlbfs/inode.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 412f295acebe..81dab95f67ed 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -73,13 +73,13 @@ enum hugetlb_param { }; static const struct fs_parameter_spec hugetlb_fs_parameters[] = { - fsparam_u32 ("gid", Opt_gid), + fsparam_gid ("gid", Opt_gid), fsparam_string("min_size", Opt_min_size), fsparam_u32oct("mode", Opt_mode), fsparam_string("nr_inodes", Opt_nr_inodes), fsparam_string("pagesize", Opt_pagesize), fsparam_string("size", Opt_size), - fsparam_u32 ("uid", Opt_uid), + fsparam_uid ("uid", Opt_uid), {} }; @@ -1376,15 +1376,11 @@ static int hugetlbfs_parse_param(struct fs_context *fc, struct fs_parameter *par switch (opt) { case Opt_uid: - ctx->uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(ctx->uid)) - goto bad_val; + ctx->uid = result.uid; return 0; case Opt_gid: - ctx->gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(ctx->gid)) - goto bad_val; + ctx->gid = result.gid; return 0; case Opt_mode: From 6a265845db285bb90d29d0f223176ae2fdd422d2 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:36:14 -0500 Subject: [PATCH 10/20] isofs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/3e57caa1-33e0-4456-8e07-60922439e479@redhat.com Signed-off-by: Christian Brauner --- fs/isofs/inode.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 93b1077a380a..ed548efdd9bb 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -326,8 +326,8 @@ static const struct fs_parameter_spec isofs_param_spec[] = { fsparam_u32 ("session", Opt_session), fsparam_u32 ("sbsector", Opt_sb), fsparam_enum ("check", Opt_check, isofs_param_check), - fsparam_u32 ("uid", Opt_uid), - fsparam_u32 ("gid", Opt_gid), + fsparam_uid ("uid", Opt_uid), + fsparam_gid ("gid", Opt_gid), /* Note: mode/dmode historically accepted %u not strictly %o */ fsparam_u32 ("mode", Opt_mode), fsparam_u32 ("dmode", Opt_dmode), @@ -344,8 +344,6 @@ static int isofs_parse_param(struct fs_context *fc, struct isofs_options *popt = fc->fs_private; struct fs_parse_result result; int opt; - kuid_t uid; - kgid_t gid; unsigned int n; /* There are no remountable options */ @@ -409,17 +407,11 @@ static int isofs_parse_param(struct fs_context *fc, case Opt_ignore: break; case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return -EINVAL; - popt->uid = uid; + popt->uid = result.uid; popt->uid_set = 1; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return -EINVAL; - popt->gid = gid; + popt->gid = result.gid; popt->gid_set = 1; break; case Opt_mode: From c449cb5d1bcedb8dda85ef8d58e3fc7b246655c7 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:37:21 -0500 Subject: [PATCH 11/20] ntfs3: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/04bf30db-8542-48dc-9060-7f7dc08eda22@redhat.com Signed-off-by: Christian Brauner --- fs/ntfs3/super.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 27fbde2701b6..c5b688c5f984 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -259,8 +259,8 @@ enum Opt { // clang-format off static const struct fs_parameter_spec ntfs_fs_parameters[] = { - fsparam_u32("uid", Opt_uid), - fsparam_u32("gid", Opt_gid), + fsparam_uid("uid", Opt_uid), + fsparam_gid("gid", Opt_gid), fsparam_u32oct("umask", Opt_umask), fsparam_u32oct("dmask", Opt_dmask), fsparam_u32oct("fmask", Opt_fmask), @@ -319,14 +319,10 @@ static int ntfs_fs_parse_param(struct fs_context *fc, switch (opt) { case Opt_uid: - opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(opts->fs_uid)) - return invalf(fc, "ntfs3: Invalid value for uid."); + opts->fs_uid = result.uid; break; case Opt_gid: - opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(opts->fs_gid)) - return invalf(fc, "ntfs3: Invalid value for gid."); + opts->fs_gid = result.gid; break; case Opt_umask: if (result.uint_32 & ~07777) From 2ec07010b6a9a03f7aa45c2499cf8a03118d4428 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:38:12 -0500 Subject: [PATCH 12/20] tmpfs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/06b54c7c-4f08-4d99-93d1-32b9f6706209@redhat.com Signed-off-by: Christian Brauner --- mm/shmem.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/mm/shmem.c b/mm/shmem.c index f5d60436b604..0847bf663ded 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3903,14 +3903,14 @@ static const struct constant_table shmem_param_enums_huge[] = { }; const struct fs_parameter_spec shmem_fs_parameters[] = { - fsparam_u32 ("gid", Opt_gid), + fsparam_gid ("gid", Opt_gid), fsparam_enum ("huge", Opt_huge, shmem_param_enums_huge), fsparam_u32oct("mode", Opt_mode), fsparam_string("mpol", Opt_mpol), fsparam_string("nr_blocks", Opt_nr_blocks), fsparam_string("nr_inodes", Opt_nr_inodes), fsparam_string("size", Opt_size), - fsparam_u32 ("uid", Opt_uid), + fsparam_uid ("uid", Opt_uid), fsparam_flag ("inode32", Opt_inode32), fsparam_flag ("inode64", Opt_inode64), fsparam_flag ("noswap", Opt_noswap), @@ -3970,9 +3970,7 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->mode = result.uint_32 & 07777; break; case Opt_uid: - kuid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(kuid)) - goto bad_value; + kuid = result.uid; /* * The requested uid must be representable in the @@ -3984,9 +3982,7 @@ static int shmem_parse_one(struct fs_context *fc, struct fs_parameter *param) ctx->uid = kuid; break; case Opt_gid: - kgid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(kgid)) - goto bad_value; + kgid = result.gid; /* * The requested gid must be representable in the From 3229e3a5a3745049f982818fb2ea9273e7b08f09 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:39:26 -0500 Subject: [PATCH 13/20] smb: client: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/2543358a-b97e-45ce-8cdc-3de1dd9a782f@redhat.com Signed-off-by: Christian Brauner --- fs/smb/client/fs_context.c | 39 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c index 3bbac925d076..bc926ab2555b 100644 --- a/fs/smb/client/fs_context.c +++ b/fs/smb/client/fs_context.c @@ -128,12 +128,14 @@ const struct fs_parameter_spec smb3_fs_parameters[] = { fsparam_flag("compress", Opt_compress), fsparam_flag("witness", Opt_witness), + /* Mount options which take uid or gid */ + fsparam_uid("backupuid", Opt_backupuid), + fsparam_gid("backupgid", Opt_backupgid), + fsparam_uid("uid", Opt_uid), + fsparam_uid("cruid", Opt_cruid), + fsparam_gid("gid", Opt_gid), + /* Mount options which take numeric value */ - fsparam_u32("backupuid", Opt_backupuid), - fsparam_u32("backupgid", Opt_backupgid), - fsparam_u32("uid", Opt_uid), - fsparam_u32("cruid", Opt_cruid), - fsparam_u32("gid", Opt_gid), fsparam_u32("file_mode", Opt_file_mode), fsparam_u32("dirmode", Opt_dirmode), fsparam_u32("dir_mode", Opt_dirmode), @@ -951,8 +953,6 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, int i, opt; bool is_smb3 = !strcmp(fc->fs_type->name, "smb3"); bool skip_parsing = false; - kuid_t uid; - kgid_t gid; cifs_dbg(FYI, "CIFS: parsing cifs mount option '%s'\n", param->key); @@ -1083,38 +1083,23 @@ static int smb3_fs_context_parse_param(struct fs_context *fc, } break; case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->linux_uid = uid; + ctx->linux_uid = result.uid; ctx->uid_specified = true; break; case Opt_cruid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->cred_uid = uid; + ctx->cred_uid = result.uid; ctx->cruid_specified = true; break; case Opt_backupuid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - goto cifs_parse_mount_err; - ctx->backupuid = uid; + ctx->backupuid = result.uid; ctx->backupuid_specified = true; break; case Opt_backupgid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->backupgid = gid; + ctx->backupgid = result.gid; ctx->backupgid_specified = true; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - goto cifs_parse_mount_err; - ctx->linux_gid = gid; + ctx->linux_gid = result.gid; ctx->gid_specified = true; break; case Opt_port: From b548291690d1afc33e66d956b82bbd3c250660ed Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:40:44 -0500 Subject: [PATCH 14/20] tracefs: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/6c9b0b16-e61b-4dfc-852d-e2eb5bb11b82@redhat.com Acked-by: Steven Rostedt (Google) Signed-off-by: Christian Brauner --- fs/tracefs/inode.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 7c29f4afc23d..1028ab6d9a74 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -296,9 +296,9 @@ enum { }; static const struct fs_parameter_spec tracefs_param_specs[] = { - fsparam_u32 ("gid", Opt_gid), + fsparam_gid ("gid", Opt_gid), fsparam_u32oct ("mode", Opt_mode), - fsparam_u32 ("uid", Opt_uid), + fsparam_uid ("uid", Opt_uid), {} }; @@ -306,8 +306,6 @@ static int tracefs_parse_param(struct fs_context *fc, struct fs_parameter *param { struct tracefs_fs_info *opts = fc->s_fs_info; struct fs_parse_result result; - kuid_t uid; - kgid_t gid; int opt; opt = fs_parse(fc, tracefs_param_specs, param, &result); @@ -316,16 +314,10 @@ static int tracefs_parse_param(struct fs_context *fc, struct fs_parameter *param switch (opt) { case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return invalf(fc, "Unknown uid"); - opts->uid = uid; + opts->uid = result.uid; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return invalf(fc, "Unknown gid"); - opts->gid = gid; + opts->gid = result.gid; break; case Opt_mode: opts->mode = result.uint_32 & S_IALLUGO; From da99d45bd55193690beee3acff7b69e0939405dd Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 27 Jun 2024 19:42:25 -0500 Subject: [PATCH 15/20] vboxsf: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers From: Eric Sandeen Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/58862d35-a026-4866-ab7f-fa09dda8ac1f@redhat.com Reviewed-by: Hans de Goede Signed-off-by: Christian Brauner --- fs/vboxsf/super.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/vboxsf/super.c b/fs/vboxsf/super.c index ffb1d565da39..e95b8a48d8a0 100644 --- a/fs/vboxsf/super.c +++ b/fs/vboxsf/super.c @@ -41,8 +41,8 @@ enum { opt_nls, opt_uid, opt_gid, opt_ttl, opt_dmode, opt_fmode, static const struct fs_parameter_spec vboxsf_fs_parameters[] = { fsparam_string ("nls", opt_nls), - fsparam_u32 ("uid", opt_uid), - fsparam_u32 ("gid", opt_gid), + fsparam_uid ("uid", opt_uid), + fsparam_gid ("gid", opt_gid), fsparam_u32 ("ttl", opt_ttl), fsparam_u32oct ("dmode", opt_dmode), fsparam_u32oct ("fmode", opt_fmode), @@ -55,8 +55,6 @@ static int vboxsf_parse_param(struct fs_context *fc, struct fs_parameter *param) { struct vboxsf_fs_context *ctx = fc->fs_private; struct fs_parse_result result; - kuid_t uid; - kgid_t gid; int opt; opt = fs_parse(fc, vboxsf_fs_parameters, param, &result); @@ -73,16 +71,10 @@ static int vboxsf_parse_param(struct fs_context *fc, struct fs_parameter *param) param->string = NULL; break; case opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return -EINVAL; - ctx->o.uid = uid; + ctx->o.uid = result.uid; break; case opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return -EINVAL; - ctx->o.gid = gid; + ctx->o.gid = result.gid; break; case opt_ttl: ctx->o.ttl = msecs_to_jiffies(result.uint_32); From 206d3d8e006c77b8016de586423de37dbff9e6fc Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 2 Jul 2024 17:41:22 -0500 Subject: [PATCH 16/20] fat: move debug into fat_mount_options Move the debug variable into fat_mount_options for consistency and to facilitate conversion to new mount API. Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/f6155247-32ee-4cfe-b808-9102b17f7cd1@redhat.com Acked-by: OGAWA Hirofumi Signed-off-by: Christian Brauner --- fs/fat/fat.h | 3 ++- fs/fat/inode.c | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 66cf4778cf3b..37ced7bb06d5 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -51,7 +51,8 @@ struct fat_mount_options { tz_set:1, /* Filesystem timestamps' offset set */ rodir:1, /* allow ATTR_RO for directory */ discard:1, /* Issue discard requests on deletions */ - dos1xfloppy:1; /* Assume default BPB for DOS 1.x floppies */ + dos1xfloppy:1, /* Assume default BPB for DOS 1.x floppies */ + debug:1; /* Not currently used */ }; #define FAT_HASH_BITS 8 diff --git a/fs/fat/inode.c b/fs/fat/inode.c index d9e6fbb6f246..2a6537ba0d49 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1132,7 +1132,7 @@ static const match_table_t vfat_tokens = { }; static int parse_options(struct super_block *sb, char *options, int is_vfat, - int silent, int *debug, struct fat_mount_options *opts) + int silent, struct fat_mount_options *opts) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -1162,7 +1162,7 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, opts->tz_set = 0; opts->nfs = 0; opts->errors = FAT_ERRORS_RO; - *debug = 0; + opts->debug = 0; opts->utf8 = IS_ENABLED(CONFIG_FAT_DEFAULT_UTF8) && is_vfat; @@ -1210,7 +1210,7 @@ static int parse_options(struct super_block *sb, char *options, int is_vfat, opts->showexec = 1; break; case Opt_debug: - *debug = 1; + opts->debug = 1; break; case Opt_immutable: opts->sys_immutable = 1; @@ -1614,7 +1614,6 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, struct msdos_sb_info *sbi; u16 logical_sector_size; u32 total_sectors, total_clusters, fat_clusters, rootdir_sectors; - int debug; long error; char buf[50]; struct timespec64 ts; @@ -1643,7 +1642,7 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); - error = parse_options(sb, data, isvfat, silent, &debug, &sbi->options); + error = parse_options(sb, data, isvfat, silent, &sbi->options); if (error) goto out_fail; From 634440b69c7f7c67478bc775f2c6977afd4adc82 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 2 Jul 2024 17:44:27 -0500 Subject: [PATCH 17/20] fat: Convert to new mount api vfat and msdos share a common set of options, with additional, unique options for each filesystem. Each filesystem calls common fc initialization and parsing routines, with an "is_vfat" parameter. For parsing, if the option is not found in the common parameter_spec, parsing is retried with the fs-specific parameter_spec. This patch leaves nls loading to fill_super, so the codepage and charset options are not validated as they are requested. This matches current behavior. It would be possible to test-load as each option is parsed, but that would make i.e. mount -o "iocharset=nope,iocharset=iso8859-1" fail, where it does not fail today because only the last iocharset option is considered. The obsolete "conv=" option is set up with an enum of acceptable values; currently invalid "conv=" options are rejected as such, even though the option is obsolete, so this patch preserves that behavior. Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/a9411b02-5f8e-4e1e-90aa-0c032d66c312@redhat.com Acked-by: OGAWA Hirofumi Signed-off-by: Christian Brauner --- fs/fat/fat.h | 15 +- fs/fat/inode.c | 687 ++++++++++++++++++++++--------------------- fs/fat/namei_msdos.c | 38 ++- fs/fat/namei_vfat.c | 38 ++- 4 files changed, 421 insertions(+), 357 deletions(-) diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 37ced7bb06d5..d3e426de5f01 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include /* * vfat shortname flags @@ -416,12 +418,21 @@ extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos); extern struct inode *fat_build_inode(struct super_block *sb, struct msdos_dir_entry *de, loff_t i_pos); extern int fat_sync_inode(struct inode *inode); -extern int fat_fill_super(struct super_block *sb, void *data, int silent, - int isvfat, void (*setup)(struct super_block *)); +extern int fat_fill_super(struct super_block *sb, struct fs_context *fc, + void (*setup)(struct super_block *)); extern int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de); extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2); + +extern const struct fs_parameter_spec fat_param_spec[]; +int fat_init_fs_context(struct fs_context *fc, bool is_vfat); +void fat_free_fc(struct fs_context *fc); + +int fat_parse_param(struct fs_context *fc, struct fs_parameter *param, + bool is_vfat); +int fat_reconfigure(struct fs_context *fc); + static inline unsigned long fat_dir_hash(int logstart) { return hash_32(logstart, FAT_HASH_BITS); diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 2a6537ba0d49..b83b39f2f69b 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -804,16 +803,17 @@ static void __exit fat_destroy_inodecache(void) kmem_cache_destroy(fat_inode_cachep); } -static int fat_remount(struct super_block *sb, int *flags, char *data) +int fat_reconfigure(struct fs_context *fc) { bool new_rdonly; + struct super_block *sb = fc->root->d_sb; struct msdos_sb_info *sbi = MSDOS_SB(sb); - *flags |= SB_NODIRATIME | (sbi->options.isvfat ? 0 : SB_NOATIME); + fc->sb_flags |= SB_NODIRATIME | (sbi->options.isvfat ? 0 : SB_NOATIME); sync_filesystem(sb); /* make sure we update state on remount. */ - new_rdonly = *flags & SB_RDONLY; + new_rdonly = fc->sb_flags & SB_RDONLY; if (new_rdonly != sb_rdonly(sb)) { if (new_rdonly) fat_set_state(sb, 0, 0); @@ -822,6 +822,7 @@ static int fat_remount(struct super_block *sb, int *flags, char *data) } return 0; } +EXPORT_SYMBOL_GPL(fat_reconfigure); static int fat_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -939,8 +940,6 @@ static const struct super_operations fat_sops = { .evict_inode = fat_evict_inode, .put_super = fat_put_super, .statfs = fat_statfs, - .remount_fs = fat_remount, - .show_options = fat_show_options, }; @@ -1037,355 +1036,290 @@ static int fat_show_options(struct seq_file *m, struct dentry *root) } enum { - Opt_check_n, Opt_check_r, Opt_check_s, Opt_uid, Opt_gid, - Opt_umask, Opt_dmask, Opt_fmask, Opt_allow_utime, Opt_codepage, - Opt_usefree, Opt_nocase, Opt_quiet, Opt_showexec, Opt_debug, - Opt_immutable, Opt_dots, Opt_nodots, - Opt_charset, Opt_shortname_lower, Opt_shortname_win95, - Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, - Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, - Opt_obsolete, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont, - Opt_err_panic, Opt_err_ro, Opt_discard, Opt_nfs, Opt_time_offset, - Opt_nfs_stale_rw, Opt_nfs_nostale_ro, Opt_err, Opt_dos1xfloppy, + Opt_check, Opt_uid, Opt_gid, Opt_umask, Opt_dmask, Opt_fmask, + Opt_allow_utime, Opt_codepage, Opt_usefree, Opt_nocase, Opt_quiet, + Opt_showexec, Opt_debug, Opt_immutable, Opt_dots, Opt_dotsOK, + Opt_charset, Opt_shortname, Opt_utf8, Opt_utf8_bool, + Opt_uni_xl, Opt_uni_xl_bool, Opt_nonumtail, Opt_nonumtail_bool, + Opt_obsolete, Opt_flush, Opt_tz, Opt_rodir, Opt_errors, Opt_discard, + Opt_nfs, Opt_nfs_enum, Opt_time_offset, Opt_dos1xfloppy, }; -static const match_table_t fat_tokens = { - {Opt_check_r, "check=relaxed"}, - {Opt_check_s, "check=strict"}, - {Opt_check_n, "check=normal"}, - {Opt_check_r, "check=r"}, - {Opt_check_s, "check=s"}, - {Opt_check_n, "check=n"}, - {Opt_uid, "uid=%u"}, - {Opt_gid, "gid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_dmask, "dmask=%o"}, - {Opt_fmask, "fmask=%o"}, - {Opt_allow_utime, "allow_utime=%o"}, - {Opt_codepage, "codepage=%u"}, - {Opt_usefree, "usefree"}, - {Opt_nocase, "nocase"}, - {Opt_quiet, "quiet"}, - {Opt_showexec, "showexec"}, - {Opt_debug, "debug"}, - {Opt_immutable, "sys_immutable"}, - {Opt_flush, "flush"}, - {Opt_tz_utc, "tz=UTC"}, - {Opt_time_offset, "time_offset=%d"}, - {Opt_err_cont, "errors=continue"}, - {Opt_err_panic, "errors=panic"}, - {Opt_err_ro, "errors=remount-ro"}, - {Opt_discard, "discard"}, - {Opt_nfs_stale_rw, "nfs"}, - {Opt_nfs_stale_rw, "nfs=stale_rw"}, - {Opt_nfs_nostale_ro, "nfs=nostale_ro"}, - {Opt_dos1xfloppy, "dos1xfloppy"}, - {Opt_obsolete, "conv=binary"}, - {Opt_obsolete, "conv=text"}, - {Opt_obsolete, "conv=auto"}, - {Opt_obsolete, "conv=b"}, - {Opt_obsolete, "conv=t"}, - {Opt_obsolete, "conv=a"}, - {Opt_obsolete, "fat=%u"}, - {Opt_obsolete, "blocksize=%u"}, - {Opt_obsolete, "cvf_format=%20s"}, - {Opt_obsolete, "cvf_options=%100s"}, - {Opt_obsolete, "posix"}, - {Opt_err, NULL}, -}; -static const match_table_t msdos_tokens = { - {Opt_nodots, "nodots"}, - {Opt_nodots, "dotsOK=no"}, - {Opt_dots, "dots"}, - {Opt_dots, "dotsOK=yes"}, - {Opt_err, NULL} -}; -static const match_table_t vfat_tokens = { - {Opt_charset, "iocharset=%s"}, - {Opt_shortname_lower, "shortname=lower"}, - {Opt_shortname_win95, "shortname=win95"}, - {Opt_shortname_winnt, "shortname=winnt"}, - {Opt_shortname_mixed, "shortname=mixed"}, - {Opt_utf8_no, "utf8=0"}, /* 0 or no or false */ - {Opt_utf8_no, "utf8=no"}, - {Opt_utf8_no, "utf8=false"}, - {Opt_utf8_yes, "utf8=1"}, /* empty or 1 or yes or true */ - {Opt_utf8_yes, "utf8=yes"}, - {Opt_utf8_yes, "utf8=true"}, - {Opt_utf8_yes, "utf8"}, - {Opt_uni_xl_no, "uni_xlate=0"}, /* 0 or no or false */ - {Opt_uni_xl_no, "uni_xlate=no"}, - {Opt_uni_xl_no, "uni_xlate=false"}, - {Opt_uni_xl_yes, "uni_xlate=1"}, /* empty or 1 or yes or true */ - {Opt_uni_xl_yes, "uni_xlate=yes"}, - {Opt_uni_xl_yes, "uni_xlate=true"}, - {Opt_uni_xl_yes, "uni_xlate"}, - {Opt_nonumtail_no, "nonumtail=0"}, /* 0 or no or false */ - {Opt_nonumtail_no, "nonumtail=no"}, - {Opt_nonumtail_no, "nonumtail=false"}, - {Opt_nonumtail_yes, "nonumtail=1"}, /* empty or 1 or yes or true */ - {Opt_nonumtail_yes, "nonumtail=yes"}, - {Opt_nonumtail_yes, "nonumtail=true"}, - {Opt_nonumtail_yes, "nonumtail"}, - {Opt_rodir, "rodir"}, - {Opt_err, NULL} +static const struct constant_table fat_param_check[] = { + {"relaxed", 'r'}, + {"r", 'r'}, + {"strict", 's'}, + {"s", 's'}, + {"normal", 'n'}, + {"n", 'n'}, + {} }; -static int parse_options(struct super_block *sb, char *options, int is_vfat, - int silent, struct fat_mount_options *opts) +static const struct constant_table fat_param_tz[] = { + {"UTC", 0}, + {} +}; + +static const struct constant_table fat_param_errors[] = { + {"continue", FAT_ERRORS_CONT}, + {"panic", FAT_ERRORS_PANIC}, + {"remount-ro", FAT_ERRORS_RO}, + {} +}; + + +static const struct constant_table fat_param_nfs[] = { + {"stale_rw", FAT_NFS_STALE_RW}, + {"nostale_ro", FAT_NFS_NOSTALE_RO}, + {} +}; + +/* + * These are all obsolete but we still reject invalid options. + * The corresponding values are therefore meaningless. + */ +static const struct constant_table fat_param_conv[] = { + {"binary", 0}, + {"text", 0}, + {"auto", 0}, + {"b", 0}, + {"t", 0}, + {"a", 0}, + {} +}; + +/* Core options. See below for vfat and msdos extras */ +const struct fs_parameter_spec fat_param_spec[] = { + fsparam_enum ("check", Opt_check, fat_param_check), + fsparam_u32 ("uid", Opt_uid), + fsparam_u32 ("gid", Opt_gid), + fsparam_u32oct ("umask", Opt_umask), + fsparam_u32oct ("dmask", Opt_dmask), + fsparam_u32oct ("fmask", Opt_fmask), + fsparam_u32oct ("allow_utime", Opt_allow_utime), + fsparam_u32 ("codepage", Opt_codepage), + fsparam_flag ("usefree", Opt_usefree), + fsparam_flag ("nocase", Opt_nocase), + fsparam_flag ("quiet", Opt_quiet), + fsparam_flag ("showexec", Opt_showexec), + fsparam_flag ("debug", Opt_debug), + fsparam_flag ("sys_immutable", Opt_immutable), + fsparam_flag ("flush", Opt_flush), + fsparam_enum ("tz", Opt_tz, fat_param_tz), + fsparam_s32 ("time_offset", Opt_time_offset), + fsparam_enum ("errors", Opt_errors, fat_param_errors), + fsparam_flag ("discard", Opt_discard), + fsparam_flag ("nfs", Opt_nfs), + fsparam_enum ("nfs", Opt_nfs_enum, fat_param_nfs), + fsparam_flag ("dos1xfloppy", Opt_dos1xfloppy), + __fsparam(fs_param_is_enum, "conv", + Opt_obsolete, fs_param_deprecated, fat_param_conv), + __fsparam(fs_param_is_u32, "fat", + Opt_obsolete, fs_param_deprecated, NULL), + __fsparam(fs_param_is_u32, "blocksize", + Opt_obsolete, fs_param_deprecated, NULL), + __fsparam(fs_param_is_string, "cvf_format", + Opt_obsolete, fs_param_deprecated, NULL), + __fsparam(fs_param_is_string, "cvf_options", + Opt_obsolete, fs_param_deprecated, NULL), + __fsparam(NULL, "posix", + Opt_obsolete, fs_param_deprecated, NULL), + {} +}; +EXPORT_SYMBOL_GPL(fat_param_spec); + +static const struct fs_parameter_spec msdos_param_spec[] = { + fsparam_flag_no ("dots", Opt_dots), + fsparam_bool ("dotsOK", Opt_dotsOK), + {} +}; + +static const struct constant_table fat_param_shortname[] = { + {"lower", VFAT_SFN_DISPLAY_LOWER | VFAT_SFN_CREATE_WIN95}, + {"win95", VFAT_SFN_DISPLAY_WIN95 | VFAT_SFN_CREATE_WIN95}, + {"winnt", VFAT_SFN_DISPLAY_WINNT | VFAT_SFN_CREATE_WINNT}, + {"mixed", VFAT_SFN_DISPLAY_WINNT | VFAT_SFN_CREATE_WIN95}, + {} +}; + +static const struct fs_parameter_spec vfat_param_spec[] = { + fsparam_string ("iocharset", Opt_charset), + fsparam_enum ("shortname", Opt_shortname, fat_param_shortname), + fsparam_flag ("utf8", Opt_utf8), + fsparam_bool ("utf8", Opt_utf8_bool), + fsparam_flag ("uni_xlate", Opt_uni_xl), + fsparam_bool ("uni_xlate", Opt_uni_xl_bool), + fsparam_flag ("nonumtail", Opt_nonumtail), + fsparam_bool ("nonumtail", Opt_nonumtail_bool), + fsparam_flag ("rodir", Opt_rodir), + {} +}; + +int fat_parse_param(struct fs_context *fc, struct fs_parameter *param, + bool is_vfat) { - char *p; - substring_t args[MAX_OPT_ARGS]; - int option; - char *iocharset; + struct fat_mount_options *opts = fc->fs_private; + struct fs_parse_result result; + int opt; + kuid_t uid; + kgid_t gid; - opts->isvfat = is_vfat; + /* remount options have traditionally been ignored */ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) + return 0; - opts->fs_uid = current_uid(); - opts->fs_gid = current_gid(); - opts->fs_fmask = opts->fs_dmask = current_umask(); - opts->allow_utime = -1; - opts->codepage = fat_default_codepage; - fat_reset_iocharset(opts); - if (is_vfat) { - opts->shortname = VFAT_SFN_DISPLAY_WINNT|VFAT_SFN_CREATE_WIN95; - opts->rodir = 0; - } else { - opts->shortname = 0; - opts->rodir = 1; + opt = fs_parse(fc, fat_param_spec, param, &result); + /* If option not found in fat_param_spec, try vfat/msdos options */ + if (opt == -ENOPARAM) { + if (is_vfat) + opt = fs_parse(fc, vfat_param_spec, param, &result); + else + opt = fs_parse(fc, msdos_param_spec, param, &result); } - opts->name_check = 'n'; - opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0; - opts->unicode_xlate = 0; - opts->numtail = 1; - opts->usefree = opts->nocase = 0; - opts->tz_set = 0; - opts->nfs = 0; - opts->errors = FAT_ERRORS_RO; - opts->debug = 0; - opts->utf8 = IS_ENABLED(CONFIG_FAT_DEFAULT_UTF8) && is_vfat; + if (opt < 0) + return opt; - if (!options) - goto out; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - - token = match_token(p, fat_tokens, args); - if (token == Opt_err) { - if (is_vfat) - token = match_token(p, vfat_tokens, args); - else - token = match_token(p, msdos_tokens, args); - } - switch (token) { - case Opt_check_s: - opts->name_check = 's'; - break; - case Opt_check_r: - opts->name_check = 'r'; - break; - case Opt_check_n: - opts->name_check = 'n'; - break; - case Opt_usefree: - opts->usefree = 1; - break; - case Opt_nocase: - if (!is_vfat) - opts->nocase = 1; - else { - /* for backward compatibility */ - opts->shortname = VFAT_SFN_DISPLAY_WIN95 - | VFAT_SFN_CREATE_WIN95; - } - break; - case Opt_quiet: - opts->quiet = 1; - break; - case Opt_showexec: - opts->showexec = 1; - break; - case Opt_debug: - opts->debug = 1; - break; - case Opt_immutable: - opts->sys_immutable = 1; - break; - case Opt_uid: - if (match_int(&args[0], &option)) - return -EINVAL; - opts->fs_uid = make_kuid(current_user_ns(), option); - if (!uid_valid(opts->fs_uid)) - return -EINVAL; - break; - case Opt_gid: - if (match_int(&args[0], &option)) - return -EINVAL; - opts->fs_gid = make_kgid(current_user_ns(), option); - if (!gid_valid(opts->fs_gid)) - return -EINVAL; - break; - case Opt_umask: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->fs_fmask = opts->fs_dmask = option; - break; - case Opt_dmask: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->fs_dmask = option; - break; - case Opt_fmask: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->fs_fmask = option; - break; - case Opt_allow_utime: - if (match_octal(&args[0], &option)) - return -EINVAL; - opts->allow_utime = option & (S_IWGRP | S_IWOTH); - break; - case Opt_codepage: - if (match_int(&args[0], &option)) - return -EINVAL; - opts->codepage = option; - break; - case Opt_flush: - opts->flush = 1; - break; - case Opt_time_offset: - if (match_int(&args[0], &option)) - return -EINVAL; - /* - * GMT+-12 zones may have DST corrections so at least - * 13 hours difference is needed. Make the limit 24 - * just in case someone invents something unusual. - */ - if (option < -24 * 60 || option > 24 * 60) - return -EINVAL; - opts->tz_set = 1; - opts->time_offset = option; - break; - case Opt_tz_utc: - opts->tz_set = 1; - opts->time_offset = 0; - break; - case Opt_err_cont: - opts->errors = FAT_ERRORS_CONT; - break; - case Opt_err_panic: - opts->errors = FAT_ERRORS_PANIC; - break; - case Opt_err_ro: - opts->errors = FAT_ERRORS_RO; - break; - case Opt_nfs_stale_rw: - opts->nfs = FAT_NFS_STALE_RW; - break; - case Opt_nfs_nostale_ro: - opts->nfs = FAT_NFS_NOSTALE_RO; - break; - case Opt_dos1xfloppy: - opts->dos1xfloppy = 1; - break; - - /* msdos specific */ - case Opt_dots: - opts->dotsOK = 1; - break; - case Opt_nodots: - opts->dotsOK = 0; - break; - - /* vfat specific */ - case Opt_charset: - fat_reset_iocharset(opts); - iocharset = match_strdup(&args[0]); - if (!iocharset) - return -ENOMEM; - opts->iocharset = iocharset; - break; - case Opt_shortname_lower: - opts->shortname = VFAT_SFN_DISPLAY_LOWER - | VFAT_SFN_CREATE_WIN95; - break; - case Opt_shortname_win95: + switch (opt) { + case Opt_check: + opts->name_check = result.uint_32; + break; + case Opt_usefree: + opts->usefree = 1; + break; + case Opt_nocase: + if (!is_vfat) + opts->nocase = 1; + else { + /* for backward compatibility */ opts->shortname = VFAT_SFN_DISPLAY_WIN95 - | VFAT_SFN_CREATE_WIN95; - break; - case Opt_shortname_winnt: - opts->shortname = VFAT_SFN_DISPLAY_WINNT - | VFAT_SFN_CREATE_WINNT; - break; - case Opt_shortname_mixed: - opts->shortname = VFAT_SFN_DISPLAY_WINNT - | VFAT_SFN_CREATE_WIN95; - break; - case Opt_utf8_no: /* 0 or no or false */ - opts->utf8 = 0; - break; - case Opt_utf8_yes: /* empty or 1 or yes or true */ - opts->utf8 = 1; - break; - case Opt_uni_xl_no: /* 0 or no or false */ - opts->unicode_xlate = 0; - break; - case Opt_uni_xl_yes: /* empty or 1 or yes or true */ - opts->unicode_xlate = 1; - break; - case Opt_nonumtail_no: /* 0 or no or false */ - opts->numtail = 1; /* negated option */ - break; - case Opt_nonumtail_yes: /* empty or 1 or yes or true */ - opts->numtail = 0; /* negated option */ - break; - case Opt_rodir: - opts->rodir = 1; - break; - case Opt_discard: - opts->discard = 1; - break; - - /* obsolete mount options */ - case Opt_obsolete: - fat_msg(sb, KERN_INFO, "\"%s\" option is obsolete, " - "not supported now", p); - break; - /* unknown option */ - default: - if (!silent) { - fat_msg(sb, KERN_ERR, - "Unrecognized mount option \"%s\" " - "or missing value", p); - } - return -EINVAL; + | VFAT_SFN_CREATE_WIN95; } - } + break; + case Opt_quiet: + opts->quiet = 1; + break; + case Opt_showexec: + opts->showexec = 1; + break; + case Opt_debug: + opts->debug = 1; + break; + case Opt_immutable: + opts->sys_immutable = 1; + break; + case Opt_uid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) + return -EINVAL; + opts->fs_uid = uid; + break; + case Opt_gid: + gid = make_kgid(current_user_ns(), result.uint_32); + if (!gid_valid(gid)) + return -EINVAL; + opts->fs_gid = gid; + break; + case Opt_umask: + opts->fs_fmask = opts->fs_dmask = result.uint_32; + break; + case Opt_dmask: + opts->fs_dmask = result.uint_32; + break; + case Opt_fmask: + opts->fs_fmask = result.uint_32; + break; + case Opt_allow_utime: + opts->allow_utime = result.uint_32 & (S_IWGRP | S_IWOTH); + break; + case Opt_codepage: + opts->codepage = result.uint_32; + break; + case Opt_flush: + opts->flush = 1; + break; + case Opt_time_offset: + /* + * GMT+-12 zones may have DST corrections so at least + * 13 hours difference is needed. Make the limit 24 + * just in case someone invents something unusual. + */ + if (result.int_32 < -24 * 60 || result.int_32 > 24 * 60) + return -EINVAL; + opts->tz_set = 1; + opts->time_offset = result.int_32; + break; + case Opt_tz: + opts->tz_set = 1; + opts->time_offset = result.uint_32; + break; + case Opt_errors: + opts->errors = result.uint_32; + break; + case Opt_nfs: + opts->nfs = FAT_NFS_STALE_RW; + break; + case Opt_nfs_enum: + opts->nfs = result.uint_32; + break; + case Opt_dos1xfloppy: + opts->dos1xfloppy = 1; + break; -out: - /* UTF-8 doesn't provide FAT semantics */ - if (!strcmp(opts->iocharset, "utf8")) { - fat_msg(sb, KERN_WARNING, "utf8 is not a recommended IO charset" - " for FAT filesystems, filesystem will be " - "case sensitive!"); - } + /* msdos specific */ + case Opt_dots: /* dots / nodots */ + opts->dotsOK = !result.negated; + break; + case Opt_dotsOK: /* dotsOK = yes/no */ + opts->dotsOK = result.boolean; + break; - /* If user doesn't specify allow_utime, it's initialized from dmask. */ - if (opts->allow_utime == (unsigned short)-1) - opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); - if (opts->unicode_xlate) - opts->utf8 = 0; - if (opts->nfs == FAT_NFS_NOSTALE_RO) { - sb->s_flags |= SB_RDONLY; - sb->s_export_op = &fat_export_ops_nostale; + /* vfat specific */ + case Opt_charset: + fat_reset_iocharset(opts); + opts->iocharset = param->string; + param->string = NULL; /* Steal string */ + break; + case Opt_shortname: + opts->shortname = result.uint_32; + break; + case Opt_utf8: + opts->utf8 = 1; + break; + case Opt_utf8_bool: + opts->utf8 = result.boolean; + break; + case Opt_uni_xl: + opts->unicode_xlate = 1; + break; + case Opt_uni_xl_bool: + opts->unicode_xlate = result.boolean; + break; + case Opt_nonumtail: + opts->numtail = 0; /* negated option */ + break; + case Opt_nonumtail_bool: + opts->numtail = !result.boolean; /* negated option */ + break; + case Opt_rodir: + opts->rodir = 1; + break; + case Opt_discard: + opts->discard = 1; + break; + + /* obsolete mount options */ + case Opt_obsolete: + printk(KERN_INFO "FAT-fs: \"%s\" option is obsolete, " + "not supported now", param->key); + break; + default: + return -EINVAL; } return 0; } +EXPORT_SYMBOL_GPL(fat_parse_param); static int fat_read_root(struct inode *inode) { @@ -1604,9 +1538,11 @@ out: /* * Read the super block of an MS-DOS FS. */ -int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, +int fat_fill_super(struct super_block *sb, struct fs_context *fc, void (*setup)(struct super_block *)) { + struct fat_mount_options *opts = fc->fs_private; + int silent = fc->sb_flags & SB_SILENT; struct inode *root_inode = NULL, *fat_inode = NULL; struct inode *fsinfo_inode = NULL; struct buffer_head *bh; @@ -1642,9 +1578,27 @@ int fat_fill_super(struct super_block *sb, void *data, int silent, int isvfat, ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); - error = parse_options(sb, data, isvfat, silent, &sbi->options); - if (error) - goto out_fail; + /* UTF-8 doesn't provide FAT semantics */ + if (!strcmp(opts->iocharset, "utf8")) { + fat_msg(sb, KERN_WARNING, "utf8 is not a recommended IO charset" + " for FAT filesystems, filesystem will be" + " case sensitive!"); + } + + /* If user doesn't specify allow_utime, it's initialized from dmask. */ + if (opts->allow_utime == (unsigned short)-1) + opts->allow_utime = ~opts->fs_dmask & (S_IWGRP | S_IWOTH); + if (opts->unicode_xlate) + opts->utf8 = 0; + if (opts->nfs == FAT_NFS_NOSTALE_RO) { + sb->s_flags |= SB_RDONLY; + sb->s_export_op = &fat_export_ops_nostale; + } + + /* Apply parsed options to sbi (structure copy) */ + sbi->options = *opts; + /* Transfer ownership of iocharset to sbi->options */ + opts->iocharset = NULL; setup(sb); /* flavour-specific stuff that needs options */ @@ -1949,6 +1903,57 @@ int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2) } EXPORT_SYMBOL_GPL(fat_flush_inodes); +int fat_init_fs_context(struct fs_context *fc, bool is_vfat) +{ + struct fat_mount_options *opts; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return -ENOMEM; + + opts->isvfat = is_vfat; + opts->fs_uid = current_uid(); + opts->fs_gid = current_gid(); + opts->fs_fmask = opts->fs_dmask = current_umask(); + opts->allow_utime = -1; + opts->codepage = fat_default_codepage; + fat_reset_iocharset(opts); + if (is_vfat) { + opts->shortname = VFAT_SFN_DISPLAY_WINNT|VFAT_SFN_CREATE_WIN95; + opts->rodir = 0; + } else { + opts->shortname = 0; + opts->rodir = 1; + } + opts->name_check = 'n'; + opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0; + opts->unicode_xlate = 0; + opts->numtail = 1; + opts->usefree = opts->nocase = 0; + opts->tz_set = 0; + opts->nfs = 0; + opts->errors = FAT_ERRORS_RO; + opts->debug = 0; + + opts->utf8 = IS_ENABLED(CONFIG_FAT_DEFAULT_UTF8) && is_vfat; + + fc->fs_private = opts; + /* fc->ops assigned by caller */ + + return 0; +} +EXPORT_SYMBOL_GPL(fat_init_fs_context); + +void fat_free_fc(struct fs_context *fc) +{ + struct fat_mount_options *opts = fc->fs_private; + + if (opts->iocharset != fat_default_iocharset) + kfree(opts->iocharset); + kfree(fc->fs_private); +} +EXPORT_SYMBOL_GPL(fat_free_fc); + static int __init init_fat_fs(void) { int err; diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c index 2116c486843b..f06f6ba643cc 100644 --- a/fs/fat/namei_msdos.c +++ b/fs/fat/namei_msdos.c @@ -650,24 +650,48 @@ static void setup(struct super_block *sb) sb->s_flags |= SB_NOATIME; } -static int msdos_fill_super(struct super_block *sb, void *data, int silent) +static int msdos_fill_super(struct super_block *sb, struct fs_context *fc) { - return fat_fill_super(sb, data, silent, 0, setup); + return fat_fill_super(sb, fc, setup); } -static struct dentry *msdos_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int msdos_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, msdos_fill_super); + return get_tree_bdev(fc, msdos_fill_super); +} + +static int msdos_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + return fat_parse_param(fc, param, false); +} + +static const struct fs_context_operations msdos_context_ops = { + .parse_param = msdos_parse_param, + .get_tree = msdos_get_tree, + .reconfigure = fat_reconfigure, + .free = fat_free_fc, +}; + +static int msdos_init_fs_context(struct fs_context *fc) +{ + int err; + + /* Initialize with is_vfat == false */ + err = fat_init_fs_context(fc, false); + if (err) + return err; + + fc->ops = &msdos_context_ops; + return 0; } static struct file_system_type msdos_fs_type = { .owner = THIS_MODULE, .name = "msdos", - .mount = msdos_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, + .init_fs_context = msdos_init_fs_context, + .parameters = fat_param_spec, }; MODULE_ALIAS_FS("msdos"); diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c index c4d00999a433..6423e1dedf14 100644 --- a/fs/fat/namei_vfat.c +++ b/fs/fat/namei_vfat.c @@ -1195,24 +1195,48 @@ static void setup(struct super_block *sb) sb->s_d_op = &vfat_dentry_ops; } -static int vfat_fill_super(struct super_block *sb, void *data, int silent) +static int vfat_fill_super(struct super_block *sb, struct fs_context *fc) { - return fat_fill_super(sb, data, silent, 1, setup); + return fat_fill_super(sb, fc, setup); } -static struct dentry *vfat_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, - void *data) +static int vfat_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, vfat_fill_super); + return get_tree_bdev(fc, vfat_fill_super); +} + +static int vfat_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + return fat_parse_param(fc, param, true); +} + +static const struct fs_context_operations vfat_context_ops = { + .parse_param = vfat_parse_param, + .get_tree = vfat_get_tree, + .reconfigure = fat_reconfigure, + .free = fat_free_fc, +}; + +static int vfat_init_fs_context(struct fs_context *fc) +{ + int err; + + /* Initialize with is_vfat == true */ + err = fat_init_fs_context(fc, true); + if (err) + return err; + + fc->ops = &vfat_context_ops; + return 0; } static struct file_system_type vfat_fs_type = { .owner = THIS_MODULE, .name = "vfat", - .mount = vfat_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP, + .init_fs_context = vfat_init_fs_context, + .parameters = fat_param_spec, }; MODULE_ALIAS_FS("vfat"); From d02f0bb332d52c0330a1ef11cf98b7ec6448b99b Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 2 Jul 2024 17:45:57 -0500 Subject: [PATCH 18/20] fat: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/1a67d2a8-0aae-42a2-9c0f-21cd4cd87d13@redhat.com Acked-by: OGAWA Hirofumi Signed-off-by: Christian Brauner --- fs/fat/inode.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/fs/fat/inode.c b/fs/fat/inode.c index b83b39f2f69b..8fbf5edb7aa2 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -1091,8 +1091,8 @@ static const struct constant_table fat_param_conv[] = { /* Core options. See below for vfat and msdos extras */ const struct fs_parameter_spec fat_param_spec[] = { fsparam_enum ("check", Opt_check, fat_param_check), - fsparam_u32 ("uid", Opt_uid), - fsparam_u32 ("gid", Opt_gid), + fsparam_uid ("uid", Opt_uid), + fsparam_gid ("gid", Opt_gid), fsparam_u32oct ("umask", Opt_umask), fsparam_u32oct ("dmask", Opt_dmask), fsparam_u32oct ("fmask", Opt_fmask), @@ -1161,8 +1161,6 @@ int fat_parse_param(struct fs_context *fc, struct fs_parameter *param, struct fat_mount_options *opts = fc->fs_private; struct fs_parse_result result; int opt; - kuid_t uid; - kgid_t gid; /* remount options have traditionally been ignored */ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) @@ -1209,16 +1207,10 @@ int fat_parse_param(struct fs_context *fc, struct fs_parameter *param, opts->sys_immutable = 1; break; case Opt_uid: - uid = make_kuid(current_user_ns(), result.uint_32); - if (!uid_valid(uid)) - return -EINVAL; - opts->fs_uid = uid; + opts->fs_uid = result.uid; break; case Opt_gid: - gid = make_kgid(current_user_ns(), result.uint_32); - if (!gid_valid(gid)) - return -EINVAL; - opts->fs_gid = gid; + opts->fs_gid = result.gid; break; case Opt_umask: opts->fs_fmask = opts->fs_dmask = result.uint_32; From 525bd65aa759ec320af1dc06e114ed69733e9e23 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 2 Jul 2024 17:22:41 -0500 Subject: [PATCH 19/20] fuse: verify {g,u}id mount options correctly As was done in 0200679fc795 ("tmpfs: verify {g,u}id mount options correctly") we need to validate that the requested uid and/or gid is representable in the filesystem's idmapping. Cribbing from the above commit log, The contract for {g,u}id mount options and {g,u}id values in general set from userspace has always been that they are translated according to the caller's idmapping. In so far, fuse has been doing the correct thing. But since fuse is mountable in unprivileged contexts it is also necessary to verify that the resulting {k,g}uid is representable in the namespace of the superblock. Fixes: c30da2e981a7 ("fuse: convert to use the new mount API") Cc: stable@vger.kernel.org # 5.4+ Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/8f07d45d-c806-484d-a2e3-7a2199df1cd2@redhat.com Reviewed-by: Christian Brauner Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- fs/fuse/inode.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 99e44ea7d875..32fe6fa72f46 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -755,6 +755,8 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) struct fs_parse_result result; struct fuse_fs_context *ctx = fsc->fs_private; int opt; + kuid_t kuid; + kgid_t kgid; if (fsc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { /* @@ -799,16 +801,30 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) break; case OPT_USER_ID: - ctx->user_id = make_kuid(fsc->user_ns, result.uint_32); - if (!uid_valid(ctx->user_id)) + kuid = make_kuid(fsc->user_ns, result.uint_32); + if (!uid_valid(kuid)) return invalfc(fsc, "Invalid user_id"); + /* + * The requested uid must be representable in the + * filesystem's idmapping. + */ + if (!kuid_has_mapping(fsc->user_ns, kuid)) + return invalfc(fsc, "Invalid user_id"); + ctx->user_id = kuid; ctx->user_id_present = true; break; case OPT_GROUP_ID: - ctx->group_id = make_kgid(fsc->user_ns, result.uint_32); - if (!gid_valid(ctx->group_id)) + kgid = make_kgid(fsc->user_ns, result.uint_32);; + if (!gid_valid(kgid)) return invalfc(fsc, "Invalid group_id"); + /* + * The requested gid must be representable in the + * filesystem's idmapping. + */ + if (!kgid_has_mapping(fsc->user_ns, kgid)) + return invalfc(fsc, "Invalid group_id"); + ctx->group_id = kgid; ctx->group_id_present = true; break; From eea6a8322efd3982e59c41a5b61948a0b043ca58 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 2 Jul 2024 17:23:24 -0500 Subject: [PATCH 20/20] fuse: Convert to new uid/gid option parsing helpers Convert to new uid/gid option parsing helpers Signed-off-by: Eric Sandeen Link: https://lore.kernel.org/r/4e1a4efa-4ca5-4358-acee-40efd07c3c44@redhat.com Reviewed-by: Christian Brauner Reviewed-by: Josef Bacik Signed-off-by: Christian Brauner --- fs/fuse/inode.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 32fe6fa72f46..d8ab4e93916f 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -740,8 +740,8 @@ static const struct fs_parameter_spec fuse_fs_parameters[] = { fsparam_string ("source", OPT_SOURCE), fsparam_u32 ("fd", OPT_FD), fsparam_u32oct ("rootmode", OPT_ROOTMODE), - fsparam_u32 ("user_id", OPT_USER_ID), - fsparam_u32 ("group_id", OPT_GROUP_ID), + fsparam_uid ("user_id", OPT_USER_ID), + fsparam_gid ("group_id", OPT_GROUP_ID), fsparam_flag ("default_permissions", OPT_DEFAULT_PERMISSIONS), fsparam_flag ("allow_other", OPT_ALLOW_OTHER), fsparam_u32 ("max_read", OPT_MAX_READ), @@ -801,9 +801,7 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) break; case OPT_USER_ID: - kuid = make_kuid(fsc->user_ns, result.uint_32); - if (!uid_valid(kuid)) - return invalfc(fsc, "Invalid user_id"); + kuid = result.uid; /* * The requested uid must be representable in the * filesystem's idmapping. @@ -815,9 +813,7 @@ static int fuse_parse_param(struct fs_context *fsc, struct fs_parameter *param) break; case OPT_GROUP_ID: - kgid = make_kgid(fsc->user_ns, result.uint_32);; - if (!gid_valid(kgid)) - return invalfc(fsc, "Invalid group_id"); + kgid = result.gid; /* * The requested gid must be representable in the * filesystem's idmapping.