Message ID | a9411b02-5f8e-4e1e-90aa-0c032d66c312@redhat.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | fat: convert to the new mount API | expand |
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");
Forgot Cc OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> writes: > 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");
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");
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> --- 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(-)