From: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
To: Eric Sandeen <sandeen@redhat.com>
Cc: linux-fsdevel@vger.kernel.org, Christian Brauner <brauner@kernel.org>
Subject: Re: [PATCH V2 2/3] fat: Convert to new mount api
Date: Wed, 03 Jul 2024 14:19:57 +0900 [thread overview]
Message-ID: <87ed8brr0i.fsf@mail.parknet.co.jp> (raw)
In-Reply-To: <a9411b02-5f8e-4e1e-90aa-0c032d66c312@redhat.com> (Eric Sandeen's message of "Tue, 2 Jul 2024 17:44:27 -0500")
Eric Sandeen <sandeen@redhat.com> writes:
> 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 <sandeen@redhat.com>
Looks good.
Acked-by: OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Thanks.
> ---
>
> V2: address review comments:
>
> - remove unused buf variable in fat_parse_param
> - remove unnecessary externs in new prototypes
> - mark deprecated options with fs_param_deprecated
> - fix double-free of opts->codepage
> - change is_vfat parameters to boolsw
>
> fs/fat/fat.h | 15 +-
> fs/fat/inode.c | 683 ++++++++++++++++++++++---------------------
> fs/fat/namei_msdos.c | 38 ++-
> fs/fat/namei_vfat.c | 38 ++-
> 4 files changed, 419 insertions(+), 355 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 <linux/hash.h>
> #include <linux/ratelimit.h>
> #include <linux/msdos_fs.h>
> +#include <linux/fs_context.h>
> +#include <linux/fs_parser.h>
>
> /*
> * 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 <linux/mpage.h>
> #include <linux/vfs.h>
> #include <linux/seq_file.h>
> -#include <linux/parser.h>
> #include <linux/uio.h>
> #include <linux/blkdev.h>
> #include <linux/backing-dev.h>
> @@ -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)
> -{
> - char *p;
> - substring_t args[MAX_OPT_ARGS];
> - int option;
> - char *iocharset;
> +static const struct constant_table fat_param_tz[] = {
> + {"UTC", 0},
> + {}
> +};
>
> - opts->isvfat = is_vfat;
> +static const struct constant_table fat_param_errors[] = {
> + {"continue", FAT_ERRORS_CONT},
> + {"panic", FAT_ERRORS_PANIC},
> + {"remount-ro", FAT_ERRORS_RO},
> + {}
> +};
>
> - 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;
> +static const struct constant_table fat_param_nfs[] = {
> + {"stale_rw", FAT_NFS_STALE_RW},
> + {"nostale_ro", FAT_NFS_NOSTALE_RO},
> + {}
> +};
>
> - if (!options)
> - goto out;
> +/*
> + * 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},
> + {}
> +};
>
> - while ((p = strsep(&options, ",")) != NULL) {
> - int token;
> - if (!*p)
> - continue;
> +/* 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);
>
> - 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;
> +static const struct fs_parameter_spec msdos_param_spec[] = {
> + fsparam_flag_no ("dots", Opt_dots),
> + fsparam_bool ("dotsOK", Opt_dotsOK),
> + {}
> +};
>
> - /* msdos specific */
> - case Opt_dots:
> - opts->dotsOK = 1;
> - break;
> - case Opt_nodots:
> - opts->dotsOK = 0;
> - break;
> +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},
> + {}
> +};
>
> - /* 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:
> - 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;
> +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),
> + {}
> +};
>
> - /* 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;
> - }
> - }
> +int fat_parse_param(struct fs_context *fc, struct fs_parameter *param,
> + bool is_vfat)
> +{
> + 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)
> + return 0;
>
> -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!");
> + 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);
> }
>
> - /* 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;
> + if (opt < 0)
> + return opt;
> +
> + 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_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;
> +
> + /* msdos specific */
> + case Opt_dots: /* dots / nodots */
> + opts->dotsOK = !result.negated;
> + break;
> + case Opt_dotsOK: /* dotsOK = yes/no */
> + opts->dotsOK = result.boolean;
> + break;
> +
> + /* 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 @@ static int fat_read_static_bpb(struct super_block *sb,
> /*
> * 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");
--
OGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
next prev parent reply other threads:[~2024-07-03 5:19 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-02 22:39 [PATCH V2 0/3] fat: convert to the new mount API Eric Sandeen
2024-07-02 22:41 ` [PATCH V2 1/3] fat: move debug into fat_mount_options Eric Sandeen
2024-07-03 5:19 ` OGAWA Hirofumi
2024-07-02 22:44 ` [PATCH V2 2/3] fat: Convert to new mount api Eric Sandeen
2024-07-03 5:19 ` OGAWA Hirofumi [this message]
2024-07-03 5:21 ` OGAWA Hirofumi
2024-07-02 22:45 ` [PATCH V2 3/3] fat: Convert to new uid/gid option parsing helpers Eric Sandeen
2024-07-03 5:20 ` OGAWA Hirofumi
2024-07-03 8:49 ` [PATCH V2 0/3] fat: convert to the new mount API Christian Brauner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87ed8brr0i.fsf@mail.parknet.co.jp \
--to=hirofumi@mail.parknet.co.jp \
--cc=brauner@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=sandeen@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.