lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20211026114116.dowt2p3rgukhvrrx@andromeda.lan>
Date:   Tue, 26 Oct 2021 13:41:16 +0200
From:   Carlos Maiolino <cmaiolino@...hat.com>
To:     Lukas Czerner <lczerner@...hat.com>
Cc:     linux-ext4@...r.kernel.org, tytso@....edu,
        linux-fsdevel@...r.kernel.org
Subject: Re: [PATCH v3 09/13] ext4: Completely separate options parsing and
 sb setup

On Thu, Oct 21, 2021 at 01:45:04PM +0200, Lukas Czerner wrote:
> The new mount api separates option parsing and super block setup into
> two distinc steps and so we need to separate the options parsing out of
> the ext4_fill_super() and ext4_remount().
> 
> In order to achieve this we have to create new ext4_fill_super() and
> ext4_remount() functions which will serve its purpose only until we
> actually do convert to the new api (as such they are only temporary for
> this patch series) and move the option parsing out of the old function
> which will now be renamed to __ext4_full_super() and __ext4_remount().

				__ext4_fill_super() ?

> 
> There is a small complication in the fact that while the mount option
> parsing is going to happen before we get to __ext4_fill_super(), the
> mount options stored in the super block itself needs to be applied
> first, before the user specified mount options.
> 
> So with this patch we're going through the following sequence:
> 
> - parse user provided options (including sb block)
> - initialize sbi and store s_sb_block if provided
> - in __ext4_fill_super()
> 	- read the super block
> 	- parse and apply options specified in s_mount_opts
> 	- check and apply user provided options stored in ctx
> 	- continue with the regular ext4_fill_super operation
> 
> It's not exactly the most elegant solution, but if we still want to
> support s_mount_opts we have to do it in this order.
> 
> Signed-off-by: Lukas Czerner <lczerner@...hat.com>
> ---
>  fs/ext4/super.c | 399 ++++++++++++++++++++++++++++++++----------------
>  1 file changed, 264 insertions(+), 135 deletions(-)
> 
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 97addca438ad..fd48353e8259 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -1952,29 +1952,6 @@ static const match_table_t tokens = {
>  	{Opt_err, NULL},
>  };
>  
> -static ext4_fsblk_t get_sb_block(void **data)
> -{
> -	ext4_fsblk_t	sb_block;
> -	char		*options = (char *) *data;
> -
> -	if (!options || strncmp(options, "sb=", 3) != 0)
> -		return 1;	/* Default location */
> -
> -	options += 3;
> -	/* TODO: use simple_strtoll with >32bit ext4 */
> -	sb_block = simple_strtoul(options, &options, 0);
> -	if (*options && *options != ',') {
> -		printk(KERN_ERR "EXT4-fs: Invalid sb specification: %s\n",
> -		       (char *) *data);
> -		return 1;
> -	}
> -	if (*options == ',')
> -		options++;
> -	*data = (void *) options;
> -
> -	return sb_block;
> -}
> -
>  #define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
>  #define DEFAULT_MB_OPTIMIZE_SCAN	(-1)
>  
> @@ -2177,6 +2154,7 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
>  #define EXT4_SPEC_s_resgid			(1 << 15)
>  #define EXT4_SPEC_s_commit_interval		(1 << 16)
>  #define EXT4_SPEC_s_fc_debug_max_replay		(1 << 17)
> +#define EXT4_SPEC_s_sb_block			(1 << 18)
>  
>  struct ext4_fs_context {
>  	char		*s_qf_names[EXT4_MAXQUOTAS];
> @@ -2209,6 +2187,7 @@ struct ext4_fs_context {
>  	u32		s_min_batch_time;
>  	kuid_t		s_resuid;
>  	kgid_t		s_resgid;
> +	ext4_fsblk_t	s_sb_block;
>  };
>  
>  #ifdef CONFIG_QUOTA
> @@ -2323,7 +2302,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  		ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
>  		break;
>  	case Opt_sb:
> -		return 1;	/* handled by get_sb_block() */
> +		if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
> +			ext4_msg(NULL, KERN_WARNING,
> +				 "Ignoring %s option on remount", param->key);
> +		} else {
> +			ctx->s_sb_block = result.uint_32;
> +			ctx->spec |= EXT4_SPEC_s_sb_block;
> +		}
> +		return 1;
>  	case Opt_removed:
>  		ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
>  			 param->key);
> @@ -2592,24 +2578,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  	return 1;
>  }
>  
> -static int parse_options(char *options, struct super_block *sb,
> -			 struct ext4_fs_context *ctx,
> -			 int is_remount)
> +static int parse_options(struct fs_context *fc, char *options)
>  {
>  	struct fs_parameter param;
> -	struct fs_context fc;
>  	int ret;
>  	char *key;
>  
>  	if (!options)
> -		return 1;
> -
> -	memset(&fc, 0, sizeof(fc));
> -	fc.fs_private = ctx;
> -	fc.s_fs_info = EXT4_SB(sb);
> -
> -	if (is_remount)
> -		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
> +		return 0;
>  
>  	while ((key = strsep(&options, ",")) != NULL) {
>  		if (*key) {
> @@ -2628,34 +2604,83 @@ static int parse_options(char *options, struct super_block *sb,
>  				param.string = kmemdup_nul(value, v_len,
>  							   GFP_KERNEL);
>  				if (!param.string)
> -					return 0;
> +					return -ENOMEM;
>  				param.type = fs_value_is_string;
>  			}
>  
>  			param.key = key;
>  			param.size = v_len;
>  
> -			ret = handle_mount_opt(&fc, &param);
> +			ret = handle_mount_opt(fc, &param);
>  			if (param.string)
>  				kfree(param.string);
>  			if (ret < 0)
> -				return 0;
> +				return ret;
>  		}
>  	}
>  
> -	ret = ext4_validate_options(&fc);
> +	ret = ext4_validate_options(fc);
>  	if (ret < 0)
> -		return 0;
> +		return ret;
>  
> -	ret = ext4_check_opt_consistency(&fc, sb);
> -	if (ret < 0)
> +	return 0;
> +}
> +
> +static int parse_apply_sb_mount_options(struct super_block *sb,
> +					struct ext4_fs_context *m_ctx)
> +{
> +	struct ext4_sb_info *sbi = EXT4_SB(sb);
> +	char *s_mount_opts = NULL;
> +	struct ext4_fs_context *s_ctx = NULL;
> +	struct fs_context *fc = NULL;
> +	int ret = -ENOMEM;
> +
> +	if (!sbi->s_es->s_mount_opts[0])
>  		return 0;
>  
> -	ret = ext4_apply_options(&fc, sb);
> +	s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
> +				sizeof(sbi->s_es->s_mount_opts),
> +				GFP_KERNEL);
> +	if (!s_mount_opts)
> +		return ret;
> +
> +	fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
> +	if (!fc)
> +		goto out_free;
> +
> +	s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
> +	if (!s_ctx)
> +		goto out_free;
> +
> +	fc->fs_private = s_ctx;
> +	fc->s_fs_info = sbi;
> +
> +	ret = parse_options(fc, s_mount_opts);
>  	if (ret < 0)
> -		return 0;
> +		goto parse_failed;
>  
> -	return 1;
> +	ret = ext4_check_opt_consistency(fc, sb);
> +	if (ret < 0) {
> +parse_failed:
> +		ext4_msg(sb, KERN_WARNING,
> +			 "failed to parse options in superblock: %s",
> +			 s_mount_opts);
> +		ret = 0;
> +		goto out_free;
> +	}
> +
> +	if (s_ctx->spec & EXT4_SPEC_JOURNAL_DEV)
> +		m_ctx->journal_devnum = s_ctx->journal_devnum;
> +	if (s_ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
> +		m_ctx->journal_ioprio = s_ctx->journal_ioprio;
> +
> +	ret = ext4_apply_options(fc, sb);
> +
> +out_free:
> +	kfree(s_ctx);
> +	kfree(fc);
> +	kfree(s_mount_opts);
> +	return ret;
>  }
>  
>  static void ext4_apply_quota_options(struct fs_context *fc,
> @@ -4350,21 +4375,53 @@ static void ext4_setup_csum_trigger(struct super_block *sb,
>  	sbi->s_journal_triggers[type].tr_triggers.t_frozen = trigger;
>  }
>  
> -static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> +static void ext4_free_sbi(struct ext4_sb_info *sbi)
> +{
> +	if (!sbi)
> +		return;
> +
> +	kfree(sbi->s_blockgroup_lock);
> +	fs_put_dax(sbi->s_daxdev);
> +	kfree(sbi);
> +}
> +
> +static struct ext4_sb_info *ext4_alloc_sbi(struct super_block *sb)
> +{
> +	struct ext4_sb_info *sbi;
> +
> +	sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
> +	if (!sbi)
> +		return NULL;
> +
> +	sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev);
> +
> +	sbi->s_blockgroup_lock =
> +		kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
> +
> +	if (!sbi->s_blockgroup_lock)
> +		goto err_out;
> +
> +	sb->s_fs_info = sbi;
> +	sbi->s_sb = sb;
> +	return sbi;
> +err_out:
> +	fs_put_dax(sbi->s_daxdev);
> +	kfree(sbi);
> +	return NULL;
> +}
> +
> +static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb,
> +			     int silent)
>  {
> -	struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
> -	char *orig_data = kstrdup(data, GFP_KERNEL);
>  	struct buffer_head *bh, **group_desc;
>  	struct ext4_super_block *es = NULL;
> -	struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
> +	struct ext4_sb_info *sbi = EXT4_SB(sb);
>  	struct flex_groups **flex_groups;
>  	ext4_fsblk_t block;
> -	ext4_fsblk_t sb_block = get_sb_block(&data);
>  	ext4_fsblk_t logical_sb_block;
>  	unsigned long offset = 0;
>  	unsigned long def_mount_opts;
>  	struct inode *root;
> -	const char *descr;
>  	int ret = -ENOMEM;
>  	int blocksize, clustersize;
>  	unsigned int db_count;
> @@ -4373,32 +4430,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	__u64 blocks_count;
>  	int err = 0;
>  	ext4_group_t first_not_zeroed;
> -	struct ext4_fs_context parsed_opts = {0};
> +	struct ext4_fs_context *ctx = fc->fs_private;
>  
>  	/* Set defaults for the variables that will be set during parsing */
> -	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
> -	parsed_opts.journal_devnum = 0;
> -	parsed_opts.mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
> -
> -	if ((data && !orig_data) || !sbi)
> -		goto out_free_base;
> +	ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
> +	ctx->mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
>  
> -	sbi->s_daxdev = dax_dev;
> -	sbi->s_blockgroup_lock =
> -		kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
> -	if (!sbi->s_blockgroup_lock)
> -		goto out_free_base;
> -
> -	sb->s_fs_info = sbi;
> -	sbi->s_sb = sb;
>  	sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
> -	sbi->s_sb_block = sb_block;
>  	sbi->s_sectors_written_start =
>  		part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
>  
> -	/* Cleanup superblock name */
> -	strreplace(sb->s_id, '/', '!');
> -
>  	/* -EINVAL is default */
>  	ret = -EINVAL;
>  	blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
> @@ -4412,10 +4453,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	 * block sizes.  We need to calculate the offset from buffer start.
>  	 */
>  	if (blocksize != EXT4_MIN_BLOCK_SIZE) {
> -		logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
> +		logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
>  		offset = do_div(logical_sb_block, blocksize);
>  	} else {
> -		logical_sb_block = sb_block;
> +		logical_sb_block = sbi->s_sb_block;
>  	}
>  
>  	bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
> @@ -4620,21 +4661,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  		}
>  	}
>  
> -	if (sbi->s_es->s_mount_opts[0]) {
> -		char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
> -					      sizeof(sbi->s_es->s_mount_opts),
> -					      GFP_KERNEL);
> -		if (!s_mount_opts)
> -			goto failed_mount;
> -		if (!parse_options(s_mount_opts, sb, &parsed_opts, 0)) {
> -			ext4_msg(sb, KERN_WARNING,
> -				 "failed to parse options in superblock: %s",
> -				 s_mount_opts);
> -		}
> -		kfree(s_mount_opts);
> -	}
> +	err = parse_apply_sb_mount_options(sb, ctx);
> +	if (err < 0)
> +		goto failed_mount;
> +
>  	sbi->s_def_mount_opt = sbi->s_mount_opt;
> -	if (!parse_options((char *) data, sb, &parsed_opts, 0))
> +
> +	err = ext4_check_opt_consistency(fc, sb);
> +	if (err < 0)
> +		goto failed_mount;
> +
> +	err = ext4_apply_options(fc, sb);
> +	if (err < 0)
>  		goto failed_mount;
>  
>  #ifdef CONFIG_UNICODE
> @@ -4773,7 +4811,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  		goto failed_mount;
>  	}
>  
> -	if (dax_supported(dax_dev, sb->s_bdev, blocksize, 0,
> +	if (dax_supported(sbi->s_daxdev, sb->s_bdev, blocksize, 0,
>  			bdev_nr_sectors(sb->s_bdev)))
>  		set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);
>  
> @@ -4811,7 +4849,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  			goto failed_mount;
>  		}
>  
> -		logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
> +		logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
>  		offset = do_div(logical_sb_block, blocksize);
>  		bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
>  		if (IS_ERR(bh)) {
> @@ -5127,7 +5165,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	 * root first: it may be modified in the journal!
>  	 */
>  	if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
> -		err = ext4_load_journal(sb, es, parsed_opts.journal_devnum);
> +		err = ext4_load_journal(sb, es, ctx->journal_devnum);
>  		if (err)
>  			goto failed_mount3a;
>  	} else if (test_opt(sb, NOLOAD) && !sb_rdonly(sb) &&
> @@ -5227,7 +5265,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  		goto failed_mount_wq;
>  	}
>  
> -	set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
> +	set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
>  
>  	sbi->s_journal->j_submit_inode_data_buffers =
>  		ext4_journal_submit_inode_data_buffers;
> @@ -5339,9 +5377,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	 * turned off by passing "mb_optimize_scan=0". This can also be
>  	 * turned on forcefully by passing "mb_optimize_scan=1".
>  	 */
> -	if (parsed_opts.mb_optimize_scan == 1)
> +	if (ctx->mb_optimize_scan == 1)
>  		set_opt2(sb, MB_OPTIMIZE_SCAN);
> -	else if (parsed_opts.mb_optimize_scan == 0)
> +	else if (ctx->mb_optimize_scan == 0)
>  		clear_opt2(sb, MB_OPTIMIZE_SCAN);
>  	else if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD)
>  		set_opt2(sb, MB_OPTIMIZE_SCAN);
> @@ -5443,15 +5481,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  		if (err)
>  			goto failed_mount9;
>  	}
> -	if (EXT4_SB(sb)->s_journal) {
> -		if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
> -			descr = " journalled data mode";
> -		else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
> -			descr = " ordered data mode";
> -		else
> -			descr = " writeback data mode";
> -	} else
> -		descr = "out journal";
>  
>  	if (test_opt(sb, DISCARD)) {
>  		struct request_queue *q = bdev_get_queue(sb->s_bdev);
> @@ -5461,14 +5490,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  				 "the device does not support discard");
>  	}
>  
> -	if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
> -		ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
> -			 "Opts: %.*s%s%s. Quota mode: %s.", descr,
> -			 (int) sizeof(sbi->s_es->s_mount_opts),
> -			 sbi->s_es->s_mount_opts,
> -			 *sbi->s_es->s_mount_opts ? "; " : "", orig_data,
> -			 ext4_quota_mode(sb));
> -
>  	if (es->s_error_count)
>  		mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
>  
> @@ -5479,7 +5500,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	atomic_set(&sbi->s_warning_count, 0);
>  	atomic_set(&sbi->s_msg_count, 0);
>  
> -	kfree(orig_data);
>  	return 0;
>  
>  cantfind_ext4:
> @@ -5565,14 +5585,92 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>  	ext4_blkdev_remove(sbi);
>  out_fail:
>  	sb->s_fs_info = NULL;
> -	kfree(sbi->s_blockgroup_lock);
> -out_free_base:
> -	kfree(sbi);
> -	kfree(orig_data);
> -	fs_put_dax(dax_dev);
>  	return err ? err : ret;
>  }
>  
> +static void cleanup_ctx(struct ext4_fs_context *ctx)
> +{
> +	int i;
> +
> +	if (!ctx)
> +		return;
> +
> +	for (i = 0; i < EXT4_MAXQUOTAS; i++) {
> +		kfree(ctx->s_qf_names[i]);
> +	}
> +
> +	kfree(ctx->test_dummy_enc_arg);
> +}
> +
> +static int ext4_fill_super(struct super_block *sb, void *data, int silent)
> +{
> +	struct ext4_fs_context ctx;
> +	struct ext4_sb_info *sbi;
> +	struct fs_context fc;
> +	const char *descr;
> +	char *orig_data;
> +	int ret = -ENOMEM;
> +
> +	orig_data = kstrdup(data, GFP_KERNEL);
> +	if (data && !orig_data)
> +		return -ENOMEM;
> +
> +	/* Cleanup superblock name */
> +	strreplace(sb->s_id, '/', '!');
> +
> +	memset(&fc, 0, sizeof(fc));
> +	memset(&ctx, 0, sizeof(ctx));
> +	fc.fs_private = &ctx;
> +
> +	ret = parse_options(&fc, (char *) data);
> +	if (ret < 0)
> +		goto free_data;
> +
> +	sbi = ext4_alloc_sbi(sb);
> +	if (!sbi) {
> +		ret = -ENOMEM;
> +		goto free_data;
> +	}
> +
> +	fc.s_fs_info = sbi;
> +
> +	sbi->s_sb_block = 1;	/* Default super block location */
> +	if (ctx.spec & EXT4_SPEC_s_sb_block)
> +		sbi->s_sb_block = ctx.s_sb_block;
> +
> +	ret = __ext4_fill_super(&fc, sb, silent);
> +	if (ret < 0)
> +		goto free_sbi;
> +
> +	if (EXT4_SB(sb)->s_journal) {
> +		if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
> +			descr = " journalled data mode";
> +		else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
> +			descr = " ordered data mode";
> +		else
> +			descr = " writeback data mode";
> +	} else
> +		descr = "out journal";
> +
> +	if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
> +		ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
> +			 "Opts: %.*s%s%s. Quota mode: %s.", descr,
> +			 (int) sizeof(sbi->s_es->s_mount_opts),
> +			 sbi->s_es->s_mount_opts,
> +			 *sbi->s_es->s_mount_opts ? "; " : "", orig_data,
> +			 ext4_quota_mode(sb));
> +
> +	kfree(orig_data);
> +	cleanup_ctx(&ctx);
> +	return 0;
> +free_sbi:
> +	ext4_free_sbi(sbi);
> +free_data:
> +	kfree(orig_data);
> +	cleanup_ctx(&ctx);
> +	return ret;
> +}
> +
>  /*
>   * Setup any per-fs journal parameters now.  We'll do this both on
>   * initial mount, once the journal has been initialised but before we've
> @@ -6201,8 +6299,10 @@ struct ext4_mount_options {
>  #endif
>  };
>  
> -static int ext4_remount(struct super_block *sb, int *flags, char *data)
> +static int __ext4_remount(struct fs_context *fc, struct super_block *sb,
> +			  int *flags)
>  {
> +	struct ext4_fs_context *ctx = fc->fs_private;
>  	struct ext4_super_block *es;
>  	struct ext4_sb_info *sbi = EXT4_SB(sb);
>  	unsigned long old_sb_flags, vfs_flags;
> @@ -6214,14 +6314,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  	int i, j;
>  	char *to_free[EXT4_MAXQUOTAS];
>  #endif
> -	char *orig_data = kstrdup(data, GFP_KERNEL);
> -	struct ext4_fs_context parsed_opts;
> -
> -	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
> -	parsed_opts.journal_devnum = 0;
>  
> -	if (data && !orig_data)
> -		return -ENOMEM;
> +	ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
>  
>  	/* Store the original options */
>  	old_sb_flags = sb->s_flags;
> @@ -6242,14 +6336,13 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  			if (!old_opts.s_qf_names[i]) {
>  				for (j = 0; j < i; j++)
>  					kfree(old_opts.s_qf_names[j]);
> -				kfree(orig_data);
>  				return -ENOMEM;
>  			}
>  		} else
>  			old_opts.s_qf_names[i] = NULL;
>  #endif
>  	if (sbi->s_journal && sbi->s_journal->j_task->io_context)
> -		parsed_opts.journal_ioprio =
> +		ctx->journal_ioprio =
>  			sbi->s_journal->j_task->io_context->ioprio;
>  
>  	/*
> @@ -6260,10 +6353,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  	vfs_flags = SB_LAZYTIME | SB_I_VERSION;
>  	sb->s_flags = (sb->s_flags & ~vfs_flags) | (*flags & vfs_flags);
>  
> -	if (!parse_options(data, sb, &parsed_opts, 1)) {
> -		err = -EINVAL;
> -		goto restore_opts;
> -	}
> +	ext4_apply_options(fc, sb);
>  
>  	if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
>  	    test_opt(sb, JOURNAL_CHECKSUM)) {
> @@ -6310,7 +6400,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  
>  	if (sbi->s_journal) {
>  		ext4_init_journal_params(sb, sbi->s_journal);
> -		set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
> +		set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
>  	}
>  
>  	/* Flush outstanding errors before changing fs state */
> @@ -6475,9 +6565,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  	 */
>  	*flags = (*flags & ~vfs_flags) | (sb->s_flags & vfs_flags);
>  
> -	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
> -		 orig_data, ext4_quota_mode(sb));
> -	kfree(orig_data);
>  	return 0;
>  
>  restore_opts:
> @@ -6503,10 +6590,52 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
>  #endif
>  	if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
>  		ext4_stop_mmpd(sbi);
> -	kfree(orig_data);
>  	return err;
>  }
>  
> +static int ext4_remount(struct super_block *sb, int *flags, char *data)
> +{
> +	struct ext4_sb_info *sbi = EXT4_SB(sb);
> +	struct ext4_fs_context ctx;
> +	struct fs_context fc;
> +	char *orig_data;
> +	int ret;
> +
> +	orig_data = kstrdup(data, GFP_KERNEL);
> +	if (data && !orig_data)
> +		return -ENOMEM;
> +
> +	memset(&fc, 0, sizeof(fc));
> +	memset(&ctx, 0, sizeof(ctx));
> +
> +	fc.fs_private = &ctx;
> +	fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
> +	fc.s_fs_info = sbi;
> +
> +	ret = parse_options(&fc, (char *) data);
> +	if (ret < 0)
> +		goto err_out;
> +
> +	ret = ext4_check_opt_consistency(&fc, sb);
> +	if (ret < 0)
> +		goto err_out;
> +
> +	ret = __ext4_remount(&fc, sb, flags);
> +	if (ret < 0)
> +		goto err_out;
> +
> +	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
> +		 orig_data, ext4_quota_mode(sb));
> +	cleanup_ctx(&ctx);
> +	kfree(orig_data);
> +	return 0;
> +
> +err_out:
> +	cleanup_ctx(&ctx);
> +	kfree(orig_data);
> +	return ret;
> +}
> +
>  #ifdef CONFIG_QUOTA
>  static int ext4_statfs_project(struct super_block *sb,
>  			       kprojid_t projid, struct kstatfs *buf)
> -- 
> 2.31.1
> 

-- 
Carlos

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