Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 04/23, Eric Sandeen wrote:
> From: Hongbo Li <lihongbo22@xxxxxxxxxx>
> 
> The new mount api separates option parsing and super block setup
> into two distinct steps and so we need to separate the options
> parsing out of the parse_options().
> 
> In order to achieve this, here we handle the mount options with
> three steps:
>   - Firstly, we move sb/sbi out of handle_mount_opt.
>     As the former patch introduced f2fs_fs_context, so we record
>     the changed mount options in this context. In handle_mount_opt,
>     sb/sbi is null, so we should move all relative code out of
>     handle_mount_opt (thus, some check case which use sb/sbi should
>     move out).
>   - Secondly, we introduce the some check helpers to keep the option
>     consistent.
>     During filling superblock period, sb/sbi are ready. So we check
>     the f2fs_fs_context which holds the mount options base on sb/sbi.
>   - Thirdly, we apply the new mount options to sb/sbi.
>     After checking the f2fs_fs_context, all changed on mount options
>     are valid. So we can apply them to sb/sbi directly.
> 
> After do these, option parsing and super block setting have been
> decoupled. Also it should have retained the original execution
> flow.
> 
> Signed-off-by: Hongbo Li <lihongbo22@xxxxxxxxxx>
> [sandeen: forward port, minor fixes and updates]
> Signed-off-by: Eric Sandeen <sandeen@xxxxxxxxxx>
> ---
>  fs/f2fs/super.c | 693 +++++++++++++++++++++++++++++++++++-------------
>  1 file changed, 510 insertions(+), 183 deletions(-)
> 
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 15befeb45c94..149134775870 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -360,6 +360,12 @@ static inline void ctx_clear_opt(struct f2fs_fs_context *ctx,
>  	ctx->opt_mask |= flag;
>  }
>  
> +static inline bool ctx_test_opt(struct f2fs_fs_context *ctx,
> +				unsigned int flag)
> +{
> +	return ctx->info.opt & flag;
> +}
> +
>  static inline void ctx_set_flags(struct f2fs_fs_context *ctx,
>  				 unsigned int flag)
>  {
> @@ -533,51 +539,6 @@ static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
>  	ctx->qname_mask |= 1 << qtype;
>  	return 0;
>  }
> -
> -static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
> -{
> -	/*
> -	 * We do the test below only for project quotas. 'usrquota' and
> -	 * 'grpquota' mount options are allowed even without quota feature
> -	 * to support legacy quotas in quota files.
> -	 */
> -	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi)) {
> -		f2fs_err(sbi, "Project quota feature not enabled. Cannot enable project quota enforcement.");
> -		return -1;
> -	}
> -	if (F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
> -			F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
> -			F2FS_OPTION(sbi).s_qf_names[PRJQUOTA]) {
> -		if (test_opt(sbi, USRQUOTA) &&
> -				F2FS_OPTION(sbi).s_qf_names[USRQUOTA])
> -			clear_opt(sbi, USRQUOTA);
> -
> -		if (test_opt(sbi, GRPQUOTA) &&
> -				F2FS_OPTION(sbi).s_qf_names[GRPQUOTA])
> -			clear_opt(sbi, GRPQUOTA);
> -
> -		if (test_opt(sbi, PRJQUOTA) &&
> -				F2FS_OPTION(sbi).s_qf_names[PRJQUOTA])
> -			clear_opt(sbi, PRJQUOTA);
> -
> -		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
> -				test_opt(sbi, PRJQUOTA)) {
> -			f2fs_err(sbi, "old and new quota format mixing");
> -			return -1;
> -		}
> -
> -		if (!F2FS_OPTION(sbi).s_jquota_fmt) {
> -			f2fs_err(sbi, "journaled quota format not specified");
> -			return -1;
> -		}
> -	}
> -
> -	if (f2fs_sb_has_quota_ino(sbi) && F2FS_OPTION(sbi).s_jquota_fmt) {
> -		f2fs_info(sbi, "QUOTA feature is enabled, so ignore jquota_fmt");
> -		F2FS_OPTION(sbi).s_jquota_fmt = 0;
> -	}
> -	return 0;
> -}
>  #endif
>  
>  static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
> @@ -636,28 +597,28 @@ static bool is_compress_extension_exist(struct f2fs_mount_info *info,
>   * extension will be treated as special cases and will not be compressed.
>   * 3. Don't allow the non-compress extension specifies all files.
>   */
> -static int f2fs_test_compress_extension(struct f2fs_sb_info *sbi)
> +static int f2fs_test_compress_extension(unsigned char (*noext)[F2FS_EXTENSION_LEN],
> +					int noext_cnt,
> +					unsigned char (*ext)[F2FS_EXTENSION_LEN],
> +					int ext_cnt)
>  {
> -	unsigned char (*ext)[F2FS_EXTENSION_LEN];
> -	unsigned char (*noext)[F2FS_EXTENSION_LEN];
> -	int ext_cnt, noext_cnt, index = 0, no_index = 0;
> -
> -	ext = F2FS_OPTION(sbi).extensions;
> -	ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
> -	noext = F2FS_OPTION(sbi).noextensions;
> -	noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
> +	int index = 0, no_index = 0;
>  
>  	if (!noext_cnt)
>  		return 0;
>  
>  	for (no_index = 0; no_index < noext_cnt; no_index++) {
> +		if (strlen(noext[no_index]) == 0)
> +			continue;
>  		if (!strcasecmp("*", noext[no_index])) {
> -			f2fs_info(sbi, "Don't allow the nocompress extension specifies all files");
> +			f2fs_info(NULL, "Don't allow the nocompress extension specifies all files");
>  			return -EINVAL;
>  		}
>  		for (index = 0; index < ext_cnt; index++) {
> +			if (strlen(ext[index]) == 0)
> +				continue;
>  			if (!strcasecmp(ext[index], noext[no_index])) {
> -				f2fs_info(sbi, "Don't allow the same extension %s appear in both compress and nocompress extension",
> +				f2fs_info(NULL, "Don't allow the same extension %s appear in both compress and nocompress extension",
>  						ext[index]);
>  				return -EINVAL;
>  			}
> @@ -749,7 +710,6 @@ static int f2fs_set_zstd_level(struct f2fs_fs_context *ctx, const char *str)
>  static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  {
>  	struct f2fs_fs_context *ctx = fc->fs_private;
> -	struct f2fs_sb_info *sbi = fc->s_fs_info;
>  #ifdef CONFIG_F2FS_FS_COMPRESSION
>  	unsigned char (*ext)[F2FS_EXTENSION_LEN];
>  	unsigned char (*noext)[F2FS_EXTENSION_LEN];
> @@ -758,15 +718,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  #endif
>  	substring_t args[MAX_OPT_ARGS];
>  	struct fs_parse_result result;
> -	int is_remount;
>  	int token, ret, arg;
>  
>  	token = fs_parse(fc, f2fs_param_specs, param, &result);
>  	if (token < 0)
>  		return token;
>  
> -	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
> -
>  	switch (token) {
>  	case Opt_gc_background:
>  		F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
> @@ -780,19 +737,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
>  		break;
>  	case Opt_discard:
> -		if (result.negated) {
> -			if (f2fs_hw_should_discard(sbi)) {
> -				f2fs_warn(NULL, "discard is required for zoned block devices");
> -				return -EINVAL;
> -			}
> +		if (result.negated)
>  			ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
> -		} else {
> -			if (!f2fs_hw_support_discard(sbi)) {
> -				f2fs_warn(NULL, "device does not support discard");
> -				break;
> -			}
> +		else
>  			ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
> -		}
>  		break;
>  	case Opt_noheap:
>  	case Opt_heap:
> @@ -812,6 +760,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
>  		break;
>  	case Opt_inline_xattr_size:
> +		if (result.int_32 < MIN_INLINE_XATTR_SIZE ||
> +			result.int_32 > MAX_INLINE_XATTR_SIZE) {
> +			f2fs_err(NULL, "inline xattr size is out of range: %zu ~ %zu",
> +				MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
> +			return -EINVAL;
> +		}
>  		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
>  		F2FS_CTX_INFO(ctx).inline_xattr_size = result.int_32;
>  		ctx->spec_mask |= F2FS_SPEC_inline_xattr_size;
> @@ -873,27 +827,18 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ctx_set_opt(ctx, F2FS_MOUNT_FASTBOOT);
>  		break;
>  	case Opt_extent_cache:
> -		if (result.negated) {
> -			if (f2fs_sb_has_device_alias(sbi)) {
> -				f2fs_err(sbi, "device aliasing requires extent cache");
> -				return -EINVAL;
> -			}
> +		if (result.negated)
>  			ctx_clear_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
> -		} else
> +		else
>  			ctx_set_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
>  		break;
>  	case Opt_data_flush:
>  		ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
>  		break;
>  	case Opt_reserve_root:
> -		if (test_opt(sbi, RESERVE_ROOT)) {
> -			f2fs_info(NULL, "Preserve previous reserve_root=%u",
> -				  F2FS_OPTION(sbi).root_reserved_blocks);
> -		} else {
> -			ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
> -			F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
> -			ctx->spec_mask |= F2FS_SPEC_reserve_root;
> -		}
> +		ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
> +		F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
> +		ctx->spec_mask |= F2FS_SPEC_reserve_root;
>  		break;
>  	case Opt_resuid:
>  		F2FS_CTX_INFO(ctx).s_resuid = result.uid;
> @@ -909,7 +854,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		break;
>  #ifdef CONFIG_F2FS_FAULT_INJECTION
>  	case Opt_fault_injection:
> -		if (f2fs_build_fault_attr(sbi, result.int_32, 0, FAULT_RATE))
> +		if (result.int_32 > INT_MAX)
>  			return -EINVAL;
>  		F2FS_CTX_INFO(ctx).fault_info.inject_rate = result.int_32;
>  		ctx->spec_mask |= F2FS_SPEC_fault_injection;
> @@ -917,7 +862,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		break;
>  
>  	case Opt_fault_type:
> -		if (f2fs_build_fault_attr(sbi, 0, result.int_32, FAULT_TYPE))
> +		if (result.uint_32 > BIT(FAULT_MAX))
>  			return -EINVAL;
>  		F2FS_CTX_INFO(ctx).fault_info.inject_type = result.uint_32;
>  		ctx->spec_mask |= F2FS_SPEC_fault_type;
> @@ -1051,10 +996,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		break;
>  #ifdef CONFIG_F2FS_FS_COMPRESSION
>  	case Opt_compress_algorithm:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		name = param->string;
>  		if (!strcmp(name, "lzo")) {
>  #ifdef CONFIG_F2FS_FS_LZO
> @@ -1098,10 +1039,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  			return -EINVAL;
>  		break;
>  	case Opt_compress_log_size:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
>  		    result.uint_32 > MAX_COMPRESS_LOG_SIZE) {
>  			f2fs_err(NULL,
> @@ -1112,10 +1049,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ctx->spec_mask |= F2FS_SPEC_compress_log_size;
>  		break;
>  	case Opt_compress_extension:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		name = param->string;
>  		ext = F2FS_CTX_INFO(ctx).extensions;
>  		ext_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
> @@ -1136,10 +1069,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ctx->spec_mask |= F2FS_SPEC_compress_extension;
>  		break;
>  	case Opt_nocompress_extension:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		name = param->string;
>  		noext = F2FS_CTX_INFO(ctx).noextensions;
>  		noext_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
> @@ -1160,26 +1089,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ctx->spec_mask |= F2FS_SPEC_nocompress_extension;
>  		break;
>  	case Opt_compress_chksum:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		F2FS_CTX_INFO(ctx).compress_chksum = true;
>  		ctx->spec_mask |= F2FS_SPEC_compress_chksum;
>  		break;
>  	case Opt_compress_mode:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		F2FS_CTX_INFO(ctx).compress_mode = result.uint_32;
>  		ctx->spec_mask |= F2FS_SPEC_compress_mode;
>  		break;
>  	case Opt_compress_cache:
> -		if (!f2fs_sb_has_compression(sbi)) {
> -			f2fs_info(NULL, "Image doesn't support compression");
> -			break;
> -		}
>  		ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
>  		break;
>  #else
> @@ -1224,24 +1141,15 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  	return 0;
>  }
>  
> -static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
> +static int parse_options(struct fs_context *fc, char *options)
>  {
>  	struct fs_parameter param;
> -	struct fs_context fc;
> -	struct f2fs_fs_context ctx;
>  	char *key;
>  	int ret;
>  
>  	if (!options)
>  		return 0;
>  
> -	memset(&fc, 0, sizeof(fc));
> -	fc.s_fs_info = sbi;
> -	fc.fs_private = &ctx;
> -
> -	if (is_remount)
> -		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
> -
>  	while ((key = strsep(&options, ",")) != NULL) {
>  		if (*key) {
>  			size_t v_len = 0;
> @@ -1265,7 +1173,7 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
>  			param.key = key;
>  			param.size = v_len;
>  
> -			ret = handle_mount_opt(&fc, &param);
> +			ret = handle_mount_opt(fc, &param);
>  			kfree(param.string);
>  			if (ret < 0)
>  				return ret;
> @@ -1274,24 +1182,293 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
>  	return 0;
>  }
>  
> -static int f2fs_validate_options(struct f2fs_sb_info *sbi)
> +/*
> + * Check quota settings consistency.
> + */
> +static int f2fs_check_quota_consistency(struct fs_context *fc,
> +					struct super_block *sb)
>  {
> -#ifdef CONFIG_QUOTA
> -	if (f2fs_check_quota_options(sbi))
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> + #ifdef CONFIG_QUOTA
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	bool quota_feature = f2fs_sb_has_quota_ino(sbi);
> +	bool quota_turnon = sb_any_quota_loaded(sb);
> +	char *old_qname, *new_qname;
> +	bool usr_qf_name, grp_qf_name, prj_qf_name, usrquota, grpquota, prjquota;
> +	int i;
> +
> +	/*
> +	 * We do the test below only for project quotas. 'usrquota' and
> +	 * 'grpquota' mount options are allowed even without quota feature
> +	 * to support legacy quotas in quota files.
> +	 */
> +	if (ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA) &&
> +			!f2fs_sb_has_project_quota(sbi)) {
> +		f2fs_err(sbi, "Project quota feature not enabled. Cannot enable project quota enforcement.");
>  		return -EINVAL;
> +	}
> +
> +	if (ctx->qname_mask) {
> +		for (i = 0; i < MAXQUOTAS; i++) {
> +			if (!(ctx->qname_mask & (1 << i)))
> +				continue;
> +
> +			old_qname = F2FS_OPTION(sbi).s_qf_names[i];
> +			new_qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
> +			if (quota_turnon &&
> +				!!old_qname != !!new_qname)
> +				goto err_jquota_change;
> +
> +			if (old_qname) {
> +				if (strcmp(old_qname, new_qname) == 0) {
> +					ctx->qname_mask &= ~(1 << i);
> +					continue;
> +				}
> +				goto err_jquota_specified;
> +			}
> +
> +			if (quota_feature) {
> +				f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
> +				ctx->qname_mask &= ~(1 << i);
> +				kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
> +				F2FS_CTX_INFO(ctx).s_qf_names[i] = NULL;
> +			}
> +		}
> +	}
> +
> +	/* Make sure we don't mix old and new quota format */
> +	usr_qf_name = F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
> +			F2FS_CTX_INFO(ctx).s_qf_names[USRQUOTA];
> +	grp_qf_name = F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
> +			F2FS_CTX_INFO(ctx).s_qf_names[GRPQUOTA];
> +	prj_qf_name = F2FS_OPTION(sbi).s_qf_names[PRJQUOTA] ||
> +			F2FS_CTX_INFO(ctx).s_qf_names[PRJQUOTA];
> +	usrquota = test_opt(sbi, USRQUOTA) ||
> +			ctx_test_opt(ctx, F2FS_MOUNT_USRQUOTA);
> +	grpquota = test_opt(sbi, GRPQUOTA) ||
> +			ctx_test_opt(ctx, F2FS_MOUNT_GRPQUOTA);
> +	prjquota = test_opt(sbi, PRJQUOTA) ||
> +			ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA);
> +
> +	if (usr_qf_name) {
> +		ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
> +		usrquota = false;
> +	}
> +	if (grp_qf_name) {
> +		ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
> +		grpquota = false;
> +	}
> +	if (prj_qf_name) {
> +		ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
> +		prjquota = false;
> +	}
> +	if (usr_qf_name || grp_qf_name || prj_qf_name) {
> +		if (grpquota || usrquota || prjquota) {
> +			f2fs_err(sbi, "old and new quota format mixing");
> +			return -EINVAL;
> +		}
> +		if (!(ctx->spec_mask & F2FS_SPEC_jqfmt ||
> +				F2FS_OPTION(sbi).s_jquota_fmt)) {
> +			f2fs_err(sbi, "journaled quota format not specified");
> +			return -EINVAL;
> +		}
> +	}
> +	return 0;
> +
> +err_jquota_change:
> +	f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
> +	return -EINVAL;
> +err_jquota_specified:
> +	f2fs_err(sbi, "%s quota file already specified",
> +		 QTYPE2NAME(i));
> +	return -EINVAL;
> +
>  #else
> -	if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sbi->sb)) {
> -		f2fs_info(NULL, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
> +	if (f2fs_readonly(sbi->sb))
> +		return 0;
> +	if (f2fs_sb_has_quota_ino(sbi)) {
> +		f2fs_info(sbi, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
>  		return -EINVAL;
>  	}
> -	if (f2fs_sb_has_project_quota(sbi) && !f2fs_readonly(sbi->sb)) {
> -		f2fs_err(NULL, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
> +	if (f2fs_sb_has_project_quota(sbi)) {
> +		f2fs_err(sbi, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
>  		return -EINVAL;
>  	}
> +
> +	return 0;
>  #endif
> +}
> +
> +static int f2fs_check_test_dummy_encryption(struct fs_context *fc,
> +					    struct super_block *sb)
> +{
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +
> +	if (!fscrypt_is_dummy_policy_set(&F2FS_CTX_INFO(ctx).dummy_enc_policy))
> +		return 0;
> +
> +	if (!f2fs_sb_has_encrypt(sbi)) {
> +		f2fs_err(sbi, "Encrypt feature is off");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * This mount option is just for testing, and it's not worthwhile to
> +	 * implement the extra complexity (e.g. RCU protection) that would be
> +	 * needed to allow it to be set or changed during remount.  We do allow
> +	 * it to be specified during remount, but only if there is no change.
> +	 */
> +	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
> +		if (fscrypt_dummy_policies_equal(&F2FS_OPTION(sbi).dummy_enc_policy,
> +				&F2FS_CTX_INFO(ctx).dummy_enc_policy))
> +			return 0;
> +		f2fs_warn(sbi, "Can't set or change test_dummy_encryption on remount");
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static inline bool test_compression_spec(unsigned int mask)
> +{
> +	return mask & (F2FS_SPEC_compress_algorithm
> +			| F2FS_SPEC_compress_log_size
> +			| F2FS_SPEC_compress_extension
> +			| F2FS_SPEC_nocompress_extension
> +			| F2FS_SPEC_compress_chksum
> +			| F2FS_SPEC_compress_mode);
> +}
> +
> +static inline void clear_compression_spec(struct f2fs_fs_context *ctx)
> +{
> +	ctx->spec_mask &= ~(F2FS_SPEC_compress_algorithm
> +						| F2FS_SPEC_compress_log_size
> +						| F2FS_SPEC_compress_extension
> +						| F2FS_SPEC_nocompress_extension
> +						| F2FS_SPEC_compress_chksum
> +						| F2FS_SPEC_compress_mode);
> +}
> +
> +static int f2fs_check_compression(struct fs_context *fc,
> +				  struct super_block *sb)
> +{
> +#ifdef CONFIG_F2FS_FS_COMPRESSION
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +	int i, cnt;
> +
> +	if (!f2fs_sb_has_compression(sbi)) {
> +		if (test_compression_spec(ctx->opt_mask) ||
> +			ctx_test_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE))
> +			f2fs_info(sbi, "Image doesn't support compression");
> +		clear_compression_spec(ctx);
> +		ctx->opt_mask &= ~F2FS_MOUNT_COMPRESS_CACHE;
> +		return 0;
> +	}
> +	if (ctx->spec_mask & F2FS_SPEC_compress_extension) {
> +		cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
> +		for (i = 0; i < F2FS_CTX_INFO(ctx).compress_ext_cnt; i++) {
> +			if (is_compress_extension_exist(&F2FS_OPTION(sbi),
> +					F2FS_CTX_INFO(ctx).extensions[i], true)) {
> +				F2FS_CTX_INFO(ctx).extensions[i][0] = '\0';
> +				cnt--;
> +			}
> +		}
> +		if (F2FS_OPTION(sbi).compress_ext_cnt + cnt > COMPRESS_EXT_NUM) {
> +			f2fs_err(sbi, "invalid extension length/number");
> +			return -EINVAL;
> +		}
> +	}
> +	if (ctx->spec_mask & F2FS_SPEC_nocompress_extension) {
> +		cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
> +		for (i = 0; i < F2FS_CTX_INFO(ctx).nocompress_ext_cnt; i++) {
> +			if (is_compress_extension_exist(&F2FS_OPTION(sbi),
> +					F2FS_CTX_INFO(ctx).noextensions[i], false)) {
> +				F2FS_CTX_INFO(ctx).noextensions[i][0] = '\0';
> +				cnt--;
> +			}
> +		}
> +		if (F2FS_OPTION(sbi).nocompress_ext_cnt + cnt > COMPRESS_EXT_NUM) {
> +			f2fs_err(sbi, "invalid noextension length/number");
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (f2fs_test_compress_extension(F2FS_CTX_INFO(ctx).noextensions,
> +				F2FS_CTX_INFO(ctx).nocompress_ext_cnt,
> +				F2FS_CTX_INFO(ctx).extensions,
> +				F2FS_CTX_INFO(ctx).compress_ext_cnt)) {
> +		f2fs_err(sbi, "invalid compress or nocompress extension");
> +		return -EINVAL;
> +	}
> +	if (f2fs_test_compress_extension(F2FS_CTX_INFO(ctx).noextensions,
> +				F2FS_CTX_INFO(ctx).nocompress_ext_cnt,
> +				F2FS_OPTION(sbi).extensions,
> +				F2FS_OPTION(sbi).compress_ext_cnt)) {
> +		f2fs_err(sbi, "invalid compress or nocompress extension");
> +		return -EINVAL;
> +	}
> +	if (f2fs_test_compress_extension(F2FS_OPTION(sbi).noextensions,
> +				F2FS_OPTION(sbi).nocompress_ext_cnt,
> +				F2FS_CTX_INFO(ctx).extensions,
> +				F2FS_CTX_INFO(ctx).compress_ext_cnt)) {
> +		f2fs_err(sbi, "invalid compress or nocompress extension");
> +		return -EINVAL;
> +	}
> +#endif
> +	return 0;
> +}
> +
> +static int f2fs_check_opt_consistency(struct fs_context *fc,
> +				      struct super_block *sb)
> +{
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +	int err;
> +
> +	if (ctx_test_opt(ctx, F2FS_MOUNT_NORECOVERY) && !f2fs_readonly(sb))
> +		return -EINVAL;
> +
> +	if (f2fs_hw_should_discard(sbi) && (ctx->opt_mask & F2FS_MOUNT_DISCARD)
> +				&& !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {

Applied.

       if (f2fs_hw_should_discard(sbi) &&
                       (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
                       !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {


> +		f2fs_warn(sbi, "discard is required for zoned block devices");
> +		return -EINVAL;
> +	}
> +
> +	if (f2fs_sb_has_device_alias(sbi)) {

Shouldn't this be?

	if (f2fs_sb_has_device_alias(sbi) &&
			!ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {


> +		f2fs_err(sbi, "device aliasing requires extent cache");
> +		return -EINVAL;
> +	}
> +
> +	if (!f2fs_hw_support_discard(sbi) && (ctx->opt_mask & F2FS_MOUNT_DISCARD)
> +				&& ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {

       if (!f2fs_hw_support_discard(sbi) &&
                       (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
                       ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {

> +		f2fs_warn(sbi, "device does not support discard");
> +		ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
> +		ctx->opt_mask &= ~F2FS_MOUNT_DISCARD;
> +	}
> +
> +	if (test_opt(sbi, RESERVE_ROOT) && (ctx->opt_mask & F2FS_MOUNT_RESERVE_ROOT)
> +				&& ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_ROOT)) {

       if (test_opt(sbi, RESERVE_ROOT) &&
                       (ctx->opt_mask & F2FS_MOUNT_RESERVE_ROOT) &&
                       ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_ROOT)) {


> +		f2fs_info(sbi, "Preserve previous reserve_root=%u",
> +			F2FS_OPTION(sbi).root_reserved_blocks);
> +		ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
> +		ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
> +	}
> +
> +	err = f2fs_check_test_dummy_encryption(fc, sb);
> +	if (err)
> +		return err;
> +
> +	err = f2fs_check_compression(fc, sb);
> +	if (err)
> +		return err;
> +
> +	err = f2fs_check_quota_consistency(fc, sb);
> +	if (err)
> +		return err;
>  
>  	if (!IS_ENABLED(CONFIG_UNICODE) && f2fs_sb_has_casefold(sbi)) {
> -		f2fs_err(NULL,
> +		f2fs_err(sbi,
>  			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
>  		return -EINVAL;
>  	}
> @@ -1303,75 +1480,210 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
>  	 */
>  	if (f2fs_sb_has_blkzoned(sbi)) {
>  #ifdef CONFIG_BLK_DEV_ZONED
> -		if (F2FS_OPTION(sbi).discard_unit !=
> -						DISCARD_UNIT_SECTION) {
> -			f2fs_info(NULL, "Zoned block device doesn't need small discard, set discard_unit=section by default");
> -			F2FS_OPTION(sbi).discard_unit =
> -					DISCARD_UNIT_SECTION;
> +		if ((ctx->spec_mask & F2FS_SPEC_discard_unit) &&
> +		F2FS_CTX_INFO(ctx).discard_unit != DISCARD_UNIT_SECTION) {
> +			f2fs_info(sbi, "Zoned block device doesn't need small discard, set discard_unit=section by default");
> +			F2FS_CTX_INFO(ctx).discard_unit = DISCARD_UNIT_SECTION;
>  		}
>  
> -		if (F2FS_OPTION(sbi).fs_mode != FS_MODE_LFS) {
> -			f2fs_info(NULL, "Only lfs mode is allowed with zoned block device feature");
> +		if ((ctx->spec_mask & F2FS_SPEC_mode) &&
> +		F2FS_CTX_INFO(ctx).fs_mode != FS_MODE_LFS) {
> +			f2fs_info(sbi, "Only lfs mode is allowed with zoned block device feature");
>  			return -EINVAL;
>  		}
>  #else
> -		f2fs_err(NULL, "Zoned block device support is not enabled");
> +		f2fs_err(sbi, "Zoned block device support is not enabled");
>  		return -EINVAL;
>  #endif
>  	}
>  
> -#ifdef CONFIG_F2FS_FS_COMPRESSION
> -	if (f2fs_test_compress_extension(sbi)) {
> -		f2fs_err(NULL, "invalid compress or nocompress extension");
> -		return -EINVAL;
> -	}
> -#endif
> -
> -	if (test_opt(sbi, INLINE_XATTR_SIZE)) {
> -		int min_size, max_size;
> -
> +	if (ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE)) {
>  		if (!f2fs_sb_has_extra_attr(sbi) ||
>  			!f2fs_sb_has_flexible_inline_xattr(sbi)) {
> -			f2fs_err(NULL, "extra_attr or flexible_inline_xattr feature is off");
> -			return -EINVAL;
> -		}
> -		if (!test_opt(sbi, INLINE_XATTR)) {
> -			f2fs_err(NULL, "inline_xattr_size option should be set with inline_xattr option");
> +			f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is off");
>  			return -EINVAL;
>  		}
> -
> -		min_size = MIN_INLINE_XATTR_SIZE;
> -		max_size = MAX_INLINE_XATTR_SIZE;
> -
> -		if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
> -				F2FS_OPTION(sbi).inline_xattr_size > max_size) {
> -			f2fs_err(NULL, "inline xattr size is out of range: %d ~ %d",
> -				 min_size, max_size);
> +		if (!ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR)) {
> +			f2fs_err(sbi, "inline_xattr_size option should be set with inline_xattr option");
>  			return -EINVAL;
>  		}
>  	}
>  
> -	if (test_opt(sbi, ATGC) && f2fs_lfs_mode(sbi)) {
> -		f2fs_err(NULL, "LFS is not compatible with ATGC");
> +	if (ctx_test_opt(ctx, F2FS_MOUNT_ATGC) &&
> +	    F2FS_CTX_INFO(ctx).fs_mode == FS_MODE_LFS) {
> +		f2fs_err(sbi, "LFS is not compatible with ATGC");
>  		return -EINVAL;
>  	}
>  
> -	if (f2fs_is_readonly(sbi) && test_opt(sbi, FLUSH_MERGE)) {
> -		f2fs_err(NULL, "FLUSH_MERGE not compatible with readonly mode");
> +	if (f2fs_is_readonly(sbi) && ctx_test_opt(ctx, F2FS_MOUNT_FLUSH_MERGE)) {
> +		f2fs_err(sbi, "FLUSH_MERGE not compatible with readonly mode");
>  		return -EINVAL;
>  	}
>  
>  	if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
> -		f2fs_err(NULL, "Allow to mount readonly mode only");
> +		f2fs_err(sbi, "Allow to mount readonly mode only");
>  		return -EROFS;
>  	}
> +	return 0;
> +}
>  
> -	if (test_opt(sbi, NORECOVERY) && !f2fs_readonly(sbi->sb)) {
> -		f2fs_err(sbi, "norecovery requires readonly mount");
> -		return -EINVAL;
> +static void f2fs_apply_quota_options(struct fs_context *fc,
> +				     struct super_block *sb)
> +{
> +#ifdef CONFIG_QUOTA
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +	bool quota_feature = f2fs_sb_has_quota_ino(sbi);
> +	char *qname;
> +	int i;
> +
> +	if (quota_feature)
> +		return;
> +
> +	for (i = 0; i < MAXQUOTAS; i++) {
> +		if (!(ctx->qname_mask & (1 << i)))
> +			continue;
> +
> +		qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
> +		if (qname)
> +			set_opt(sbi, QUOTA);
> +		F2FS_OPTION(sbi).s_qf_names[i] = qname;
> +		F2FS_CTX_INFO(ctx).s_qf_names[i] = NULL;
>  	}
>  
> -	return 0;
> +	if (ctx->spec_mask & F2FS_SPEC_jqfmt)
> +		F2FS_OPTION(sbi).s_jquota_fmt = F2FS_CTX_INFO(ctx).s_jquota_fmt;
> +
> +	if (quota_feature && F2FS_OPTION(sbi).s_jquota_fmt) {
> +		f2fs_info(sbi, "QUOTA feature is enabled, so ignore jquota_fmt");
> +		F2FS_OPTION(sbi).s_jquota_fmt = 0;
> +	}
> +#endif
> +}
> +
> +static void f2fs_apply_test_dummy_encryption(struct fs_context *fc,
> +					     struct super_block *sb)
> +{
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +
> +	if (!fscrypt_is_dummy_policy_set(&F2FS_CTX_INFO(ctx).dummy_enc_policy) ||
> +		/* if already set, it was already verified to be the same */
> +		fscrypt_is_dummy_policy_set(&F2FS_OPTION(sbi).dummy_enc_policy))
> +		return;
> +	F2FS_OPTION(sbi).dummy_enc_policy = F2FS_CTX_INFO(ctx).dummy_enc_policy;
> +	memset(&F2FS_CTX_INFO(ctx).dummy_enc_policy, 0,
> +		sizeof(F2FS_CTX_INFO(ctx).dummy_enc_policy));
> +	f2fs_warn(sbi, "Test dummy encryption mode enabled");
> +}
> +
> +static void f2fs_apply_compression(struct fs_context *fc,
> +				   struct super_block *sb)
> +{
> +#ifdef CONFIG_F2FS_FS_COMPRESSION
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +	unsigned char (*ctx_ext)[F2FS_EXTENSION_LEN];
> +	unsigned char (*sbi_ext)[F2FS_EXTENSION_LEN];
> +	int ctx_cnt, sbi_cnt, i;
> +
> +	if (ctx->spec_mask & F2FS_SPEC_compress_level)
> +		F2FS_OPTION(sbi).compress_level =
> +					F2FS_CTX_INFO(ctx).compress_level;
> +	if (ctx->spec_mask & F2FS_SPEC_compress_algorithm)
> +		F2FS_OPTION(sbi).compress_algorithm =
> +					F2FS_CTX_INFO(ctx).compress_algorithm;
> +	if (ctx->spec_mask & F2FS_SPEC_compress_log_size)
> +		F2FS_OPTION(sbi).compress_log_size =
> +					F2FS_CTX_INFO(ctx).compress_log_size;
> +	if (ctx->spec_mask & F2FS_SPEC_compress_chksum)
> +		F2FS_OPTION(sbi).compress_chksum =
> +					F2FS_CTX_INFO(ctx).compress_chksum;
> +	if (ctx->spec_mask & F2FS_SPEC_compress_mode)
> +		F2FS_OPTION(sbi).compress_mode =
> +					F2FS_CTX_INFO(ctx).compress_mode;
> +	if (ctx->spec_mask & F2FS_SPEC_compress_extension) {
> +		ctx_ext = F2FS_CTX_INFO(ctx).extensions;
> +		ctx_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
> +		sbi_ext = F2FS_OPTION(sbi).extensions;
> +		sbi_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
> +		for (i = 0; i < ctx_cnt; i++) {
> +			if (strlen(ctx_ext[i]) == 0)
> +				continue;
> +			strscpy(sbi_ext[sbi_cnt], ctx_ext[i]);
> +			sbi_cnt++;
> +		}
> +		F2FS_OPTION(sbi).compress_ext_cnt = sbi_cnt;
> +	}
> +	if (ctx->spec_mask & F2FS_SPEC_nocompress_extension) {
> +		ctx_ext = F2FS_CTX_INFO(ctx).noextensions;
> +		ctx_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
> +		sbi_ext = F2FS_OPTION(sbi).noextensions;
> +		sbi_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
> +		for (i = 0; i < ctx_cnt; i++) {
> +			if (strlen(ctx_ext[i]) == 0)
> +				continue;
> +			strscpy(sbi_ext[sbi_cnt], ctx_ext[i]);
> +			sbi_cnt++;
> +		}
> +		F2FS_OPTION(sbi).nocompress_ext_cnt = sbi_cnt;
> +	}
> +#endif
> +}
> +
> +static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
> +{
> +	struct f2fs_fs_context *ctx = fc->fs_private;
> +	struct f2fs_sb_info *sbi = F2FS_SB(sb);
> +
> +	F2FS_OPTION(sbi).opt &= ~ctx->opt_mask;
> +	F2FS_OPTION(sbi).opt |= F2FS_CTX_INFO(ctx).opt;
> +	sb->s_flags &= ~ctx->sflags_mask;
> +	sb->s_flags |= ctx->sflags;
> +
> +	if (ctx->spec_mask & F2FS_SPEC_background_gc)
> +		F2FS_OPTION(sbi).bggc_mode = F2FS_CTX_INFO(ctx).bggc_mode;
> +	if (ctx->spec_mask & F2FS_SPEC_inline_xattr_size)
> +		F2FS_OPTION(sbi).inline_xattr_size =
> +					F2FS_CTX_INFO(ctx).inline_xattr_size;
> +	if (ctx->spec_mask & F2FS_SPEC_active_logs)
> +		F2FS_OPTION(sbi).active_logs = F2FS_CTX_INFO(ctx).active_logs;
> +	if (ctx->spec_mask & F2FS_SPEC_reserve_root)
> +		F2FS_OPTION(sbi).root_reserved_blocks =
> +					F2FS_CTX_INFO(ctx).root_reserved_blocks;
> +	if (ctx->spec_mask & F2FS_SPEC_resgid)
> +		F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
> +	if (ctx->spec_mask & F2FS_SPEC_resuid)
> +		F2FS_OPTION(sbi).s_resuid = F2FS_CTX_INFO(ctx).s_resuid;
> +	if (ctx->spec_mask & F2FS_SPEC_mode)
> +		F2FS_OPTION(sbi).fs_mode = F2FS_CTX_INFO(ctx).fs_mode;
> +#ifdef CONFIG_F2FS_FAULT_INJECTION
> +	if (ctx->spec_mask & F2FS_SPEC_fault_injection)
> +		(void)f2fs_build_fault_attr(sbi,
> +		F2FS_CTX_INFO(ctx).fault_info.inject_rate, 0, FAULT_RATE);
> +	if (ctx->spec_mask & F2FS_SPEC_fault_type)
> +		(void)f2fs_build_fault_attr(sbi, 0,
> +			F2FS_CTX_INFO(ctx).fault_info.inject_type, FAULT_TYPE);
> +#endif
> +	if (ctx->spec_mask & F2FS_SPEC_alloc_mode)
> +		F2FS_OPTION(sbi).alloc_mode = F2FS_CTX_INFO(ctx).alloc_mode;
> +	if (ctx->spec_mask & F2FS_SPEC_fsync_mode)
> +		F2FS_OPTION(sbi).fsync_mode = F2FS_CTX_INFO(ctx).fsync_mode;
> +	if (ctx->spec_mask & F2FS_SPEC_checkpoint_disable_cap)
> +		F2FS_OPTION(sbi).unusable_cap = F2FS_CTX_INFO(ctx).unusable_cap;
> +	if (ctx->spec_mask & F2FS_SPEC_checkpoint_disable_cap_perc)
> +		F2FS_OPTION(sbi).unusable_cap_perc =
> +					F2FS_CTX_INFO(ctx).unusable_cap_perc;
> +	if (ctx->spec_mask & F2FS_SPEC_discard_unit)
> +		F2FS_OPTION(sbi).discard_unit = F2FS_CTX_INFO(ctx).discard_unit;
> +	if (ctx->spec_mask & F2FS_SPEC_memory_mode)
> +		F2FS_OPTION(sbi).memory_mode = F2FS_CTX_INFO(ctx).memory_mode;
> +	if (ctx->spec_mask & F2FS_SPEC_errors)
> +		F2FS_OPTION(sbi).errors = F2FS_CTX_INFO(ctx).errors;
> +
> +	f2fs_apply_compression(fc, sb);
> +	f2fs_apply_test_dummy_encryption(fc, sb);
> +	f2fs_apply_quota_options(fc, sb);
>  }
>  
>  static struct inode *f2fs_alloc_inode(struct super_block *sb)
> @@ -2275,6 +2587,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  {
>  	struct f2fs_sb_info *sbi = F2FS_SB(sb);
>  	struct f2fs_mount_info org_mount_opt;
> +	struct f2fs_fs_context ctx;
> +	struct fs_context fc;
>  	unsigned long old_sb_flags;
>  	int err;
>  	bool need_restart_gc = false, need_stop_gc = false;
> @@ -2331,11 +2645,22 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  
>  	default_options(sbi, true);
>  
> +	memset(&fc, 0, sizeof(fc));
> +	memset(&ctx, 0, sizeof(ctx));
> +	fc.fs_private = &ctx;
> +	fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
> +
>  	/* parse mount options */
> -	err = parse_options(sbi, data, true);
> +	err = parse_options(&fc, data);
>  	if (err)
>  		goto restore_opts;
>  
> +	err = f2fs_check_opt_consistency(&fc, sb);
> +	if (err < 0)
> +		goto restore_opts;
> +
> +	f2fs_apply_options(&fc, sb);
> +
>  #ifdef CONFIG_BLK_DEV_ZONED
>  	if (f2fs_sb_has_blkzoned(sbi) &&
>  		sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
> @@ -2346,11 +2671,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  		goto restore_opts;
>  	}
>  #endif
> -
> -	err = f2fs_validate_options(sbi);
> -	if (err)
> -		goto restore_opts;
> -
>  	/* flush outstanding errors before changing fs state */
>  	flush_work(&sbi->s_error_work);
>  
> @@ -4429,6 +4749,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  {
>  	struct f2fs_sb_info *sbi;
>  	struct f2fs_super_block *raw_super;
> +	struct f2fs_fs_context ctx;
> +	struct fs_context fc;
>  	struct inode *root;
>  	int err;
>  	bool skip_recovery = false, need_fsck = false;
> @@ -4445,6 +4767,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	raw_super = NULL;
>  	valid_super_block = -1;
>  	recovery = 0;
> +	memset(&fc, 0, sizeof(fc));
> +	memset(&ctx, 0, sizeof(ctx));
> +	fc.fs_private = &ctx;
>  
>  	/* allocate memory for f2fs-specific super block info */
>  	sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL);
> @@ -4502,14 +4827,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  		goto free_sb_buf;
>  	}
>  
> -	err = parse_options(sbi, options, false);
> +	err = parse_options(&fc, options);
>  	if (err)
>  		goto free_options;
>  
> -	err = f2fs_validate_options(sbi);
> -	if (err)
> +	err = f2fs_check_opt_consistency(&fc, sb);
> +	if (err < 0)
>  		goto free_options;
>  
> +	f2fs_apply_options(&fc, sb);
> +
>  	sb->s_maxbytes = max_file_blocks(NULL) <<
>  				le32_to_cpu(raw_super->log_blocksize);
>  	sb->s_max_links = F2FS_LINK_MAX;
> -- 
> 2.49.0




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux