OSDN Git Service

ext4: move quota configuration out of handle_mount_opt()
authorLukas Czerner <lczerner@redhat.com>
Wed, 27 Oct 2021 14:18:50 +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 so move quota confiquration out of handle_mount_opt() by
noting the quota file names in the ext4_fs_context structure to be
able to apply it later.

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

index 877eaca..c082aea 100644 (file)
@@ -89,6 +89,10 @@ static void ext4_clear_request_list(void);
 static struct inode *ext4_get_journal_inode(struct super_block *sb,
                                            unsigned int journal_inum);
 static int ext4_validate_options(struct fs_context *fc);
+static int ext4_check_quota_consistency(struct fs_context *fc,
+                                       struct super_block *sb);
+static void ext4_apply_quota_options(struct fs_context *fc,
+                                    struct super_block *sb);
 
 /*
  * Lock ordering
@@ -1986,71 +1990,6 @@ static const char deprecated_msg[] =
        "Mount option \"%s\" will be removed by %s\n"
        "Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
 
-#ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype,
-                      struct fs_parameter *param)
-{
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
-       char *qname, *old_qname = get_qf_name(sb, sbi, qtype);
-       int ret = -1;
-
-       if (sb_any_quota_loaded(sb) && !old_qname) {
-               ext4_msg(sb, KERN_ERR,
-                       "Cannot change journaled "
-                       "quota options when quota turned on");
-               return -1;
-       }
-       if (ext4_has_feature_quota(sb)) {
-               ext4_msg(sb, KERN_INFO, "Journaled quota options "
-                        "ignored when QUOTA feature is enabled");
-               return 1;
-       }
-       qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
-       if (!qname) {
-               ext4_msg(sb, KERN_ERR,
-                       "Not enough memory for storing quotafile name");
-               return -1;
-       }
-       if (old_qname) {
-               if (strcmp(old_qname, qname) == 0)
-                       ret = 1;
-               else
-                       ext4_msg(sb, KERN_ERR,
-                                "%s quota file already specified",
-                                QTYPE2NAME(qtype));
-               goto errout;
-       }
-       if (strchr(qname, '/')) {
-               ext4_msg(sb, KERN_ERR,
-                       "quotafile must be on filesystem root");
-               goto errout;
-       }
-       rcu_assign_pointer(sbi->s_qf_names[qtype], qname);
-       set_opt(sb, QUOTA);
-       return 1;
-errout:
-       kfree(qname);
-       return ret;
-}
-
-static int clear_qf_name(struct super_block *sb, int qtype)
-{
-
-       struct ext4_sb_info *sbi = EXT4_SB(sb);
-       char *old_qname = get_qf_name(sb, sbi, qtype);
-
-       if (sb_any_quota_loaded(sb) && old_qname) {
-               ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
-                       " when quota turned on");
-               return -1;
-       }
-       rcu_assign_pointer(sbi->s_qf_names[qtype], NULL);
-       synchronize_rcu();
-       kfree(old_qname);
-       return 1;
-}
-#endif
-
 #define MOPT_SET       0x0001
 #define MOPT_CLEAR     0x0002
 #define MOPT_NOSUPPORT 0x0004
@@ -2254,11 +2193,70 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb,
 }
 
 struct ext4_fs_context {
-       unsigned long journal_devnum;
-       unsigned int journal_ioprio;
-       int mb_optimize_scan;
+       char            *s_qf_names[EXT4_MAXQUOTAS];
+       int             s_jquota_fmt;   /* Format of quota to use */
+       unsigned short  qname_spec;
+       unsigned long   journal_devnum;
+       unsigned int    journal_ioprio;
+       int             mb_optimize_scan;
 };
 
