On 04/23, Eric Sandeen wrote: > From: Hongbo Li <lihongb...@huawei.com> > > 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 <lihongb...@huawei.com> > [sandeen: forward port, minor fixes and updates] > Signed-off-by: Eric Sandeen <sand...@redhat.com> > --- > 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",
I fixed this to %lu ~ %lu to avoid build warning. > + 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, ¶m); > + ret = handle_mount_opt(fc, ¶m); > 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)) { > + f2fs_warn(sbi, "discard is required for zoned block devices"); > + return -EINVAL; > + } > + > + if (f2fs_sb_has_device_alias(sbi)) { > + 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)) { > + 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)) { > + 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 _______________________________________________ Linux-f2fs-devel mailing list Linux-f2fs-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel