2020-04-28 16:46:23

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 00/17] ext4: new mount API conversion

The following patch converts the ext4 to use the new mount API
(Documentation/filesystems/mount_api.txt).

The series can be applied on top of the current mainline tree and the work
is based on the patches from David Howells (thank you David). It was built
and tested with xfstests and custom test for ext4 mount options that was
sent over at [email protected] for inclusion into xfstests.

I've tried to avoid big unrelated changes to the original ext4_fill_super()
and ext4_remount, though it could definitely use some cleanup. This can
be done after the conversion with a separate patch set as I don't want
to pollute the conversion with additional cleanup work.

NOTE:
There seems to be a regression somewhere in the new mount api as running
generic/085 in the loop locks up the machine. I was not able to track
the cause of it, but it seems to be outside of ext4. Dave is already
looking into it.

Changes:
v2: rebased to current kernel

-Lukas


2020-04-28 16:46:23

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 01/17] fs_parse: allow parameter value to be empty

Allow parameter value to be empty by spcifying fs_param_can_be_empty
flag.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/fs_parser.c | 31 +++++++++++++++++++++++--------
include/linux/fs_parser.h | 2 +-
2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/fs/fs_parser.c b/fs/fs_parser.c
index ab53e42a874a..0d44801919f9 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -200,6 +200,8 @@ int fs_param_is_bool(struct p_log *log, const struct fs_parameter_spec *p,
int b;
if (param->type != fs_value_is_string)
return fs_param_bad_value(log, param);
+ if (!*param->string && (p->flags & fs_param_can_be_empty))
+ return 0;
b = lookup_constant(bool_names, param->string, -1);
if (b == -1)
return fs_param_bad_value(log, param);
@@ -212,8 +214,11 @@ int fs_param_is_u32(struct p_log *log, const struct fs_parameter_spec *p,
struct fs_parameter *param, struct fs_parse_result *result)
{
int base = (unsigned long)p->data;
- if (param->type != fs_value_is_string ||
- kstrtouint(param->string, base, &result->uint_32) < 0)
+ if (param->type != fs_value_is_string)
+ return fs_param_bad_value(log, param);
+ if (!*param->string && (p->flags & fs_param_can_be_empty))
+ return 0;
+ if (kstrtouint(param->string, base, &result->uint_32) < 0)
return fs_param_bad_value(log, param);
return 0;
}
@@ -222,8 +227,11 @@ EXPORT_SYMBOL(fs_param_is_u32);
int fs_param_is_s32(struct p_log *log, const struct fs_parameter_spec *p,
struct fs_parameter *param, struct fs_parse_result *result)
{
- if (param->type != fs_value_is_string ||
- kstrtoint(param->string, 0, &result->int_32) < 0)
+ if (param->type != fs_value_is_string)
+ return fs_param_bad_value(log, param);
+ if (!*param->string && (p->flags & fs_param_can_be_empty))
+ return 0;
+ if (kstrtoint(param->string, 0, &result->int_32) < 0)
return fs_param_bad_value(log, param);
return 0;
}
@@ -232,8 +240,11 @@ EXPORT_SYMBOL(fs_param_is_s32);
int fs_param_is_u64(struct p_log *log, const struct fs_parameter_spec *p,
struct fs_parameter *param, struct fs_parse_result *result)
{
- if (param->type != fs_value_is_string ||
- kstrtoull(param->string, 0, &result->uint_64) < 0)
+ if (param->type != fs_value_is_string)
+ return fs_param_bad_value(log, param);
+ if (!*param->string && (p->flags & fs_param_can_be_empty))
+ return 0;
+ if (kstrtoull(param->string, 0, &result->uint_64) < 0)
return fs_param_bad_value(log, param);
return 0;
}
@@ -245,6 +256,8 @@ int fs_param_is_enum(struct p_log *log, const struct fs_parameter_spec *p,
const struct constant_table *c;
if (param->type != fs_value_is_string)
return fs_param_bad_value(log, param);
+ if (!*param->string && (p->flags & fs_param_can_be_empty))
+ return 0;
c = __lookup_constant(p->data, param->string);
if (!c)
return fs_param_bad_value(log, param);
@@ -256,7 +269,8 @@ EXPORT_SYMBOL(fs_param_is_enum);
int fs_param_is_string(struct p_log *log, const struct fs_parameter_spec *p,
struct fs_parameter *param, struct fs_parse_result *result)
{
- if (param->type != fs_value_is_string || !*param->string)
+ if (param->type != fs_value_is_string ||
+ (!*param->string && !(p->flags & fs_param_can_be_empty)))
return fs_param_bad_value(log, param);
return 0;
}
@@ -276,7 +290,8 @@ int fs_param_is_fd(struct p_log *log, const struct fs_parameter_spec *p,
{
switch (param->type) {
case fs_value_is_string:
- if (kstrtouint(param->string, 0, &result->uint_32) < 0)
+ if ((!*param->string && !(p->flags & fs_param_can_be_empty)) ||
+ kstrtouint(param->string, 0, &result->uint_32) < 0)
break;
if (result->uint_32 <= INT_MAX)
return 0;
diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h
index 2eab6d5f6736..1cde756ef0fd 100644
--- a/include/linux/fs_parser.h
+++ b/include/linux/fs_parser.h
@@ -42,7 +42,7 @@ struct fs_parameter_spec {
u8 opt; /* Option number (returned by fs_parse()) */
unsigned short flags;
#define fs_param_neg_with_no 0x0002 /* "noxxx" is negative param */
-#define fs_param_neg_with_empty 0x0004 /* "xxx=" is negative param */
+#define fs_param_can_be_empty 0x0004 /* "xxx=" is allowed */
#define fs_param_deprecated 0x0008 /* The param is deprecated */
const void *data;
};
--
2.21.1

2020-04-28 16:46:23

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 02/17] ext4: Add fs parameter specifications for mount options

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 132 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index bf5fcb477f66..fed2e4cafb38 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -47,6 +47,9 @@
#include <linux/kthread.h>
#include <linux/freezer.h>

+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
+
#include "ext4.h"
#include "ext4_extents.h" /* Needed for trace points definition */
#include "ext4_jbd2.h"
@@ -1521,6 +1524,135 @@ enum {
Opt_dioread_nolock, Opt_dioread_lock,
Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
+ Opt_errors, Opt_data, Opt_data_err, Opt_jqfmt,
+};
+
+static const struct constant_table ext4_param_errors[] = {
+ {"continue", Opt_err_cont},
+ {"panic", Opt_err_panic},
+ {"remount-ro", Opt_err_ro},
+ {}
+};
+
+static const struct constant_table ext4_param_data[] = {
+ {"journal", Opt_data_journal},
+ {"ordered", Opt_data_ordered},
+ {"writeback", Opt_data_writeback},
+ {}
+};
+
+static const struct constant_table ext4_param_data_err[] = {
+ {"abort", Opt_data_err_abort},
+ {"ignore", Opt_data_err_ignore},
+ {}
+};
+
+static const struct constant_table ext4_param_jqfmt[] = {
+ {"vfsold", Opt_jqfmt_vfsold},
+ {"vfsv0", Opt_jqfmt_vfsv0},
+ {"vfsv1", Opt_jqfmt_vfsv1},
+ {}
+};
+
+/* String parameter that allows empty argument */
+#define fsparam_string_empty(NAME, OPT) \
+ __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
+
+/*
+ * Mount option specification
+ * We don't use fsparam_flag_no because of the way we set the
+ * options and the way we show them in _ext4_show_options(). To
+ * keep the changes to a minimum, let's keep the negative options
+ * separate for now.
+ */
+static const struct fs_parameter_spec ext4_param_specs[] = {
+ fsparam_flag ("bsddf", Opt_bsd_df),
+ fsparam_flag ("minixdf", Opt_minix_df),
+ fsparam_flag ("grpid", Opt_grpid),
+ fsparam_flag ("bsdgroups", Opt_grpid),
+ fsparam_flag ("nogrpid", Opt_nogrpid),
+ fsparam_flag ("sysvgroups", Opt_nogrpid),
+ fsparam_u32 ("resgid", Opt_resgid),
+ fsparam_u32 ("resuid", Opt_resuid),
+ fsparam_u32 ("sb", Opt_sb),
+ fsparam_enum ("errors", Opt_errors, ext4_param_errors),
+ fsparam_flag ("nouid32", Opt_nouid32),
+ fsparam_flag ("debug", Opt_debug),
+ fsparam_flag ("oldalloc", Opt_removed),
+ fsparam_flag ("orlov", Opt_removed),
+ fsparam_flag ("user_xattr", Opt_user_xattr),
+ fsparam_flag ("nouser_xattr", Opt_nouser_xattr),
+ fsparam_flag ("acl", Opt_acl),
+ fsparam_flag ("noacl", Opt_noacl),
+ fsparam_flag ("norecovery", Opt_noload),
+ fsparam_flag ("noload", Opt_noload),
+ fsparam_flag ("bh", Opt_removed),
+ fsparam_flag ("nobh", Opt_removed),
+ fsparam_u32 ("commit", Opt_commit),
+ fsparam_u32 ("min_batch_time", Opt_min_batch_time),
+ fsparam_u32 ("max_batch_time", Opt_max_batch_time),
+ fsparam_u32 ("journal_dev", Opt_journal_dev),
+ fsparam_bdev ("journal_path", Opt_journal_path),
+ fsparam_flag ("journal_checksum", Opt_journal_checksum),
+ fsparam_flag ("nojournal_checksum", Opt_nojournal_checksum),
+ fsparam_flag ("journal_async_commit",Opt_journal_async_commit),
+ fsparam_flag ("abort", Opt_abort),
+ fsparam_enum ("data", Opt_data, ext4_param_data),
+ fsparam_enum ("data_err", Opt_data_err,
+ ext4_param_data_err),
+ fsparam_string_empty
+ ("usrjquota", Opt_usrjquota),
+ fsparam_string_empty
+ ("grpjquota", Opt_grpjquota),
+ fsparam_enum ("jqfmt", Opt_jqfmt, ext4_param_jqfmt),
+ fsparam_flag ("grpquota", Opt_grpquota),
+ fsparam_flag ("quota", Opt_quota),
+ fsparam_flag ("noquota", Opt_noquota),
+ fsparam_flag ("usrquota", Opt_usrquota),
+ fsparam_flag ("prjquota", Opt_prjquota),
+ fsparam_flag ("barrier", Opt_barrier),
+ fsparam_u32 ("barrier", Opt_barrier),
+ fsparam_flag ("nobarrier", Opt_nobarrier),
+ fsparam_flag ("i_version", Opt_i_version),
+ fsparam_flag ("dax", Opt_dax),
+ fsparam_u32 ("stripe", Opt_stripe),
+ fsparam_flag ("delalloc", Opt_delalloc),
+ fsparam_flag ("nodelalloc", Opt_nodelalloc),
+ fsparam_flag ("warn_on_error", Opt_warn_on_error),
+ fsparam_flag ("nowarn_on_error", Opt_nowarn_on_error),
+ fsparam_flag ("lazytime", Opt_lazytime),
+ fsparam_flag ("nolazytime", Opt_nolazytime),
+ fsparam_u32 ("debug_want_extra_isize",
+ Opt_debug_want_extra_isize),
+ fsparam_flag ("mblk_io_submit", Opt_removed),
+ fsparam_flag ("nomblk_io_submit", Opt_removed),
+ fsparam_flag ("block_validity", Opt_block_validity),
+ fsparam_flag ("noblock_validity", Opt_noblock_validity),
+ fsparam_u32 ("inode_readahead_blks",
+ Opt_inode_readahead_blks),
+ fsparam_u32 ("journal_ioprio", Opt_journal_ioprio),
+ fsparam_u32 ("auto_da_alloc", Opt_auto_da_alloc),
+ fsparam_flag ("auto_da_alloc", Opt_auto_da_alloc),
+ fsparam_flag ("noauto_da_alloc", Opt_noauto_da_alloc),
+ fsparam_flag ("dioread_nolock", Opt_dioread_nolock),
+ fsparam_flag ("nodioread_nolock", Opt_dioread_lock),
+ fsparam_flag ("dioread_lock", Opt_dioread_lock),
+ fsparam_flag ("discard", Opt_discard),
+ fsparam_flag ("nodiscard", Opt_nodiscard),
+ fsparam_u32 ("init_itable", Opt_init_itable),
+ fsparam_flag ("init_itable", Opt_init_itable),
+ fsparam_flag ("noinit_itable", Opt_noinit_itable),
+ fsparam_u32 ("max_dir_size_kb", Opt_max_dir_size_kb),
+ fsparam_flag ("test_dummy_encryption",
+ Opt_test_dummy_encryption),
+ fsparam_flag ("nombcache", Opt_nombcache),
+ fsparam_flag ("no_mbcache", Opt_nombcache), /* for backward compatibility */
+ fsparam_string ("check", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("nocheck", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("reservation", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("noreservation", Opt_removed), /* mount option from ext2/3 */
+ fsparam_u32 ("journal", Opt_removed), /* mount option from ext2/3 */
+ {}
};

static const match_table_t tokens = {
--
2.21.1

2020-04-28 16:46:26

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 03/17] ext4: move option validation to a separate function

Move option validation out of parse_options() into a separate function
ext4_validate_options().

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index fed2e4cafb38..5f8d21568c8d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,6 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
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 super_block *sb);

/*
* Lock ordering
@@ -2253,10 +2254,9 @@ static int parse_options(char *options, struct super_block *sb,
unsigned int *journal_ioprio,
int is_remount)
{
- struct ext4_sb_info __maybe_unused *sbi = EXT4_SB(sb);
- char *p, __maybe_unused *usr_qf_name, __maybe_unused *grp_qf_name;
substring_t args[MAX_OPT_ARGS];
int token;
+ char *p;

if (!options)
return 1;
@@ -2274,7 +2274,14 @@ static int parse_options(char *options, struct super_block *sb,
journal_ioprio, is_remount) < 0)
return 0;
}
+ return ext4_validate_options(sb);
+}
+
+static int ext4_validate_options(struct super_block *sb)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
#ifdef CONFIG_QUOTA
+ 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
--
2.21.1

2020-04-28 16:46:26

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 04/17] ext4: Change handle_mount_opt() to use fs_parameter

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 236 +++++++++++++++++++++++++++++-------------------
1 file changed, 141 insertions(+), 95 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5f8d21568c8d..2c6fea451d7d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1778,7 +1778,8 @@ static const char deprecated_msg[] =
"Contact [email protected] if you think we should keep it.\n";

#ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
+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);
@@ -1795,7 +1796,7 @@ static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
"ignored when QUOTA feature is enabled");
return 1;
}
- qname = match_strdup(args);
+ qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
if (!qname) {
ext4_msg(sb, KERN_ERR,
"Not enough memory for storing quotafile name");
@@ -1984,35 +1985,49 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
}
#endif

-static int handle_mount_opt(struct super_block *sb, char *opt, int token,
- substring_t *args, unsigned long *journal_devnum,
- unsigned int *journal_ioprio, int is_remount)
+struct ext4_fs_context {
+ unsigned long journal_devnum;
+ unsigned int journal_ioprio;
+};
+
+static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
{
- struct ext4_sb_info *sbi = EXT4_SB(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;
const struct mount_opts *m;
+ struct fs_parse_result result;
kuid_t uid;
kgid_t gid;
- int arg = 0;
+ int token;
+
+ token = fs_parse(fc, ext4_param_specs, param, &result);
+ if (token < 0)
+ return token;

#ifdef CONFIG_QUOTA
- if (token == Opt_usrjquota)
- return set_qf_name(sb, USRQUOTA, &args[0]);
- else if (token == Opt_grpjquota)
- return set_qf_name(sb, GRPQUOTA, &args[0]);
- else if (token == Opt_offusrjquota)
- return clear_qf_name(sb, USRQUOTA);
- else if (token == Opt_offgrpjquota)
- return clear_qf_name(sb, GRPQUOTA);
+ if (token == Opt_usrjquota) {
+ if (!*param->string)
+ return clear_qf_name(sb, USRQUOTA);
+ else
+ return set_qf_name(sb, USRQUOTA, param);
+ } else if (token == Opt_grpjquota) {
+ if (!*param->string)
+ return clear_qf_name(sb, GRPQUOTA);
+ else
+ return set_qf_name(sb, GRPQUOTA, param);
+ }
#endif
switch (token) {
case Opt_noacl:
case Opt_nouser_xattr:
- ext4_msg(sb, KERN_WARNING, deprecated_msg, opt, "3.5");
+ ext4_msg(sb, KERN_WARNING, deprecated_msg, param->key, "3.5");
break;
case Opt_sb:
return 1; /* handled by get_sb_block() */
case Opt_removed:
- ext4_msg(sb, KERN_WARNING, "Ignoring removed %s option", opt);
+ ext4_msg(sb, KERN_WARNING, "Ignoring removed %s option",
+ param->key);
return 1;
case Opt_abort:
sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
@@ -2026,6 +2041,11 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
case Opt_nolazytime:
sb->s_flags &= ~SB_LAZYTIME;
return 1;
+ case Opt_errors:
+ case Opt_data:
+ case Opt_data_err:
+ case Opt_jqfmt:
+ token = result.uint_32;
}

for (m = ext4_mount_opts; m->token != Opt_err; m++)
@@ -2034,25 +2054,23 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,

if (m->token == Opt_err) {
ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" "
- "or missing value", opt);
+ "or missing value", param->key);
return -1;
}

if ((m->flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
ext4_msg(sb, KERN_ERR,
- "Mount option \"%s\" incompatible with ext2", opt);
+ "Mount option \"%s\" incompatible with ext2",
+ param->string);
return -1;
}
if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
ext4_msg(sb, KERN_ERR,
- "Mount option \"%s\" incompatible with ext3", opt);
+ "Mount option \"%s\" incompatible with ext3",
+ param->string);
return -1;
}

- if (args->from && !(m->flags & MOPT_STRING) && match_int(args, &arg))
- return -1;
- if (args->from && (m->flags & MOPT_GTE0) && (arg < 0))
- return -1;
if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
set_opt2(sb, EXPLICIT_DELALLOC);
@@ -2070,115 +2088,104 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
}

if (m->flags & MOPT_NOSUPPORT) {
- ext4_msg(sb, KERN_ERR, "%s option not supported", opt);
+ ext4_msg(sb, KERN_ERR, "%s option not supported",
+ param->key);
} else if (token == Opt_commit) {
- if (arg == 0)
- arg = JBD2_DEFAULT_MAX_COMMIT_AGE;
- else if (arg > INT_MAX / HZ) {
+ if (result.uint_32 == 0)
+ sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
+ else if (result.uint_32 > INT_MAX / HZ) {
ext4_msg(sb, KERN_ERR,
"Invalid commit interval %d, "
"must be smaller than %d",
- arg, INT_MAX / HZ);
+ result.uint_32, INT_MAX / HZ);
return -1;
}
- sbi->s_commit_interval = HZ * arg;
+ sbi->s_commit_interval = HZ * result.uint_32;
} else if (token == Opt_debug_want_extra_isize) {
- if ((arg & 1) ||
- (arg < 4) ||
- (arg > (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
+ if ((result.uint_32 & 1) ||
+ (result.uint_32 < 4) ||
+ (result.uint_32 >
+ (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
ext4_msg(sb, KERN_ERR,
- "Invalid want_extra_isize %d", arg);
+ "Invalid want_extra_isize %d", result.uint_32);
return -1;
}
- sbi->s_want_extra_isize = arg;
+ sbi->s_want_extra_isize = result.uint_32;
} else if (token == Opt_max_batch_time) {
- sbi->s_max_batch_time = arg;
+ sbi->s_max_batch_time = result.uint_32;
} else if (token == Opt_min_batch_time) {
- sbi->s_min_batch_time = arg;
+ sbi->s_min_batch_time = result.uint_32;
} else if (token == Opt_inode_readahead_blks) {
- if (arg && (arg > (1 << 30) || !is_power_of_2(arg))) {
+ if (result.uint_32 &&
+ (result.uint_32 > (1 << 30) ||
+ !is_power_of_2(result.uint_32))) {
ext4_msg(sb, KERN_ERR,
"EXT4-fs: inode_readahead_blks must be "
"0 or a power of 2 smaller than 2^31");
return -1;
}
- sbi->s_inode_readahead_blks = arg;
+ sbi->s_inode_readahead_blks = result.uint_32;
} else if (token == Opt_init_itable) {
set_opt(sb, INIT_INODE_TABLE);
- if (!args->from)
- arg = EXT4_DEF_LI_WAIT_MULT;
- sbi->s_li_wait_mult = arg;
+ sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+ if (param->type == fs_value_is_string)
+ sbi->s_li_wait_mult = result.uint_32;
} else if (token == Opt_max_dir_size_kb) {
- sbi->s_max_dir_size_kb = arg;
+ sbi->s_max_dir_size_kb = result.uint_32;
} else if (token == Opt_stripe) {
- sbi->s_stripe = arg;
+ sbi->s_stripe = result.uint_32;
} else if (token == Opt_resuid) {
- uid = make_kuid(current_user_ns(), arg);
+ uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
- ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
+ ext4_msg(sb, KERN_ERR, "Invalid uid value %d",
+ result.uint_32);
return -1;
}
sbi->s_resuid = uid;
} else if (token == Opt_resgid) {
- gid = make_kgid(current_user_ns(), arg);
+ gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid)) {
- ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+ ext4_msg(sb, KERN_ERR, "Invalid gid value %d",
+ result.uint_32);
return -1;
}
sbi->s_resgid = gid;
} else if (token == Opt_journal_dev) {
- if (is_remount) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(sb, KERN_ERR,
"Cannot specify journal on remount");
return -1;
}
- *journal_devnum = arg;
+ ctx->journal_devnum = result.uint_32;
} else if (token == Opt_journal_path) {
- char *journal_path;
struct inode *journal_inode;
struct path path;
int error;

- if (is_remount) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(sb, KERN_ERR,
"Cannot specify journal on remount");
return -1;
}
- journal_path = match_strdup(&args[0]);
- if (!journal_path) {
- ext4_msg(sb, KERN_ERR, "error: could not dup "
- "journal device string");
- return -1;
- }

- error = kern_path(journal_path, LOOKUP_FOLLOW, &path);
+ error = fs_lookup_param(fc, param, 1, &path);
if (error) {
ext4_msg(sb, KERN_ERR, "error: could not find "
- "journal device path: error %d", error);
- kfree(journal_path);
+ "journal device path");
return -1;
}

