ext4: get rid of super block and sbi from handle_mount_ops()
authorLukas Czerner <lczerner@redhat.com>
Wed, 27 Oct 2021 14:18:52 +0000 (16:18 +0200)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 9 Dec 2021 19:11:07 +0000 (14:11 -0500)
At the parsing phase of mount in the new mount api sb will not be
available. We've already removed some uses of sb and sbi, but now we
need to get rid of the rest of it.

Use ext4_fs_context to store all of the configuration specification so
that it can be later applied to the super block and sbi.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Link: https://lore.kernel.org/r/20211027141857.33657-9-lczerner@redhat.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/super.c

index 68395631d2226bea8423631d574eaded14104019..1e4908f5f593a0c1947c4614857be1f0eb986dbf 100644 (file)
@@ -91,8 +91,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 void ext4_apply_quota_options(struct fs_context *fc,
-                                    struct super_block *sb);
+static int ext4_apply_options(struct fs_context *fc, struct super_block *sb);
 
 /*
  * Lock ordering
@@ -2149,57 +2148,74 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
 }
 #endif
 
-static int ext4_set_test_dummy_encryption(struct super_block *sb,
-                                         struct fs_parameter *param,
-                                         bool is_remount)
+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;
 
-       /*
-        * 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 (is_remount && !sbi->s_dummy_enc_policy.policy) {
-               ext4_msg(sb, KERN_WARNING,
-                        "Can't set test_dummy_encryption on remount");
-               return -1;
-       }
-       err = fscrypt_set_test_dummy_encryption(sb, param->string,
+       err = fscrypt_set_test_dummy_encryption(sb, arg,
                                                &sbi->s_dummy_enc_policy);
        if (err) {
-               if (err == -EEXIST)
-                       ext4_msg(sb, KERN_WARNING,
-                                "Can't change test_dummy_encryption on remount");
-               else if (err == -EINVAL)
-                       ext4_msg(sb, KERN_WARNING,
-                                "Value of option \"%s\" is unrecognized",
-                                param->key);
-               else
-                       ext4_msg(sb, KERN_WARNING,
-                                "Error processing option \"%s\" [%d]",
-                                param->key, err);
-               return -1;
+               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");
-#else
-       ext4_msg(sb, KERN_WARNING,
-                "Test dummy encryption mount option ignored");
 #endif
-       return 1;
+       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)
+#define EXT4_SPEC_s_inode_readahead_blks       (1 << 10)
+#define EXT4_SPEC_s_li_wait_mult               (1 << 11)
+#define EXT4_SPEC_s_max_dir_size_kb            (1 << 12)
+#define EXT4_SPEC_s_stripe                     (1 << 13)
+#define EXT4_SPEC_s_resuid                     (1 << 14)
+#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)
+
 struct ext4_fs_context {
        char            *s_qf_names[EXT4_MAXQUOTAS];
+       char            *test_dummy_enc_arg;
        int             s_jquota_fmt;   /* Format of quota to use */
+       int             mb_optimize_scan;
+#ifdef CONFIG_EXT4_DEBUG
+       int s_fc_debug_max_replay;
+#endif
        unsigned short  qname_spec;
+       unsigned long   vals_s_flags;   /* Bits to set in s_flags */
+       unsigned long   mask_s_flags;   /* Bits changed in s_flags */
        unsigned long   journal_devnum;
+       unsigned long   s_commit_interval;
+       unsigned long   s_stripe;
+       unsigned int    s_inode_readahead_blks;
+       unsigned int    s_want_extra_isize;
+       unsigned int    s_li_wait_mult;
+       unsigned int    s_max_dir_size_kb;
        unsigned int    journal_ioprio;
-       int             mb_optimize_scan;
+       unsigned int    vals_s_mount_opt;
+       unsigned int    mask_s_mount_opt;
+       unsigned int    vals_s_mount_opt2;
+       unsigned int    mask_s_mount_opt2;
+       unsigned int    vals_s_mount_flags;
+       unsigned int    mask_s_mount_flags;
        unsigned int    opt_flags;      /* MOPT flags */
+       unsigned int    spec;
+       u32             s_max_batch_time;
+       u32             s_min_batch_time;
+       kuid_t          s_resuid;
+       kgid_t          s_resgid;
 };
 
 #ifdef CONFIG_QUOTA
@@ -2239,6 +2255,7 @@ static int note_qf_name(struct fs_context *fc, int qtype,
        }
        ctx->s_qf_names[qtype] = qname;
        ctx->qname_spec |= 1 << qtype;
+       ctx->spec |= EXT4_SPEC_JQUOTA;
        return 0;
 }
 
@@ -2254,15 +2271,35 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)
 
        ctx->s_qf_names[qtype] = NULL;
        ctx->qname_spec |= 1 << qtype;
+       ctx->spec |= EXT4_SPEC_JQUOTA;
        return 0;
 }
 #endif
 
+#define EXT4_SET_CTX(name)                                             \
+static inline void ctx_set_##name(struct ext4_fs_context *ctx, int flag)\
+{                                                                      \
+       ctx->mask_s_##name |= flag;                                     \
+       ctx->vals_s_##name |= flag;                                     \
+}                                                                      \
+static inline void ctx_clear_##name(struct ext4_fs_context *ctx, int flag)\
+{                                                                      \
+       ctx->mask_s_##name |= flag;                                     \
+       ctx->vals_s_##name &= ~flag;                                    \
+}                                                                      \
+static inline bool ctx_test_##name(struct ext4_fs_context *ctx, int flag)\
+{                                                                      \
+       return ((ctx->vals_s_##name & flag) != 0);                      \
+}                                                                      \
+
+EXT4_SET_CTX(flags);
+EXT4_SET_CTX(mount_opt);
+EXT4_SET_CTX(mount_opt2);
+EXT4_SET_CTX(mount_flags);
+
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
        struct ext4_fs_context *ctx = fc->fs_private;
-       struct ext4_sb_info *sbi = fc->s_fs_info;
-       struct super_block *sb = sbi->s_sb;
        struct fs_parse_result result;
        const struct mount_opts *m;
        int is_remount;
@@ -2300,20 +2337,20 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                         param->key);
                return 1;
        case Opt_abort:
-               ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
+               ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
                return 1;
        case Opt_i_version:
-               sb->s_flags |= SB_I_VERSION;
+               ctx_set_flags(ctx, SB_I_VERSION);
                return 1;
        case Opt_lazytime:
-               sb->s_flags |= SB_LAZYTIME;
+               ctx_set_flags(ctx, SB_LAZYTIME);
                return 1;
        case Opt_nolazytime:
-               sb->s_flags &= ~SB_LAZYTIME;
+               ctx_clear_flags(ctx, SB_LAZYTIME);
                return 1;
        case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-               sb->s_flags |= SB_INLINECRYPT;
+               ctx_set_flags(ctx, SB_INLINECRYPT);
 #else
                ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
 #endif
@@ -2340,21 +2377,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
        if (m->flags & MOPT_EXPLICIT) {
                if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
-                       set_opt2(sb, EXPLICIT_DELALLOC);
+                       ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
                } else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
-                       set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
+                       ctx_set_mount_opt2(ctx,
+                                      EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
                } else
                        return -EINVAL;
        }
        if (m->flags & MOPT_CLEAR_ERR)
