diff options
| -rw-r--r-- | fs/ntfs3/ntfs_fs.h | 1 | ||||
| -rw-r--r-- | fs/ntfs3/super.c | 433 | 
2 files changed, 229 insertions, 205 deletions
| diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h index 98c90c399ee2..aa18f12b7096 100644 --- a/fs/ntfs3/ntfs_fs.h +++ b/fs/ntfs3/ntfs_fs.h @@ -52,6 +52,7 @@  // clang-format on  struct ntfs_mount_options { +	char *nls_name;  	struct nls_table *nls;  	kuid_t fs_uid; diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c index 0f3820342051..befa78d3cb26 100644 --- a/fs/ntfs3/super.c +++ b/fs/ntfs3/super.c @@ -28,11 +28,12 @@  #include <linux/buffer_head.h>  #include <linux/exportfs.h>  #include <linux/fs.h> +#include <linux/fs_context.h> +#include <linux/fs_parser.h>  #include <linux/iversion.h>  #include <linux/log2.h>  #include <linux/module.h>  #include <linux/nls.h> -#include <linux/parser.h>  #include <linux/seq_file.h>  #include <linux/statfs.h> @@ -205,9 +206,11 @@ void *ntfs_put_shared(void *ptr)  	return ret;  } -static inline void clear_mount_options(struct ntfs_mount_options *options) +static inline void put_mount_options(struct ntfs_mount_options *options)  { +	kfree(options->nls_name);  	unload_nls(options->nls); +	kfree(options);  }  enum Opt { @@ -229,205 +232,171 @@ enum Opt {  	Opt_err,  }; -static const match_table_t ntfs_tokens = { -	{ Opt_uid, "uid=%u" }, -	{ Opt_gid, "gid=%u" }, -	{ Opt_umask, "umask=%o" }, -	{ Opt_dmask, "dmask=%o" }, -	{ Opt_fmask, "fmask=%o" }, -	{ Opt_immutable, "sys_immutable" }, -	{ Opt_discard, "discard" }, -	{ Opt_force, "force" }, -	{ Opt_sparse, "sparse" }, -	{ Opt_nohidden, "nohidden" }, -	{ Opt_acl, "acl" }, -	{ Opt_showmeta, "showmeta" }, -	{ Opt_nls, "nls=%s" }, -	{ Opt_prealloc, "prealloc" }, -	{ Opt_no_acs_rules, "no_acs_rules" }, -	{ Opt_err, NULL }, +static const struct fs_parameter_spec ntfs_fs_parameters[] = { +	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_flag_no("sys_immutable",	Opt_immutable), +	fsparam_flag_no("discard",		Opt_discard), +	fsparam_flag_no("force",		Opt_force), +	fsparam_flag_no("sparse",		Opt_sparse), +	fsparam_flag("nohidden",		Opt_nohidden), +	fsparam_flag_no("acl",			Opt_acl), +	fsparam_flag_no("showmeta",		Opt_showmeta), +	fsparam_string("nls",			Opt_nls), +	fsparam_flag_no("prealloc",		Opt_prealloc), +	fsparam_flag("no_acs_rules",		Opt_no_acs_rules), +	{}  }; -static noinline int ntfs_parse_options(struct super_block *sb, char *options, -				       int silent, -				       struct ntfs_mount_options *opts) +/* + * Load nls table or if @nls is utf8 then return NULL. + */ +static struct nls_table *ntfs_load_nls(char *nls)  { -	char *p; -	substring_t args[MAX_OPT_ARGS]; -	int option; -	char nls_name[30]; -	struct nls_table *nls; +	struct nls_table *ret; -	opts->fs_uid = current_uid(); -	opts->fs_gid = current_gid(); -	opts->fs_fmask_inv = opts->fs_dmask_inv = ~current_umask(); -	nls_name[0] = 0; +	if (!nls) +		nls = CONFIG_NLS_DEFAULT; -	if (!options) -		goto out; +	if (strcmp(nls, "utf8") == 0) +		return NULL; -	while ((p = strsep(&options, ","))) { -		int token; +	if (strcmp(nls, CONFIG_NLS_DEFAULT) == 0) +		return load_nls_default(); -		if (!*p) -			continue; +	ret = load_nls(nls); +	if (ret) +		return ret; -		token = match_token(p, ntfs_tokens, args); -		switch (token) { -		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; -			opts->uid = 1; -			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; -			opts->gid = 1; -			break; -		case Opt_umask: -			if (match_octal(&args[0], &option)) -				return -EINVAL; -			opts->fs_fmask_inv = opts->fs_dmask_inv = ~option; -			opts->fmask = opts->dmask = 1; -			break; -		case Opt_dmask: -			if (match_octal(&args[0], &option)) -				return -EINVAL; -			opts->fs_dmask_inv = ~option; -			opts->dmask = 1; -			break; -		case Opt_fmask: -			if (match_octal(&args[0], &option)) -				return -EINVAL; -			opts->fs_fmask_inv = ~option; -			opts->fmask = 1; -			break; -		case Opt_discard: -			opts->discard = 1; -			break; -		case Opt_force: -			opts->force = 1; -			break; -		case Opt_sparse: -			opts->sparse = 1; -			break; -		case Opt_nohidden: -			opts->nohidden = 1; -			break; -		case Opt_acl: +	return ERR_PTR(-EINVAL); +} + +static int ntfs_fs_parse_param(struct fs_context *fc, +			       struct fs_parameter *param) +{ +	struct ntfs_mount_options *opts = fc->fs_private; +	struct fs_parse_result result; +	int opt; + +	opt = fs_parse(fc, ntfs_fs_parameters, param, &result); +	if (opt < 0) +		return opt; + +	switch (opt) { +	case Opt_uid: +		opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); +		if (!uid_valid(opts->fs_uid)) +			return invalf(fc, "ntfs3: Invalid value for uid."); +		opts->uid = 1; +		break; +	case Opt_gid: +		opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); +		if (!gid_valid(opts->fs_gid)) +			return invalf(fc, "ntfs3: Invalid value for gid."); +		opts->gid = 1; +		break; +	case Opt_umask: +		if (result.uint_32 & ~07777) +			return invalf(fc, "ntfs3: Invalid value for umask."); +		opts->fs_fmask_inv = ~result.uint_32; +		opts->fs_dmask_inv = ~result.uint_32; +		opts->fmask = 1; +		opts->dmask = 1; +		break; +	case Opt_dmask: +		if (result.uint_32 & ~07777) +			return invalf(fc, "ntfs3: Invalid value for dmask."); +		opts->fs_dmask_inv = ~result.uint_32; +		opts->dmask = 1; +		break; +	case Opt_fmask: +		if (result.uint_32 & ~07777) +			return invalf(fc, "ntfs3: Invalid value for fmask."); +		opts->fs_fmask_inv = ~result.uint_32; +		opts->fmask = 1; +		break; +	case Opt_immutable: +		opts->sys_immutable = result.negated ? 0 : 1; +		break; +	case Opt_discard: +		opts->discard = result.negated ? 0 : 1; +		break; +	case Opt_force: +		opts->force = result.negated ? 0 : 1; +		break; +	case Opt_sparse: +		opts->sparse = result.negated ? 0 : 1; +		break; +	case Opt_nohidden: +		opts->nohidden = 1; +		break; +	case Opt_acl: +		if (!result.negated)  #ifdef CONFIG_NTFS3_FS_POSIX_ACL -			sb->s_flags |= SB_POSIXACL; -			break; +			fc->sb_flags |= SB_POSIXACL;  #else -			ntfs_err(sb, "support for ACL not compiled in!"); -			return -EINVAL; +			return invalf(fc, "ntfs3: Support for ACL not compiled in!");  #endif -		case Opt_showmeta: -			opts->showmeta = 1; -			break; -		case Opt_nls: -			match_strlcpy(nls_name, &args[0], sizeof(nls_name)); -			break; -		case Opt_prealloc: -			opts->prealloc = 1; -			break; -		case Opt_no_acs_rules: -			opts->no_acs_rules = 1; -			break; -		default: -			if (!silent) -				ntfs_err( -					sb, -					"Unrecognized mount option \"%s\" or missing value", -					p); -			//return -EINVAL; -		} -	} - -out: -	if (!strcmp(nls_name[0] ? nls_name : CONFIG_NLS_DEFAULT, "utf8")) { -		/* -		 * For UTF-8 use utf16s_to_utf8s()/utf8s_to_utf16s() -		 * instead of NLS. -		 */ -		nls = NULL; -	} else if (nls_name[0]) { -		nls = load_nls(nls_name); -		if (!nls) { -			ntfs_err(sb, "failed to load \"%s\"", nls_name); -			return -EINVAL; -		} -	} else { -		nls = load_nls_default(); -		if (!nls) { -			ntfs_err(sb, "failed to load default nls"); -			return -EINVAL; -		} +		else +			fc->sb_flags &= ~SB_POSIXACL; +		break; +	case Opt_showmeta: +		opts->showmeta = result.negated ? 0 : 1; +		break; +	case Opt_nls: +		kfree(opts->nls_name); +		opts->nls_name = param->string; +		param->string = NULL; +		break; +	case Opt_prealloc: +		opts->prealloc = result.negated ? 0 : 1; +		break; +	case Opt_no_acs_rules: +		opts->no_acs_rules = 1; +		break; +	default: +		/* Should not be here unless we forget add case. */ +		return -EINVAL;  	} -	opts->nls = nls; -  	return 0;  } -static int ntfs_remount(struct super_block *sb, int *flags, char *data) +static int ntfs_fs_reconfigure(struct fs_context *fc)  { -	int err, ro_rw; +	struct super_block *sb = fc->root->d_sb;  	struct ntfs_sb_info *sbi = sb->s_fs_info; -	struct ntfs_mount_options old_opts; -	char *orig_data = kstrdup(data, GFP_KERNEL); - -	if (data && !orig_data) -		return -ENOMEM; - -	/* Store  original options. */ -	memcpy(&old_opts, sbi->options, sizeof(old_opts)); -	clear_mount_options(sbi->options); -	memset(sbi->options, 0, sizeof(old_opts)); - -	err = ntfs_parse_options(sb, data, 0, sbi->options); -	if (err) -		goto restore_opts; +	struct ntfs_mount_options *new_opts = fc->fs_private; +	int ro_rw; -	ro_rw = sb_rdonly(sb) && !(*flags & SB_RDONLY); +	ro_rw = sb_rdonly(sb) && !(fc->sb_flags & SB_RDONLY);  	if (ro_rw && (sbi->flags & NTFS_FLAGS_NEED_REPLAY)) { -		ntfs_warn( -			sb, -			"Couldn't remount rw because journal is not replayed. Please umount/remount instead\n"); -		err = -EINVAL; -		goto restore_opts; +		errorf(fc, "ntfs3: Couldn't remount rw because journal is not replayed. Please umount/remount instead\n"); +		return -EINVAL; +	} + +	new_opts->nls = ntfs_load_nls(new_opts->nls_name); +	if (IS_ERR(new_opts->nls)) { +		new_opts->nls = NULL; +		errorf(fc, "ntfs3: Cannot load nls %s", new_opts->nls_name); +		return -EINVAL;  	} +	if (new_opts->nls != sbi->options->nls) +		return invalf(fc, "ntfs3: Cannot use different nls when remounting!");  	sync_filesystem(sb);  	if (ro_rw && (sbi->volume.flags & VOLUME_FLAG_DIRTY) && -	    !sbi->options->force) { -		ntfs_warn(sb, "volume is dirty and \"force\" flag is not set!"); -		err = -EINVAL; -		goto restore_opts; +	    !new_opts->force) { +		errorf(fc, "ntfs3: Volume is dirty and \"force\" flag is not set!"); +		return -EINVAL;  	} -	clear_mount_options(&old_opts); - -	ntfs_info(sb, "re-mounted. Opts: %s", orig_data); -	err = 0; -	goto out; - -restore_opts: -	clear_mount_options(sbi->options); -	memcpy(sbi->options, &old_opts, sizeof(old_opts)); +	memcpy(sbi->options, new_opts, sizeof(*new_opts)); -out: -	kfree(orig_data); -	return err; +	return 0;  }  static struct kmem_cache *ntfs_inode_cachep; @@ -506,9 +475,6 @@ static noinline void put_ntfs(struct ntfs_sb_info *sbi)  	xpress_free_decompressor(sbi->compress.xpress);  	lzx_free_decompressor(sbi->compress.lzx);  #endif -	clear_mount_options(sbi->options); -	kfree(sbi->options); -  	kfree(sbi);  } @@ -519,7 +485,9 @@ static void ntfs_put_super(struct super_block *sb)  	/* Mark rw ntfs as clear, if possible. */  	ntfs_set_state(sbi, NTFS_DIRTY_CLEAR); +	put_mount_options(sbi->options);  	put_ntfs(sbi); +	sb->s_fs_info = NULL;  	sync_blockdev(sb->s_bdev);  } @@ -635,7 +603,6 @@ static const struct super_operations ntfs_sops = {  	.statfs = ntfs_statfs,  	.show_options = ntfs_show_options,  	.sync_fs = ntfs_sync_fs, -	.remount_fs = ntfs_remount,  	.write_inode = ntfs3_write_inode,  }; @@ -905,10 +872,10 @@ out:  /*   * ntfs_fill_super - Try to mount.   */ -static int ntfs_fill_super(struct super_block *sb, void *data, int silent) +static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)  {  	int err; -	struct ntfs_sb_info *sbi; +	struct ntfs_sb_info *sbi = sb->s_fs_info;  	struct block_device *bdev = sb->s_bdev;  	struct inode *bd_inode = bdev->bd_inode;  	struct request_queue *rq = bdev_get_queue(bdev); @@ -927,17 +894,6 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)  	ref.high = 0; -	sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); -	if (!sbi) -		return -ENOMEM; - -	sbi->options = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS); -	if (!sbi->options) { -		kfree(sbi); -		return -ENOMEM; -	} - -	sb->s_fs_info = sbi;  	sbi->sb = sb;  	sb->s_flags |= SB_NODIRATIME;  	sb->s_magic = 0x7366746e; // "ntfs" @@ -949,9 +905,12 @@ static int ntfs_fill_super(struct super_block *sb, void *data, int silent)  	ratelimit_state_init(&sbi->msg_ratelimit, DEFAULT_RATELIMIT_INTERVAL,  			     DEFAULT_RATELIMIT_BURST); -	err = ntfs_parse_options(sb, data, silent, sbi->options); -	if (err) -		goto out; +	sbi->options->nls = ntfs_load_nls(sbi->options->nls_name); +	if (IS_ERR(sbi->options->nls)) { +		sbi->options->nls = NULL; +		errorf(fc, "Cannot load nls %s", sbi->options->nls_name); +		return -EINVAL; +	}  	if (!rq || !blk_queue_discard(rq) || !rq->limits.discard_granularity) {  		; @@ -1344,6 +1303,9 @@ load_root:  		goto out;  	} +	fc->fs_private = NULL; +	fc->s_fs_info = NULL; +  	return 0;  out: @@ -1354,9 +1316,6 @@ out:  		sb->s_root = NULL;  	} -	put_ntfs(sbi); - -	sb->s_fs_info = NULL;  	return err;  } @@ -1426,19 +1385,83 @@ int ntfs_discard(struct ntfs_sb_info *sbi, CLST lcn, CLST len)  	return err;  } -static struct dentry *ntfs_mount(struct file_system_type *fs_type, int flags, -				 const char *dev_name, void *data) +static int ntfs_fs_get_tree(struct fs_context *fc) +{ +	return get_tree_bdev(fc, ntfs_fill_super); +} + +/* + * ntfs_fs_free - Free fs_context. + * + * Note that this will be called after fill_super and reconfigure + * even when they pass. So they have to take pointers if they pass. + */ +static void ntfs_fs_free(struct fs_context *fc) +{ +	struct ntfs_mount_options *opts = fc->fs_private; +	struct ntfs_sb_info *sbi = fc->s_fs_info; + +	if (sbi) +		put_ntfs(sbi); + +	if (opts) +		put_mount_options(opts); +} + +static const struct fs_context_operations ntfs_context_ops = { +	.parse_param	= ntfs_fs_parse_param, +	.get_tree	= ntfs_fs_get_tree, +	.reconfigure	= ntfs_fs_reconfigure, +	.free		= ntfs_fs_free, +}; + +/* + * ntfs_init_fs_context - Initialize spi and opts + * + * This will called when mount/remount. We will first initiliaze + * options so that if remount we can use just that. + */ +static int ntfs_init_fs_context(struct fs_context *fc)  { -	return mount_bdev(fs_type, flags, dev_name, data, ntfs_fill_super); +	struct ntfs_mount_options *opts; +	struct ntfs_sb_info *sbi; + +	opts = kzalloc(sizeof(struct ntfs_mount_options), GFP_NOFS); +	if (!opts) +		return -ENOMEM; + +	/* Default options. */ +	opts->fs_uid = current_uid(); +	opts->fs_gid = current_gid(); +	opts->fs_fmask_inv = ~current_umask(); +	opts->fs_dmask_inv = ~current_umask(); + +	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) +		goto ok; + +	sbi = kzalloc(sizeof(struct ntfs_sb_info), GFP_NOFS); +	if (!sbi) { +		kfree(opts); +		return -ENOMEM; +	} + +	sbi->options = opts; +	fc->s_fs_info = sbi; +ok: +	fc->fs_private = opts; +	fc->ops = &ntfs_context_ops; + +	return 0;  }  // clang-format off  static struct file_system_type ntfs_fs_type = { -	.owner		= THIS_MODULE, -	.name		= "ntfs3", -	.mount		= ntfs_mount, -	.kill_sb	= kill_block_super, -	.fs_flags	= FS_REQUIRES_DEV | FS_ALLOW_IDMAP, +	.owner			= THIS_MODULE, +	.name			= "ntfs3", +	.init_fs_context	= ntfs_init_fs_context, +	.parameters		= ntfs_fs_parameters, +	.kill_sb		= kill_block_super, +	.fs_flags		= FS_REQUIRES_DEV | FS_ALLOW_IDMAP,  };  // clang-format on | 