journal_inode = d_inode(path.dentry);
- if (!S_ISBLK(journal_inode->i_mode)) {
- ext4_msg(sb, KERN_ERR, "error: journal path %s "
- "is not a block device", journal_path);
- path_put(&path);
- kfree(journal_path);
- return -1;
- }
-
- *journal_devnum = new_encode_dev(journal_inode->i_rdev);
+ ctx->journal_devnum = new_encode_dev(journal_inode->i_rdev);
path_put(&path);
- kfree(journal_path);
} else if (token == Opt_journal_ioprio) {
- if (arg > 7) {
+ if (result.uint_32 > 7) {
ext4_msg(sb, KERN_ERR, "Invalid journal IO priority"
" (must be 0-7)");
return -1;
}
- *journal_ioprio =
- IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
+ ctx->journal_ioprio =
+ IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
} else if (token == Opt_test_dummy_encryption) {
#ifdef CONFIG_FS_ENCRYPTION
sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
@@ -2189,7 +2196,7 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
"Test dummy encryption mount option ignored");
#endif
} else if (m->flags & MOPT_DATAJ) {
- if (is_remount) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
if (!sbi->s_journal)
ext4_msg(sb, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
@@ -2231,17 +2238,22 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
} else if (token == Opt_data_err_ignore) {
sbi->s_mount_opt &= ~m->mount_opt;
} else {
- if (!args->from)
- arg = 1;
+ unsigned int set = 0;
+
+ if ((param->type == fs_value_is_flag) ||
+ result.uint_32 > 0)
+ set = 1;
+
if (m->flags & MOPT_CLEAR)
- arg = !arg;
+ set = !set;
else if (unlikely(!(m->flags & MOPT_SET))) {
ext4_msg(sb, KERN_WARNING,
- "buggy handling of option %s", opt);
+ "buggy handling of option %s",
+ param->key);
WARN_ON(1);
return -1;
}
- if (arg != 0)
+ if (set != 0)
sbi->s_mount_opt |= m->mount_opt;
else
sbi->s_mount_opt &= ~m->mount_opt;
@@ -2254,26 +2266,60 @@ static int parse_options(char *options, struct super_block *sb,
unsigned int *journal_ioprio,
int is_remount)
{
- substring_t args[MAX_OPT_ARGS];
- int token;
- char *p;
+ struct ext4_fs_context ctx;
+ struct fs_parameter param;
+ struct fs_context fc;
+ int ret;
+ char *key;

if (!options)
return 1;

- while ((p = strsep(&options, ",")) != NULL) {
- if (!*p)
- continue;
- /*
- * Initialize args struct so we know whether arg was
- * found; some options take optional arguments.
- */
- args[0].to = args[0].from = NULL;
- token = match_token(p, tokens, args);
- if (handle_mount_opt(sb, p, token, args, journal_devnum,
- journal_ioprio, is_remount) < 0)
- return 0;
+ memset(&fc, 0, sizeof(fc));
+ memset(&ctx, 0, sizeof(ctx));
+ fc.fs_private = &ctx;
+ fc.s_fs_info = EXT4_SB(sb);
+
+ if (is_remount)
+ fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
+
+ while ((key = strsep(&options, ",")) != NULL) {
+ if (*key) {
+ size_t v_len = 0;
+ char *value = strchr(key, '=');
+
+ param.type = fs_value_is_flag;
+ param.string = NULL;
+
+ if (value) {
+ if (value == key)
+ continue;
+
+ *value++ = 0;
+ v_len = strlen(value);
+ param.string = kmemdup_nul(value, v_len,
+ GFP_KERNEL);
+ if (!param.string)
+ return 0;
+ param.type = fs_value_is_string;
+ }
+
+ param.key = key;
+ param.size = v_len;
+
+ ret = handle_mount_opt(&fc, &param);
+ if (param.string)
+ kfree(param.string);
+ if (ret < 0)
+ return 0;
+ }
}
+
+ if (journal_devnum)
+ *journal_devnum = ctx.journal_devnum;
+ if (journal_ioprio)
+ *journal_ioprio = ctx.journal_ioprio;
+
return ext4_validate_options(sb);
}

--
2.21.1

2020-04-28 16:46:30

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 07/17] ext4: check ext2/3 compatibility outside handle_mount_opt()

At the parsing phase of mount in the new mount api sb will not be
available so move ext2/3 compatibility check outside handle_mount_opt().
Unfortunately we will lose the ability to show exactly which option is
not compatible.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 41 +++++++++++++++++++++++++----------------
1 file changed, 25 insertions(+), 16 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index ae0ef04b3be4..5317ab324033 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -89,8 +89,8 @@ 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 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);

@@ -1931,6 +1931,7 @@ struct ext4_fs_context {
unsigned short qname_spec;
unsigned long journal_devnum;
unsigned int journal_ioprio;
+ unsigned int opt_flags; /* MOPT flags */
};

#ifdef CONFIG_QUOTA
@@ -2050,25 +2051,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
if (token == m->token)
break;

+ ctx->opt_flags |= m->flags;
+
if (m->token == Opt_err) {
ext4_msg(NULL, KERN_ERR, "Unrecognized mount option \"%s\" "
"or missing value", param->key);
return -EINVAL;
}

- if ((m->flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
- ext4_msg(NULL, KERN_ERR,
- "Mount option \"%s\" incompatible with ext2",
- param->string);
- return -EINVAL;
- }
- if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
- ext4_msg(NULL, KERN_ERR,
- "Mount option \"%s\" incompatible with ext3",
- param->string);
- return -EINVAL;
- }
-
if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
set_opt2(sb, EXPLICIT_DELALLOC);
@@ -2305,7 +2295,7 @@ static int parse_options(char *options, struct super_block *sb,
if (ret < 0)
return 0;

- ret = ext4_check_quota_consistency(&fc, sb);
+ ret = ext4_check_opt_consistency(&fc, sb);
if (ret < 0)
return 0;

@@ -2398,6 +2388,25 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
#endif
}

+static int ext4_check_opt_consistency(struct fs_context *fc,
+ struct super_block *sb)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
+
+ if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
+ ext4_msg(NULL, KERN_ERR,
+ "Mount option(s) incompatible with ext2");
+ return -EINVAL;
+ }
+ if ((ctx->opt_flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
+ ext4_msg(NULL, KERN_ERR,
+ "Mount option(s) incompatible with ext3");
+ return -EINVAL;
+ }
+
+ return ext4_check_quota_consistency(fc, sb);
+}
+
static int ext4_validate_options(struct fs_context *fc)
{
struct ext4_sb_info *sbi = fc->s_fs_info;
--
2.21.1

2020-04-28 16:46:33

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

At the parsing phase of mount in the new mount api sb will not be
available so allow sb to be NULL in ext4_msg and use that in
handle_mount_opt().

Also change return value to appropriate -EINVAL where needed.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 120 +++++++++++++++++++++++++-----------------------
1 file changed, 63 insertions(+), 57 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2c6fea451d7d..2f7e49bfbf71 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,7 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
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 super_block *sb);
+static int ext4_validate_options(struct fs_context *fc);

/*
* Lock ordering
@@ -754,13 +754,14 @@ void __ext4_msg(struct super_block *sb,
struct va_format vaf;
va_list args;

- if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
+ if (sb &&
+ !___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
return;

va_start(args, fmt);
vaf.fmt = fmt;
vaf.va = &args;
- printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
+ printk("%sEXT4-fs (%s): %pV\n", prefix, sb ? sb->s_id : "n/a", &vaf);
va_end(args);
}

@@ -2021,12 +2022,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
switch (token) {
case Opt_noacl:
case Opt_nouser_xattr:
- ext4_msg(sb, KERN_WARNING, deprecated_msg, param->key, "3.5");
+ ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
break;
case Opt_sb:
return 1; /* handled by get_sb_block() */
case Opt_removed:
- ext4_msg(sb, KERN_WARNING, "Ignoring removed %s option",
+ ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
param->key);
return 1;
case Opt_abort:
@@ -2053,22 +2054,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
break;

if (m->token == Opt_err) {
- ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" "
+ ext4_msg(NULL, KERN_ERR, "Unrecognized mount option \"%s\" "
"or missing value", param->key);
- return -1;
+ return -EINVAL;
}

if ((m->flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Mount option \"%s\" incompatible with ext2",
param->string);
- return -1;
+ return -EINVAL;
}
if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Mount option \"%s\" incompatible with ext3",
param->string);
- return -1;
+ return -EINVAL;
}

if (m->flags & MOPT_EXPLICIT) {
@@ -2077,28 +2078,28 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
} else
- return -1;
+ return -EINVAL;
}
if (m->flags & MOPT_CLEAR_ERR)
clear_opt(sb, ERRORS_MASK);
if (token == Opt_noquota && sb_any_quota_loaded(sb)) {
- ext4_msg(sb, KERN_ERR, "Cannot change quota "
+ ext4_msg(NULL, KERN_ERR, "Cannot change quota "
"options when quota turned on");
- return -1;
+ return -EINVAL;
}

if (m->flags & MOPT_NOSUPPORT) {
- ext4_msg(sb, KERN_ERR, "%s option not supported",
+ 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;
else if (result.uint_32 > INT_MAX / HZ) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Invalid commit interval %d, "
"must be smaller than %d",
result.uint_32, INT_MAX / HZ);
- return -1;
+ return -EINVAL;
}
sbi->s_commit_interval = HZ * result.uint_32;
} else if (token == Opt_debug_want_extra_isize) {
@@ -2106,9 +2107,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
(result.uint_32 < 4) ||
(result.uint_32 >
(sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Invalid want_extra_isize %d", result.uint_32);
- return -1;
+ return -EINVAL;
}
sbi->s_want_extra_isize = result.uint_32;
} else if (token == Opt_max_batch_time) {
@@ -2119,10 +2120,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
if (result.uint_32 &&
(result.uint_32 > (1 << 30) ||
!is_power_of_2(result.uint_32))) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"EXT4-fs: inode_readahead_blks must be "
"0 or a power of 2 smaller than 2^31");
- return -1;
+ return -EINVAL;
}
sbi->s_inode_readahead_blks = result.uint_32;
} else if (token == Opt_init_itable) {
@@ -2137,24 +2138,24 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
} else if (token == Opt_resuid) {
uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
- ext4_msg(sb, KERN_ERR, "Invalid uid value %d",
+ ext4_msg(NULL, KERN_ERR, "Invalid uid value %d",
result.uint_32);
- return -1;
+ return -EINVAL;
}
sbi->s_resuid = uid;
} else if (token == Opt_resgid) {
gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid)) {
- ext4_msg(sb, KERN_ERR, "Invalid gid value %d",
+ ext4_msg(NULL, KERN_ERR, "Invalid gid value %d",
result.uint_32);
- return -1;
+ return -EINVAL;
}
sbi->s_resgid = gid;
} else if (token == Opt_journal_dev) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Cannot specify journal on remount");
- return -1;
+ return -EINVAL;
}
ctx->journal_devnum = result.uint_32;
} else if (token == Opt_journal_path) {
@@ -2163,16 +2164,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
int error;

if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Cannot specify journal on remount");
- return -1;
+ return -EINVAL;
}

error = fs_lookup_param(fc, param, 1, &path);
if (error) {
- ext4_msg(sb, KERN_ERR, "error: could not find "
+ ext4_msg(NULL, KERN_ERR, "error: could not find "
"journal device path");
- return -1;
+ return -EINVAL;
}

journal_inode = d_inode(path.dentry);
@@ -2180,29 +2181,29 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
path_put(&path);
} else if (token == Opt_journal_ioprio) {
if (result.uint_32 > 7) {
- ext4_msg(sb, KERN_ERR, "Invalid journal IO priority"
+ ext4_msg(NULL, KERN_ERR, "Invalid journal IO priority"
" (must be 0-7)");
- return -1;
+ return -EINVAL;
}
ctx->journal_ioprio =
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
} else if (token == Opt_test_dummy_encryption) {
#ifdef CONFIG_FS_ENCRYPTION
sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
- ext4_msg(sb, KERN_WARNING,
+ ext4_msg(NULL, KERN_WARNING,
"Test dummy encryption mode enabled");
#else
- ext4_msg(sb, KERN_WARNING,
+ ext4_msg(NULL, KERN_WARNING,
"Test dummy encryption mount option ignored");
#endif
} else if (m->flags & MOPT_DATAJ) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
if (!sbi->s_journal)
- ext4_msg(sb, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
+ 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(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Cannot change data mode on remount");
- return -1;
+ return -EINVAL;
}
} else {
clear_opt(sb, DATA_FLAGS);
@@ -2212,12 +2213,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
} else if (m->flags & MOPT_QFMT) {
if (sb_any_quota_loaded(sb) &&
sbi->s_jquota_fmt != m->mount_opt) {
- ext4_msg(sb, KERN_ERR, "Cannot change journaled "
+ ext4_msg(NULL, KERN_ERR, "Cannot change journaled "
"quota options when quota turned on");
- return -1;
+ return -EINVAL;
}
if (ext4_has_feature_quota(sb)) {
- ext4_msg(sb, KERN_INFO,
+ ext4_msg(NULL, KERN_INFO,
"Quota format mount options ignored "
"when QUOTA feature is enabled");
return 1;
@@ -2226,12 +2227,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
#endif
} else if (token == Opt_dax) {
#ifdef CONFIG_FS_DAX
- ext4_msg(sb, KERN_WARNING,
+ ext4_msg(NULL, KERN_WARNING,
"DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
sbi->s_mount_opt |= m->mount_opt;
#else
- ext4_msg(sb, KERN_INFO, "dax option not supported");
- return -1;
+ ext4_msg(NULL, KERN_INFO, "dax option not supported");
+ return -EINVAL;
#endif
} else if (token == Opt_data_err_abort) {
sbi->s_mount_opt |= m->mount_opt;
@@ -2247,11 +2248,11 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
if (m->flags & MOPT_CLEAR)
set = !set;
else if (unlikely(!(m->flags & MOPT_SET))) {
- ext4_msg(sb, KERN_WARNING,
+ ext4_msg(NULL, KERN_WARNING,
"buggy handling of option %s",
param->key);
WARN_ON(1);
- return -1;
+ return -EINVAL;
}
if (set != 0)
sbi->s_mount_opt |= m->mount_opt;
@@ -2320,12 +2321,17 @@ static int parse_options(char *options, struct super_block *sb,
if (journal_ioprio)
*journal_ioprio = ctx.journal_ioprio;

- return ext4_validate_options(sb);
+ ret = ext4_validate_options(&fc);
+ if (ret < 0)
+ return 0;
+
+ return 1;
}

-static int ext4_validate_options(struct super_block *sb)
+static int ext4_validate_options(struct fs_context *fc)
{
- struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_sb_info *sbi = fc->s_fs_info;
+ struct super_block *sb = sbi->s_sb;
#ifdef CONFIG_QUOTA
char *usr_qf_name, *grp_qf_name;
/*
@@ -2334,9 +2340,9 @@ static int ext4_validate_options(struct super_block *sb)
* to support legacy quotas in quota files.
*/
if (test_opt(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
- ext4_msg(sb, KERN_ERR, "Project quota feature not enabled. "
+ ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
"Cannot enable project quota enforcement.");
- return 0;
+ return -EINVAL;
}
usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
@@ -2348,15 +2354,15 @@ static int ext4_validate_options(struct super_block *sb)
clear_opt(sb, GRPQUOTA);

if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
- ext4_msg(sb, KERN_ERR, "old and new quota "
+ ext4_msg(NULL, KERN_ERR, "old and new quota "
"format mixing");
- return 0;
+ return -EINVAL;
}

if (!sbi->s_jquota_fmt) {
- ext4_msg(sb, KERN_ERR, "journaled quota format "
+ ext4_msg(NULL, KERN_ERR, "journaled quota format "
"not specified");
- return 0;
+ return -EINVAL;
}
}
#endif
@@ -2364,11 +2370,11 @@ static int ext4_validate_options(struct super_block *sb)
int blocksize =
BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
if (blocksize < PAGE_SIZE)
- ext4_msg(sb, KERN_WARNING, "Warning: mounting with an "
+ ext4_msg(NULL, KERN_WARNING, "Warning: mounting with an "
"experimental mount option 'dioread_nolock' "
"for blocksize < PAGE_SIZE");
}
- return 1;
+ return 0;
}

static inline void ext4_show_quota_options(struct seq_file *seq,
--
2.21.1

2020-04-28 16:46:34

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 09/17] ext4: parse Opt_sb in handle_mount_opt()

Currently the sb block is parsed from within ext4_fill_super(), however
since the new mount api separates the option parsing and super block setup
into two distinct steps we need to move the parsing of sb block into
handle_mount_opt().

In preparation for the next step we also need to refactor ext4_fill_super
so that we can parse options separately. Unfortunately we still need to
parse options specified in the super block itself and that needs to be
done after we've read the super block from disk. Another complication is
that we really want to apply the options from super block before we
apply user specified mount options.

So with this patch we're going through the following sequence:

- parse user provided options (including sb block)
- initialize sbi and store s_sb_block if provided
- in __ext4_fill_super()
- read the super block
- parse and apply options specified in s_mount_opts
- check and apply user provided options stored in ctx
- continue with the regular ext4_fill_super operation

It's ugly, but if we still want to support s_mount_opts we have to do it
in this order. __ext4_fill_super would really benefit from some more
refactoring, but that will have to wait until after the mount api
conversion is done.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 291 +++++++++++++++++++++++++++++++++---------------
1 file changed, 199 insertions(+), 92 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 3b29069eb633..ea7c8f7d4c7c 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1753,29 +1753,6 @@ static const match_table_t tokens = {
{Opt_err, NULL},
};

-static ext4_fsblk_t get_sb_block(void **data)
-{
- ext4_fsblk_t sb_block;
- char *options = (char *) *data;
-
- if (!options || strncmp(options, "sb=", 3) != 0)
- return 1; /* Default location */
-
- options += 3;
- /* TODO: use simple_strtoll with >32bit ext4 */
- sb_block = simple_strtoul(options, &options, 0);
- if (*options && *options != ',') {
- printk(KERN_ERR "EXT4-fs: Invalid sb specification: %s\n",
- (char *) *data);
- return 1;
- }
- if (*options == ',')
- options++;
- *data = (void *) options;
-
- return sb_block;
-}
-
#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
static const char deprecated_msg[] =
"Mount option \"%s\" will be removed by %s\n"
@@ -1852,6 +1829,7 @@ static const struct mount_opts {
{Opt_stripe, 0, MOPT_GTE0},
{Opt_resuid, 0, MOPT_GTE0},
{Opt_resgid, 0, MOPT_GTE0},
+ {Opt_sb, 0, MOPT_GTE0},
{Opt_journal_dev, 0, MOPT_NO_EXT2 | MOPT_GTE0},
{Opt_journal_path, 0, MOPT_NO_EXT2 | MOPT_STRING},
{Opt_journal_ioprio, 0, MOPT_NO_EXT2 | MOPT_GTE0},
@@ -1940,6 +1918,7 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
#define EXT4_SPEC_s_resuid (1 << 13)
#define EXT4_SPEC_s_resgid (1 << 14)
#define EXT4_SPEC_s_commit_interval (1 << 15)
+#define EXT4_SPEC_s_sb_block (1 << 16)

struct ext4_fs_context {
char *s_qf_names[EXT4_MAXQUOTAS];
@@ -1967,6 +1946,7 @@ struct ext4_fs_context {
u32 s_min_batch_time;
kuid_t s_resuid;
kgid_t s_resgid;
+ ext4_fsblk_t s_sb_block;
};

#ifdef CONFIG_QUOTA
@@ -2079,8 +2059,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
case Opt_nouser_xattr:
ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
break;
- case Opt_sb:
- return 1; /* handled by get_sb_block() */
case Opt_removed:
ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
param->key);
@@ -2198,6 +2176,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
ctx->s_resgid = gid;
ctx->spec |= EXT4_SPEC_s_resgid;
+ } else if (token == Opt_sb) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Ignoring %s option on remount", param->key);
+ } else {
+ ctx->s_sb_block = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_sb_block;
+ }
} else if (token == Opt_journal_dev) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(NULL, KERN_ERR,
@@ -2292,27 +2278,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
return 1;
}

-static int parse_options(char *options, struct super_block *sb,
- unsigned long *journal_devnum,
- unsigned int *journal_ioprio,
- int is_remount)
+static int parse_options(struct fs_context *fc, char *options)
{
- struct ext4_fs_context ctx;
struct fs_parameter param;
- struct fs_context fc;
int ret;
char *key;

if (!options)
- return 1;
-
- memset(&fc, 0, sizeof(fc));
- memset(&ctx, 0, sizeof(ctx));
- fc.fs_private = &ctx;
- fc.s_fs_info = EXT4_SB(sb);
-
- if (is_remount)
- fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
+ return 0;

while ((key = strsep(&options, ",")) != NULL) {
if (*key) {
@@ -2331,36 +2304,72 @@ static int parse_options(char *options, struct super_block *sb,
param.string = kmemdup_nul(value, v_len,
GFP_KERNEL);
if (!param.string)
- return 0;
+ return -ENOMEM;
param.type = fs_value_is_string;
}

param.key = key;
param.size = v_len;

- ret = handle_mount_opt(&fc, &param);
+ ret = handle_mount_opt(fc, &param);
if (param.string)
kfree(param.string);
if (ret < 0)
- return 0;
+ return ret;
}
}

- ret = ext4_validate_options(&fc);
+ ret = ext4_validate_options(fc);
if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int parse_apply_options(char *options, struct super_block *sb,
+ unsigned long *journal_devnum,
+ unsigned int *journal_ioprio,
+ int is_remount)
+{
+ struct ext4_fs_context *ctx;
+ struct fs_context *fc;
+ int ret = -ENOMEM;
+
+ if (!options)
return 0;

- ret = ext4_check_opt_consistency(&fc, sb);
+ fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
+ if (!fc)
+ return ret;
+ ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
+ if (!ctx)
+ goto out_free;
+
+ fc->fs_private = ctx;
+ fc->s_fs_info = EXT4_SB(sb);
+
+ if (is_remount)
+ fc->purpose = FS_CONTEXT_FOR_RECONFIGURE;
+
+ ret = parse_options(fc, options);
if (ret < 0)
- return 0;
+ goto out_free;

- if (ctx.spec & EXT4_SPEC_JOURNAL_DEV)
- *journal_devnum = ctx.journal_devnum;
- if (ctx.spec & EXT4_SPEC_JOURNAL_IOPRIO)
- *journal_ioprio = ctx.journal_ioprio;
+ ret = ext4_check_opt_consistency(fc, sb);
+ if (ret < 0)
+ goto out_free;

- ext4_apply_options(&fc, sb);
- return 1;
+ if (ctx->spec & EXT4_SPEC_JOURNAL_DEV)
+ *journal_devnum = ctx->journal_devnum;
+ if (ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
+ *journal_ioprio = ctx->journal_ioprio;
+
+ ext4_apply_options(fc, sb);
+
+out_free:
+ kfree(ctx);
+ kfree(fc);
+ return ret;
}

static void ext4_apply_quota_options(struct fs_context *fc,
@@ -4094,16 +4103,49 @@ static void ext4_set_resv_clusters(struct super_block *sb)
atomic64_set(&sbi->s_resv_clusters, resv_clusters);
}

-static int ext4_fill_super(struct super_block *sb, void *data, int silent)
+static void ext4_free_sbi(struct ext4_sb_info *sbi)
+{
+ if (!sbi)
+ return;
+
+ kfree(sbi->s_blockgroup_lock);
+ fs_put_dax(sbi->s_daxdev);
+ kfree(sbi);
+}
+
+static struct ext4_sb_info *ext4_alloc_sbi(struct super_block *sb)
+{
+ struct ext4_sb_info *sbi;
+
+ sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+ if (!sbi)
+ return NULL;
+
+ sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev);
+
+ sbi->s_blockgroup_lock =
+ kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
+
+ if (!sbi->s_blockgroup_lock)
+ goto err_out;
+
+ sb->s_fs_info = sbi;
+ sbi->s_sb = sb;
+ return sbi;
+err_out:
+ fs_put_dax(sbi->s_daxdev);
+ kfree(sbi);
+ return NULL;
+}
+
+static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb,
+ int silent)
{
- struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
- char *orig_data = kstrdup(data, GFP_KERNEL);
struct buffer_head *bh, **group_desc;
struct ext4_super_block *es = NULL;
- struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
struct flex_groups **flex_groups;
ext4_fsblk_t block;
- ext4_fsblk_t sb_block = get_sb_block(&data);
ext4_fsblk_t logical_sb_block;
unsigned long offset = 0;
unsigned long journal_devnum = 0;
@@ -4119,26 +4161,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
int err = 0;
unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
ext4_group_t first_not_zeroed;
+ struct ext4_fs_context *ctx = fc->fs_private;

- if ((data && !orig_data) || !sbi)
- goto out_free_base;
-
- sbi->s_daxdev = dax_dev;
- sbi->s_blockgroup_lock =
- kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
- if (!sbi->s_blockgroup_lock)
- goto out_free_base;
-
- sb->s_fs_info = sbi;
- sbi->s_sb = sb;
sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
- sbi->s_sb_block = sb_block;
if (sb->s_bdev->bd_part)
sbi->s_sectors_written_start =
part_stat_read(sb->s_bdev->bd_part, sectors[STAT_WRITE]);

- /* Cleanup superblock name */
- strreplace(sb->s_id, '/', '!');

/* -EINVAL is default */
ret = -EINVAL;
@@ -4153,10 +4182,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
* block sizes. We need to calculate the offset from buffer start.
*/
if (blocksize != EXT4_MIN_BLOCK_SIZE) {
- logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
+ logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
offset = do_div(logical_sb_block, blocksize);
} else {
- logical_sb_block = sb_block;
+ logical_sb_block = sbi->s_sb_block;
}

if (!(bh = sb_bread_unmovable(sb, logical_sb_block))) {
@@ -4354,8 +4383,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
GFP_KERNEL);
if (!s_mount_opts)
goto failed_mount;
- if (!parse_options(s_mount_opts, sb, &journal_devnum,
- &journal_ioprio, 0)) {
+ if (parse_apply_options(s_mount_opts, sb, &journal_devnum,
+ &journal_ioprio, 0) < 0) {
ext4_msg(sb, KERN_WARNING,
"failed to parse options in superblock: %s",
s_mount_opts);
@@ -4363,10 +4392,19 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
kfree(s_mount_opts);
}
sbi->s_def_mount_opt = sbi->s_mount_opt;
- if (!parse_options((char *) data, sb, &journal_devnum,
- &journal_ioprio, 0))
+
+ /* Now check and apply options we've got in fs context */
+ err = ext4_check_opt_consistency(fc, sb);
+ if (err < 0)
goto failed_mount;

+ if (ctx->spec & EXT4_SPEC_JOURNAL_DEV)
+ journal_devnum = ctx->journal_devnum;
+ if (ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
+ journal_ioprio = ctx->journal_ioprio;
+
+ ext4_apply_options(fc, sb);
+
#ifdef CONFIG_UNICODE
if (ext4_has_feature_casefold(sb) && !sbi->s_encoding) {
const struct ext4_sb_encodings *encoding_info;
@@ -4555,7 +4593,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
}

brelse(bh);
- logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
+ logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
offset = do_div(logical_sb_block, blocksize);
bh = sb_bread_unmovable(sb, logical_sb_block);
if (!bh) {
@@ -5126,11 +5164,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
}

if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
- ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
- "Opts: %.*s%s%s", descr,
- (int) sizeof(sbi->s_es->s_mount_opts),
- sbi->s_es->s_mount_opts,
- *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
+ ext4_msg(sb, KERN_INFO, "mounted filesystem with%s.", descr);

if (es->s_error_count)
mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
@@ -5140,7 +5174,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);

- kfree(orig_data);
return 0;

cantfind_ext4:
@@ -5219,14 +5252,89 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
brelse(bh);
out_fail:
sb->s_fs_info = NULL;
- kfree(sbi->s_blockgroup_lock);
-out_free_base:
- kfree(sbi);
- kfree(orig_data);
- fs_put_dax(dax_dev);
return err ? err : ret;
}

+static void cleanup_ctx(struct ext4_fs_context *ctx)
+{
+ int i;
+
+ if (!ctx)
+ return;
+
+ for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+ kfree(ctx->s_qf_names[i]);
+ }
+}
+
+static int ext4_fill_super(struct super_block *sb, void *data, int silent)
+{
+ struct ext4_fs_context ctx;
+ struct ext4_sb_info *sbi;
+ struct fs_context fc;
+ const char *descr;
+ char *orig_data;
+ int ret = -ENOMEM;
+
+ orig_data = kstrdup(data, GFP_KERNEL);
+ if (data && !orig_data)
+ return -ENOMEM;
+
+ /* Cleanup superblock name */
+ strreplace(sb->s_id, '/', '!');
+
+ memset(&fc, 0, sizeof(fc));
+ memset(&ctx, 0, sizeof(ctx));
+ fc.fs_private = &ctx;
+
+ ret = parse_options(&fc, (char *) data);
+ if (ret < 0)
+ goto free_data;
+
+ sbi = ext4_alloc_sbi(sb);
+ if (!sbi) {
+ ret = -ENOMEM;
+ goto free_data;
+ }
+
+ fc.s_fs_info = sbi;
+
+ sbi->s_sb_block = 1; /* Default super block location */
+ if (ctx.spec & EXT4_SPEC_s_sb_block)
+ sbi->s_sb_block = ctx.s_sb_block;
+
+ ret = __ext4_fill_super(&fc, sb, silent);
+ if (ret < 0)
+ goto free_sbi;
+
+ if (EXT4_SB(sb)->s_journal) {
+ if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
+ descr = " journalled data mode";
+ else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
+ descr = " ordered data mode";
+ else
+ descr = " writeback data mode";
+ } else
+ descr = "out journal";
+
+ if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
+ ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
+ "Opts: %.*s%s%s", descr,
+ (int) sizeof(sbi->s_es->s_mount_opts),
+ sbi->s_es->s_mount_opts,
+ *sbi->s_es->s_mount_opts ? "; " : "", (char *)orig_data);
+
+ kfree(orig_data);
+ cleanup_ctx(&ctx);
+ return 0;
+free_sbi:
+ ext4_free_sbi(sbi);
+free_data:
+ kfree(orig_data);
+ cleanup_ctx(&ctx);
+ return ret;
+}
+
/*
* Setup any per-fs journal parameters now. We'll do this both on
* initial mount, once the journal has been initialised but before we've
@@ -5822,10 +5930,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_journal && sbi->s_journal->j_task->io_context)
journal_ioprio = sbi->s_journal->j_task->io_context->ioprio;

- if (!parse_options(data, sb, NULL, &journal_ioprio, 1)) {
- err = -EINVAL;
+ err = parse_apply_options(data, sb, NULL, &journal_ioprio, 1);
+ if (err < 0)
goto restore_opts;
- }

if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
test_opt(sb, JOURNAL_CHECKSUM)) {
--
2.21.1

2020-04-28 16:46:39

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 10/17] ext4: clean up return values in handle_mount_opt()

Clean up return values in handle_mount_opt() and rename the function to
ext4_parse_param()

Now we can use it in fs_context_operations as .parse_param.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index ea7c8f7d4c7c..81238dddc3b7 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -92,6 +92,7 @@ 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_options(struct fs_context *fc, struct super_block *sb);
+static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);

/*
* Lock ordering
@@ -121,6 +122,10 @@ static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
* transaction start -> page lock(s) -> i_data_sem (rw)
*/

+static const struct fs_context_operations ext4_context_ops = {
+ .parse_param = ext4_parse_param,
+};
+
#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
static struct file_system_type ext2_fs_type = {
.owner = THIS_MODULE,
@@ -2028,7 +2033,7 @@ EXT4_SET_CTX(mount_opt2);
EXT4_SET_CTX(mount_flags);


-static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
+static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
struct ext4_fs_context *ctx = fc->fs_private;
const struct mount_opts *m;
@@ -2062,19 +2067,19 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
case Opt_removed:
ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
param->key);
- return 1;
+ return 0;
case Opt_abort:
set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
- return 1;
+ return 0;
case Opt_i_version:
set_flags(ctx, SB_I_VERSION);
- return 1;
+ return 0;
case Opt_lazytime:
set_flags(ctx, SB_LAZYTIME);
- return 1;
+ return 0;
case Opt_nolazytime:
clear_flags(ctx, SB_LAZYTIME);
- return 1;
+ return 0;
case Opt_errors:
case Opt_data:
case Opt_data_err:
@@ -2275,7 +2280,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
else
clear_mount_opt(ctx, m->mount_opt);
}
- return 1;
+ return 0;
}

static int parse_options(struct fs_context *fc, char *options)
@@ -2311,7 +2316,7 @@ static int parse_options(struct fs_context *fc, char *options)
param.key = key;
param.size = v_len;

- ret = handle_mount_opt(fc, &param);
+ ret = ext4_parse_param(fc, &param);
if (param.string)
kfree(param.string);
if (ret < 0)
--
2.21.1

2020-04-28 16:46:39

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 11/17] ext4: add ext4_get_tree for the new mount API

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 81238dddc3b7..729a07c298e2 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -93,6 +93,7 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb);
static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
+static int ext4_get_tree(struct fs_context *fc);

/*
* Lock ordering
@@ -124,6 +125,7 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);

static const struct fs_context_operations ext4_context_ops = {
.parse_param = ext4_parse_param,
+ .get_tree = ext4_get_tree,
};

#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
@@ -5340,6 +5342,42 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
return ret;
}

+static int ext4_fill_super_fc(struct super_block *sb, struct fs_context *fc)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
+ struct ext4_sb_info *sbi;
+ int ret;
+
+ sbi = ext4_alloc_sbi(sb);
+ if (!sbi)
+ return -ENOMEM;
+
+ fc->s_fs_info = sbi;
+
+ /* Cleanup superblock name */
+ strreplace(sb->s_id, '/', '!');
+
+ sbi->s_sb_block = 1; /* Default super block location */
+ if (ctx->spec & EXT4_SPEC_s_sb_block)
+ sbi->s_sb_block = ctx->s_sb_block;
+
+ ret = __ext4_fill_super(fc, sb, fc->sb_flags & SB_SILENT);
+ if (ret < 0)
+ goto free_sbi;
+
+ return 0;
+
+free_sbi:
+ ext4_free_sbi(sbi);
+ fc->s_fs_info = NULL;
+ return ret;
+}
+
+static int ext4_get_tree(struct fs_context *fc)
+{
+ return get_tree_bdev(fc, ext4_fill_super_fc);
+}
+
/*
* Setup any per-fs journal parameters now. We'll do this both on
* initial mount, once the journal has been initialised but before we've
--
2.21.1

2020-04-28 16:46:38

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 08/17] ext4: get rid of super block and sbi from handle_mount_ops()

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 ged rid of the rest of it.

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

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 379 ++++++++++++++++++++++++++++++++++--------------
1 file changed, 272 insertions(+), 107 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5317ab324033..3b29069eb633 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -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 void ext4_apply_options(struct fs_context *fc, struct super_block *sb);

/*
* Lock ordering
@@ -1925,13 +1924,49 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
}
#endif

+#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_s_want_extra_isize (1 << 6)
+#define EXT4_SPEC_s_max_batch_time (1 << 7)
+#define EXT4_SPEC_s_min_batch_time (1 << 8)
+#define EXT4_SPEC_s_inode_readahead_blks (1 << 9)
+#define EXT4_SPEC_s_li_wait_mult (1 << 10)
+#define EXT4_SPEC_s_max_dir_size_kb (1 << 11)
+#define EXT4_SPEC_s_stripe (1 << 12)
+#define EXT4_SPEC_s_resuid (1 << 13)
+#define EXT4_SPEC_s_resgid (1 << 14)
+#define EXT4_SPEC_s_commit_interval (1 << 15)
+
struct ext4_fs_context {
char *s_qf_names[EXT4_MAXQUOTAS];
int s_jquota_fmt; /* Format of quota to use */
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;
+ 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
@@ -1970,6 +2005,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;
}

@@ -1985,15 +2021,36 @@ 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 set_##name(struct ext4_fs_context *ctx, int flag) \
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name |= flag; \
+} \
+static inline void clear_##name(struct ext4_fs_context *ctx, int flag) \
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name &= ~flag; \
+} \
+static inline bool 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;
const struct mount_opts *m;
struct fs_parse_result result;
kuid_t uid;
@@ -2029,16 +2086,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
param->key);
return 1;
case Opt_abort:
- sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
+ set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
return 1;
case Opt_i_version:
- sb->s_flags |= SB_I_VERSION;
+ set_flags(ctx, SB_I_VERSION);
return 1;
case Opt_lazytime:
- sb->s_flags |= SB_LAZYTIME;
+ set_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_nolazytime:
- sb->s_flags &= ~SB_LAZYTIME;
+ clear_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_errors:
case Opt_data:
@@ -2061,21 +2118,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);
+ set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
- set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
+ set_mount_opt2(ctx,
+ EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
} else
return -EINVAL;
}
if (m->flags & MOPT_CLEAR_ERR)
- clear_opt(sb, ERRORS_MASK);
+ 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, "
@@ -2083,21 +2141,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) ||
@@ -2107,16 +2166,20 @@ 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;
+ 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;
} 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)) {
@@ -2124,7 +2187,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)) {
@@ -2132,7 +2196,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 (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(NULL, KERN_ERR,
@@ -2140,6 +2205,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;
@@ -2160,6 +2226,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) {
@@ -2169,9 +2236,10 @@ 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) {
#ifdef CONFIG_FS_ENCRYPTION
- sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
+ set_mount_flags(ctx, EXT4_MF_TEST_DUMMY_ENCRYPTION);
ext4_msg(NULL, KERN_WARNING,
"Test dummy encryption mode enabled");
#else
@@ -2179,35 +2247,27 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
"Test dummy encryption mount option ignored");
#endif
} else if (m->flags & MOPT_DATAJ) {
- if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
- 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;
- }
+ clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+ 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) {
#ifdef CONFIG_FS_DAX
ext4_msg(NULL, KERN_WARNING,
"DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
- sbi->s_mount_opt |= m->mount_opt;
+ set_mount_opt(ctx, m->mount_opt);
#else
ext4_msg(NULL, KERN_INFO, "dax option not supported");
return -EINVAL;
#endif
} else if (token == Opt_data_err_abort) {
- sbi->s_mount_opt |= m->mount_opt;
+ set_mount_opt(ctx, m->mount_opt);
} else if (token == Opt_data_err_ignore) {
- sbi->s_mount_opt &= ~m->mount_opt;
+ clear_mount_opt(ctx, m->mount_opt);
} else {
unsigned int set = 0;

@@ -2225,9 +2285,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
return -EINVAL;
}
if (set != 0)
- sbi->s_mount_opt |= m->mount_opt;
+ set_mount_opt(ctx, m->mount_opt);
else
- sbi->s_mount_opt &= ~m->mount_opt;
+ clear_mount_opt(ctx, m->mount_opt);
}
return 1;
}
@@ -2286,11 +2346,6 @@ static int parse_options(char *options, struct super_block *sb,
}
}

- if (journal_devnum)
- *journal_devnum = ctx.journal_devnum;
- if (journal_ioprio)
- *journal_ioprio = ctx.journal_ioprio;
-
ret = ext4_validate_options(&fc);
if (ret < 0)
return 0;
@@ -2299,9 +2354,12 @@ 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);
+ if (ctx.spec & EXT4_SPEC_JOURNAL_DEV)
+ *journal_devnum = ctx.journal_devnum;
+ if (ctx.spec & EXT4_SPEC_JOURNAL_IOPRIO)
+ *journal_ioprio = ctx.journal_ioprio;

+ ext4_apply_options(&fc, sb);
return 1;
}

@@ -2309,20 +2367,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
}

@@ -2337,17 +2405,43 @@ 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;

- if (ctx->qname_spec && quota_loaded) {
- if (quota_feature)
- goto err_feature;
+ /*
+ * 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_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 &&
+ !test_mount_opt(ctx, quota_flags))
+ goto err_quota_change;
+
+ if (ctx->spec & EXT4_SPEC_JQUOTA) {
+
+ if (quota_feature) {
+ ext4_msg(NULL, KERN_INFO,
+ "Ext4: Journaled quota options ignored when "
+ "QUOTA feature is enabled");
+ return 0;
+ }

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] &&
@@ -2357,15 +2451,52 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
}
}

- 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 = (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ test_opt(sb, USRQUOTA));
+
+ grpquota = (test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
+ test_opt(sb, GRPQUOTA));
+
+ if (usr_qf_name) {
+ clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
+ usrquota = false;
+ }
+ if (grp_qf_name) {
+ 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:
@@ -2379,10 +2510,6 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
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 0;
#else
return 0;
#endif
@@ -2392,6 +2519,7 @@ 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;

if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
ext4_msg(NULL, KERN_ERR,
@@ -2404,56 +2532,93 @@ 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 (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");
+ }
+
+ if ((ctx->spec & EXT4_SPEC_DATAJ) &&
+ (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)) {
+ if (!sbi->s_journal) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Remounting file system with no journal "
+ "so ignoring journalled data option");
+ 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;
+ }
+ }
+
return ext4_check_quota_consistency(fc, sb);
}

-static int ext4_validate_options(struct fs_context *fc)
+static void 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;
+
+ 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);
+
+ ext4_apply_quota_options(fc, sb);
+}
+
+static int ext4_validate_options(struct fs_context *fc)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
#ifdef CONFIG_QUOTA
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 (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
+ clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);

- if (test_opt(sb, GRPQUOTA) && grp_qf_name)
- clear_opt(sb, GRPQUOTA);
+ if (test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) && grp_qf_name)
+ clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);

- if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+ if (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ 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 mount option 'dioread_nolock' "
- "for blocksize < PAGE_SIZE");
- }
- return 0;
+ return 1;
}

static inline void ext4_show_quota_options(struct seq_file *seq,
--
2.21.1

2020-04-28 16:46:44

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 13/17] ext4: add ext4_reconfigure for the new mount API

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index a340b4943544..9e10c42c300c 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -94,6 +94,7 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int ext4_get_tree(struct fs_context *fc);
+static int ext4_reconfigure(struct fs_context *fc);

/*
* Lock ordering
@@ -126,6 +127,7 @@ static int ext4_get_tree(struct fs_context *fc);
static const struct fs_context_operations ext4_context_ops = {
.parse_param = ext4_parse_param,
.get_tree = ext4_get_tree,
+ .reconfigure = ext4_reconfigure,
};

#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
@@ -6237,6 +6239,25 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
return ret;
}

+static int ext4_reconfigure(struct fs_context *fc)
+{
+ struct super_block *sb = fc->root->d_sb;
+ int flags = fc->sb_flags;
+ int ret;
+
+ fc->s_fs_info = EXT4_SB(sb);
+
+ ret = ext4_check_opt_consistency(fc, sb);
+ if (ret < 0)
+ return ret;
+
+ ret = __ext4_remount(fc, sb, &flags);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
#ifdef CONFIG_QUOTA
static int ext4_statfs_project(struct super_block *sb,
kprojid_t projid, struct kstatfs *buf)
--
2.21.1

2020-04-28 16:46:47

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 14/17] ext4: add ext4_fc_free for the new mount API

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 9e10c42c300c..df7d1a724f1b 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -95,6 +95,7 @@ static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int ext4_get_tree(struct fs_context *fc);
static int ext4_reconfigure(struct fs_context *fc);
+static void ext4_fc_free(struct fs_context *fc);

/*
* Lock ordering
@@ -128,6 +129,7 @@ static const struct fs_context_operations ext4_context_ops = {
.parse_param = ext4_parse_param,
.get_tree = ext4_get_tree,
.reconfigure = ext4_reconfigure,
+ .free = ext4_fc_free,
};

#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
@@ -1958,6 +1960,19 @@ struct ext4_fs_context {
ext4_fsblk_t s_sb_block;
};

+static void ext4_fc_free(struct fs_context *fc)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
+ int i;
+
+ if (!ctx)
+ return;
+
+ for (i = 0; i < EXT4_MAXQUOTAS; i++)
+ kfree(ctx->s_qf_names[i]);
+ kfree(ctx);
+}
+
#ifdef CONFIG_QUOTA
/*
* Note the name of the specified quota file.
--
2.21.1

2020-04-28 16:46:55

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 15/17] ext4: change token2str() to use ext4_param_specs

Chage token2str() to use ext4_param_specs instead of tokens so that we
can get rid of tokens entirely.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index df7d1a724f1b..41075fdd076a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2691,12 +2691,12 @@ static inline void ext4_show_quota_options(struct seq_file *seq,

static const char *token2str(int token)
{
- const struct match_token *t;
+ const struct fs_parameter_spec *spec;

- for (t = tokens; t->token != Opt_err; t++)
- if (t->token == token && !strchr(t->pattern, '='))
+ for (spec = ext4_param_specs; spec->name != NULL; spec++)
+ if (spec->opt == token && !spec->type)
break;
- return t->pattern;
+ return spec->name;
}

/*
--
2.21.1

2020-04-28 16:47:04

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 17/17] ext4: Remove unused code from old mount api

Additionally rename ext4_fill_super_fc to ext4_fill_super

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 232 +-----------------------------------------------
1 file changed, 3 insertions(+), 229 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d52abb87ff80..0f8ef3423e3a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -74,12 +74,9 @@ static void ext4_mark_recovery_complete(struct super_block *sb,
static void ext4_clear_journal_err(struct super_block *sb,
struct ext4_super_block *es);
static int ext4_sync_fs(struct super_block *sb, int wait);
-static int ext4_remount(struct super_block *sb, int *flags, char *data);
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
static int ext4_unfreeze(struct super_block *sb);
static int ext4_freeze(struct super_block *sb);
-static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data);
static inline int ext2_feature_set_ok(struct super_block *sb);
static inline int ext3_feature_set_ok(struct super_block *sb);
static int ext4_feature_set_ok(struct super_block *sb, int readonly);
@@ -1531,7 +1528,7 @@ enum {
Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
- Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
+ Opt_usrjquota, Opt_grpjquota,
Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version, Opt_dax,
@@ -1674,99 +1671,6 @@ static const struct fs_parameter_spec ext4_param_specs[] = {
{}
};

-static const match_table_t tokens = {
- {Opt_bsd_df, "bsddf"},
- {Opt_minix_df, "minixdf"},
- {Opt_grpid, "grpid"},
- {Opt_grpid, "bsdgroups"},
- {Opt_nogrpid, "nogrpid"},
- {Opt_nogrpid, "sysvgroups"},
- {Opt_resgid, "resgid=%u"},
- {Opt_resuid, "resuid=%u"},
- {Opt_sb, "sb=%u"},
- {Opt_err_cont, "errors=continue"},
- {Opt_err_panic, "errors=panic"},
- {Opt_err_ro, "errors=remount-ro"},
- {Opt_nouid32, "nouid32"},
- {Opt_debug, "debug"},
- {Opt_removed, "oldalloc"},
- {Opt_removed, "orlov"},
- {Opt_user_xattr, "user_xattr"},
- {Opt_nouser_xattr, "nouser_xattr"},
- {Opt_acl, "acl"},
- {Opt_noacl, "noacl"},
- {Opt_noload, "norecovery"},
- {Opt_noload, "noload"},
- {Opt_removed, "nobh"},
- {Opt_removed, "bh"},
- {Opt_commit, "commit=%u"},
- {Opt_min_batch_time, "min_batch_time=%u"},
- {Opt_max_batch_time, "max_batch_time=%u"},
- {Opt_journal_dev, "journal_dev=%u"},
- {Opt_journal_path, "journal_path=%s"},
- {Opt_journal_checksum, "journal_checksum"},
- {Opt_nojournal_checksum, "nojournal_checksum"},
- {Opt_journal_async_commit, "journal_async_commit"},
- {Opt_abort, "abort"},
- {Opt_data_journal, "data=journal"},
- {Opt_data_ordered, "data=ordered"},
- {Opt_data_writeback, "data=writeback"},
- {Opt_data_err_abort, "data_err=abort"},
- {Opt_data_err_ignore, "data_err=ignore"},
- {Opt_offusrjquota, "usrjquota="},
- {Opt_usrjquota, "usrjquota=%s"},
- {Opt_offgrpjquota, "grpjquota="},
- {Opt_grpjquota, "grpjquota=%s"},
- {Opt_jqfmt_vfsold, "jqfmt=vfsold"},
- {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
- {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
- {Opt_grpquota, "grpquota"},
- {Opt_noquota, "noquota"},
- {Opt_quota, "quota"},
- {Opt_usrquota, "usrquota"},
- {Opt_prjquota, "prjquota"},
- {Opt_barrier, "barrier=%u"},
- {Opt_barrier, "barrier"},
- {Opt_nobarrier, "nobarrier"},
- {Opt_i_version, "i_version"},
- {Opt_dax, "dax"},
- {Opt_stripe, "stripe=%u"},
- {Opt_delalloc, "delalloc"},
- {Opt_warn_on_error, "warn_on_error"},
- {Opt_nowarn_on_error, "nowarn_on_error"},
- {Opt_lazytime, "lazytime"},
- {Opt_nolazytime, "nolazytime"},
- {Opt_debug_want_extra_isize, "debug_want_extra_isize=%u"},
- {Opt_nodelalloc, "nodelalloc"},
- {Opt_removed, "mblk_io_submit"},
- {Opt_removed, "nomblk_io_submit"},
- {Opt_block_validity, "block_validity"},
- {Opt_noblock_validity, "noblock_validity"},
- {Opt_inode_readahead_blks, "inode_readahead_blks=%u"},
- {Opt_journal_ioprio, "journal_ioprio=%u"},
- {Opt_auto_da_alloc, "auto_da_alloc=%u"},
- {Opt_auto_da_alloc, "auto_da_alloc"},
- {Opt_noauto_da_alloc, "noauto_da_alloc"},
- {Opt_dioread_nolock, "dioread_nolock"},
- {Opt_dioread_lock, "nodioread_nolock"},
- {Opt_dioread_lock, "dioread_lock"},
- {Opt_discard, "discard"},
- {Opt_nodiscard, "nodiscard"},
- {Opt_init_itable, "init_itable=%u"},
- {Opt_init_itable, "init_itable"},
- {Opt_noinit_itable, "noinit_itable"},
- {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
- {Opt_test_dummy_encryption, "test_dummy_encryption"},
- {Opt_nombcache, "nombcache"},
- {Opt_nombcache, "no_mbcache"}, /* for backward compatibility */
- {Opt_removed, "check=none"}, /* mount option from ext2/3 */
- {Opt_removed, "nocheck"}, /* mount option from ext2/3 */
- {Opt_removed, "reservation"}, /* mount option from ext2/3 */
- {Opt_removed, "noreservation"}, /* mount option from ext2/3 */
- {Opt_removed, "journal=%u"}, /* mount option from ext2/3 */
- {Opt_err, NULL},
-};
-
#define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
static const char deprecated_msg[] =
"Mount option \"%s\" will be removed by %s\n"
@@ -1875,8 +1779,6 @@ static const struct mount_opts {
MOPT_CLEAR | MOPT_Q},
{Opt_usrjquota, 0, MOPT_Q},
{Opt_grpjquota, 0, MOPT_Q},
- {Opt_offusrjquota, 0, MOPT_Q},
- {Opt_offgrpjquota, 0, MOPT_Q},
{Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
{Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
{Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
@@ -5296,87 +5198,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb,
return err ? err : ret;
}

-static void cleanup_ctx(struct ext4_fs_context *ctx)
-{
- int i;
-
- if (!ctx)
- return;
-
- for (i = 0; i < EXT4_MAXQUOTAS; i++) {
- kfree(ctx->s_qf_names[i]);
- }
-}
-
-static int ext4_fill_super(struct super_block *sb, void *data, int silent)
-{
- struct ext4_fs_context ctx;
- struct ext4_sb_info *sbi;
- struct fs_context fc;
- const char *descr;
- char *orig_data;
- int ret = -ENOMEM;
-
- orig_data = kstrdup(data, GFP_KERNEL);
- if (data && !orig_data)
- return -ENOMEM;
-
- /* Cleanup superblock name */
- strreplace(sb->s_id, '/', '!');
-
- memset(&fc, 0, sizeof(fc));
- memset(&ctx, 0, sizeof(ctx));
- fc.fs_private = &ctx;
-
- ret = parse_options(&fc, (char *) data);
- if (ret < 0)
- goto free_data;
-
- sbi = ext4_alloc_sbi(sb);
- if (!sbi) {
- ret = -ENOMEM;
- goto free_data;
- }
-
- fc.s_fs_info = sbi;
-
- sbi->s_sb_block = 1; /* Default super block location */
- if (ctx.spec & EXT4_SPEC_s_sb_block)
- sbi->s_sb_block = ctx.s_sb_block;
-
- ret = __ext4_fill_super(&fc, sb, silent);
- if (ret < 0)
- goto free_sbi;
-
- if (EXT4_SB(sb)->s_journal) {
- if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
- descr = " journalled data mode";
- else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
- descr = " ordered data mode";
- else
- descr = " writeback data mode";
- } else
- descr = "out journal";
-
- if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
- ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
- "Opts: %.*s%s%s", descr,
- (int) sizeof(sbi->s_es->s_mount_opts),
- sbi->s_es->s_mount_opts,
- *sbi->s_es->s_mount_opts ? "; " : "", (char *)orig_data);
-
- kfree(orig_data);
- cleanup_ctx(&ctx);
- return 0;
-free_sbi:
- ext4_free_sbi(sbi);
-free_data:
- kfree(orig_data);
- cleanup_ctx(&ctx);
- return ret;
-}
-
-static int ext4_fill_super_fc(struct super_block *sb, struct fs_context *fc)
+static int ext4_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct ext4_fs_context *ctx = fc->fs_private;
struct ext4_sb_info *sbi;
@@ -5409,7 +5231,7 @@ static int ext4_fill_super_fc(struct super_block *sb, struct fs_context *fc)

static int ext4_get_tree(struct fs_context *fc)
{
- return get_tree_bdev(fc, ext4_fill_super_fc);
+ return get_tree_bdev(fc, ext4_fill_super);
}

/*
@@ -6229,48 +6051,6 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb,
return err;
}

-static int ext4_remount(struct super_block *sb, int *flags, char *data)
-{
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- struct ext4_fs_context ctx;
- struct fs_context fc;
- char *orig_data;
- int ret;
-
- orig_data = kstrdup(data, GFP_KERNEL);
- if (data && !orig_data)
- return -ENOMEM;
-
- memset(&fc, 0, sizeof(fc));
- memset(&ctx, 0, sizeof(ctx));
-
- fc.fs_private = &ctx;
- fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
- fc.s_fs_info = sbi;
-
- ret = parse_options(&fc, (char *) data);
- if (ret < 0)
- goto err_out;
-
- ret = ext4_check_opt_consistency(&fc, sb);
- if (ret < 0)
- goto err_out;
-
- ret = __ext4_remount(&fc, sb, flags);
- if (ret < 0)
- goto err_out;
-
- ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
- cleanup_ctx(&ctx);
- kfree(orig_data);
- return 0;
-
-err_out:
- cleanup_ctx(&ctx);
- kfree(orig_data);
- return ret;
-}
-
static int ext4_reconfigure(struct fs_context *fc)
{
struct super_block *sb = fc->root->d_sb;
@@ -6780,12 +6560,6 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
}
#endif

-static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
-{
- return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
-}
-
#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
static inline void register_as_ext2(void)
{
--
2.21.1

2020-04-28 16:48:21

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 12/17] ext4: refactor ext4_remount()

Refactor ext4_remount() so that we can parse mount options separately.

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 62 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 50 insertions(+), 12 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 729a07c298e2..a340b4943544 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -5926,8 +5926,10 @@ struct ext4_mount_options {
#endif
};

-static int ext4_remount(struct super_block *sb, int *flags, char *data)
+static int __ext4_remount(struct fs_context *fc, struct super_block *sb,
+ int *flags)
{
+ struct ext4_fs_context *ctx = fc->fs_private;
struct ext4_super_block *es;
struct ext4_sb_info *sbi = EXT4_SB(sb);
unsigned long old_sb_flags;
@@ -5940,10 +5942,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
int i, j;
char *to_free[EXT4_MAXQUOTAS];
#endif
- char *orig_data = kstrdup(data, GFP_KERNEL);
-
- if (data && !orig_data)
- return -ENOMEM;

/* Store the original options */
old_sb_flags = sb->s_flags;
@@ -5964,7 +5962,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (!old_opts.s_qf_names[i]) {
for (j = 0; j < i; j++)
kfree(old_opts.s_qf_names[j]);
- kfree(orig_data);
return -ENOMEM;
}
} else
@@ -5973,9 +5970,10 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (sbi->s_journal && sbi->s_journal->j_task->io_context)
journal_ioprio = sbi->s_journal->j_task->io_context->ioprio;

- err = parse_apply_options(data, sb, NULL, &journal_ioprio, 1);
- if (err < 0)
- goto restore_opts;
+ if (ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
+ journal_ioprio = ctx->journal_ioprio;
+
+ ext4_apply_options(fc, sb);

if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
test_opt(sb, JOURNAL_CHECKSUM)) {
@@ -6172,8 +6170,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
#endif

*flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
- ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
- kfree(orig_data);
+ ext4_msg(sb, KERN_INFO, "re-mounted.");
return 0;

restore_opts:
@@ -6195,10 +6192,51 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
for (i = 0; i < EXT4_MAXQUOTAS; i++)
kfree(to_free[i]);
#endif
- kfree(orig_data);
return err;
}

+static int ext4_remount(struct super_block *sb, int *flags, char *data)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_fs_context ctx;
+ struct fs_context fc;
+ char *orig_data;
+ int ret;
+
+ orig_data = kstrdup(data, GFP_KERNEL);
+ if (data && !orig_data)
+ return -ENOMEM;
+
+ memset(&fc, 0, sizeof(fc));
+ memset(&ctx, 0, sizeof(ctx));
+
+ fc.fs_private = &ctx;
+ fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
+ fc.s_fs_info = sbi;
+
+ ret = parse_options(&fc, (char *) data);
+ if (ret < 0)
+ goto err_out;
+
+ ret = ext4_check_opt_consistency(&fc, sb);
+ if (ret < 0)
+ goto err_out;
+
+ ret = __ext4_remount(&fc, sb, flags);
+ if (ret < 0)
+ goto err_out;
+
+ ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s", orig_data);
+ cleanup_ctx(&ctx);
+ kfree(orig_data);
+ return 0;
+
+err_out:
+ cleanup_ctx(&ctx);
+ kfree(orig_data);
+ return ret;
+}
+
#ifdef CONFIG_QUOTA
static int ext4_statfs_project(struct super_block *sb,
kprojid_t projid, struct kstatfs *buf)
--
2.21.1

2020-04-28 16:48:21

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 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 <[email protected]>
---
fs/ext4/super.c | 248 +++++++++++++++++++++++++++++++-----------------
1 file changed, 159 insertions(+), 89 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2f7e49bfbf71..ae0ef04b3be4 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
@@ -1778,71 +1782,6 @@ static const char deprecated_msg[] =
"Mount option \"%s\" will be removed by %s\n"
"Contact [email protected] 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
@@ -1987,10 +1926,68 @@ 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])
+ 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;
@@ -2009,14 +2006,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) {
@@ -2082,11 +2079,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",
@@ -2211,19 +2203,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) {
#ifdef CONFIG_FS_DAX
@@ -2325,9 +2305,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] && 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,
+ "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 0;
+#else
+ return 0;
+#endif
+}
+
static int ext4_validate_options(struct fs_context *fc)
{
struct ext4_sb_info *sbi = fc->s_fs_info;
--
2.21.1

2020-04-28 16:48:28

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH v2 16/17] ext4: switch to the new mount API

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 50 +++++++++++++++++++++++++++++++++----------------
1 file changed, 34 insertions(+), 16 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 41075fdd076a..d52abb87ff80 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -96,6 +96,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int ext4_get_tree(struct fs_context *fc);
static int ext4_reconfigure(struct fs_context *fc);
static void ext4_fc_free(struct fs_context *fc);
+static int ext4_init_fs_context(struct fs_context *fc);
+static const struct fs_parameter_spec ext4_param_specs[];

