[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20191106101457.11237-7-lczerner@redhat.com>
Date: Wed, 6 Nov 2019 11:14:46 +0100
From: Lukas Czerner <lczerner@...hat.com>
To: linux-ext4@...r.kernel.org
Cc: Theodore Ts'o <tytso@....edu>, David Howells <dhowells@...hat.com>,
Al Viro <viro@...iv.linux.org.uk>
Subject: [PATCH 06/17] ext4: move quota configuration out of handle_mount_opt()
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@...hat.com>
---
fs/ext4/super.c | 249 +++++++++++++++++++++++++++++++-----------------
1 file changed, 160 insertions(+), 89 deletions(-)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 12483f3b1f78..affcdaf63b6e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -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
@@ -1692,71 +1696,6 @@ static const char deprecated_msg[] =
"Mount option \"%s\" will be removed by %s\n"
"Contact linux-ext4@...r.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
@@ -1901,10 +1840,69 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
#endif
struct ext4_fs_context {
- unsigned long journal_devnum;
- unsigned int journal_ioprio;
+ 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;
};
+#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, "EXT4-fs: 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,
+ "EXT4-fs: Quota file already specified");
+ 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])
+ return 0;
+
+ 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;
@@ -1923,14 +1921,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
#ifdef CONFIG_QUOTA
if (token == Opt_usrjquota) {
if (result.negated)
- 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 (result.negated)
- 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) {
@@ -1996,11 +1994,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",
@@ -2117,19 +2110,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 -EINVAL;
- }
- sbi->s_jquota_fmt = m->mount_opt;
+ ctx->s_jquota_fmt = m->mount_opt;
#endif
} else if (token == Opt_dax) {
#ifdef CONFIG_FS_DAX
@@ -2231,9 +2212,99 @@ 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] &&
+ 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,
+ "Ext4: Cannot change quota options when quota turned on");
+ return -EINVAL;
+err_jquota_change:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Cannot change journaled quota "
+ "options when quota turned on");
+ return -EINVAL;
+err_jquota_specified:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Quota file already specified");
+ return -EINVAL;
+err_feature:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Journaled quota options ignored "
+ "when QUOTA feature is enabled");
+ return -EINVAL;
+#else
+ return 0;
+#endif
+}
+
static int ext4_validate_options(struct fs_context *fc)
{
struct ext4_sb_info *sbi = fc->s_fs_info;
--
2.21.0
Powered by blists - more mailing lists