Skip to content

Commit

Permalink
ext4: move quota configuration out of handle_mount_opt()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Lukas Czerner authored and Theodore Ts'o committed Dec 9, 2021
1 parent da812f6 commit e6e268c
Showing 1 changed file with 165 additions and 93 deletions.
258 changes: 165 additions & 93 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit e6e268c

Please sign in to comment.