/*
* Lock ordering
@@ -134,11 +136,12 @@ static const struct fs_context_operations ext4_context_ops = {

#if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
static struct file_system_type ext2_fs_type = {
- .owner = THIS_MODULE,
- .name = "ext2",
- .mount = ext4_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .owner = THIS_MODULE,
+ .name = "ext2",
+ .init_fs_context = ext4_init_fs_context,
+ .parameters = ext4_param_specs,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext2");
MODULE_ALIAS("ext2");
@@ -149,11 +152,12 @@ MODULE_ALIAS("ext2");


static struct file_system_type ext3_fs_type = {
- .owner = THIS_MODULE,
- .name = "ext3",
- .mount = ext4_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .owner = THIS_MODULE,
+ .name = "ext3",
+ .init_fs_context = ext4_init_fs_context,
+ .parameters = ext4_param_specs,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext3");
MODULE_ALIAS("ext3");
@@ -1501,7 +1505,6 @@ static const struct super_operations ext4_sops = {
.freeze_fs = ext4_freeze,
.unfreeze_fs = ext4_unfreeze,
.statfs = ext4_statfs,
- .remount_fs = ext4_remount,
.show_options = ext4_show_options,
#ifdef CONFIG_QUOTA
.quota_read = ext4_quota_read,
@@ -1973,6 +1976,20 @@ static void ext4_fc_free(struct fs_context *fc)
kfree(ctx);
}

+int ext4_init_fs_context(struct fs_context *fc)
+{
+ struct xfs_fs_context *ctx;
+
+ ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ fc->fs_private = ctx;
+ fc->ops = &ext4_context_ops;
+
+ return 0;
+}
+
#ifdef CONFIG_QUOTA
/*
* Note the name of the specified quota file.
@@ -6826,11 +6843,12 @@ static inline int ext3_feature_set_ok(struct super_block *sb)
}

static struct file_system_type ext4_fs_type = {
- .owner = THIS_MODULE,
- .name = "ext4",
- .mount = ext4_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .owner = THIS_MODULE,
+ .name = "ext4",
+ .init_fs_context = ext4_init_fs_context,
+ .parameters = ext4_param_specs,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext4");

--
2.21.1

2020-04-28 16:48:35

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On Tue, Apr 28, 2020 at 06:45:24PM +0200, Lukas Czerner wrote:
> At the parsing phase of mount in the new mount api sb will not be
> available so allow sb to be NULL in ext4_msg and use that in
> handle_mount_opt().
>
> Also change return value to appropriate -EINVAL where needed.

Shouldn't mount-time messages be reported using the logfc and family
helpers from include/linux/fs_context.h? (which btw, have really
horrible over-generic names).

2020-04-28 16:58:53

by Lukas Czerner

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On Tue, Apr 28, 2020 at 09:48:08AM -0700, Christoph Hellwig wrote:
> On Tue, Apr 28, 2020 at 06:45:24PM +0200, Lukas Czerner wrote:
> > At the parsing phase of mount in the new mount api sb will not be
> > available so allow sb to be NULL in ext4_msg and use that in
> > handle_mount_opt().
> >
> > Also change return value to appropriate -EINVAL where needed.
>
> Shouldn't mount-time messages be reported using the logfc and family
> helpers from include/linux/fs_context.h? (which btw, have really
> horrible over-generic names).

I am sure it should at some point, but I am not really sure how ready
it is at the moment. Last time I checked David told me not to bother
using it yet.

Is it ready yet David ? Should we be switching to it ?

-Lukas

2020-04-29 02:38:56

by Eric Sandeen

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On 4/28/20 11:45 AM, Lukas Czerner wrote:
> At the parsing phase of mount in the new mount api sb will not be
> available so allow sb to be NULL in ext4_msg and use that in
> handle_mount_opt().
>
> Also change return value to appropriate -EINVAL where needed.
>
> Signed-off-by: Lukas Czerner <[email protected]>
> ---
> fs/ext4/super.c | 120 +++++++++++++++++++++++++-----------------------
> 1 file changed, 63 insertions(+), 57 deletions(-)
>
> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> index 2c6fea451d7d..2f7e49bfbf71 100644
> --- a/fs/ext4/super.c
> +++ b/fs/ext4/super.c
> @@ -88,7 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
> 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 super_block *sb);
> +static int ext4_validate_options(struct fs_context *fc);
>
> /*
> * Lock ordering
> @@ -754,13 +754,14 @@ void __ext4_msg(struct super_block *sb,
> struct va_format vaf;
> va_list args;
>
> - if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
> + if (sb &&
> + !___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
> return;
>
> va_start(args, fmt);
> vaf.fmt = fmt;
> vaf.va = &args;
> - printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
> + printk("%sEXT4-fs (%s): %pV\n", prefix, sb ? sb->s_id : "n/a", &vaf);

Tiny nitpick, I might drop "n/a" - I'm not sure that makes anything clearer:

"EXT4-fs (n/a): message" seems confusing, but maybe that's just me.

FWIW xfs just removes the s_id print altogether if it's not available, i.e.:

static void
__xfs_printk(
const char *level,
const struct xfs_mount *mp,
struct va_format *vaf)
{
if (mp && mp->m_super) {
printk("%sXFS (%s): %pV\n", level, mp->m_super->s_id, vaf);
return;
}
printk("%sXFS: %pV\n", level, vaf);
}

*shrug* up to personal preference I suppose.

Thanks,
-Eric

2020-04-29 02:55:52

by Ian Kent

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On Tue, 2020-04-28 at 18:57 +0200, Lukas Czerner wrote:
> On Tue, Apr 28, 2020 at 09:48:08AM -0700, Christoph Hellwig wrote:
> > On Tue, Apr 28, 2020 at 06:45:24PM +0200, Lukas Czerner wrote:
> > > At the parsing phase of mount in the new mount api sb will not be
> > > available so allow sb to be NULL in ext4_msg and use that in
> > > handle_mount_opt().
> > >
> > > Also change return value to appropriate -EINVAL where needed.
> >
> > Shouldn't mount-time messages be reported using the logfc and
> > family
> > helpers from include/linux/fs_context.h? (which btw, have really
> > horrible over-generic names).
>
> I am sure it should at some point, but I am not really sure how ready
> it is at the moment. Last time I checked David told me not to bother
> using it yet.
>
> Is it ready yet David ? Should we be switching to it ?

The mount-API log macros tend to cause user confusion because they
often lead to unexpected log messages.

We're seeing that now with bugs logged due to unexpected messages
resulting from the NFS mount-API conversion.

I'd recommend mostly avoiding using the macros until there's been
time to reconsider how they should work, after all fsopen() and
friends will still get errno errors just not the passed string
description.

Ian

2020-04-29 11:05:46

by Lukas Czerner

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On Tue, Apr 28, 2020 at 09:38:11PM -0500, Eric Sandeen wrote:
> On 4/28/20 11:45 AM, Lukas Czerner wrote:
> > At the parsing phase of mount in the new mount api sb will not be
> > available so allow sb to be NULL in ext4_msg and use that in
> > handle_mount_opt().
> >
> > Also change return value to appropriate -EINVAL where needed.
> >
> > Signed-off-by: Lukas Czerner <[email protected]>
> > ---
> > fs/ext4/super.c | 120 +++++++++++++++++++++++++-----------------------
> > 1 file changed, 63 insertions(+), 57 deletions(-)
> >
> > diff --git a/fs/ext4/super.c b/fs/ext4/super.c
> > index 2c6fea451d7d..2f7e49bfbf71 100644
> > --- a/fs/ext4/super.c
> > +++ b/fs/ext4/super.c
> > @@ -88,7 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
> > 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 super_block *sb);
> > +static int ext4_validate_options(struct fs_context *fc);
> >
> > /*
> > * Lock ordering
> > @@ -754,13 +754,14 @@ void __ext4_msg(struct super_block *sb,
> > struct va_format vaf;
> > va_list args;
> >
> > - if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
> > + if (sb &&
> > + !___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
> > return;
> >
> > va_start(args, fmt);
> > vaf.fmt = fmt;
> > vaf.va = &args;
> > - printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
> > + printk("%sEXT4-fs (%s): %pV\n", prefix, sb ? sb->s_id : "n/a", &vaf);
>
> Tiny nitpick, I might drop "n/a" - I'm not sure that makes anything clearer:
>
> "EXT4-fs (n/a): message" seems confusing, but maybe that's just me.
>
> FWIW xfs just removes the s_id print altogether if it's not available, i.e.:
>
> static void
> __xfs_printk(
> const char *level,
> const struct xfs_mount *mp,
> struct va_format *vaf)
> {
> if (mp && mp->m_super) {
> printk("%sXFS (%s): %pV\n", level, mp->m_super->s_id, vaf);
> return;
> }
> printk("%sXFS: %pV\n", level, vaf);
> }
>
> *shrug* up to personal preference I suppose.

I wanted the message to stay more-or-less uniform, but I think you're
right. I'll drop the (n/a).

-Lukas

>
> Thanks,
> -Eric
>

2020-04-29 11:10:22

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH v2 05/17] ext4: Allow sb to be NULL in ext4_msg()

On Wed, Apr 29, 2020 at 10:53:00AM +0800, Ian Kent wrote:
> The mount-API log macros tend to cause user confusion because they
> often lead to unexpected log messages.
>
> We're seeing that now with bugs logged due to unexpected messages
> resulting from the NFS mount-API conversion.
>
> I'd recommend mostly avoiding using the macros until there's been
> time to reconsider how they should work, after all fsopen() and
> friends will still get errno errors just not the passed string
> description.

And when is that time going to be? Should we convert existing users
back and remove that functionality if it doesn't work properly?