+#ifdef CONFIG_QUOTA
+/*
+ * Note the name of the specified quota file.
+ */
+static int note_qf_name(struct fs_context *fc, int qtype,
+                      struct fs_parameter *param)
+{
+       struct ext4_fs_context *ctx = fc->fs_private;
+       char *qname;
+
+       if (param->size < 1) {
+               ext4_msg(NULL, KERN_ERR, "Missing quota name");
+               return -EINVAL;
+       }
+       if (strchr(param->string, '/')) {
+               ext4_msg(NULL, KERN_ERR,
+                        "quotafile must be on filesystem root");
+               return -EINVAL;
+       }
+       if (ctx->s_qf_names[qtype]) {
+               if (strcmp(ctx->s_qf_names[qtype], param->string) != 0) {
+                       ext4_msg(NULL, KERN_ERR,
+                                "%s quota file already specified",
+                                QTYPE2NAME(qtype));
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+       if (!qname) {
+               ext4_msg(NULL, KERN_ERR,
+                        "Not enough memory for storing quotafile name");
+               return -ENOMEM;
+       }
+       ctx->s_qf_names[qtype] = qname;
+       ctx->qname_spec |= 1 << qtype;
+       return 0;
+}
+
+/*
+ * Clear the name of the specified quota file.
+ */
+static int unnote_qf_name(struct fs_context *fc, int qtype)
+{
+       struct ext4_fs_context *ctx = fc->fs_private;
+
+       if (ctx->s_qf_names[qtype])
+               kfree(ctx->s_qf_names[qtype]);
+
+       ctx->s_qf_names[qtype] = NULL;
+       ctx->qname_spec |= 1 << qtype;
+       return 0;
+}
+#endif
+
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
        struct ext4_fs_context *ctx = fc->fs_private;
@@ -2279,14 +2277,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_QUOTA
        if (token == Opt_usrjquota) {
                if (!*param->string)
-                       return clear_qf_name(sb, USRQUOTA);
+                       return unnote_qf_name(fc, USRQUOTA);
                else
-                       return set_qf_name(sb, USRQUOTA, param);
+                       return note_qf_name(fc, USRQUOTA, param);
        } else if (token == Opt_grpjquota) {
                if (!*param->string)
-                       return clear_qf_name(sb, GRPQUOTA);
+                       return unnote_qf_name(fc, GRPQUOTA);
                else
-                       return set_qf_name(sb, GRPQUOTA, param);
+                       return note_qf_name(fc, GRPQUOTA, param);
        }
 #endif
        switch (token) {
@@ -2360,11 +2358,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
        }
        if (m->flags & MOPT_CLEAR_ERR)
                clear_opt(sb, ERRORS_MASK);
-       if (token == Opt_noquota && sb_any_quota_loaded(sb)) {
-               ext4_msg(NULL, KERN_ERR, "Cannot change quota "
-                        "options when quota turned on");
-               return -EINVAL;
-       }
 
        if (m->flags & MOPT_NOSUPPORT) {
                ext4_msg(NULL, KERN_ERR, "%s option not supported",
@@ -2486,19 +2479,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
                }
 #ifdef CONFIG_QUOTA
        } else if (m->flags & MOPT_QFMT) {
-               if (sb_any_quota_loaded(sb) &&
-                   sbi->s_jquota_fmt != m->mount_opt) {
-                       ext4_msg(NULL, KERN_ERR, "Cannot change journaled "
-                                "quota options when quota turned on");
-                       return -EINVAL;
-               }
-               if (ext4_has_feature_quota(sb)) {
-                       ext4_msg(NULL, KERN_INFO,
-                                "Quota format mount options ignored "
-                                "when QUOTA feature is enabled");
-                       return 1;
-               }
-               sbi->s_jquota_fmt = m->mount_opt;
+               ctx->s_jquota_fmt = m->mount_opt;
 #endif
        } else if (token == Opt_dax || token == Opt_dax_always ||
                   token == Opt_dax_inode || token == Opt_dax_never) {
@@ -2595,7 +2576,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 }
 
 static int parse_options(char *options, struct super_block *sb,
-                        struct ext4_fs_context *ret_opts,
+                        struct ext4_fs_context *ctx,
                         int is_remount)
 {
        struct fs_parameter param;
@@ -2607,7 +2588,7 @@ static int parse_options(char *options, struct super_block *sb,
                return 1;
 
        memset(&fc, 0, sizeof(fc));
-       fc.fs_private = ret_opts;
+       fc.fs_private = ctx;
        fc.s_fs_info = EXT4_SB(sb);
 
        if (is_remount)
@@ -2649,9 +2630,100 @@ static int parse_options(char *options, struct super_block *sb,
        if (ret < 0)
                return 0;
 
+       ret = ext4_check_quota_consistency(&fc, sb);
+       if (ret < 0)
+               return 0;
+
+       if (ctx->qname_spec)
+               ext4_apply_quota_options(&fc, sb);
+
        return 1;
 }
 
+static void ext4_apply_quota_options(struct fs_context *fc,
+                                    struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+       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);
+       }
+#endif
+}
+
+/*
+ * Check quota settings consistency.
+ */
+static int ext4_check_quota_consistency(struct fs_context *fc,
+                                       struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+       struct ext4_fs_context *ctx = fc->fs_private;
+       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;
+
+       if (ctx->qname_spec && quota_loaded) {
+               if (quota_feature)
+                       goto err_feature;
+
+               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])
+                               goto err_jquota_change;
+
+                       if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
+                           strcmp(sbi->s_qf_names[i],
+                                  ctx->s_qf_names[i]) != 0)
+                               goto err_jquota_specified;
+               }
+       }
+
+       if (ctx->s_jquota_fmt) {
+               if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
+                       goto err_quota_change;
+               if (quota_feature) {
+                       ext4_msg(NULL, KERN_INFO, "Quota format mount options "
+                                "ignored when QUOTA feature is enabled");
+                       return 0;
+               }
+       }
+       return 0;
+
+err_quota_change:
+       ext4_msg(NULL, KERN_ERR,
+                "Cannot change quota options when quota turned on");
+       return -EINVAL;
+err_jquota_change:
+       ext4_msg(NULL, KERN_ERR, "Cannot change journaled quota "
+                "options when quota turned on");
+       return -EINVAL;
+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
+}
+
 static int ext4_validate_options(struct fs_context *fc)
 {
        struct ext4_sb_info *sbi = fc->s_fs_info;
@@ -4105,7 +4177,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        __u64 blocks_count;
        int err = 0;
        ext4_group_t first_not_zeroed;
-       struct ext4_fs_context parsed_opts;
+       struct ext4_fs_context parsed_opts = {0};
 
        /* Set defaults for the variables that will be set during parsing */
        parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;