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",
+                               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)) {
+               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

Reply via email to