-               clear_opt(sb, ERRORS_MASK);
+               ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
 
        if (m->flags & MOPT_NOSUPPORT) {
                ext4_msg(NULL, KERN_ERR, "%s option not supported",
                         param->key);
        } else if (token == Opt_commit) {
                if (result.uint_32 == 0)
-                       sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
+                       ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
                else if (result.uint_32 > INT_MAX / HZ) {
                        ext4_msg(NULL, KERN_ERR,
                                 "Invalid commit interval %d, "
@@ -2362,21 +2400,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                                 result.uint_32, INT_MAX / HZ);
                        return -EINVAL;
                }
-               sbi->s_commit_interval = HZ * result.uint_32;
+               ctx->s_commit_interval = HZ * result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_commit_interval;
        } else if (token == Opt_debug_want_extra_isize) {
-               if ((result.uint_32 & 1) ||
-                   (result.uint_32 < 4) ||
-                   (result.uint_32 >
-                    (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
+               if ((result.uint_32 & 1) || (result.uint_32 < 4)) {
                        ext4_msg(NULL, KERN_ERR,
                                 "Invalid want_extra_isize %d", result.uint_32);
                        return -EINVAL;
                }
-               sbi->s_want_extra_isize = result.uint_32;
+               ctx->s_want_extra_isize = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_want_extra_isize;
        } else if (token == Opt_max_batch_time) {
-               sbi->s_max_batch_time = result.uint_32;
+               ctx->s_max_batch_time = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_max_batch_time;
        } else if (token == Opt_min_batch_time) {
-               sbi->s_min_batch_time = result.uint_32;
+               ctx->s_min_batch_time = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_min_batch_time;
        } else if (token == Opt_inode_readahead_blks) {
                if (result.uint_32 &&
                    (result.uint_32 > (1 << 30) ||
@@ -2386,20 +2425,25 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                                 "0 or a power of 2 smaller than 2^31");
                        return -EINVAL;
                }
-               sbi->s_inode_readahead_blks = result.uint_32;
+               ctx->s_inode_readahead_blks = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
        } else if (token == Opt_init_itable) {
-               set_opt(sb, INIT_INODE_TABLE);
-               sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+               ctx_set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
+               ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
                if (param->type == fs_value_is_string)
-                       sbi->s_li_wait_mult = result.uint_32;
+                       ctx->s_li_wait_mult = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_li_wait_mult;
        } else if (token == Opt_max_dir_size_kb) {
-               sbi->s_max_dir_size_kb = result.uint_32;
+               ctx->s_max_dir_size_kb = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
 #ifdef CONFIG_EXT4_DEBUG
        } else if (token == Opt_fc_debug_max_replay) {
-               sbi->s_fc_debug_max_replay = result.uint_32;
+               ctx->s_fc_debug_max_replay = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_fc_debug_max_replay;
 #endif
        } else if (token == Opt_stripe) {
-               sbi->s_stripe = result.uint_32;
+               ctx->s_stripe = result.uint_32;
+               ctx->spec |= EXT4_SPEC_s_stripe;
        } else if (token == Opt_resuid) {
                uid = make_kuid(current_user_ns(), result.uint_32);
                if (!uid_valid(uid)) {
@@ -2407,7 +2451,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                                 result.uint_32);
                        return -EINVAL;
                }
-               sbi->s_resuid = uid;
+               ctx->s_resuid = uid;
+               ctx->spec |= EXT4_SPEC_s_resuid;
        } else if (token == Opt_resgid) {
                gid = make_kgid(current_user_ns(), result.uint_32);
                if (!gid_valid(gid)) {
@@ -2415,7 +2460,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                                 result.uint_32);
                        return -EINVAL;
                }
-               sbi->s_resgid = gid;
+               ctx->s_resgid = gid;
+               ctx->spec |= EXT4_SPEC_s_resgid;
        } else if (token == Opt_journal_dev) {
                if (is_remount) {
                        ext4_msg(NULL, KERN_ERR,
@@ -2423,6 +2469,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                        return -EINVAL;
                }
                ctx->journal_devnum = result.uint_32;
+               ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
        } else if (token == Opt_journal_path) {
                struct inode *journal_inode;
                struct path path;
@@ -2443,6 +2490,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
                journal_inode = d_inode(path.dentry);
                ctx->journal_devnum = new_encode_dev(journal_inode->i_rdev);
+               ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
                path_put(&path);
        } else if (token == Opt_journal_ioprio) {
                if (result.uint_32 > 7) {
@@ -2452,24 +2500,37 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                }
                ctx->journal_ioprio =
                        IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
+               ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
        } else if (token == Opt_test_dummy_encryption) {
-               return ext4_set_test_dummy_encryption(sb, param, is_remount);
-       } else if (m->flags & MOPT_DATAJ) {
-               if (is_remount) {
-                       if (!sbi->s_journal)
-                               ext4_msg(NULL, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
-                       else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
-                               ext4_msg(NULL, KERN_ERR,
-                                        "Cannot change data mode on remount");
-                               return -EINVAL;
-                       }
-               } else {
-                       clear_opt(sb, DATA_FLAGS);
-                       sbi->s_mount_opt |= m->mount_opt;
+#ifdef CONFIG_FS_ENCRYPTION
+               if (param->type == fs_value_is_flag) {
+                       ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
+                       ctx->test_dummy_enc_arg = NULL;
+                       return 1;
+               }
+               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);
+#else
+               ext4_msg(NULL, KERN_WARNING,
+                        "Test dummy encryption mount option ignored");
+#endif
+       } else if (m->flags & MOPT_DATAJ) {
+               ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+               ctx_set_mount_opt(ctx, m->mount_opt);
+               ctx->spec |= EXT4_SPEC_DATAJ;
 #ifdef CONFIG_QUOTA
        } else if (m->flags & MOPT_QFMT) {
                ctx->s_jquota_fmt = m->mount_opt;
+               ctx->spec |= EXT4_SPEC_JQFMT;
 #endif
        } else if (token == Opt_dax || token == Opt_dax_always ||
                   token == Opt_dax_inode || token == Opt_dax_never) {
@@ -2477,56 +2538,30 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                switch (token) {
                case Opt_dax:
                case Opt_dax_always:
-                       if (is_remount &&
-                           (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
-                            (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
-                       fail_dax_change_remount:
-                               ext4_msg(NULL, KERN_ERR, "can't change "
-                                        "dax mount option while remounting");
-                               return -EINVAL;
-                       }
-                       if (is_remount &&
-                           (test_opt(sb, DATA_FLAGS) ==
-                            EXT4_MOUNT_JOURNAL_DATA)) {
-                                   ext4_msg(NULL, KERN_ERR, "can't mount with "
-                                            "both data=journal and dax");
-                                   return -EINVAL;
-                       }
-                       ext4_msg(NULL, KERN_WARNING,
-                               "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
-                       sbi->s_mount_opt |= EXT4_MOUNT_DAX_ALWAYS;
-                       sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+                       ctx_set_mount_opt(ctx, m->mount_opt);
+                       ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
                        break;
                case Opt_dax_never:
-                       if (is_remount &&
-                           (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
-                            (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS)))
-                               goto fail_dax_change_remount;
-                       sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
-                       sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+                       ctx_set_mount_opt2(ctx, m->mount_opt);
+                       ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
                        break;
                case Opt_dax_inode:
-                       if (is_remount &&
-                           ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
-                            (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
-                            !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE)))
-                               goto fail_dax_change_remount;
-                       sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
-                       sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+                       ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
+                       ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
                        /* Strictly for printing options */
-                       sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_INODE;
+                       ctx_set_mount_opt2(ctx, m->mount_opt);
                        break;
                }
 #else
                ext4_msg(NULL, KERN_INFO, "dax option not supported");
-               sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
-               sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+               ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
+               ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
                return -EINVAL;
 #endif
        } else if (token == Opt_data_err_abort) {
-               sbi->s_mount_opt |= m->mount_opt;
+               ctx_set_mount_opt(ctx, m->mount_opt);
        } else if (token == Opt_data_err_ignore) {
-               sbi->s_mount_opt &= ~m->mount_opt;
+               ctx_clear_mount_opt(ctx, m->mount_opt);
        } else if (token == Opt_mb_optimize_scan) {
                if (result.int_32 != 0 && result.int_32 != 1) {
                        ext4_msg(NULL, KERN_WARNING,
@@ -2552,14 +2587,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                }
                if (m->flags & MOPT_2) {
                        if (set != 0)
-                               sbi->s_mount_opt2 |= m->mount_opt;
+                               ctx_set_mount_opt2(ctx, m->mount_opt);
                        else
-                               sbi->s_mount_opt2 &= ~m->mount_opt;
+                               ctx_clear_mount_opt2(ctx, m->mount_opt);
                } else {
                        if (set != 0)
-                               sbi->s_mount_opt |= m->mount_opt;
+                               ctx_set_mount_opt(ctx, m->mount_opt);
                        else
-                               sbi->s_mount_opt &= ~m->mount_opt;
+                               ctx_clear_mount_opt(ctx, m->mount_opt);
                }
        }
        return 1;
@@ -2624,8 +2659,9 @@ static int parse_options(char *options, struct super_block *sb,
        if (ret < 0)
                return 0;
 
-       if (ctx->qname_spec)
-               ext4_apply_quota_options(&fc, sb);
+       ret = ext4_apply_options(&fc, sb);
+       if (ret < 0)
+               return 0;
 
        return 1;
 }
@@ -2634,20 +2670,30 @@ static void ext4_apply_quota_options(struct fs_context *fc,
                                     struct super_block *sb)
 {
 #ifdef CONFIG_QUOTA
+       bool quota_feature = ext4_has_feature_quota(sb);
        struct ext4_fs_context *ctx = fc->fs_private;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        char *qname;
        int i;
 
-       for (i = 0; i < EXT4_MAXQUOTAS; i++) {
-               if (!(ctx->qname_spec & (1 << i)))
-                       continue;
-               qname = ctx->s_qf_names[i]; /* May be NULL */
-               ctx->s_qf_names[i] = NULL;
-               kfree(sbi->s_qf_names[i]);
-               rcu_assign_pointer(sbi->s_qf_names[i], qname);
-               set_opt(sb, QUOTA);
+       if (quota_feature)
+               return;
+
+       if (ctx->spec & EXT4_SPEC_JQUOTA) {
+               for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+                       if (!(ctx->qname_spec & (1 << i)))
+                               continue;
+
+                       qname = ctx->s_qf_names[i]; /* May be NULL */
+                       ctx->s_qf_names[i] = NULL;
+                       kfree(sbi->s_qf_names[i]);
+                       rcu_assign_pointer(sbi->s_qf_names[i], qname);
+                       set_opt(sb, QUOTA);
+               }
        }
+
+       if (ctx->spec & EXT4_SPEC_JQFMT)
+               sbi->s_jquota_fmt = ctx->s_jquota_fmt;
 #endif
 }
 
@@ -2662,17 +2708,36 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        bool quota_feature = ext4_has_feature_quota(sb);
        bool quota_loaded = sb_any_quota_loaded(sb);
-       int i;
+       bool usr_qf_name, grp_qf_name, usrquota, grpquota;
+       int quota_flags, 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_mount_opt(ctx, EXT4_MOUNT_PRJQUOTA) &&
+           !ext4_has_feature_project(sb)) {
+               ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
+                        "Cannot enable project quota enforcement.");
+               return -EINVAL;
+       }
+
+       quota_flags = EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
+                     EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA;
+       if (quota_loaded &&
+           ctx->mask_s_mount_opt & quota_flags &&
+           !ctx_test_mount_opt(ctx, quota_flags))
+               goto err_quota_change;
 
-       if (ctx->qname_spec && quota_loaded) {
-               if (quota_feature)
-                       goto err_feature;
+       if (ctx->spec & EXT4_SPEC_JQUOTA) {
 
                for (i = 0; i < EXT4_MAXQUOTAS; i++) {
                        if (!(ctx->qname_spec & (1 << i)))
                                continue;
 
-                       if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+                       if (quota_loaded &&
+                           !!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
                                goto err_jquota_change;
 
                        if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
@@ -2680,17 +2745,60 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
                                   ctx->s_qf_names[i]) != 0)
                                goto err_jquota_specified;
                }
+
+               if (quota_feature) {
+                       ext4_msg(NULL, KERN_INFO,
+                                "Journaled quota options ignored when "
+                                "QUOTA feature is enabled");
+                       return 0;
+               }
        }
 
-       if (ctx->s_jquota_fmt) {
+       if (ctx->spec & EXT4_SPEC_JQFMT) {
                if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
-                       goto err_quota_change;
+                       goto err_jquota_change;
                if (quota_feature) {
                        ext4_msg(NULL, KERN_INFO, "Quota format mount options "
                                 "ignored when QUOTA feature is enabled");
                        return 0;
                }
        }
+
+       /* Make sure we don't mix old and new quota format */
+       usr_qf_name = (get_qf_name(sb, sbi, USRQUOTA) ||
+                      ctx->s_qf_names[USRQUOTA]);
+       grp_qf_name = (get_qf_name(sb, sbi, GRPQUOTA) ||
+                      ctx->s_qf_names[GRPQUOTA]);
+
+       usrquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+                   test_opt(sb, USRQUOTA));
+
+       grpquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
+                   test_opt(sb, GRPQUOTA));
+
+       if (usr_qf_name) {
+               ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
+               usrquota = false;
+       }
+       if (grp_qf_name) {
+               ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
+               grpquota = false;
+       }
+
+       if (usr_qf_name || grp_qf_name) {
+               if (usrquota || grpquota) {
+                       ext4_msg(NULL, KERN_ERR, "old and new quota "
+                                "format mixing");
+                       return -EINVAL;
+               }
+
+               if (!(ctx->spec & EXT4_SPEC_JQFMT || sbi->s_jquota_fmt)) {
+                       ext4_msg(NULL, KERN_ERR, "journaled quota format "
+                                "not specified");
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 
 err_quota_change:
@@ -2705,10 +2813,6 @@ err_jquota_specified:
        ext4_msg(NULL, KERN_ERR, "%s quota file already specified",
                 QTYPE2NAME(i));
        return -EINVAL;
-err_feature:
-       ext4_msg(NULL, KERN_ERR, "Journaled quota options ignored "
-                "when QUOTA feature is enabled");
-       return 0;
 #else
        return 0;
 #endif
@@ -2718,6 +2822,8 @@ static int ext4_check_opt_consistency(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 is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 
        if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
                ext4_msg(NULL, KERN_ERR,
@@ -2730,57 +2836,146 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
                return -EINVAL;
        }
 
+       if (ctx->s_want_extra_isize >
+           (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE)) {
+               ext4_msg(NULL, KERN_ERR,
+                        "Invalid want_extra_isize %d",
+                        ctx->s_want_extra_isize);
+               return -EINVAL;
+       }
+
+       if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DIOREAD_NOLOCK)) {
+               int blocksize =
+                       BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
+               if (blocksize < PAGE_SIZE)
+                       ext4_msg(NULL, KERN_WARNING, "Warning: mounting with an "
+                                "experimental mount option 'dioread_nolock' "
+                                "for blocksize < PAGE_SIZE");
+       }
+
+#ifdef CONFIG_FS_ENCRYPTION
+       /*
+        * 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 ((ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION) &&
+           is_remount && !sbi->s_dummy_enc_policy.policy) {
+               ext4_msg(NULL, KERN_WARNING,
+                        "Can't set test_dummy_encryption on remount");
+               return -1;
+       }
+#endif
+
+       if ((ctx->spec & EXT4_SPEC_DATAJ) && is_remount) {
+               if (!sbi->s_journal) {
+                       ext4_msg(NULL, KERN_WARNING,
+                                "Remounting file system with no journal "
+                                "so ignoring journalled data option");
+                       ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+               } else if (ctx->mask_s_mount_opt & EXT4_MOUNT_DATA_FLAGS) {
+                       ext4_msg(NULL, KERN_ERR, "Cannot change data mode "
+                                "on remount");
+                       return -EINVAL;
+               }
+       }
+
+       if (is_remount) {
+               if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+                   (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) {
+                       ext4_msg(NULL, KERN_ERR, "can't mount with "
+                                "both data=journal and dax");
+                       return -EINVAL;
+               }
+
+               if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+                   (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+                    (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
+fail_dax_change_remount:
+                       ext4_msg(NULL, KERN_ERR, "can't change "
+                                "dax mount option while remounting");
+                       return -EINVAL;
+               } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER) &&
+                        (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+                         (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS))) {
+                       goto fail_dax_change_remount;
+               } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE) &&
+                          ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+                           (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+                           !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE))) {
+                       goto fail_dax_change_remount;
+               }
+       }
+
        return ext4_check_quota_consistency(fc, sb);
 }
 
-static int ext4_validate_options(struct fs_context *fc)
+static int 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;
-       struct super_block *sb = sbi->s_sb;
+       int ret = 0;
+
+       sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
+       sbi->s_mount_opt |= ctx->vals_s_mount_opt;
+       sbi->s_mount_opt2 &= ~ctx->mask_s_mount_opt2;
+       sbi->s_mount_opt2 |= ctx->vals_s_mount_opt2;
+       sbi->s_mount_flags &= ~ctx->mask_s_mount_flags;
+       sbi->s_mount_flags |= ctx->vals_s_mount_flags;
+       sb->s_flags &= ~ctx->mask_s_flags;
+       sb->s_flags |= ctx->vals_s_flags;
+
+#define APPLY(X) ({ if (ctx->spec & EXT4_SPEC_##X) sbi->X = ctx->X; })
+       APPLY(s_commit_interval);
+       APPLY(s_stripe);
+       APPLY(s_max_batch_time);
+       APPLY(s_min_batch_time);
+       APPLY(s_want_extra_isize);
+       APPLY(s_inode_readahead_blks);
+       APPLY(s_max_dir_size_kb);
+       APPLY(s_li_wait_mult);
+       APPLY(s_resgid);
+       APPLY(s_resuid);
+
+#ifdef CONFIG_EXT4_DEBUG
+       APPLY(s_fc_debug_max_replay);
+#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;
+}
+
+
+static int ext4_validate_options(struct fs_context *fc)
+{
 #ifdef CONFIG_QUOTA
+       struct ext4_fs_context *ctx = fc->fs_private;
        char *usr_qf_name, *grp_qf_name;
-       /*
-        * 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(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
-               ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
-                        "Cannot enable project quota enforcement.");
-               return -EINVAL;
-       }
-       usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
-       grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
+
+       usr_qf_name = ctx->s_qf_names[USRQUOTA];
+       grp_qf_name = ctx->s_qf_names[GRPQUOTA];
+
        if (usr_qf_name || grp_qf_name) {
-               if (test_opt(sb, USRQUOTA) && usr_qf_name)
-                       clear_opt(sb, USRQUOTA);
+               if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
+                       ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
 
-               if (test_opt(sb, GRPQUOTA) && grp_qf_name)
-                       clear_opt(sb, GRPQUOTA);
+               if (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) && grp_qf_name)
+                       ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
 
-               if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+               if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+                   ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA)) {
                        ext4_msg(NULL, KERN_ERR, "old and new quota "
-                                       "format mixing");
-                       return -EINVAL;
-               }
-
-               if (!sbi->s_jquota_fmt) {
-                       ext4_msg(NULL, KERN_ERR, "journaled quota format "
-                                       "not specified");
+                                "format mixing");
                        return -EINVAL;
                }
        }
 #endif
-       if (test_opt(sb, DIOREAD_NOLOCK)) {
-               int blocksize =
-                       BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
-               if (blocksize < PAGE_SIZE)
-                       ext4_msg(NULL, KERN_WARNING,
-                                "Warning: mounting with an experimental "
-                                "option 'dioread_nolock' for "
-                                "blocksize < PAGE_SIZE");
-       }
-       return 0;
+       return 1;
 }
 
 static inline void ext4_show_quota_options(struct seq_file *seq,