diff options
Diffstat (limited to 'fs/ext4/super.c')
| -rw-r--r-- | fs/ext4/super.c | 172 | 
1 files changed, 95 insertions, 77 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 450c918d68fc..845f2f8aee5f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -87,7 +87,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,  static int ext4_validate_options(struct fs_context *fc);  static int ext4_check_opt_consistency(struct fs_context *fc,  				      struct super_block *sb); -static int ext4_apply_options(struct fs_context *fc, struct super_block *sb); +static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);  static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);  static int ext4_get_tree(struct fs_context *fc);  static int ext4_reconfigure(struct fs_context *fc); @@ -1870,31 +1870,12 @@ ext4_sb_read_encoding(const struct ext4_super_block *es)  }  #endif -static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg) -{ -#ifdef CONFIG_FS_ENCRYPTION -	struct ext4_sb_info *sbi = EXT4_SB(sb); -	int err; - -	err = fscrypt_set_test_dummy_encryption(sb, arg, -						&sbi->s_dummy_enc_policy); -	if (err) { -		ext4_msg(sb, KERN_WARNING, -			 "Error while setting test dummy encryption [%d]", err); -		return err; -	} -	ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled"); -#endif -	return 0; -} -  #define EXT4_SPEC_JQUOTA			(1 <<  0)  #define EXT4_SPEC_JQFMT				(1 <<  1)  #define EXT4_SPEC_DATAJ				(1 <<  2)  #define EXT4_SPEC_SB_BLOCK			(1 <<  3)  #define EXT4_SPEC_JOURNAL_DEV			(1 <<  4)  #define EXT4_SPEC_JOURNAL_IOPRIO		(1 <<  5) -#define EXT4_SPEC_DUMMY_ENCRYPTION		(1 <<  6)  #define EXT4_SPEC_s_want_extra_isize		(1 <<  7)  #define EXT4_SPEC_s_max_batch_time		(1 <<  8)  #define EXT4_SPEC_s_min_batch_time		(1 <<  9) @@ -1911,7 +1892,7 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)  struct ext4_fs_context {  	char		*s_qf_names[EXT4_MAXQUOTAS]; -	char		*test_dummy_enc_arg; +	struct fscrypt_dummy_policy dummy_enc_policy;  	int		s_jquota_fmt;	/* Format of quota to use */  #ifdef CONFIG_EXT4_DEBUG  	int s_fc_debug_max_replay; @@ -1953,7 +1934,7 @@ static void ext4_fc_free(struct fs_context *fc)  	for (i = 0; i < EXT4_MAXQUOTAS; i++)  		kfree(ctx->s_qf_names[i]); -	kfree(ctx->test_dummy_enc_arg); +	fscrypt_free_dummy_policy(&ctx->dummy_enc_policy);  	kfree(ctx);  } @@ -2029,6 +2010,29 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)  }  #endif +static int ext4_parse_test_dummy_encryption(const struct fs_parameter *param, +					    struct ext4_fs_context *ctx) +{ +	int err; + +	if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) { +		ext4_msg(NULL, KERN_WARNING, +			 "test_dummy_encryption option not supported"); +		return -EINVAL; +	} +	err = fscrypt_parse_test_dummy_encryption(param, +						  &ctx->dummy_enc_policy); +	if (err == -EINVAL) { +		ext4_msg(NULL, KERN_WARNING, +			 "Value of option \"%s\" is unrecognized", param->key); +	} else if (err == -EEXIST) { +		ext4_msg(NULL, KERN_WARNING, +			 "Conflicting test_dummy_encryption options"); +		return -EINVAL; +	} +	return err; +} +  #define EXT4_SET_CTX(name)						\  static inline void ctx_set_##name(struct ext4_fs_context *ctx,		\  				  unsigned long flag)			\ @@ -2291,29 +2295,7 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)  		ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;  		return 0;  	case Opt_test_dummy_encryption: -#ifdef CONFIG_FS_ENCRYPTION -		if (param->type == fs_value_is_flag) { -			ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION; -			ctx->test_dummy_enc_arg = NULL; -			return 0; -		} -		if (*param->string && -		    !(!strcmp(param->string, "v1") || -		      !strcmp(param->string, "v2"))) { -			ext4_msg(NULL, KERN_WARNING, -				 "Value of option \"%s\" is unrecognized", -				 param->key); -			return -EINVAL; -		} -		ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION; -		ctx->test_dummy_enc_arg = kmemdup_nul(param->string, param->size, -						      GFP_KERNEL); -		return 0; -#else -		ext4_msg(NULL, KERN_WARNING, -			 "test_dummy_encryption option not supported"); -		return -EINVAL; -#endif +		return ext4_parse_test_dummy_encryption(param, ctx);  	case Opt_dax:  	case Opt_dax_type:  #ifdef CONFIG_FS_DAX @@ -2504,7 +2486,8 @@ parse_failed:  	if (s_ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)  		m_ctx->journal_ioprio = s_ctx->journal_ioprio; -	ret = ext4_apply_options(fc, sb); +	ext4_apply_options(fc, sb); +	ret = 0;  out_free:  	if (fc) { @@ -2673,11 +2656,11 @@ err_jquota_specified:  static int ext4_check_test_dummy_encryption(const struct fs_context *fc,  					    struct super_block *sb)  { -#ifdef CONFIG_FS_ENCRYPTION  	const struct ext4_fs_context *ctx = fc->fs_private;  	const struct ext4_sb_info *sbi = EXT4_SB(sb); +	int err; -	if (!(ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION)) +	if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy))  		return 0;  	if (!ext4_has_feature_encrypt(sb)) { @@ -2691,14 +2674,46 @@ static int ext4_check_test_dummy_encryption(const struct fs_context *fc,  	 * 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 && -	    !sbi->s_dummy_enc_policy.policy) { +	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { +		if (fscrypt_dummy_policies_equal(&sbi->s_dummy_enc_policy, +						 &ctx->dummy_enc_policy)) +			return 0;  		ext4_msg(NULL, KERN_WARNING, -			 "Can't set test_dummy_encryption on remount"); +			 "Can't set or change test_dummy_encryption on remount");  		return -EINVAL;  	} -#endif /* CONFIG_FS_ENCRYPTION */ -	return 0; +	/* Also make sure s_mount_opts didn't contain a conflicting value. */ +	if (fscrypt_is_dummy_policy_set(&sbi->s_dummy_enc_policy)) { +		if (fscrypt_dummy_policies_equal(&sbi->s_dummy_enc_policy, +						 &ctx->dummy_enc_policy)) +			return 0; +		ext4_msg(NULL, KERN_WARNING, +			 "Conflicting test_dummy_encryption options"); +		return -EINVAL; +	} +	/* +	 * fscrypt_add_test_dummy_key() technically changes the super_block, so +	 * technically it should be delayed until ext4_apply_options() like the +	 * other changes.  But since we never get here for remounts (see above), +	 * and this is the last chance to report errors, we do it here. +	 */ +	err = fscrypt_add_test_dummy_key(sb, &ctx->dummy_enc_policy); +	if (err) +		ext4_msg(NULL, KERN_WARNING, +			 "Error adding test dummy encryption key [%d]", err); +	return err; +} + +static void ext4_apply_test_dummy_encryption(struct ext4_fs_context *ctx, +					     struct super_block *sb) +{ +	if (!fscrypt_is_dummy_policy_set(&ctx->dummy_enc_policy) || +	    /* if already set, it was already verified to be the same */ +	    fscrypt_is_dummy_policy_set(&EXT4_SB(sb)->s_dummy_enc_policy)) +		return; +	EXT4_SB(sb)->s_dummy_enc_policy = ctx->dummy_enc_policy; +	memset(&ctx->dummy_enc_policy, 0, sizeof(ctx->dummy_enc_policy)); +	ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");  }  static int ext4_check_opt_consistency(struct fs_context *fc, @@ -2785,11 +2800,10 @@ fail_dax_change_remount:  	return ext4_check_quota_consistency(fc, sb);  } -static int ext4_apply_options(struct fs_context *fc, struct super_block *sb) +static void ext4_apply_options(struct fs_context *fc, struct super_block *sb)  {  	struct ext4_fs_context *ctx = fc->fs_private;  	struct ext4_sb_info *sbi = fc->s_fs_info; -	int ret = 0;  	sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;  	sbi->s_mount_opt |= ctx->vals_s_mount_opt; @@ -2825,11 +2839,7 @@ static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)  #endif  	ext4_apply_quota_options(fc, sb); - -	if (ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION) -		ret = ext4_set_test_dummy_encryption(sb, ctx->test_dummy_enc_arg); - -	return ret; +	ext4_apply_test_dummy_encryption(ctx, sb);  } @@ -4552,9 +4562,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)  	if (err < 0)  		goto failed_mount; -	err = ext4_apply_options(fc, sb); -	if (err < 0) -		goto failed_mount; +	ext4_apply_options(fc, sb);  #if IS_ENABLED(CONFIG_UNICODE)  	if (ext4_has_feature_casefold(sb) && !sb->s_encoding) { @@ -5302,14 +5310,6 @@ no_journal:  		err = percpu_counter_init(&sbi->s_freeinodes_counter, freei,  					  GFP_KERNEL);  	} -	/* -	 * Update the checksum after updating free space/inode -	 * counters.  Otherwise the superblock can have an incorrect -	 * checksum in the buffer cache until it is written out and -	 * e2fsprogs programs trying to open a file system immediately -	 * after it is mounted can fail. -	 */ -	ext4_superblock_csum_set(sb);  	if (!err)  		err = percpu_counter_init(&sbi->s_dirs_counter,  					  ext4_count_dirs(sb), GFP_KERNEL); @@ -5367,6 +5367,14 @@ no_journal:  	EXT4_SB(sb)->s_mount_state |= EXT4_ORPHAN_FS;  	ext4_orphan_cleanup(sb, es);  	EXT4_SB(sb)->s_mount_state &= ~EXT4_ORPHAN_FS; +	/* +	 * Update the checksum after updating free space/inode counters and +	 * ext4_orphan_cleanup. Otherwise the superblock can have an incorrect +	 * checksum in the buffer cache until it is written out and +	 * e2fsprogs programs trying to open a file system immediately +	 * after it is mounted can fail. +	 */ +	ext4_superblock_csum_set(sb);  	if (needs_recovery) {  		ext4_msg(sb, KERN_INFO, "recovery complete");  		err = ext4_mark_recovery_complete(sb, es); @@ -5898,7 +5906,6 @@ static void ext4_update_super(struct super_block *sb)  static int ext4_commit_super(struct super_block *sb)  {  	struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; -	int error = 0;  	if (!sbh)  		return -EINVAL; @@ -5907,6 +5914,13 @@ static int ext4_commit_super(struct super_block *sb)  	ext4_update_super(sb); +	lock_buffer(sbh); +	/* Buffer got discarded which means block device got invalidated */ +	if (!buffer_mapped(sbh)) { +		unlock_buffer(sbh); +		return -EIO; +	} +  	if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {  		/*  		 * Oh, dear.  A previous attempt to write the @@ -5921,17 +5935,21 @@ static int ext4_commit_super(struct super_block *sb)  		clear_buffer_write_io_error(sbh);  		set_buffer_uptodate(sbh);  	} -	BUFFER_TRACE(sbh, "marking dirty"); -	mark_buffer_dirty(sbh); -	error = __sync_dirty_buffer(sbh, -		REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0)); +	get_bh(sbh); +	/* Clear potential dirty bit if it was journalled update */ +	clear_buffer_dirty(sbh); +	sbh->b_end_io = end_buffer_write_sync; +	submit_bh(REQ_OP_WRITE, +		  REQ_SYNC | (test_opt(sb, BARRIER) ? REQ_FUA : 0), sbh); +	wait_on_buffer(sbh);  	if (buffer_write_io_error(sbh)) {  		ext4_msg(sb, KERN_ERR, "I/O error while writing "  		       "superblock");  		clear_buffer_write_io_error(sbh);  		set_buffer_uptodate(sbh); +		return -EIO;  	} -	return error; +	return 0;  }  /*  |