diff --git a/fs/udf/super.c b/fs/udf/super.c index 1d1fb8a27655..adb39e08b2f2 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -40,20 +40,20 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include #include #include #include +#include +#include #include "udf_sb.h" #include "udf_i.h" @@ -91,16 +91,20 @@ enum { UDF_MAX_LINKS = 0xffff }; #define UDF_MAX_FILESIZE (1ULL << 42) /* These are the "meat" - everything else is stuffing */ -static int udf_fill_super(struct super_block *, void *, int); +static int udf_fill_super(struct super_block *sb, struct fs_context *fc); static void udf_put_super(struct super_block *); static int udf_sync_fs(struct super_block *, int); -static int udf_remount_fs(struct super_block *, int *, char *); static void udf_load_logicalvolint(struct super_block *, struct kernel_extent_ad); static void udf_open_lvid(struct super_block *); static void udf_close_lvid(struct super_block *); static unsigned int udf_count_free(struct super_block *); static int udf_statfs(struct dentry *, struct kstatfs *); static int udf_show_options(struct seq_file *, struct dentry *); +static int udf_init_fs_context(struct fs_context *fc); +static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param); +static int udf_reconfigure(struct fs_context *fc); +static void udf_free_fc(struct fs_context *fc); +static const struct fs_parameter_spec udf_param_spec[]; struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) { @@ -119,18 +123,25 @@ struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb) } /* UDF filesystem type */ -static struct dentry *udf_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) +static int udf_get_tree(struct fs_context *fc) { - return mount_bdev(fs_type, flags, dev_name, data, udf_fill_super); + return get_tree_bdev(fc, udf_fill_super); } +static const struct fs_context_operations udf_context_ops = { + .parse_param = udf_parse_param, + .get_tree = udf_get_tree, + .reconfigure = udf_reconfigure, + .free = udf_free_fc, +}; + static struct file_system_type udf_fstype = { .owner = THIS_MODULE, .name = "udf", - .mount = udf_mount, .kill_sb = kill_block_super, .fs_flags = FS_REQUIRES_DEV, + .init_fs_context = udf_init_fs_context, + .parameters = udf_param_spec, }; MODULE_ALIAS_FS("udf"); @@ -204,7 +215,6 @@ static const struct super_operations udf_sb_ops = { .put_super = udf_put_super, .sync_fs = udf_sync_fs, .statfs = udf_statfs, - .remount_fs = udf_remount_fs, .show_options = udf_show_options, }; @@ -222,6 +232,65 @@ struct udf_options { struct nls_table *nls_map; }; +/* + * UDF has historically preserved prior mount options across + * a remount, so copy those here if remounting, otherwise set + * initial mount defaults. + */ +static void udf_init_options(struct fs_context *fc, struct udf_options *uopt) +{ + if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { + struct super_block *sb = fc->root->d_sb; + struct udf_sb_info *sbi = UDF_SB(sb); + + uopt->flags = sbi->s_flags; + uopt->uid = sbi->s_uid; + uopt->gid = sbi->s_gid; + uopt->umask = sbi->s_umask; + uopt->fmode = sbi->s_fmode; + uopt->dmode = sbi->s_dmode; + uopt->nls_map = NULL; + } else { + uopt->flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | + (1 << UDF_FLAG_STRICT); + /* + * By default we'll use overflow[ug]id when UDF + * inode [ug]id == -1 + */ + uopt->uid = make_kuid(current_user_ns(), overflowuid); + uopt->gid = make_kgid(current_user_ns(), overflowgid); + uopt->umask = 0; + uopt->fmode = UDF_INVALID_MODE; + uopt->dmode = UDF_INVALID_MODE; + uopt->nls_map = NULL; + uopt->session = 0xFFFFFFFF; + } +} + +static int udf_init_fs_context(struct fs_context *fc) +{ + struct udf_options *uopt; + + uopt = kzalloc(sizeof(*uopt), GFP_KERNEL); + if (!uopt) + return -ENOMEM; + + udf_init_options(fc, uopt); + + fc->fs_private = uopt; + fc->ops = &udf_context_ops; + + return 0; +} + +static void udf_free_fc(struct fs_context *fc) +{ + struct udf_options *uopt = fc->fs_private; + + unload_nls(uopt->nls_map); + kfree(fc->fs_private); +} + static int __init init_udf_fs(void) { int err; @@ -357,7 +426,7 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root) } /* - * udf_parse_options + * udf_parse_param * * PURPOSE * Parse mount options. @@ -400,12 +469,12 @@ static int udf_show_options(struct seq_file *seq, struct dentry *root) * yield highly unpredictable results. * * PRE-CONDITIONS - * options Pointer to mount options string. - * uopts Pointer to mount options variable. + * fc fs_context with pointer to mount options variable. + * param Pointer to fs_parameter being parsed. * * POST-CONDITIONS - * 1 Mount options parsed okay. - * 0 Error parsing mount options. + * 0 Mount options parsed okay. + * errno Error parsing mount options. * * HISTORY * July 1, 1997 - Andrew E. Mileski @@ -417,228 +486,193 @@ enum { Opt_noadinicb, Opt_adinicb, Opt_shortad, Opt_longad, Opt_gid, Opt_uid, Opt_umask, Opt_session, Opt_lastblock, Opt_anchor, Opt_volume, Opt_partition, Opt_fileset, - Opt_rootdir, Opt_utf8, Opt_iocharset, - Opt_err, Opt_uforget, Opt_uignore, Opt_gforget, Opt_gignore, - Opt_fmode, Opt_dmode + Opt_rootdir, Opt_utf8, Opt_iocharset, Opt_err, Opt_fmode, Opt_dmode }; -static const match_table_t tokens = { - {Opt_novrs, "novrs"}, - {Opt_nostrict, "nostrict"}, - {Opt_bs, "bs=%u"}, - {Opt_unhide, "unhide"}, - {Opt_undelete, "undelete"}, - {Opt_noadinicb, "noadinicb"}, - {Opt_adinicb, "adinicb"}, - {Opt_shortad, "shortad"}, - {Opt_longad, "longad"}, - {Opt_uforget, "uid=forget"}, - {Opt_uignore, "uid=ignore"}, - {Opt_gforget, "gid=forget"}, - {Opt_gignore, "gid=ignore"}, - {Opt_gid, "gid=%u"}, - {Opt_uid, "uid=%u"}, - {Opt_umask, "umask=%o"}, - {Opt_session, "session=%u"}, - {Opt_lastblock, "lastblock=%u"}, - {Opt_anchor, "anchor=%u"}, - {Opt_volume, "volume=%u"}, - {Opt_partition, "partition=%u"}, - {Opt_fileset, "fileset=%u"}, - {Opt_rootdir, "rootdir=%u"}, - {Opt_utf8, "utf8"}, - {Opt_iocharset, "iocharset=%s"}, - {Opt_fmode, "mode=%o"}, - {Opt_dmode, "dmode=%o"}, - {Opt_err, NULL} -}; +static const struct fs_parameter_spec udf_param_spec[] = { + fsparam_flag ("novrs", Opt_novrs), + fsparam_flag ("nostrict", Opt_nostrict), + fsparam_u32 ("bs", Opt_bs), + fsparam_flag ("unhide", Opt_unhide), + fsparam_flag ("undelete", Opt_undelete), + fsparam_flag_no ("adinicb", Opt_adinicb), + fsparam_flag ("shortad", Opt_shortad), + fsparam_flag ("longad", Opt_longad), + fsparam_string ("gid", Opt_gid), + fsparam_string ("uid", Opt_uid), + fsparam_u32 ("umask", Opt_umask), + fsparam_u32 ("session", Opt_session), + fsparam_u32 ("lastblock", Opt_lastblock), + fsparam_u32 ("anchor", Opt_anchor), + fsparam_u32 ("volume", Opt_volume), + fsparam_u32 ("partition", Opt_partition), + fsparam_u32 ("fileset", Opt_fileset), + fsparam_u32 ("rootdir", Opt_rootdir), + fsparam_flag ("utf8", Opt_utf8), + fsparam_string ("iocharset", Opt_iocharset), + fsparam_u32 ("mode", Opt_fmode), + fsparam_u32 ("dmode", Opt_dmode), + {} + }; -static int udf_parse_options(char *options, struct udf_options *uopt, - bool remount) +static int udf_parse_param(struct fs_context *fc, struct fs_parameter *param) { - char *p; - int option; unsigned int uv; + unsigned int n; + struct udf_options *uopt = fc->fs_private; + struct fs_parse_result result; + int token; + bool remount = (fc->purpose & FS_CONTEXT_FOR_RECONFIGURE); - uopt->session = 0xFFFFFFFF; - uopt->lastblock = 0; - uopt->anchor = 0; + token = fs_parse(fc, udf_param_spec, param, &result); + if (token < 0) + return token; - if (!options) - return 1; - - while ((p = strsep(&options, ",")) != NULL) { - substring_t args[MAX_OPT_ARGS]; - int token; - unsigned n; - if (!*p) - continue; - - token = match_token(p, tokens, args); - switch (token) { - case Opt_novrs: - uopt->flags |= (1 << UDF_FLAG_NOVRS); - break; - case Opt_bs: - if (match_int(&args[0], &option)) - return 0; - n = option; - if (n != 512 && n != 1024 && n != 2048 && n != 4096) - return 0; - uopt->blocksize = n; - uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); - break; - case Opt_unhide: - uopt->flags |= (1 << UDF_FLAG_UNHIDE); - break; - case Opt_undelete: - uopt->flags |= (1 << UDF_FLAG_UNDELETE); - break; - case Opt_noadinicb: + switch (token) { + case Opt_novrs: + uopt->flags |= (1 << UDF_FLAG_NOVRS); + break; + case Opt_bs: + n = result.uint_32; + if (n != 512 && n != 1024 && n != 2048 && n != 4096) + return -EINVAL; + uopt->blocksize = n; + uopt->flags |= (1 << UDF_FLAG_BLOCKSIZE_SET); + break; + case Opt_unhide: + uopt->flags |= (1 << UDF_FLAG_UNHIDE); + break; + case Opt_undelete: + uopt->flags |= (1 << UDF_FLAG_UNDELETE); + break; + case Opt_adinicb: + if (result.negated) uopt->flags &= ~(1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_adinicb: + else uopt->flags |= (1 << UDF_FLAG_USE_AD_IN_ICB); - break; - case Opt_shortad: - uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_longad: - uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); - break; - case Opt_gid: - if (match_uint(args, &uv)) - return 0; - uopt->gid = make_kgid(current_user_ns(), uv); - if (!gid_valid(uopt->gid)) - return 0; + break; + case Opt_shortad: + uopt->flags |= (1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_longad: + uopt->flags &= ~(1 << UDF_FLAG_USE_SHORT_AD); + break; + case Opt_gid: + if (kstrtoint(param->string, 10, &uv) == 0) { + kgid_t gid = make_kgid(current_user_ns(), uv); + if (!gid_valid(gid)) + return -EINVAL; + uopt->gid = gid; uopt->flags |= (1 << UDF_FLAG_GID_SET); - break; - case Opt_uid: - if (match_uint(args, &uv)) - return 0; - uopt->uid = make_kuid(current_user_ns(), uv); - if (!uid_valid(uopt->uid)) - return 0; - uopt->flags |= (1 << UDF_FLAG_UID_SET); - break; - case Opt_umask: - if (match_octal(args, &option)) - return 0; - uopt->umask = option; - break; - case Opt_nostrict: - uopt->flags &= ~(1 << UDF_FLAG_STRICT); - break; - case Opt_session: - if (match_int(args, &option)) - return 0; - uopt->session = option; - if (!remount) - uopt->flags |= (1 << UDF_FLAG_SESSION_SET); - break; - case Opt_lastblock: - if (match_int(args, &option)) - return 0; - uopt->lastblock = option; - if (!remount) - uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET); - break; - case Opt_anchor: - if (match_int(args, &option)) - return 0; - uopt->anchor = option; - break; - case Opt_volume: - case Opt_partition: - case Opt_fileset: - case Opt_rootdir: - /* Ignored (never implemented properly) */ - break; - case Opt_utf8: - if (!remount) { - unload_nls(uopt->nls_map); - uopt->nls_map = NULL; - } - break; - case Opt_iocharset: - if (!remount) { - unload_nls(uopt->nls_map); - uopt->nls_map = NULL; - } - /* When nls_map is not loaded then UTF-8 is used */ - if (!remount && strcmp(args[0].from, "utf8") != 0) { - uopt->nls_map = load_nls(args[0].from); - if (!uopt->nls_map) { - pr_err("iocharset %s not found\n", - args[0].from); - return 0; - } - } - break; - case Opt_uforget: - uopt->flags |= (1 << UDF_FLAG_UID_FORGET); - break; - case Opt_uignore: - case Opt_gignore: - /* These options are superseeded by uid= */ - break; - case Opt_gforget: + } else if (!strcmp(param->string, "forget")) { uopt->flags |= (1 << UDF_FLAG_GID_FORGET); - break; - case Opt_fmode: - if (match_octal(args, &option)) - return 0; - uopt->fmode = option & 0777; - break; - case Opt_dmode: - if (match_octal(args, &option)) - return 0; - uopt->dmode = option & 0777; - break; - default: - pr_err("bad mount option \"%s\" or missing value\n", p); - return 0; + } else if (!strcmp(param->string, "ignore")) { + /* this option is superseded by gid= */ + ; + } else { + return -EINVAL; } + break; + case Opt_uid: + if (kstrtoint(param->string, 10, &uv) == 0) { + kuid_t uid = make_kuid(current_user_ns(), uv); + if (!uid_valid(uid)) + return -EINVAL; + uopt->uid = uid; + uopt->flags |= (1 << UDF_FLAG_UID_SET); + } else if (!strcmp(param->string, "forget")) { + uopt->flags |= (1 << UDF_FLAG_UID_FORGET); + } else if (!strcmp(param->string, "ignore")) { + /* this option is superseded by uid= */ + ; + } else { + return -EINVAL; + } + break; + case Opt_umask: + uopt->umask = result.uint_32; + break; + case Opt_nostrict: + uopt->flags &= ~(1 << UDF_FLAG_STRICT); + break; + case Opt_session: + uopt->session = result.uint_32; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_SESSION_SET); + break; + case Opt_lastblock: + uopt->lastblock = result.uint_32; + if (!remount) + uopt->flags |= (1 << UDF_FLAG_LASTBLOCK_SET); + break; + case Opt_anchor: + uopt->anchor = result.uint_32; + break; + case Opt_volume: + case Opt_partition: + case Opt_fileset: + case Opt_rootdir: + /* Ignored (never implemented properly) */ + break; + case Opt_utf8: + if (!remount) { + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; + } + break; + case Opt_iocharset: + if (!remount) { + unload_nls(uopt->nls_map); + uopt->nls_map = NULL; + } + /* When nls_map is not loaded then UTF-8 is used */ + if (!remount && strcmp(param->string, "utf8") != 0) { + uopt->nls_map = load_nls(param->string); + if (!uopt->nls_map) { + errorf(fc, "iocharset %s not found", + param->string); + return -EINVAL;; + } + } + break; + case Opt_fmode: + uopt->fmode = result.uint_32 & 0777; + break; + case Opt_dmode: + uopt->dmode = result.uint_32 & 0777; + break; + default: + return -EINVAL; } - return 1; + return 0; } -static int udf_remount_fs(struct super_block *sb, int *flags, char *options) +static int udf_reconfigure(struct fs_context *fc) { - struct udf_options uopt; + struct udf_options *uopt = fc->fs_private; + struct super_block *sb = fc->root->d_sb; struct udf_sb_info *sbi = UDF_SB(sb); + int readonly = fc->sb_flags & SB_RDONLY; int error = 0; - if (!(*flags & SB_RDONLY) && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT)) + if (!readonly && UDF_QUERY_FLAG(sb, UDF_FLAG_RW_INCOMPAT)) return -EACCES; sync_filesystem(sb); - uopt.flags = sbi->s_flags; - uopt.uid = sbi->s_uid; - uopt.gid = sbi->s_gid; - uopt.umask = sbi->s_umask; - uopt.fmode = sbi->s_fmode; - uopt.dmode = sbi->s_dmode; - uopt.nls_map = NULL; - - if (!udf_parse_options(options, &uopt, true)) - return -EINVAL; - write_lock(&sbi->s_cred_lock); - sbi->s_flags = uopt.flags; - sbi->s_uid = uopt.uid; - sbi->s_gid = uopt.gid; - sbi->s_umask = uopt.umask; - sbi->s_fmode = uopt.fmode; - sbi->s_dmode = uopt.dmode; + sbi->s_flags = uopt->flags; + sbi->s_uid = uopt->uid; + sbi->s_gid = uopt->gid; + sbi->s_umask = uopt->umask; + sbi->s_fmode = uopt->fmode; + sbi->s_dmode = uopt->dmode; write_unlock(&sbi->s_cred_lock); - if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb)) + if (readonly == sb_rdonly(sb)) goto out_unlock; - if (*flags & SB_RDONLY) + if (readonly) udf_close_lvid(sb); else udf_open_lvid(sb); @@ -2090,23 +2124,15 @@ u64 lvid_get_unique_id(struct super_block *sb) return ret; } -static int udf_fill_super(struct super_block *sb, void *options, int silent) +static int udf_fill_super(struct super_block *sb, struct fs_context *fc) { int ret = -EINVAL; struct inode *inode = NULL; - struct udf_options uopt; + struct udf_options *uopt = fc->fs_private; struct kernel_lb_addr rootdir, fileset; struct udf_sb_info *sbi; bool lvid_open = false; - - uopt.flags = (1 << UDF_FLAG_USE_AD_IN_ICB) | (1 << UDF_FLAG_STRICT); - /* By default we'll use overflow[ug]id when UDF inode [ug]id == -1 */ - uopt.uid = make_kuid(current_user_ns(), overflowuid); - uopt.gid = make_kgid(current_user_ns(), overflowgid); - uopt.umask = 0; - uopt.fmode = UDF_INVALID_MODE; - uopt.dmode = UDF_INVALID_MODE; - uopt.nls_map = NULL; + int silent = fc->sb_flags & SB_SILENT; sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) @@ -2116,25 +2142,23 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) mutex_init(&sbi->s_alloc_mutex); - if (!udf_parse_options((char *)options, &uopt, false)) - goto parse_options_failure; - fileset.logicalBlockNum = 0xFFFFFFFF; fileset.partitionReferenceNum = 0xFFFF; - sbi->s_flags = uopt.flags; - sbi->s_uid = uopt.uid; - sbi->s_gid = uopt.gid; - sbi->s_umask = uopt.umask; - sbi->s_fmode = uopt.fmode; - sbi->s_dmode = uopt.dmode; - sbi->s_nls_map = uopt.nls_map; + sbi->s_flags = uopt->flags; + sbi->s_uid = uopt->uid; + sbi->s_gid = uopt->gid; + sbi->s_umask = uopt->umask; + sbi->s_fmode = uopt->fmode; + sbi->s_dmode = uopt->dmode; + sbi->s_nls_map = uopt->nls_map; + uopt->nls_map = NULL; rwlock_init(&sbi->s_cred_lock); - if (uopt.session == 0xFFFFFFFF) + if (uopt->session == 0xFFFFFFFF) sbi->s_session = udf_get_last_session(sb); else - sbi->s_session = uopt.session; + sbi->s_session = uopt->session; udf_debug("Multi-session=%d\n", sbi->s_session); @@ -2145,16 +2169,16 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) sb->s_magic = UDF_SUPER_MAGIC; sb->s_time_gran = 1000; - if (uopt.flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) { - ret = udf_load_vrs(sb, &uopt, silent, &fileset); + if (uopt->flags & (1 << UDF_FLAG_BLOCKSIZE_SET)) { + ret = udf_load_vrs(sb, uopt, silent, &fileset); } else { - uopt.blocksize = bdev_logical_block_size(sb->s_bdev); - while (uopt.blocksize <= 4096) { - ret = udf_load_vrs(sb, &uopt, silent, &fileset); + uopt->blocksize = bdev_logical_block_size(sb->s_bdev); + while (uopt->blocksize <= 4096) { + ret = udf_load_vrs(sb, uopt, silent, &fileset); if (ret < 0) { if (!silent && ret != -EACCES) { pr_notice("Scanning with blocksize %u failed\n", - uopt.blocksize); + uopt->blocksize); } brelse(sbi->s_lvid_bh); sbi->s_lvid_bh = NULL; @@ -2167,7 +2191,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) } else break; - uopt.blocksize <<= 1; + uopt->blocksize <<= 1; } } if (ret < 0) { @@ -2272,8 +2296,7 @@ static int udf_fill_super(struct super_block *sb, void *options, int silent) error_out: iput(sbi->s_vat_inode); -parse_options_failure: - unload_nls(uopt.nls_map); + unload_nls(uopt->nls_map); if (lvid_open) udf_close_lvid(sb); brelse(sbi->s_lvid_bh);