All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 00/13] ext4: new mount API conversion
@ 2021-10-27 14:18 Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 01/13] fs_parse: allow parameter value to be empty Lukas Czerner
                   ` (15 more replies)
  0 siblings, 16 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

After some time I am once again resurrecting the patchset to convert 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 a new ext4 mount options regression test that
was sent to the fstests list. You can check it out on github as well.

https://github.com/lczerner/xfstests/tree/ext4_mount_test

Here is a high level description of the patchset

1. Prepare the ext4 mount parameters required by the new mount API and use
   it for parsing, while still using the old API to get the options
   string.

  fs_parse: allow parameter value to be empty
  ext4: Add fs parameter specifications for mount options
  ext4: move option validation to a separate function
  ext4: Change handle_mount_opt() to use fs_parameter

2. Remove the use of ext4 super block from all the parsing code, because
   with the new mount API the parsing is going to be done before we even
   get the super block.

  ext4: Allow sb to be NULL in ext4_msg()
  ext4: move quota configuration out of handle_mount_opt()
  ext4: check ext2/3 compatibility outside handle_mount_opt()
  ext4: get rid of super block and sbi from handle_mount_ops()

3. Actually finish the separation of the parsing and super block setup
   into distinct steps. This is where the new ext4_fill_super() and
   ext4_remount() functions are created temporarily before the actual
   transition to the new API.

  ext4: Completely separate options parsing and sb setup

4. Make some last preparations and actually switch the ext4 to use the
   new mount API.

  ext4: clean up return values in handle_mount_opt()
  ext4: change token2str() to use ext4_param_specs
  ext4: switch to the new mount api

5. Cleanup the old unused structures and rearrange the parsing function.

  ext4: Remove unused match_table_t tokens

There is still a potential to do some cleanups and perhaps refactoring
such as using the fsparam_flag_no to remove the separate negative
options for example. However that can be done later after the conversion
to the new mount API which is the main purpose of the patchset.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
v3 -> v4: Fix some typos, print exact quotafile type in log messages.
          Remove explicit "Ext4:" from some log messages
V2 -> V3: Rebase to the newer kernel, including new mount options.
V1 -> V2: Rebase to the newer kernel

Lukas Czerner (13):
  fs_parse: allow parameter value to be empty
  ext4: Add fs parameter specifications for mount options
  ext4: move option validation to a separate function
  ext4: Change handle_mount_opt() to use fs_parameter
  ext4: Allow sb to be NULL in ext4_msg()
  ext4: move quota configuration out of handle_mount_opt()
  ext4: check ext2/3 compatibility outside handle_mount_opt()
  ext4: get rid of super block and sbi from handle_mount_ops()
  ext4: Completely separate options parsing and sb setup
  ext4: clean up return values in handle_mount_opt()
  ext4: change token2str() to use ext4_param_specs
  ext4: switch to the new mount api
  ext4: Remove unused match_table_t tokens

 fs/ext4/super.c           | 1848 +++++++++++++++++++++++--------------
 fs/fs_parser.c            |   31 +-
 include/linux/fs_parser.h |    2 +-
 3 files changed, 1189 insertions(+), 692 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v4 01/13] fs_parse: allow parameter value to be empty
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-29  8:44   ` Christian Brauner
  2021-10-27 14:18 ` [PATCH v4 02/13] ext4: Add fs parameter specifications for mount options Lukas Czerner
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Al Viro, Carlos Maiolino

Allow parameter value to be empty by specifying fs_param_can_be_empty
flag.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 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 3df07c0e32b3..ed40ce5742fd 100644
--- a/fs/fs_parser.c
+++ b/fs/fs_parser.c
@@ -199,6 +199,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);
@@ -211,8 +213,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;
 }
@@ -221,8 +226,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;
 }
@@ -231,8 +239,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;
 }
@@ -244,6 +255,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);
@@ -255,7 +268,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;
 }
@@ -275,7 +289,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 aab0ffc6bac6..f103c91139d4 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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 02/13] ext4: Add fs parameter specifications for mount options
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 01/13] fs_parse: allow parameter value to be empty Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 03/13] ext4: move option validation to a separate function Lukas Czerner
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

Create an array of fs_parameter_spec called ext4_param_specs to
hold the mount option specifications we're going to be using with the
new mount api.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 151 insertions(+)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 88d5d274a868..42f4a3741692 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -46,6 +46,8 @@
 #include <linux/part_stat.h>
 #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 */
@@ -1681,11 +1683,160 @@ enum {
 	Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
 	Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
 	Opt_no_prefetch_block_bitmaps, Opt_mb_optimize_scan,
+	Opt_errors, Opt_data, Opt_data_err, Opt_jqfmt, Opt_dax_type,
 #ifdef CONFIG_EXT4_DEBUG
 	Opt_fc_debug_max_replay, Opt_fc_debug_force
 #endif
 };
 
+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},
+	{}
+};
+
+static const struct constant_table ext4_param_dax[] = {
+	{"always",	Opt_dax_always},
+	{"inode",	Opt_dax_inode},
+	{"never",	Opt_dax_never},
+	{}
+};
+
+/* 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_enum	("dax",			Opt_dax_type, ext4_param_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),
+#ifdef CONFIG_EXT4_DEBUG
+	fsparam_flag	("fc_debug_force",	Opt_fc_debug_force),
+	fsparam_u32	("fc_debug_max_replay",	Opt_fc_debug_max_replay),
+#endif
+	fsparam_u32	("max_dir_size_kb",	Opt_max_dir_size_kb),
+	fsparam_flag	("test_dummy_encryption",
+						Opt_test_dummy_encryption),
+	fsparam_string	("test_dummy_encryption",
+						Opt_test_dummy_encryption),
+	fsparam_flag	("inlinecrypt",		Opt_inlinecrypt),
+	fsparam_flag	("nombcache",		Opt_nombcache),
+	fsparam_flag	("no_mbcache",		Opt_nombcache),	/* for backward compatibility */
+	fsparam_flag	("prefetch_block_bitmaps",
+						Opt_removed),
+	fsparam_flag	("no_prefetch_block_bitmaps",
+						Opt_no_prefetch_block_bitmaps),
+	fsparam_s32	("mb_optimize_scan",	Opt_mb_optimize_scan),
+	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 = {
 	{Opt_bsd_df, "bsddf"},
 	{Opt_minix_df, "minixdf"},
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 03/13] ext4: move option validation to a separate function
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 01/13] fs_parse: allow parameter value to be empty Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 02/13] ext4: Add fs parameter specifications for mount options Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 04/13] ext4: Change handle_mount_opt() to use fs_parameter Lukas Czerner
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

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

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 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 42f4a3741692..5f6ad0615a2a 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -87,6 +87,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
@@ -2575,10 +2576,9 @@ static int parse_options(char *options, struct super_block *sb,
 			 struct ext4_parsed_options *ret_opts,
 			 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;
@@ -2596,7 +2596,14 @@ static int parse_options(char *options, struct super_block *sb,
 				     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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 04/13] ext4: Change handle_mount_opt() to use fs_parameter
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (2 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 03/13] ext4: move option validation to a separate function Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 05/13] ext4: Allow sb to be NULL in ext4_msg() Lukas Czerner
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

Use the new mount option specifications to parse the options in
handle_mount_opt(). However we're still using the old API to get the
options string.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 250 +++++++++++++++++++++++++++---------------------
 1 file changed, 143 insertions(+), 107 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5f6ad0615a2a..66b8e8850726 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1974,7 +1974,8 @@ static const char deprecated_msg[] =
 	"Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
 
 #ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype, 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);
@@ -1991,7 +1992,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");
@@ -2197,8 +2198,7 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
 #endif
 
 static int ext4_set_test_dummy_encryption(struct super_block *sb,
-					  const char *opt,
-					  const substring_t *arg,
+					  struct fs_parameter *param,
 					  bool is_remount)
 {
 #ifdef CONFIG_FS_ENCRYPTION
@@ -2216,7 +2216,7 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb,
 			 "Can't set test_dummy_encryption on remount");
 		return -1;
 	}
-	err = fscrypt_set_test_dummy_encryption(sb, arg->from,
+	err = fscrypt_set_test_dummy_encryption(sb, param->string,
 						&sbi->s_dummy_enc_policy);
 	if (err) {
 		if (err == -EEXIST)
@@ -2224,11 +2224,12 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb,
 				 "Can't change test_dummy_encryption on remount");
 		else if (err == -EINVAL)
 			ext4_msg(sb, KERN_WARNING,
-				 "Value of option \"%s\" is unrecognized", opt);
+				 "Value of option \"%s\" is unrecognized",
+				 param->key);
 		else
 			ext4_msg(sb, KERN_WARNING,
 				 "Error processing option \"%s\" [%d]",
-				 opt, err);
+				 param->key, err);
 		return -1;
 	}
 	ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
@@ -2239,41 +2240,52 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb,
 	return 1;
 }
 
-struct ext4_parsed_options {
+struct ext4_fs_context {
 	unsigned long journal_devnum;
 	unsigned int journal_ioprio;
 	int mb_optimize_scan;
 };
 
-static int handle_mount_opt(struct super_block *sb, char *opt, int token,
-			    substring_t *args, struct ext4_parsed_options *parsed_opts,
-			    int is_remount)
+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;
+	struct fs_parse_result result;
 	const struct mount_opts *m;
+	int is_remount;
 	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;
+	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 
 #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:
 		ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
@@ -2294,6 +2306,12 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 		ext4_msg(sb, KERN_ERR, "inline encryption not supported");
 #endif
 		return 1;
+	case Opt_errors:
+	case Opt_data:
+	case Opt_data_err:
+	case Opt_jqfmt:
+	case Opt_dax_type:
+		token = result.uint_32;
 	}
 
 	for (m = ext4_mount_opts; m->token != Opt_err; m++)
@@ -2302,25 +2320,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->key);
 		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->key);
 		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);
@@ -2338,63 +2354,69 @@ 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;
 #ifdef CONFIG_EXT4_DEBUG
 	} else if (token == Opt_fc_debug_max_replay) {
-		sbi->s_fc_debug_max_replay = arg;
+		sbi->s_fc_debug_max_replay = result.uint_32;
 #endif
 	} 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;
@@ -2404,9 +2426,8 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 				 "Cannot specify journal on remount");
 			return -1;
 		}
-		parsed_opts->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;
@@ -2416,44 +2437,27 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 				 "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;
-		}
-
-		parsed_opts->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;
 		}
-		parsed_opts->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) {
-		return ext4_set_test_dummy_encryption(sb, opt, &args[0],
-						      is_remount);
+		return ext4_set_test_dummy_encryption(sb, param, is_remount);
 	} else if (m->flags & MOPT_DATAJ) {
 		if (is_remount) {
 			if (!sbi->s_journal)
@@ -2540,30 +2544,35 @@ 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 (token == Opt_mb_optimize_scan) {
-		if (arg != 0 && arg != 1) {
+		if (result.int_32 != 0 && result.int_32 != 1) {
 			ext4_msg(sb, KERN_WARNING,
 				 "mb_optimize_scan should be set to 0 or 1.");
 			return -1;
 		}
-		parsed_opts->mb_optimize_scan = arg;
+		ctx->mb_optimize_scan = result.int_32;
 	} 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 (m->flags & MOPT_2) {
-			if (arg != 0)
+			if (set != 0)
 				sbi->s_mount_opt2 |= m->mount_opt;
 			else
 				sbi->s_mount_opt2 &= ~m->mount_opt;
 		} else {
-			if (arg != 0)
+			if (set != 0)
 				sbi->s_mount_opt |= m->mount_opt;
 			else
 				sbi->s_mount_opt &= ~m->mount_opt;
@@ -2573,29 +2582,56 @@ static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 }
 
 static int parse_options(char *options, struct super_block *sb,
-			 struct ext4_parsed_options *ret_opts,
+			 struct ext4_fs_context *ret_opts,
 			 int is_remount)
 {
-	substring_t args[MAX_OPT_ARGS];
-	int token;
-	char *p;
+	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, ret_opts,
-				     is_remount) < 0)
-			return 0;
+	memset(&fc, 0, sizeof(fc));
+	fc.fs_private = ret_opts;
+	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;
+		}
 	}
+
 	return ext4_validate_options(sb);
 }
 
@@ -4051,7 +4087,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	__u64 blocks_count;
 	int err = 0;
 	ext4_group_t first_not_zeroed;
-	struct ext4_parsed_options parsed_opts;
+	struct ext4_fs_context parsed_opts;
 
 	/* Set defaults for the variables that will be set during parsing */
 	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
@@ -5893,7 +5929,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 	char *to_free[EXT4_MAXQUOTAS];
 #endif
 	char *orig_data = kstrdup(data, GFP_KERNEL);
-	struct ext4_parsed_options parsed_opts;
+	struct ext4_fs_context parsed_opts;
 
 	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 	parsed_opts.journal_devnum = 0;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 05/13] ext4: Allow sb to be NULL in ext4_msg()
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (3 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 04/13] ext4: Change handle_mount_opt() to use fs_parameter Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 06/13] ext4: move quota configuration out of handle_mount_opt() Lukas Czerner
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

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 <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 144 ++++++++++++++++++++++++++----------------------
 1 file changed, 78 insertions(+), 66 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 66b8e8850726..b3c545695f2f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -87,7 +87,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
@@ -907,14 +907,20 @@ void __ext4_msg(struct super_block *sb,
 	struct va_format vaf;
 	va_list args;
 
-	atomic_inc(&EXT4_SB(sb)->s_msg_count);
-	if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
-		return;
+	if (sb) {
+		atomic_inc(&EXT4_SB(sb)->s_msg_count);
+		if (!___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);
+	if (sb)
+		printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
+	else
+		printk("%sEXT4-fs: %pV\n", prefix, &vaf);
 	va_end(args);
 }
 
@@ -2279,12 +2285,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:
@@ -2303,7 +2309,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 		sb->s_flags |= SB_INLINECRYPT;
 #else
-		ext4_msg(sb, KERN_ERR, "inline encryption not supported");
+		ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
 #endif
 		return 1;
 	case Opt_errors:
@@ -2319,22 +2325,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->key);
-		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->key);
-		return -1;
+		return -EINVAL;
 	}
 
 	if (m->flags & MOPT_EXPLICIT) {
@@ -2343,28 +2349,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) {
@@ -2372,9 +2378,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) {
@@ -2385,10 +2391,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) {
@@ -2407,24 +2413,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 (is_remount) {
-			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) {
@@ -2433,16 +2439,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		int error;
 
 		if (is_remount) {
-			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);
@@ -2450,9 +2456,9 @@ 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);
@@ -2461,11 +2467,11 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	} else if (m->flags & MOPT_DATAJ) {
 		if (is_remount) {
 			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);
@@ -2475,12 +2481,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;
@@ -2497,18 +2503,18 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			    (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
 			     (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
 			fail_dax_change_remount:
-				ext4_msg(sb, KERN_ERR, "can't change "
+				ext4_msg(NULL, KERN_ERR, "can't change "
 					 "dax mount option while remounting");
-				return -1;
+				return -EINVAL;
 			}
 			if (is_remount &&
 			    (test_opt(sb, DATA_FLAGS) ==
 			     EXT4_MOUNT_JOURNAL_DATA)) {
-				    ext4_msg(sb, KERN_ERR, "can't mount with "
+				    ext4_msg(NULL, KERN_ERR, "can't mount with "
 					     "both data=journal and dax");
-				    return -1;
+				    return -EINVAL;
 			}
-			ext4_msg(sb, KERN_WARNING,
+			ext4_msg(NULL, KERN_WARNING,
 				"DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
 			sbi->s_mount_opt |= EXT4_MOUNT_DAX_ALWAYS;
 			sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
@@ -2534,10 +2540,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			break;
 		}
 #else
-		ext4_msg(sb, KERN_INFO, "dax option not supported");
+		ext4_msg(NULL, KERN_INFO, "dax option not supported");
 		sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
 		sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
-		return -1;
+		return -EINVAL;
 #endif
 	} else if (token == Opt_data_err_abort) {
 		sbi->s_mount_opt |= m->mount_opt;
@@ -2545,9 +2551,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		sbi->s_mount_opt &= ~m->mount_opt;
 	} else if (token == Opt_mb_optimize_scan) {
 		if (result.int_32 != 0 && result.int_32 != 1) {
-			ext4_msg(sb, KERN_WARNING,
+			ext4_msg(NULL, KERN_WARNING,
 				 "mb_optimize_scan should be set to 0 or 1.");
-			return -1;
+			return -EINVAL;
 		}
 		ctx->mb_optimize_scan = result.int_32;
 	} else {
@@ -2560,11 +2566,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 (m->flags & MOPT_2) {
 			if (set != 0)
@@ -2632,12 +2638,17 @@ static int parse_options(char *options, struct super_block *sb,
 		}
 	}
 
-	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;
 	/*
@@ -2646,9 +2657,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);
@@ -2660,15 +2671,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
@@ -2676,11 +2687,12 @@ 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 "
-				 "experimental mount option 'dioread_nolock' "
-				 "for blocksize < PAGE_SIZE");
+			ext4_msg(NULL, KERN_WARNING,
+				 "Warning: mounting with an experimental "
+				 "option 'dioread_nolock' for "
+				 "blocksize < PAGE_SIZE");
 	}
-	return 1;
+	return 0;
 }
 
 static inline void ext4_show_quota_options(struct seq_file *seq,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 06/13] ext4: move quota configuration out of handle_mount_opt()
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (4 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 05/13] ext4: Allow sb to be NULL in ext4_msg() Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 07/13] ext4: check ext2/3 compatibility outside handle_mount_opt() Lukas Czerner
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

At the parsing phase of mount in the new mount api sb will not be
available so move quota confiquration out of handle_mount_opt() by
noting the quota file names in the ext4_fs_context structure to be
able to apply it later.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 258 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 165 insertions(+), 93 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index b3c545695f2f..69e51b4037d3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,6 +88,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
@@ -1979,71 +1983,6 @@ static const char deprecated_msg[] =
 	"Mount option \"%s\" will be removed by %s\n"
 	"Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
 
-#ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype,
-		       struct fs_parameter *param)
-{
-	struct ext4_sb_info *sbi = EXT4_SB(sb);
-	char *qname, *old_qname = get_qf_name(sb, sbi, qtype);
-	int ret = -1;
-
-	if (sb_any_quota_loaded(sb) && !old_qname) {
-		ext4_msg(sb, KERN_ERR,
-			"Cannot change journaled "
-			"quota options when quota turned on");
-		return -1;
-	}
-	if (ext4_has_feature_quota(sb)) {
-		ext4_msg(sb, KERN_INFO, "Journaled quota options "
-			 "ignored when QUOTA feature is enabled");
-		return 1;
-	}
-	qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
-	if (!qname) {
-		ext4_msg(sb, KERN_ERR,
-			"Not enough memory for storing quotafile name");
-		return -1;
-	}
-	if (old_qname) {
-		if (strcmp(old_qname, qname) == 0)
-			ret = 1;
-		else
-			ext4_msg(sb, KERN_ERR,
-				 "%s quota file already specified",
-				 QTYPE2NAME(qtype));
-		goto errout;
-	}
-	if (strchr(qname, '/')) {
-		ext4_msg(sb, KERN_ERR,
-			"quotafile must be on filesystem root");
-		goto errout;
-	}
-	rcu_assign_pointer(sbi->s_qf_names[qtype], qname);
-	set_opt(sb, QUOTA);
-	return 1;
-errout:
-	kfree(qname);
-	return ret;
-}
-
-static int clear_qf_name(struct super_block *sb, int qtype)
-{
-
-	struct ext4_sb_info *sbi = EXT4_SB(sb);
-	char *old_qname = get_qf_name(sb, sbi, qtype);
-
-	if (sb_any_quota_loaded(sb) && old_qname) {
-		ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
-			" when quota turned on");
-		return -1;
-	}
-	rcu_assign_pointer(sbi->s_qf_names[qtype], NULL);
-	synchronize_rcu();
-	kfree(old_qname);
-	return 1;
-}
-#endif
-
 #define MOPT_SET	0x0001
 #define MOPT_CLEAR	0x0002
 #define MOPT_NOSUPPORT	0x0004
@@ -2247,11 +2186,70 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb,
 }
 
 struct ext4_fs_context {
-	unsigned long journal_devnum;
-	unsigned int journal_ioprio;
-	int mb_optimize_scan;
+	char		*s_qf_names[EXT4_MAXQUOTAS];
+	int		s_jquota_fmt;	/* Format of quota to use */
+	unsigned short	qname_spec;
+	unsigned long	journal_devnum;
+	unsigned int	journal_ioprio;
+	int 		mb_optimize_scan;
 };
 
+#ifdef CONFIG_QUOTA
+/*
+ * Note the name of the specified quota file.
+ */
+static int note_qf_name(struct fs_context *fc, int qtype,
+		       struct fs_parameter *param)
+{
+	struct ext4_fs_context *ctx = fc->fs_private;
+	char *qname;
+
+	if (param->size < 1) {
+		ext4_msg(NULL, KERN_ERR, "Missing quota name");
+		return -EINVAL;
+	}
+	if (strchr(param->string, '/')) {
+		ext4_msg(NULL, KERN_ERR,
+			 "quotafile must be on filesystem root");
+		return -EINVAL;
+	}
+	if (ctx->s_qf_names[qtype]) {
+		if (strcmp(ctx->s_qf_names[qtype], param->string) != 0) {
+			ext4_msg(NULL, KERN_ERR,
+				 "%s quota file already specified",
+				 QTYPE2NAME(qtype));
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+	if (!qname) {
+		ext4_msg(NULL, KERN_ERR,
+			 "Not enough memory for storing quotafile name");
+		return -ENOMEM;
+	}
+	ctx->s_qf_names[qtype] = qname;
+	ctx->qname_spec |= 1 << qtype;
+	return 0;
+}
+
+/*
+ * Clear the name of the specified quota file.
+ */
+static int unnote_qf_name(struct fs_context *fc, int qtype)
+{
+	struct ext4_fs_context *ctx = fc->fs_private;
+
+	if (ctx->s_qf_names[qtype])
+		kfree(ctx->s_qf_names[qtype]);
+
+	ctx->s_qf_names[qtype] = NULL;
+	ctx->qname_spec |= 1 << qtype;
+	return 0;
+}
+#endif
+
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct ext4_fs_context *ctx = fc->fs_private;
@@ -2272,14 +2270,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) {
@@ -2353,11 +2351,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",
@@ -2479,19 +2472,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		}
 #ifdef CONFIG_QUOTA
 	} else if (m->flags & MOPT_QFMT) {
-		if (sb_any_quota_loaded(sb) &&
-		    sbi->s_jquota_fmt != m->mount_opt) {
-			ext4_msg(NULL, KERN_ERR, "Cannot change journaled "
-				 "quota options when quota turned on");
-			return -EINVAL;
-		}
-		if (ext4_has_feature_quota(sb)) {
-			ext4_msg(NULL, KERN_INFO,
-				 "Quota format mount options ignored "
-				 "when QUOTA feature is enabled");
-			return 1;
-		}
-		sbi->s_jquota_fmt = m->mount_opt;
+		ctx->s_jquota_fmt = m->mount_opt;
 #endif
 	} else if (token == Opt_dax || token == Opt_dax_always ||
 		   token == Opt_dax_inode || token == Opt_dax_never) {
@@ -2588,7 +2569,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 }
 
 static int parse_options(char *options, struct super_block *sb,
-			 struct ext4_fs_context *ret_opts,
+			 struct ext4_fs_context *ctx,
 			 int is_remount)
 {
 	struct fs_parameter param;
@@ -2600,7 +2581,7 @@ static int parse_options(char *options, struct super_block *sb,
 		return 1;
 
 	memset(&fc, 0, sizeof(fc));
-	fc.fs_private = ret_opts;
+	fc.fs_private = ctx;
 	fc.s_fs_info = EXT4_SB(sb);
 
 	if (is_remount)
@@ -2642,9 +2623,100 @@ static int parse_options(char *options, struct super_block *sb,
 	if (ret < 0)
 		return 0;
 
+	ret = ext4_check_quota_consistency(&fc, sb);
+	if (ret < 0)
+		return 0;
+
+	if (ctx->qname_spec)
+		ext4_apply_quota_options(&fc, sb);
+
 	return 1;
 }
 
+static void ext4_apply_quota_options(struct fs_context *fc,
+				     struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+	struct ext4_fs_context *ctx = fc->fs_private;
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	char *qname;
+	int i;
+
+	for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+		if (!(ctx->qname_spec & (1 << i)))
+			continue;
+		qname = ctx->s_qf_names[i]; /* May be NULL */
+		ctx->s_qf_names[i] = NULL;
+		kfree(sbi->s_qf_names[i]);
+		rcu_assign_pointer(sbi->s_qf_names[i], qname);
+		set_opt(sb, QUOTA);
+	}
+#endif
+}
+
+/*
+ * Check quota settings consistency.
+ */
+static int ext4_check_quota_consistency(struct fs_context *fc,
+					struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+	struct ext4_fs_context *ctx = fc->fs_private;
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	bool quota_feature = ext4_has_feature_quota(sb);
+	bool quota_loaded = sb_any_quota_loaded(sb);
+	int i;
+
+	if (ctx->qname_spec && quota_loaded) {
+		if (quota_feature)
+			goto err_feature;
+
+		for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+			if (!(ctx->qname_spec & (1 << i)))
+				continue;
+
+			if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+				goto err_jquota_change;
+
+			if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
+			    strcmp(sbi->s_qf_names[i],
+				   ctx->s_qf_names[i]) != 0)
+				goto err_jquota_specified;
+		}
+	}
+
+	if (ctx->s_jquota_fmt) {
+		if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
+			goto err_quota_change;
+		if (quota_feature) {
+			ext4_msg(NULL, KERN_INFO, "Quota format mount options "
+				 "ignored when QUOTA feature is enabled");
+			return 0;
+		}
+	}
+	return 0;
+
+err_quota_change:
+	ext4_msg(NULL, KERN_ERR,
+		 "Cannot change quota options when quota turned on");
+	return -EINVAL;
+err_jquota_change:
+	ext4_msg(NULL, KERN_ERR, "Cannot change journaled quota "
+		 "options when quota turned on");
+	return -EINVAL;
+err_jquota_specified:
+	ext4_msg(NULL, KERN_ERR, "%s quota file already specified",
+		 QTYPE2NAME(i));
+	return -EINVAL;
+err_feature:
+	ext4_msg(NULL, KERN_ERR, "Journaled quota options ignored "
+		 "when QUOTA feature is enabled");
+	return 0;
+#else
+	return 0;
+#endif
+}
+
 static int ext4_validate_options(struct fs_context *fc)
 {
 	struct ext4_sb_info *sbi = fc->s_fs_info;
@@ -4099,7 +4171,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	__u64 blocks_count;
 	int err = 0;
 	ext4_group_t first_not_zeroed;
-	struct ext4_fs_context parsed_opts;
+	struct ext4_fs_context parsed_opts = {0};
 
 	/* Set defaults for the variables that will be set during parsing */
 	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 07/13] ext4: check ext2/3 compatibility outside handle_mount_opt()
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (5 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 06/13] ext4: move quota configuration out of handle_mount_opt() Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 08/13] ext4: get rid of super block and sbi from handle_mount_ops() Lukas Czerner
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

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 <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 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 69e51b4037d3..81027834aeb2 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,8 +88,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);
 
@@ -2192,6 +2192,7 @@ struct ext4_fs_context {
 	unsigned long	journal_devnum;
 	unsigned int	journal_ioprio;
 	int 		mb_optimize_scan;
+	unsigned int	opt_flags;	/* MOPT flags */
 };
 
 #ifdef CONFIG_QUOTA
@@ -2322,25 +2323,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->key);
-		return -EINVAL;
-	}
-	if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
-		ext4_msg(NULL, KERN_ERR,
-			 "Mount option \"%s\" incompatible with ext3",
-			 param->key);
-		return -EINVAL;
-	}
-
 	if (m->flags & MOPT_EXPLICIT) {
 		if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
 			set_opt2(sb, EXPLICIT_DELALLOC);
@@ -2623,7 +2613,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;
 
@@ -2717,6 +2707,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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 08/13] ext4: get rid of super block and sbi from handle_mount_ops()
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (6 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 07/13] ext4: check ext2/3 compatibility outside handle_mount_opt() Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 09/13] ext4: Completely separate options parsing and sb setup Lukas Czerner
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

At the parsing phase of mount in the new mount api sb will not be
available. We've already removed some uses of sb and sbi, but now we
need to get rid of the rest of it.

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

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 541 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 368 insertions(+), 173 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 81027834aeb2..4c8aaa45cf8c 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -90,8 +90,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,
 static int ext4_validate_options(struct fs_context *fc);
 static int ext4_check_opt_consistency(struct fs_context *fc,
 				      struct super_block *sb);
-static void ext4_apply_quota_options(struct fs_context *fc,
-				     struct super_block *sb);
+static int ext4_apply_options(struct fs_context *fc, struct super_block *sb);
 
 /*
  * Lock ordering
@@ -2142,57 +2141,74 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
 }
 #endif
 
-static int ext4_set_test_dummy_encryption(struct super_block *sb,
-					  struct fs_parameter *param,
-					  bool is_remount)
+static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
 {
 #ifdef CONFIG_FS_ENCRYPTION
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
 	int err;
 
-	/*
-	 * This mount option is just for testing, and it's not worthwhile to
-	 * implement the extra complexity (e.g. RCU protection) that would be
-	 * needed to allow it to be set or changed during remount.  We do allow
-	 * it to be specified during remount, but only if there is no change.
-	 */
-	if (is_remount && !sbi->s_dummy_enc_policy.policy) {
-		ext4_msg(sb, KERN_WARNING,
-			 "Can't set test_dummy_encryption on remount");
-		return -1;
-	}
-	err = fscrypt_set_test_dummy_encryption(sb, param->string,
+	err = fscrypt_set_test_dummy_encryption(sb, arg,
 						&sbi->s_dummy_enc_policy);
 	if (err) {
-		if (err == -EEXIST)
-			ext4_msg(sb, KERN_WARNING,
-				 "Can't change test_dummy_encryption on remount");
-		else if (err == -EINVAL)
-			ext4_msg(sb, KERN_WARNING,
-				 "Value of option \"%s\" is unrecognized",
-				 param->key);
-		else
-			ext4_msg(sb, KERN_WARNING,
-				 "Error processing option \"%s\" [%d]",
-				 param->key, err);
-		return -1;
+		ext4_msg(sb, KERN_WARNING,
+			 "Error while setting test dummy encryption [%d]", err);
+		return err;
 	}
 	ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
-#else
-	ext4_msg(sb, KERN_WARNING,
-		 "Test dummy encryption mount option ignored");
 #endif
-	return 1;
+	return 0;
 }
 
+#define EXT4_SPEC_JQUOTA			(1 <<  0)
+#define EXT4_SPEC_JQFMT				(1 <<  1)
+#define EXT4_SPEC_DATAJ				(1 <<  2)
+#define EXT4_SPEC_SB_BLOCK			(1 <<  3)
+#define EXT4_SPEC_JOURNAL_DEV			(1 <<  4)
+#define EXT4_SPEC_JOURNAL_IOPRIO		(1 <<  5)
+#define EXT4_SPEC_DUMMY_ENCRYPTION		(1 <<  6)
+#define EXT4_SPEC_s_want_extra_isize		(1 <<  7)
+#define EXT4_SPEC_s_max_batch_time		(1 <<  8)
+#define EXT4_SPEC_s_min_batch_time		(1 <<  9)
+#define EXT4_SPEC_s_inode_readahead_blks	(1 << 10)
+#define EXT4_SPEC_s_li_wait_mult		(1 << 11)
+#define EXT4_SPEC_s_max_dir_size_kb		(1 << 12)
+#define EXT4_SPEC_s_stripe			(1 << 13)
+#define EXT4_SPEC_s_resuid			(1 << 14)
+#define EXT4_SPEC_s_resgid			(1 << 15)
+#define EXT4_SPEC_s_commit_interval		(1 << 16)
+#define EXT4_SPEC_s_fc_debug_max_replay		(1 << 17)
+
 struct ext4_fs_context {
 	char		*s_qf_names[EXT4_MAXQUOTAS];
+	char		*test_dummy_enc_arg;
 	int		s_jquota_fmt;	/* Format of quota to use */
+	int		mb_optimize_scan;
+#ifdef CONFIG_EXT4_DEBUG
+	int s_fc_debug_max_replay;
+#endif
 	unsigned short	qname_spec;
+	unsigned long	vals_s_flags;	/* Bits to set in s_flags */
+	unsigned long	mask_s_flags;	/* Bits changed in s_flags */
 	unsigned long	journal_devnum;
+	unsigned long	s_commit_interval;
+	unsigned long	s_stripe;
+	unsigned int	s_inode_readahead_blks;
+	unsigned int	s_want_extra_isize;
+	unsigned int	s_li_wait_mult;
+	unsigned int	s_max_dir_size_kb;
 	unsigned int	journal_ioprio;
-	int 		mb_optimize_scan;
+	unsigned int	vals_s_mount_opt;
+	unsigned int	mask_s_mount_opt;
+	unsigned int	vals_s_mount_opt2;
+	unsigned int	mask_s_mount_opt2;
+	unsigned int	vals_s_mount_flags;
+	unsigned int	mask_s_mount_flags;
 	unsigned int	opt_flags;	/* MOPT flags */
+	unsigned int	spec;
+	u32		s_max_batch_time;
+	u32		s_min_batch_time;
+	kuid_t		s_resuid;
+	kgid_t		s_resgid;
 };
 
 #ifdef CONFIG_QUOTA
@@ -2232,6 +2248,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;
 }
 
@@ -2247,15 +2264,35 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)
 
 	ctx->s_qf_names[qtype] = NULL;
 	ctx->qname_spec |= 1 << qtype;
+	ctx->spec |= EXT4_SPEC_JQUOTA;
 	return 0;
 }
 #endif
 
+#define EXT4_SET_CTX(name)						\
+static inline void ctx_set_##name(struct ext4_fs_context *ctx, int flag)\
+{									\
+	ctx->mask_s_##name |= flag;					\
+	ctx->vals_s_##name |= flag;					\
+}									\
+static inline void ctx_clear_##name(struct ext4_fs_context *ctx, int flag)\
+{									\
+	ctx->mask_s_##name |= flag;					\
+	ctx->vals_s_##name &= ~flag;					\
+}									\
+static inline bool ctx_test_##name(struct ext4_fs_context *ctx, int flag)\
+{									\
+	return ((ctx->vals_s_##name & flag) != 0);			\
+}									\
+
+EXT4_SET_CTX(flags);
+EXT4_SET_CTX(mount_opt);
+EXT4_SET_CTX(mount_opt2);
+EXT4_SET_CTX(mount_flags);
+
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct ext4_fs_context *ctx = fc->fs_private;
-	struct ext4_sb_info *sbi = fc->s_fs_info;
-	struct super_block *sb = sbi->s_sb;
 	struct fs_parse_result result;
 	const struct mount_opts *m;
 	int is_remount;
@@ -2293,20 +2330,20 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			 param->key);
 		return 1;
 	case Opt_abort:
-		ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
+		ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
 		return 1;
 	case Opt_i_version:
-		sb->s_flags |= SB_I_VERSION;
+		ctx_set_flags(ctx, SB_I_VERSION);
 		return 1;
 	case Opt_lazytime:
-		sb->s_flags |= SB_LAZYTIME;
+		ctx_set_flags(ctx, SB_LAZYTIME);
 		return 1;
 	case Opt_nolazytime:
-		sb->s_flags &= ~SB_LAZYTIME;
+		ctx_clear_flags(ctx, SB_LAZYTIME);
 		return 1;
 	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-		sb->s_flags |= SB_INLINECRYPT;
+		ctx_set_flags(ctx, SB_INLINECRYPT);
 #else
 		ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
 #endif
@@ -2333,21 +2370,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
 	if (m->flags & MOPT_EXPLICIT) {
 		if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
-			set_opt2(sb, EXPLICIT_DELALLOC);
+			ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
 		} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
-			set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
+			ctx_set_mount_opt2(ctx,
+				       EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
 		} else
 			return -EINVAL;
 	}
 	if (m->flags & MOPT_CLEAR_ERR)
-		clear_opt(sb, ERRORS_MASK);
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
 
 	if (m->flags & MOPT_NOSUPPORT) {
 		ext4_msg(NULL, KERN_ERR, "%s option not supported",
 			 param->key);
 	} else if (token == Opt_commit) {
 		if (result.uint_32 == 0)
-			sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
+			ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
 		else if (result.uint_32 > INT_MAX / HZ) {
 			ext4_msg(NULL, KERN_ERR,
 				 "Invalid commit interval %d, "
@@ -2355,21 +2393,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) ||
@@ -2379,20 +2418,25 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				 "0 or a power of 2 smaller than 2^31");
 			return -EINVAL;
 		}
-		sbi->s_inode_readahead_blks = result.uint_32;
+		ctx->s_inode_readahead_blks = result.uint_32;
+		ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
 	} else if (token == Opt_init_itable) {
-		set_opt(sb, INIT_INODE_TABLE);
-		sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+		ctx_set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
+		ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
 		if (param->type == fs_value_is_string)
-			sbi->s_li_wait_mult = result.uint_32;
+			ctx->s_li_wait_mult = result.uint_32;
+		ctx->spec |= EXT4_SPEC_s_li_wait_mult;
 	} else if (token == Opt_max_dir_size_kb) {
-		sbi->s_max_dir_size_kb = result.uint_32;
+		ctx->s_max_dir_size_kb = result.uint_32;
+		ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
 #ifdef CONFIG_EXT4_DEBUG
 	} else if (token == Opt_fc_debug_max_replay) {
-		sbi->s_fc_debug_max_replay = result.uint_32;
+		ctx->s_fc_debug_max_replay = result.uint_32;
+		ctx->spec |= EXT4_SPEC_s_fc_debug_max_replay;
 #endif
 	} else if (token == Opt_stripe) {
-		sbi->s_stripe = result.uint_32;
+		ctx->s_stripe = result.uint_32;
+		ctx->spec |= EXT4_SPEC_s_stripe;
 	} else if (token == Opt_resuid) {
 		uid = make_kuid(current_user_ns(), result.uint_32);
 		if (!uid_valid(uid)) {
@@ -2400,7 +2444,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)) {
@@ -2408,7 +2453,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				 result.uint_32);
 			return -EINVAL;
 		}
-		sbi->s_resgid = gid;
+		ctx->s_resgid = gid;
+		ctx->spec |= EXT4_SPEC_s_resgid;
 	} else if (token == Opt_journal_dev) {
 		if (is_remount) {
 			ext4_msg(NULL, KERN_ERR,
@@ -2416,6 +2462,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;
@@ -2436,6 +2483,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) {
@@ -2445,24 +2493,37 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->journal_ioprio =
 			IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
+		ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
 	} else if (token == Opt_test_dummy_encryption) {
-		return ext4_set_test_dummy_encryption(sb, param, is_remount);
-	} else if (m->flags & MOPT_DATAJ) {
-		if (is_remount) {
-			if (!sbi->s_journal)
-				ext4_msg(NULL, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
-			else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
-				ext4_msg(NULL, KERN_ERR,
-					 "Cannot change data mode on remount");
-				return -EINVAL;
-			}
-		} else {
-			clear_opt(sb, DATA_FLAGS);
-			sbi->s_mount_opt |= m->mount_opt;
+#ifdef CONFIG_FS_ENCRYPTION
+		if (param->type == fs_value_is_flag) {
+			ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
+			ctx->test_dummy_enc_arg = NULL;
+			return 1;
+		}
+		if (*param->string &&
+		    !(!strcmp(param->string, "v1") ||
+		      !strcmp(param->string, "v2"))) {
+			ext4_msg(NULL, KERN_WARNING,
+				 "Value of option \"%s\" is unrecognized",
+				 param->key);
+			return -EINVAL;
 		}
+		ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
+		ctx->test_dummy_enc_arg = kmemdup_nul(param->string, param->size,
+						      GFP_KERNEL);
+#else
+		ext4_msg(NULL, KERN_WARNING,
+			 "Test dummy encryption mount option ignored");
+#endif
+	} else if (m->flags & MOPT_DATAJ) {
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+		ctx_set_mount_opt(ctx, m->mount_opt);
+		ctx->spec |= EXT4_SPEC_DATAJ;
 #ifdef CONFIG_QUOTA
 	} else if (m->flags & MOPT_QFMT) {
 		ctx->s_jquota_fmt = m->mount_opt;
+		ctx->spec |= EXT4_SPEC_JQFMT;
 #endif
 	} else if (token == Opt_dax || token == Opt_dax_always ||
 		   token == Opt_dax_inode || token == Opt_dax_never) {
@@ -2470,56 +2531,30 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		switch (token) {
 		case Opt_dax:
 		case Opt_dax_always:
-			if (is_remount &&
-			    (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
-			     (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
-			fail_dax_change_remount:
-				ext4_msg(NULL, KERN_ERR, "can't change "
-					 "dax mount option while remounting");
-				return -EINVAL;
-			}
-			if (is_remount &&
-			    (test_opt(sb, DATA_FLAGS) ==
-			     EXT4_MOUNT_JOURNAL_DATA)) {
-				    ext4_msg(NULL, KERN_ERR, "can't mount with "
-					     "both data=journal and dax");
-				    return -EINVAL;
-			}
-			ext4_msg(NULL, KERN_WARNING,
-				"DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
-			sbi->s_mount_opt |= EXT4_MOUNT_DAX_ALWAYS;
-			sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+			ctx_set_mount_opt(ctx, m->mount_opt);
+			ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 			break;
 		case Opt_dax_never:
-			if (is_remount &&
-			    (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
-			     (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS)))
-				goto fail_dax_change_remount;
-			sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
-			sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+			ctx_set_mount_opt2(ctx, m->mount_opt);
+			ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 			break;
 		case Opt_dax_inode:
-			if (is_remount &&
-			    ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
-			     (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
-			     !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE)))
-				goto fail_dax_change_remount;
-			sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
-			sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
+			ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
+			ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 			/* Strictly for printing options */
-			sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_INODE;
+			ctx_set_mount_opt2(ctx, m->mount_opt);
 			break;
 		}
 #else
 		ext4_msg(NULL, KERN_INFO, "dax option not supported");
-		sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
-		sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
+		ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 		return -EINVAL;
 #endif
 	} else if (token == Opt_data_err_abort) {
-		sbi->s_mount_opt |= m->mount_opt;
+		ctx_set_mount_opt(ctx, m->mount_opt);
 	} else if (token == Opt_data_err_ignore) {
-		sbi->s_mount_opt &= ~m->mount_opt;
+		ctx_clear_mount_opt(ctx, m->mount_opt);
 	} else if (token == Opt_mb_optimize_scan) {
 		if (result.int_32 != 0 && result.int_32 != 1) {
 			ext4_msg(NULL, KERN_WARNING,
@@ -2545,14 +2580,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		}
 		if (m->flags & MOPT_2) {
 			if (set != 0)
-				sbi->s_mount_opt2 |= m->mount_opt;
+				ctx_set_mount_opt2(ctx, m->mount_opt);
 			else
-				sbi->s_mount_opt2 &= ~m->mount_opt;
+				ctx_clear_mount_opt2(ctx, m->mount_opt);
 		} else {
 			if (set != 0)
-				sbi->s_mount_opt |= m->mount_opt;
+				ctx_set_mount_opt(ctx, m->mount_opt);
 			else
-				sbi->s_mount_opt &= ~m->mount_opt;
+				ctx_clear_mount_opt(ctx, m->mount_opt);
 		}
 	}
 	return 1;
@@ -2617,8 +2652,9 @@ static int parse_options(char *options, struct super_block *sb,
 	if (ret < 0)
 		return 0;
 
-	if (ctx->qname_spec)
-		ext4_apply_quota_options(&fc, sb);
+	ret = ext4_apply_options(&fc, sb);
+	if (ret < 0)
+		return 0;
 
 	return 1;
 }
@@ -2627,20 +2663,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
 }
 
@@ -2655,17 +2701,36 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
 	struct ext4_sb_info *sbi = EXT4_SB(sb);
 	bool quota_feature = ext4_has_feature_quota(sb);
 	bool quota_loaded = sb_any_quota_loaded(sb);
-	int i;
+	bool usr_qf_name, grp_qf_name, usrquota, grpquota;
+	int quota_flags, i;
+
+	/*
+	 * We do the test below only for project quotas. 'usrquota' and
+	 * 'grpquota' mount options are allowed even without quota feature
+	 * to support legacy quotas in quota files.
+	 */
+	if (ctx_test_mount_opt(ctx, EXT4_MOUNT_PRJQUOTA) &&
+	    !ext4_has_feature_project(sb)) {
+		ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
+			 "Cannot enable project quota enforcement.");
+		return -EINVAL;
+	}
+
+	quota_flags = EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
+		      EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA;
+	if (quota_loaded &&
+	    ctx->mask_s_mount_opt & quota_flags &&
+	    !ctx_test_mount_opt(ctx, quota_flags))
+		goto err_quota_change;
 
-	if (ctx->qname_spec && quota_loaded) {
-		if (quota_feature)
-			goto err_feature;
+	if (ctx->spec & EXT4_SPEC_JQUOTA) {
 
 		for (i = 0; i < EXT4_MAXQUOTAS; i++) {
 			if (!(ctx->qname_spec & (1 << i)))
 				continue;
 
-			if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+			if (quota_loaded &&
+			    !!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
 				goto err_jquota_change;
 
 			if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
@@ -2673,17 +2738,60 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
 				   ctx->s_qf_names[i]) != 0)
 				goto err_jquota_specified;
 		}
+
+		if (quota_feature) {
+			ext4_msg(NULL, KERN_INFO,
+				 "Journaled quota options ignored when "
+				 "QUOTA feature is enabled");
+			return 0;
+		}
 	}
 
-	if (ctx->s_jquota_fmt) {
+	if (ctx->spec & EXT4_SPEC_JQFMT) {
 		if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
-			goto err_quota_change;
+			goto err_jquota_change;
 		if (quota_feature) {
 			ext4_msg(NULL, KERN_INFO, "Quota format mount options "
 				 "ignored when QUOTA feature is enabled");
 			return 0;
 		}
 	}
+
+	/* Make sure we don't mix old and new quota format */
+	usr_qf_name = (get_qf_name(sb, sbi, USRQUOTA) ||
+		       ctx->s_qf_names[USRQUOTA]);
+	grp_qf_name = (get_qf_name(sb, sbi, GRPQUOTA) ||
+		       ctx->s_qf_names[GRPQUOTA]);
+
+	usrquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+		    test_opt(sb, USRQUOTA));
+
+	grpquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
+		    test_opt(sb, GRPQUOTA));
+
+	if (usr_qf_name) {
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
+		usrquota = false;
+	}
+	if (grp_qf_name) {
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
+		grpquota = false;
+	}
+
+	if (usr_qf_name || grp_qf_name) {
+		if (usrquota || grpquota) {
+			ext4_msg(NULL, KERN_ERR, "old and new quota "
+				 "format mixing");
+			return -EINVAL;
+		}
+
+		if (!(ctx->spec & EXT4_SPEC_JQFMT || sbi->s_jquota_fmt)) {
+			ext4_msg(NULL, KERN_ERR, "journaled quota format "
+				 "not specified");
+			return -EINVAL;
+		}
+	}
+
 	return 0;
 
 err_quota_change:
@@ -2698,10 +2806,6 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
 	ext4_msg(NULL, KERN_ERR, "%s quota file already specified",
 		 QTYPE2NAME(i));
 	return -EINVAL;
-err_feature:
-	ext4_msg(NULL, KERN_ERR, "Journaled quota options ignored "
-		 "when QUOTA feature is enabled");
-	return 0;
 #else
 	return 0;
 #endif
@@ -2711,6 +2815,8 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
 				      struct super_block *sb)
 {
 	struct ext4_fs_context *ctx = fc->fs_private;
+	struct ext4_sb_info *sbi = fc->s_fs_info;
+	int is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 
 	if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
 		ext4_msg(NULL, KERN_ERR,
@@ -2723,57 +2829,146 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
 		return -EINVAL;
 	}
 
+	if (ctx->s_want_extra_isize >
+	    (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE)) {
+		ext4_msg(NULL, KERN_ERR,
+			 "Invalid want_extra_isize %d",
+			 ctx->s_want_extra_isize);
+		return -EINVAL;
+	}
+
+	if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DIOREAD_NOLOCK)) {
+		int blocksize =
+			BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
+		if (blocksize < PAGE_SIZE)
+			ext4_msg(NULL, KERN_WARNING, "Warning: mounting with an "
+				 "experimental mount option 'dioread_nolock' "
+				 "for blocksize < PAGE_SIZE");
+	}
+
+#ifdef CONFIG_FS_ENCRYPTION
+	/*
+	 * This mount option is just for testing, and it's not worthwhile to
+	 * implement the extra complexity (e.g. RCU protection) that would be
+	 * needed to allow it to be set or changed during remount.  We do allow
+	 * it to be specified during remount, but only if there is no change.
+	 */
+	if ((ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION) &&
+	    is_remount && !sbi->s_dummy_enc_policy.policy) {
+		ext4_msg(NULL, KERN_WARNING,
+			 "Can't set test_dummy_encryption on remount");
+		return -1;
+	}
+#endif
+
+	if ((ctx->spec & EXT4_SPEC_DATAJ) && is_remount) {
+		if (!sbi->s_journal) {
+			ext4_msg(NULL, KERN_WARNING,
+				 "Remounting file system with no journal "
+				 "so ignoring journalled data option");
+			ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+		} else if (ctx->mask_s_mount_opt & EXT4_MOUNT_DATA_FLAGS) {
+			ext4_msg(NULL, KERN_ERR, "Cannot change data mode "
+				 "on remount");
+			return -EINVAL;
+		}
+	}
+
+	if (is_remount) {
+		if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+		    (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) {
+			ext4_msg(NULL, KERN_ERR, "can't mount with "
+				 "both data=journal and dax");
+			return -EINVAL;
+		}
+
+		if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
+		    (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+		     (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
+fail_dax_change_remount:
+			ext4_msg(NULL, KERN_ERR, "can't change "
+				 "dax mount option while remounting");
+			return -EINVAL;
+		} else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER) &&
+			 (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+			  (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS))) {
+			goto fail_dax_change_remount;
+		} else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE) &&
+			   ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
+			    (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
+			    !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE))) {
+			goto fail_dax_change_remount;
+		}
+	}
+
 	return ext4_check_quota_consistency(fc, sb);
 }
 
-static int ext4_validate_options(struct fs_context *fc)
+static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)
 {
+	struct ext4_fs_context *ctx = fc->fs_private;
 	struct ext4_sb_info *sbi = fc->s_fs_info;
-	struct super_block *sb = sbi->s_sb;
+	int ret = 0;
+
+	sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
+	sbi->s_mount_opt |= ctx->vals_s_mount_opt;
+	sbi->s_mount_opt2 &= ~ctx->mask_s_mount_opt2;
+	sbi->s_mount_opt2 |= ctx->vals_s_mount_opt2;
+	sbi->s_mount_flags &= ~ctx->mask_s_mount_flags;
+	sbi->s_mount_flags |= ctx->vals_s_mount_flags;
+	sb->s_flags &= ~ctx->mask_s_flags;
+	sb->s_flags |= ctx->vals_s_flags;
+
+#define APPLY(X) ({ if (ctx->spec & EXT4_SPEC_##X) sbi->X = ctx->X; })
+	APPLY(s_commit_interval);
+	APPLY(s_stripe);
+	APPLY(s_max_batch_time);
+	APPLY(s_min_batch_time);
+	APPLY(s_want_extra_isize);
+	APPLY(s_inode_readahead_blks);
+	APPLY(s_max_dir_size_kb);
+	APPLY(s_li_wait_mult);
+	APPLY(s_resgid);
+	APPLY(s_resuid);
+
+#ifdef CONFIG_EXT4_DEBUG
+	APPLY(s_fc_debug_max_replay);
+#endif
+
+	ext4_apply_quota_options(fc, sb);
+
+	if (ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION)
+		ret = ext4_set_test_dummy_encryption(sb, ctx->test_dummy_enc_arg);
+
+	return ret;
+}
+
+
+static int ext4_validate_options(struct fs_context *fc)
+{
 #ifdef CONFIG_QUOTA
+	struct ext4_fs_context *ctx = fc->fs_private;
 	char *usr_qf_name, *grp_qf_name;
-	/*
-	 * We do the test below only for project quotas. 'usrquota' and
-	 * 'grpquota' mount options are allowed even without quota feature
-	 * to support legacy quotas in quota files.
-	 */
-	if (test_opt(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
-		ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
-			 "Cannot enable project quota enforcement.");
-		return -EINVAL;
-	}
-	usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
-	grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
+
+	usr_qf_name = ctx->s_qf_names[USRQUOTA];
+	grp_qf_name = ctx->s_qf_names[GRPQUOTA];
+
 	if (usr_qf_name || grp_qf_name) {
-		if (test_opt(sb, USRQUOTA) && usr_qf_name)
-			clear_opt(sb, USRQUOTA);
+		if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
+			ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
 
-		if (test_opt(sb, GRPQUOTA) && grp_qf_name)
-			clear_opt(sb, GRPQUOTA);
+		if (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) && grp_qf_name)
+			ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
 
-		if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+		if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+		    ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA)) {
 			ext4_msg(NULL, KERN_ERR, "old and new quota "
-					"format mixing");
-			return -EINVAL;
-		}
-
-		if (!sbi->s_jquota_fmt) {
-			ext4_msg(NULL, KERN_ERR, "journaled quota format "
-					"not specified");
+				 "format mixing");
 			return -EINVAL;
 		}
 	}
 #endif
-	if (test_opt(sb, DIOREAD_NOLOCK)) {
-		int blocksize =
-			BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
-		if (blocksize < PAGE_SIZE)
-			ext4_msg(NULL, KERN_WARNING,
-				 "Warning: mounting with an experimental "
-				 "option 'dioread_nolock' for "
-				 "blocksize < PAGE_SIZE");
-	}
-	return 0;
+	return 1;
 }
 
 static inline void ext4_show_quota_options(struct seq_file *seq,
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 09/13] ext4: Completely separate options parsing and sb setup
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (7 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 08/13] ext4: get rid of super block and sbi from handle_mount_ops() Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 10/13] ext4: clean up return values in handle_mount_opt() Lukas Czerner
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

The new mount api separates option parsing and super block setup into
two distinct steps and so we need to separate the options parsing out of
the ext4_fill_super() and ext4_remount().

In order to achieve this we have to create new ext4_fill_super() and
ext4_remount() functions which will serve its purpose only until we
actually do convert to the new api (as such they are only temporary for
this patch series) and move the option parsing out of the old function
which will now be renamed to __ext4_fill_super() and __ext4_remount().

There is a small complication in the fact that while the mount option
parsing is going to happen before we get to __ext4_fill_super(), the
mount options stored in the super block itself needs to be applied
first, before the 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 not exactly the most elegant solution, but if we still want to
support s_mount_opts we have to do it in this order.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 399 ++++++++++++++++++++++++++++++++----------------
 1 file changed, 264 insertions(+), 135 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 4c8aaa45cf8c..21845f899c50 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1952,29 +1952,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))
 #define DEFAULT_MB_OPTIMIZE_SCAN	(-1)
 
@@ -2177,6 +2154,7 @@ static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
 #define EXT4_SPEC_s_resgid			(1 << 15)
 #define EXT4_SPEC_s_commit_interval		(1 << 16)
 #define EXT4_SPEC_s_fc_debug_max_replay		(1 << 17)
+#define EXT4_SPEC_s_sb_block			(1 << 18)
 
 struct ext4_fs_context {
 	char		*s_qf_names[EXT4_MAXQUOTAS];
@@ -2209,6 +2187,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
@@ -2324,7 +2303,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
 		break;
 	case Opt_sb:
-		return 1;	/* handled by get_sb_block() */
+		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;
+		}
+		return 1;
 	case Opt_removed:
 		ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
 			 param->key);
@@ -2593,24 +2579,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,
-			 struct ext4_fs_context *ctx,
-			 int is_remount)
+static int parse_options(struct fs_context *fc, char *options)
 {
 	struct fs_parameter param;
-	struct fs_context fc;
 	int ret;
 	char *key;
 
 	if (!options)
-		return 1;
-
-	memset(&fc, 0, sizeof(fc));
-	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) {
@@ -2629,34 +2605,83 @@ 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 0;
+		return ret;
 
-	ret = ext4_check_opt_consistency(&fc, sb);
-	if (ret < 0)
+	return 0;
+}
+
+static int parse_apply_sb_mount_options(struct super_block *sb,
+					struct ext4_fs_context *m_ctx)
+{
+	struct ext4_sb_info *sbi = EXT4_SB(sb);
+	char *s_mount_opts = NULL;
+	struct ext4_fs_context *s_ctx = NULL;
+	struct fs_context *fc = NULL;
+	int ret = -ENOMEM;
+
+	if (!sbi->s_es->s_mount_opts[0])
 		return 0;
 
-	ret = ext4_apply_options(&fc, sb);
+	s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
+				sizeof(sbi->s_es->s_mount_opts),
+				GFP_KERNEL);
+	if (!s_mount_opts)
+		return ret;
+
+	fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
+	if (!fc)
+		goto out_free;
+
+	s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
+	if (!s_ctx)
+		goto out_free;
+
+	fc->fs_private = s_ctx;
+	fc->s_fs_info = sbi;
+
+	ret = parse_options(fc, s_mount_opts);
 	if (ret < 0)
-		return 0;
+		goto parse_failed;
 
-	return 1;
+	ret = ext4_check_opt_consistency(fc, sb);
+	if (ret < 0) {
+parse_failed:
+		ext4_msg(sb, KERN_WARNING,
+			 "failed to parse options in superblock: %s",
+			 s_mount_opts);
+		ret = 0;
+		goto out_free;
+	}
+
+	if (s_ctx->spec & EXT4_SPEC_JOURNAL_DEV)
+		m_ctx->journal_devnum = s_ctx->journal_devnum;
+	if (s_ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
+		m_ctx->journal_ioprio = s_ctx->journal_ioprio;
+
+	ret = ext4_apply_options(fc, sb);
+
+out_free:
+	kfree(s_ctx);
+	kfree(fc);
+	kfree(s_mount_opts);
+	return ret;
 }
 
 static void ext4_apply_quota_options(struct fs_context *fc,
@@ -4352,21 +4377,53 @@ static void ext4_setup_csum_trigger(struct super_block *sb,
 	sbi->s_journal_triggers[type].tr_triggers.t_frozen = trigger;
 }
 
-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 def_mount_opts;
 	struct inode *root;
-	const char *descr;
 	int ret = -ENOMEM;
 	int blocksize, clustersize;
 	unsigned int db_count;
@@ -4375,32 +4432,16 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	__u64 blocks_count;
 	int err = 0;
 	ext4_group_t first_not_zeroed;
-	struct ext4_fs_context parsed_opts = {0};
+	struct ext4_fs_context *ctx = fc->fs_private;
 
 	/* Set defaults for the variables that will be set during parsing */
-	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
-	parsed_opts.journal_devnum = 0;
-	parsed_opts.mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
-
-	if ((data && !orig_data) || !sbi)
-		goto out_free_base;
+	ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
+	ctx->mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
 
-	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;
 	sbi->s_sectors_written_start =
 		part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
 
-	/* Cleanup superblock name */
-	strreplace(sb->s_id, '/', '!');
-
 	/* -EINVAL is default */
 	ret = -EINVAL;
 	blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
@@ -4414,10 +4455,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;
 	}
 
 	bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
@@ -4622,21 +4663,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		}
 	}
 
-	if (sbi->s_es->s_mount_opts[0]) {
-		char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
-					      sizeof(sbi->s_es->s_mount_opts),
-					      GFP_KERNEL);
-		if (!s_mount_opts)
-			goto failed_mount;
-		if (!parse_options(s_mount_opts, sb, &parsed_opts, 0)) {
-			ext4_msg(sb, KERN_WARNING,
-				 "failed to parse options in superblock: %s",
-				 s_mount_opts);
-		}
-		kfree(s_mount_opts);
-	}
+	err = parse_apply_sb_mount_options(sb, ctx);
+	if (err < 0)
+		goto failed_mount;
+
 	sbi->s_def_mount_opt = sbi->s_mount_opt;
-	if (!parse_options((char *) data, sb, &parsed_opts, 0))
+
+	err = ext4_check_opt_consistency(fc, sb);
+	if (err < 0)
+		goto failed_mount;
+
+	err = ext4_apply_options(fc, sb);
+	if (err < 0)
 		goto failed_mount;
 
 #ifdef CONFIG_UNICODE
@@ -4775,7 +4813,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		goto failed_mount;
 	}
 
-	if (dax_supported(dax_dev, sb->s_bdev, blocksize, 0,
+	if (dax_supported(sbi->s_daxdev, sb->s_bdev, blocksize, 0,
 			bdev_nr_sectors(sb->s_bdev)))
 		set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);
 
@@ -4813,7 +4851,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 			goto failed_mount;
 		}
 
-		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 = ext4_sb_bread_unmovable(sb, logical_sb_block);
 		if (IS_ERR(bh)) {
@@ -5129,7 +5167,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	 * root first: it may be modified in the journal!
 	 */
 	if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
-		err = ext4_load_journal(sb, es, parsed_opts.journal_devnum);
+		err = ext4_load_journal(sb, es, ctx->journal_devnum);
 		if (err)
 			goto failed_mount3a;
 	} else if (test_opt(sb, NOLOAD) && !sb_rdonly(sb) &&
@@ -5229,7 +5267,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		goto failed_mount_wq;
 	}
 
-	set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
+	set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
 
 	sbi->s_journal->j_submit_inode_data_buffers =
 		ext4_journal_submit_inode_data_buffers;
@@ -5341,9 +5379,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	 * turned off by passing "mb_optimize_scan=0". This can also be
 	 * turned on forcefully by passing "mb_optimize_scan=1".
 	 */
-	if (parsed_opts.mb_optimize_scan == 1)
+	if (ctx->mb_optimize_scan == 1)
 		set_opt2(sb, MB_OPTIMIZE_SCAN);
-	else if (parsed_opts.mb_optimize_scan == 0)
+	else if (ctx->mb_optimize_scan == 0)
 		clear_opt2(sb, MB_OPTIMIZE_SCAN);
 	else if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD)
 		set_opt2(sb, MB_OPTIMIZE_SCAN);
@@ -5445,15 +5483,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 		if (err)
 			goto failed_mount9;
 	}
-	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 (test_opt(sb, DISCARD)) {
 		struct request_queue *q = bdev_get_queue(sb->s_bdev);
@@ -5463,14 +5492,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 				 "the device does not support discard");
 	}
 
-	if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
-		ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
-			 "Opts: %.*s%s%s. Quota mode: %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_quota_mode(sb));
-
 	if (es->s_error_count)
 		mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
 
@@ -5481,7 +5502,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	atomic_set(&sbi->s_warning_count, 0);
 	atomic_set(&sbi->s_msg_count, 0);
 
-	kfree(orig_data);
 	return 0;
 
 cantfind_ext4:
@@ -5567,14 +5587,92 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 	ext4_blkdev_remove(sbi);
 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]);
+	}
+
+	kfree(ctx->test_dummy_enc_arg);
+}
+
+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. Quota mode: %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_quota_mode(sb));
+
+	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
@@ -6203,8 +6301,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, vfs_flags;
@@ -6216,14 +6316,8 @@ 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);
-	struct ext4_fs_context parsed_opts;
-
-	parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
-	parsed_opts.journal_devnum = 0;
 
-	if (data && !orig_data)
-		return -ENOMEM;
+	ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 
 	/* Store the original options */
 	old_sb_flags = sb->s_flags;
@@ -6244,14 +6338,13 @@ 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
 			old_opts.s_qf_names[i] = NULL;
 #endif
 	if (sbi->s_journal && sbi->s_journal->j_task->io_context)
-		parsed_opts.journal_ioprio =
+		ctx->journal_ioprio =
 			sbi->s_journal->j_task->io_context->ioprio;
 
 	/*
@@ -6262,10 +6355,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 	vfs_flags = SB_LAZYTIME | SB_I_VERSION;
 	sb->s_flags = (sb->s_flags & ~vfs_flags) | (*flags & vfs_flags);
 
-	if (!parse_options(data, sb, &parsed_opts, 1)) {
-		err = -EINVAL;
-		goto restore_opts;
-	}
+	ext4_apply_options(fc, sb);
 
 	if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
 	    test_opt(sb, JOURNAL_CHECKSUM)) {
@@ -6312,7 +6402,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 
 	if (sbi->s_journal) {
 		ext4_init_journal_params(sb, sbi->s_journal);
-		set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
+		set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
 	}
 
 	/* Flush outstanding errors before changing fs state */
@@ -6477,9 +6567,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 	 */
 	*flags = (*flags & ~vfs_flags) | (sb->s_flags & vfs_flags);
 
-	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
-		 orig_data, ext4_quota_mode(sb));
-	kfree(orig_data);
 	return 0;
 
 restore_opts:
@@ -6505,10 +6592,52 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 #endif
 	if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
 		ext4_stop_mmpd(sbi);
-	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. Quota mode: %s.",
+		 orig_data, ext4_quota_mode(sb));
+	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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 10/13] ext4: clean up return values in handle_mount_opt()
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (8 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 09/13] ext4: Completely separate options parsing and sb setup Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 11/13] ext4: change token2str() to use ext4_param_specs Lukas Czerner
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

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 <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 21845f899c50..b66087f55009 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -91,6 +91,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 int 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
@@ -118,6 +119,11 @@ static int 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,
@@ -2269,7 +2275,7 @@ 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)
+static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct ext4_fs_context *ctx = fc->fs_private;
 	struct fs_parse_result result;
@@ -2310,30 +2316,30 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			ctx->s_sb_block = result.uint_32;
 			ctx->spec |= EXT4_SPEC_s_sb_block;
 		}
-		return 1;
+		return 0;
 	case Opt_removed:
 		ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
 			 param->key);
-		return 1;
+		return 0;
 	case Opt_abort:
 		ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
-		return 1;
+		return 0;
 	case Opt_i_version:
 		ctx_set_flags(ctx, SB_I_VERSION);
-		return 1;
+		return 0;
 	case Opt_lazytime:
 		ctx_set_flags(ctx, SB_LAZYTIME);
-		return 1;
+		return 0;
 	case Opt_nolazytime:
 		ctx_clear_flags(ctx, SB_LAZYTIME);
-		return 1;
+		return 0;
 	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 		ctx_set_flags(ctx, SB_INLINECRYPT);
 #else
 		ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
 #endif
-		return 1;
+		return 0;
 	case Opt_errors:
 	case Opt_data:
 	case Opt_data_err:
@@ -2485,7 +2491,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (param->type == fs_value_is_flag) {
 			ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
 			ctx->test_dummy_enc_arg = NULL;
-			return 1;
+			return 0;
 		}
 		if (*param->string &&
 		    !(!strcmp(param->string, "v1") ||
@@ -2576,7 +2582,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				ctx_clear_mount_opt(ctx, m->mount_opt);
 		}
 	}
-	return 1;
+	return 0;
 }
 
 static int parse_options(struct fs_context *fc, char *options)
@@ -2612,7 +2618,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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 11/13] ext4: change token2str() to use ext4_param_specs
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (9 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 10/13] ext4: clean up return values in handle_mount_opt() Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 12/13] ext4: switch to the new mount api Lukas Czerner
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

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

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 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 b66087f55009..67efe9c6b9e8 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -3039,12 +3039,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.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 12/13] ext4: switch to the new mount api
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (10 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 11/13] ext4: change token2str() to use ext4_param_specs Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 14:18 ` [PATCH v4 13/13] ext4: Remove unused match_table_t tokens Lukas Czerner
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

Add the necessary functions for the fs_context_operations. Convert and
rename ext4_remount() and ext4_fill_super() to ext4_get_tree() and
ext4_reconfigure() respectively and switch the ext4 to use the new api.

One user facing change is the fact that we no longer have access to the
entire string of mount options provided by mount(2) since the mount api
does not store it anywhere. As a result we can't print the options to
the log as we did in the past after the successful mount.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 195 +++++++++++++++++++++---------------------------
 1 file changed, 86 insertions(+), 109 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 67efe9c6b9e8..55552b37bea2 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -74,12 +74,9 @@ static int ext4_mark_recovery_complete(struct super_block *sb,
 static int 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 void ext4_destroy_lazyinit_thread(void);
@@ -92,6 +89,11 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
 				      struct super_block *sb);
 static int 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);
+static int ext4_init_fs_context(struct fs_context *fc);
+static const struct fs_parameter_spec ext4_param_specs[];
 
 /*
  * Lock ordering
@@ -121,16 +123,20 @@ 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,
+	.reconfigure	= ext4_reconfigure,
+	.free		= ext4_fc_free,
 };
 
 
 #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");
@@ -141,11 +147,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");
@@ -1658,7 +1665,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,
@@ -2196,6 +2202,35 @@ 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->test_dummy_enc_arg);
+	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.
@@ -5596,61 +5631,31 @@ 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)
+static int ext4_fill_super(struct super_block *sb, struct fs_context *fc)
 {
-	int i;
-
-	if (!ctx)
-		return;
-
-	for (i = 0; i < EXT4_MAXQUOTAS; i++) {
-		kfree(ctx->s_qf_names[i]);
-	}
-
-	kfree(ctx->test_dummy_enc_arg);
-}
-
-static int ext4_fill_super(struct super_block *sb, void *data, int silent)
-{
-	struct ext4_fs_context ctx;
+	struct ext4_fs_context *ctx = fc->fs_private;
 	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;
+	int ret;
 
 	sbi = ext4_alloc_sbi(sb);
-	if (!sbi) {
+	if (!sbi)
 		ret = -ENOMEM;
-		goto free_data;
-	}
 
-	fc.s_fs_info = sbi;
+	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;
+	if (ctx->spec & EXT4_SPEC_s_sb_block)
+		sbi->s_sb_block = ctx->s_sb_block;
 
-	ret = __ext4_fill_super(&fc, sb, silent);
+	ret = __ext4_fill_super(fc, sb, fc->sb_flags & SB_SILENT);
 	if (ret < 0)
 		goto free_sbi;
 
-	if (EXT4_SB(sb)->s_journal) {
+	if (sbi->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)
@@ -5662,23 +5667,21 @@ 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. Quota mode: %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_quota_mode(sb));
-
-	kfree(orig_data);
-	cleanup_ctx(&ctx);
+			 "Quota mode: %s.", descr, ext4_quota_mode(sb));
+
 	return 0;
+
 free_sbi:
 	ext4_free_sbi(sbi);
-free_data:
-	kfree(orig_data);
-	cleanup_ctx(&ctx);
+	fc->s_fs_info = NULL;
 	return ret;
 }
 
+static int ext4_get_tree(struct fs_context *fc)
+{
+	return get_tree_bdev(fc, ext4_fill_super);
+}
+
 /*
  * 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
@@ -6601,47 +6604,26 @@ 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)
+static int ext4_reconfigure(struct fs_context *fc)
 {
-	struct ext4_sb_info *sbi = EXT4_SB(sb);
-	struct ext4_fs_context ctx;
-	struct fs_context fc;
-	char *orig_data;
+	struct super_block *sb = fc->root->d_sb;
+	int flags = fc->sb_flags;
 	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->s_fs_info = EXT4_SB(sb);
 
-	fc.fs_private = &ctx;
-	fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
-	fc.s_fs_info = sbi;
-
-	ret = parse_options(&fc, (char *) data);
+	ret = ext4_check_opt_consistency(fc, sb);
 	if (ret < 0)
-		goto err_out;
+		return ret;
 
-	ret = ext4_check_opt_consistency(&fc, sb);
+	ret = __ext4_remount(fc, sb, &flags);
 	if (ret < 0)
-		goto err_out;
+		return ret;
 
-	ret = __ext4_remount(&fc, sb, flags);
-	if (ret < 0)
-		goto err_out;
+	ext4_msg(sb, KERN_INFO, "re-mounted. Quota mode: %s.",
+		 ext4_quota_mode(sb));
 
-	ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
-		 orig_data, ext4_quota_mode(sb));
-	cleanup_ctx(&ctx);
-	kfree(orig_data);
 	return 0;
-
-err_out:
-	cleanup_ctx(&ctx);
-	kfree(orig_data);
-	return ret;
 }
 
 #ifdef CONFIG_QUOTA
@@ -7126,12 +7108,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)
 {
@@ -7189,11 +7165,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 | FS_ALLOW_IDMAP,
+	.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 | FS_ALLOW_IDMAP,
 };
 MODULE_ALIAS_FS("ext4");
 
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v4 13/13] ext4: Remove unused match_table_t tokens
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (11 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 12/13] ext4: switch to the new mount api Lukas Czerner
@ 2021-10-27 14:18 ` Lukas Czerner
  2021-10-27 21:39 ` [PATCH v4 00/13] ext4: new mount API conversion Sedat Dilek
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-27 14:18 UTC (permalink / raw)
  To: linux-ext4, tytso; +Cc: linux-fsdevel, Carlos Maiolino

Remove unused match_table_t, slim down mount_opts structure by removing
unnecessary definitions, remove redundant MOPT_ flags and clean up
ext4_parse_param() by converting the most of the if/else branching to
switch except for the MOPT_SET/MOPT_CEAR handling.

Signed-off-by: Lukas Czerner <lczerner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
---
 fs/ext4/super.c | 374 +++++++++++++++++-------------------------------
 1 file changed, 131 insertions(+), 243 deletions(-)

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 55552b37bea2..9f6e31d1670f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1682,7 +1682,7 @@ static const struct export_operations ext4_export_ops = {
 
 enum {
 	Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
-	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
+	Opt_resgid, Opt_resuid, Opt_sb,
 	Opt_nouid32, Opt_debug, Opt_removed,
 	Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
 	Opt_auto_da_alloc, Opt_noauto_da_alloc, Opt_noload,
@@ -1691,8 +1691,7 @@ enum {
 	Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
 	Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
 	Opt_inlinecrypt,
-	Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
-	Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
+	Opt_usrjquota, Opt_grpjquota, Opt_quota,
 	Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
 	Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version,
 	Opt_dax, Opt_dax_always, Opt_dax_inode, Opt_dax_never,
@@ -1712,16 +1711,16 @@ enum {
 };
 
 static const struct constant_table ext4_param_errors[] = {
-	{"continue",	Opt_err_cont},
-	{"panic",	Opt_err_panic},
-	{"remount-ro",	Opt_err_ro},
+	{"continue",	EXT4_MOUNT_ERRORS_CONT},
+	{"panic",	EXT4_MOUNT_ERRORS_PANIC},
+	{"remount-ro",	EXT4_MOUNT_ERRORS_RO},
 	{}
 };
 
 static const struct constant_table ext4_param_data[] = {
-	{"journal",	Opt_data_journal},
-	{"ordered",	Opt_data_ordered},
-	{"writeback",	Opt_data_writeback},
+	{"journal",	EXT4_MOUNT_JOURNAL_DATA},
+	{"ordered",	EXT4_MOUNT_ORDERED_DATA},
+	{"writeback",	EXT4_MOUNT_WRITEBACK_DATA},
 	{}
 };
 
@@ -1732,9 +1731,9 @@ static const struct constant_table ext4_param_data_err[] = {
 };
 
 static const struct constant_table ext4_param_jqfmt[] = {
-	{"vfsold",	Opt_jqfmt_vfsold},
-	{"vfsv0",	Opt_jqfmt_vfsv0},
-	{"vfsv1",	Opt_jqfmt_vfsv1},
+	{"vfsold",	QFMT_VFS_OLD},
+	{"vfsv0",	QFMT_VFS_V0},
+	{"vfsv1",	QFMT_VFS_V1},
 	{}
 };
 
@@ -1859,111 +1858,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_dax_always, "dax=always"},
-	{Opt_dax_inode, "dax=inode"},
-	{Opt_dax_never, "dax=never"},
-	{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"},
-#ifdef CONFIG_EXT4_DEBUG
-	{Opt_fc_debug_force, "fc_debug_force"},
-	{Opt_fc_debug_max_replay, "fc_debug_max_replay=%u"},
-#endif
-	{Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
-	{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
-	{Opt_test_dummy_encryption, "test_dummy_encryption"},
-	{Opt_inlinecrypt, "inlinecrypt"},
-	{Opt_nombcache, "nombcache"},
-	{Opt_nombcache, "no_mbcache"},	/* for backward compatibility */
-	{Opt_removed, "prefetch_block_bitmaps"},
-	{Opt_no_prefetch_block_bitmaps, "no_prefetch_block_bitmaps"},
-	{Opt_mb_optimize_scan, "mb_optimize_scan=%d"},
-	{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))
 #define DEFAULT_MB_OPTIMIZE_SCAN	(-1)
 
@@ -1975,22 +1869,18 @@ static const char deprecated_msg[] =
 #define MOPT_CLEAR	0x0002
 #define MOPT_NOSUPPORT	0x0004
 #define MOPT_EXPLICIT	0x0008
-#define MOPT_CLEAR_ERR	0x0010
-#define MOPT_GTE0	0x0020
 #ifdef CONFIG_QUOTA
 #define MOPT_Q		0
-#define MOPT_QFMT	0x0040
+#define MOPT_QFMT	0x0010
 #else
 #define MOPT_Q		MOPT_NOSUPPORT
 #define MOPT_QFMT	MOPT_NOSUPPORT
 #endif
-#define MOPT_DATAJ	0x0080
-#define MOPT_NO_EXT2	0x0100
-#define MOPT_NO_EXT3	0x0200
+#define MOPT_NO_EXT2	0x0020
+#define MOPT_NO_EXT3	0x0040
 #define MOPT_EXT4_ONLY	(MOPT_NO_EXT2 | MOPT_NO_EXT3)
-#define MOPT_STRING	0x0400
-#define MOPT_SKIP	0x0800
-#define	MOPT_2		0x1000
+#define MOPT_SKIP	0x0080
+#define	MOPT_2		0x0100
 
 static const struct mount_opts {
 	int	token;
@@ -2023,40 +1913,17 @@ static const struct mount_opts {
 				    EXT4_MOUNT_JOURNAL_CHECKSUM),
 	 MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
 	{Opt_noload, EXT4_MOUNT_NOLOAD, MOPT_NO_EXT2 | MOPT_SET},
-	{Opt_err_panic, EXT4_MOUNT_ERRORS_PANIC, MOPT_SET | MOPT_CLEAR_ERR},
-	{Opt_err_ro, EXT4_MOUNT_ERRORS_RO, MOPT_SET | MOPT_CLEAR_ERR},
-	{Opt_err_cont, EXT4_MOUNT_ERRORS_CONT, MOPT_SET | MOPT_CLEAR_ERR},
-	{Opt_data_err_abort, EXT4_MOUNT_DATA_ERR_ABORT,
-	 MOPT_NO_EXT2},
-	{Opt_data_err_ignore, EXT4_MOUNT_DATA_ERR_ABORT,
-	 MOPT_NO_EXT2},
+	{Opt_data_err, EXT4_MOUNT_DATA_ERR_ABORT, MOPT_NO_EXT2},
 	{Opt_barrier, EXT4_MOUNT_BARRIER, MOPT_SET},
 	{Opt_nobarrier, EXT4_MOUNT_BARRIER, MOPT_CLEAR},
 	{Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET},
 	{Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR},
 	{Opt_noinit_itable, EXT4_MOUNT_INIT_INODE_TABLE, MOPT_CLEAR},
-	{Opt_commit, 0, MOPT_GTE0},
-	{Opt_max_batch_time, 0, MOPT_GTE0},
-	{Opt_min_batch_time, 0, MOPT_GTE0},
-	{Opt_inode_readahead_blks, 0, MOPT_GTE0},
-	{Opt_init_itable, 0, MOPT_GTE0},
-	{Opt_dax, EXT4_MOUNT_DAX_ALWAYS, MOPT_SET | MOPT_SKIP},
-	{Opt_dax_always, EXT4_MOUNT_DAX_ALWAYS,
-		MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
-	{Opt_dax_inode, EXT4_MOUNT2_DAX_INODE,
-		MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
-	{Opt_dax_never, EXT4_MOUNT2_DAX_NEVER,
-		MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
-	{Opt_stripe, 0, MOPT_GTE0},
-	{Opt_resuid, 0, MOPT_GTE0},
-	{Opt_resgid, 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},
-	{Opt_data_journal, EXT4_MOUNT_JOURNAL_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
-	{Opt_data_ordered, EXT4_MOUNT_ORDERED_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
-	{Opt_data_writeback, EXT4_MOUNT_WRITEBACK_DATA,
-	 MOPT_NO_EXT2 | MOPT_DATAJ},
+	{Opt_dax_type, 0, MOPT_EXT4_ONLY},
+	{Opt_journal_dev, 0, MOPT_NO_EXT2},
+	{Opt_journal_path, 0, MOPT_NO_EXT2},
+	{Opt_journal_ioprio, 0, MOPT_NO_EXT2},
+	{Opt_data, 0, MOPT_NO_EXT2},
 	{Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
 	{Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR},
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
@@ -2068,7 +1935,6 @@ static const struct mount_opts {
 #endif
 	{Opt_nouid32, EXT4_MOUNT_NO_UID32, MOPT_SET},
 	{Opt_debug, EXT4_MOUNT_DEBUG, MOPT_SET},
-	{Opt_debug_want_extra_isize, 0, MOPT_GTE0},
 	{Opt_quota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA, MOPT_SET | MOPT_Q},
 	{Opt_usrquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA,
 							MOPT_SET | MOPT_Q},
@@ -2079,23 +1945,15 @@ static const struct mount_opts {
 	{Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
 		       EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA),
 							MOPT_CLEAR | MOPT_Q},
-	{Opt_usrjquota, 0, MOPT_Q | MOPT_STRING},
-	{Opt_grpjquota, 0, MOPT_Q | MOPT_STRING},
-	{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},
-	{Opt_max_dir_size_kb, 0, MOPT_GTE0},
-	{Opt_test_dummy_encryption, 0, MOPT_STRING},
+	{Opt_usrjquota, 0, MOPT_Q},
+	{Opt_grpjquota, 0, MOPT_Q},
+	{Opt_jqfmt, 0, MOPT_QFMT},
 	{Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
 	{Opt_no_prefetch_block_bitmaps, EXT4_MOUNT_NO_PREFETCH_BLOCK_BITMAPS,
 	 MOPT_SET},
-	{Opt_mb_optimize_scan, EXT4_MOUNT2_MB_OPTIMIZE_SCAN, MOPT_GTE0},
 #ifdef CONFIG_EXT4_DEBUG
 	{Opt_fc_debug_force, EXT4_MOUNT2_JOURNAL_FAST_COMMIT,
 	 MOPT_SET | MOPT_2 | MOPT_EXT4_ONLY},
-	{Opt_fc_debug_max_replay, 0, MOPT_GTE0},
 #endif
 	{Opt_err, 0, 0}
 };
@@ -2325,20 +2183,41 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		return token;
 	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 
+	for (m = ext4_mount_opts; m->token != Opt_err; m++)
+		if (token == m->token)
+			break;
+
+	ctx->opt_flags |= m->flags;
+
+	if (m->flags & MOPT_EXPLICIT) {
+		if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
+			ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
+		} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
+			ctx_set_mount_opt2(ctx,
+				       EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
+		} else
+			return -EINVAL;
+	}
+
+	if (m->flags & MOPT_NOSUPPORT) {
+		ext4_msg(NULL, KERN_ERR, "%s option not supported",
+			 param->key);
+		return 0;
+	}
+
+	switch (token) {
 #ifdef CONFIG_QUOTA
-	if (token == Opt_usrjquota) {
+	case Opt_usrjquota:
 		if (!*param->string)
 			return unnote_qf_name(fc, USRQUOTA);
 		else
 			return note_qf_name(fc, USRQUOTA, param);
-	} else if (token == Opt_grpjquota) {
+	case Opt_grpjquota:
 		if (!*param->string)
 			return unnote_qf_name(fc, GRPQUOTA);
 		else
 			return note_qf_name(fc, GRPQUOTA, param);
-	}
 #endif
-	switch (token) {
 	case Opt_noacl:
 	case Opt_nouser_xattr:
 		ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
@@ -2376,41 +2255,21 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 #endif
 		return 0;
 	case Opt_errors:
-	case Opt_data:
-	case Opt_data_err:
-	case Opt_jqfmt:
-	case Opt_dax_type:
-		token = result.uint_32;
-	}
-
-	for (m = ext4_mount_opts; m->token != Opt_err; m++)
-		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_EXPLICIT) {
-		if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
-			ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
-		} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
-			ctx_set_mount_opt2(ctx,
-				       EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
-		} else
-			return -EINVAL;
-	}
-	if (m->flags & MOPT_CLEAR_ERR)
 		ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
-
-	if (m->flags & MOPT_NOSUPPORT) {
-		ext4_msg(NULL, KERN_ERR, "%s option not supported",
-			 param->key);
-	} else if (token == Opt_commit) {
+		ctx_set_mount_opt(ctx, result.uint_32);
+		return 0;
+#ifdef CONFIG_QUOTA
+	case Opt_jqfmt:
+		ctx->s_jquota_fmt = result.uint_32;
+		ctx->spec |= EXT4_SPEC_JQFMT;
+		return 0;
+#endif
+	case Opt_data:
+		ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+		ctx_set_mount_opt(ctx, result.uint_32);
+		ctx->spec |= EXT4_SPEC_DATAJ;
+		return 0;
+	case Opt_commit:
 		if (result.uint_32 == 0)
 			ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
 		else if (result.uint_32 > INT_MAX / HZ) {
@@ -2422,7 +2281,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->s_commit_interval = HZ * result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_commit_interval;
-	} else if (token == Opt_debug_want_extra_isize) {
+		return 0;
+	case Opt_debug_want_extra_isize:
 		if ((result.uint_32 & 1) || (result.uint_32 < 4)) {
 			ext4_msg(NULL, KERN_ERR,
 				 "Invalid want_extra_isize %d", result.uint_32);
@@ -2430,13 +2290,16 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->s_want_extra_isize = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_want_extra_isize;
-	} else if (token == Opt_max_batch_time) {
+		return 0;
+	case Opt_max_batch_time:
 		ctx->s_max_batch_time = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_max_batch_time;
-	} else if (token == Opt_min_batch_time) {
+		return 0;
+	case Opt_min_batch_time:
 		ctx->s_min_batch_time = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_min_batch_time;
-	} else if (token == Opt_inode_readahead_blks) {
+		return 0;
+	case Opt_inode_readahead_blks:
 		if (result.uint_32 &&
 		    (result.uint_32 > (1 << 30) ||
 		     !is_power_of_2(result.uint_32))) {
@@ -2447,24 +2310,29 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->s_inode_readahead_blks = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
-	} else if (token == Opt_init_itable) {
+		return 0;
+	case Opt_init_itable:
 		ctx_set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
 		ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
 		if (param->type == fs_value_is_string)
 			ctx->s_li_wait_mult = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_li_wait_mult;
-	} else if (token == Opt_max_dir_size_kb) {
+		return 0;
+	case Opt_max_dir_size_kb:
 		ctx->s_max_dir_size_kb = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
+		return 0;
 #ifdef CONFIG_EXT4_DEBUG
-	} else if (token == Opt_fc_debug_max_replay) {
+	case Opt_fc_debug_max_replay:
 		ctx->s_fc_debug_max_replay = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_fc_debug_max_replay;
+		return 0;
 #endif
-	} else if (token == Opt_stripe) {
+	case Opt_stripe:
 		ctx->s_stripe = result.uint_32;
 		ctx->spec |= EXT4_SPEC_s_stripe;
-	} else if (token == Opt_resuid) {
+		return 0;
+	case Opt_resuid:
 		uid = make_kuid(current_user_ns(), result.uint_32);
 		if (!uid_valid(uid)) {
 			ext4_msg(NULL, KERN_ERR, "Invalid uid value %d",
@@ -2473,7 +2341,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->s_resuid = uid;
 		ctx->spec |= EXT4_SPEC_s_resuid;
-	} else if (token == Opt_resgid) {
+		return 0;
+	case Opt_resgid:
 		gid = make_kgid(current_user_ns(), result.uint_32);
 		if (!gid_valid(gid)) {
 			ext4_msg(NULL, KERN_ERR, "Invalid gid value %d",
@@ -2482,7 +2351,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->s_resgid = gid;
 		ctx->spec |= EXT4_SPEC_s_resgid;
-	} else if (token == Opt_journal_dev) {
+		return 0;
+	case Opt_journal_dev:
 		if (is_remount) {
 			ext4_msg(NULL, KERN_ERR,
 				 "Cannot specify journal on remount");
@@ -2490,7 +2360,9 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		}
 		ctx->journal_devnum = result.uint_32;
 		ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
-	} else if (token == Opt_journal_path) {
+		return 0;
+	case Opt_journal_path:
+	{
 		struct inode *journal_inode;
 		struct path path;
 		int error;
@@ -2512,7 +2384,9 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		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) {
+		return 0;
+	}
+	case Opt_journal_ioprio:
 		if (result.uint_32 > 7) {
 			ext4_msg(NULL, KERN_ERR, "Invalid journal IO priority"
 				 " (must be 0-7)");
@@ -2521,7 +2395,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		ctx->journal_ioprio =
 			IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
 		ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
-	} else if (token == Opt_test_dummy_encryption) {
+		return 0;
+	case Opt_test_dummy_encryption:
 #ifdef CONFIG_FS_ENCRYPTION
 		if (param->type == fs_value_is_flag) {
 			ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
@@ -2543,53 +2418,65 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 		ext4_msg(NULL, KERN_WARNING,
 			 "Test dummy encryption mount option ignored");
 #endif
-	} else if (m->flags & MOPT_DATAJ) {
-		ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
-		ctx_set_mount_opt(ctx, m->mount_opt);
-		ctx->spec |= EXT4_SPEC_DATAJ;
-#ifdef CONFIG_QUOTA
-	} else if (m->flags & MOPT_QFMT) {
-		ctx->s_jquota_fmt = m->mount_opt;
-		ctx->spec |= EXT4_SPEC_JQFMT;
-#endif
-	} else if (token == Opt_dax || token == Opt_dax_always ||
-		   token == Opt_dax_inode || token == Opt_dax_never) {
+		return 0;
+	case Opt_dax:
+	case Opt_dax_type:
 #ifdef CONFIG_FS_DAX
-		switch (token) {
+	{
+		int type = (token == Opt_dax) ?
+			   Opt_dax : result.uint_32;
+
+		switch (type) {
 		case Opt_dax:
 		case Opt_dax_always:
-			ctx_set_mount_opt(ctx, m->mount_opt);
+			ctx_set_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 			ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 			break;
 		case Opt_dax_never:
-			ctx_set_mount_opt2(ctx, m->mount_opt);
+			ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 			ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 			break;
 		case Opt_dax_inode:
 			ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 			ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 			/* Strictly for printing options */
-			ctx_set_mount_opt2(ctx, m->mount_opt);
+			ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE);
 			break;
 		}
+		return 0;
+	}
 #else
 		ext4_msg(NULL, KERN_INFO, "dax option not supported");
-		ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
-		ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 		return -EINVAL;
 #endif
-	} else if (token == Opt_data_err_abort) {
-		ctx_set_mount_opt(ctx, m->mount_opt);
-	} else if (token == Opt_data_err_ignore) {
-		ctx_clear_mount_opt(ctx, m->mount_opt);
-	} else if (token == Opt_mb_optimize_scan) {
+	case Opt_data_err:
+		if (result.uint_32 == Opt_data_err_abort)
+			ctx_set_mount_opt(ctx, m->mount_opt);
+		else if (result.uint_32 == Opt_data_err_ignore)
+			ctx_clear_mount_opt(ctx, m->mount_opt);
+		return 0;
+	case Opt_mb_optimize_scan:
 		if (result.int_32 != 0 && result.int_32 != 1) {
 			ext4_msg(NULL, KERN_WARNING,
 				 "mb_optimize_scan should be set to 0 or 1.");
 			return -EINVAL;
 		}
 		ctx->mb_optimize_scan = result.int_32;
-	} else {
+		return 0;
+	}
+
+	/*
+	 * At this point we should only be getting options requiring MOPT_SET,
+	 * or MOPT_CLEAR. Anything else is a bug
+	 */
+	if (m->token == Opt_err) {
+		ext4_msg(NULL, KERN_WARNING, "buggy handling of option %s",
+			 param->key);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	else {
 		unsigned int set = 0;
 
 		if ((param->type == fs_value_is_flag) ||
@@ -2617,6 +2504,7 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 				ctx_clear_mount_opt(ctx, m->mount_opt);
 		}
 	}
+
 	return 0;
 }
 
@@ -3105,7 +2993,7 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
 	for (m = ext4_mount_opts; m->token != Opt_err; m++) {
 		int want_set = m->flags & MOPT_SET;
 		if (((m->flags & (MOPT_SET|MOPT_CLEAR)) == 0) ||
-		    (m->flags & MOPT_CLEAR_ERR) || m->flags & MOPT_SKIP)
+		    m->flags & MOPT_SKIP)
 			continue;
 		if (!nodefs && !(m->mount_opt & (sbi->s_mount_opt ^ def_mount_opt)))
 			continue; /* skip if same as the default */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (12 preceding siblings ...)
  2021-10-27 14:18 ` [PATCH v4 13/13] ext4: Remove unused match_table_t tokens Lukas Czerner
@ 2021-10-27 21:39 ` Sedat Dilek
  2021-10-28 13:49   ` Lukas Czerner
  2021-12-09 19:17 ` Theodore Y. Ts'o
  2021-12-09 22:22 ` Theodore Ts'o
  15 siblings, 1 reply; 22+ messages in thread
From: Sedat Dilek @ 2021-10-27 21:39 UTC (permalink / raw)
  To: Lukas Czerner; +Cc: linux-ext4, tytso, linux-fsdevel, Carlos Maiolino

On Wed, Oct 27, 2021 at 11:27 PM Lukas Czerner <lczerner@redhat.com> wrote:
>
> After some time I am once again resurrecting the patchset to convert 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 a new ext4 mount options regression test that
> was sent to the fstests list. You can check it out on github as well.
>
> https://github.com/lczerner/xfstests/tree/ext4_mount_test
>
> Here is a high level description of the patchset
>
> 1. Prepare the ext4 mount parameters required by the new mount API and use
>    it for parsing, while still using the old API to get the options
>    string.
>
>   fs_parse: allow parameter value to be empty
>   ext4: Add fs parameter specifications for mount options
>   ext4: move option validation to a separate function
>   ext4: Change handle_mount_opt() to use fs_parameter
>
> 2. Remove the use of ext4 super block from all the parsing code, because
>    with the new mount API the parsing is going to be done before we even
>    get the super block.
>
>   ext4: Allow sb to be NULL in ext4_msg()
>   ext4: move quota configuration out of handle_mount_opt()
>   ext4: check ext2/3 compatibility outside handle_mount_opt()
>   ext4: get rid of super block and sbi from handle_mount_ops()
>
> 3. Actually finish the separation of the parsing and super block setup
>    into distinct steps. This is where the new ext4_fill_super() and
>    ext4_remount() functions are created temporarily before the actual
>    transition to the new API.
>
>   ext4: Completely separate options parsing and sb setup
>
> 4. Make some last preparations and actually switch the ext4 to use the
>    new mount API.
>
>   ext4: clean up return values in handle_mount_opt()
>   ext4: change token2str() to use ext4_param_specs
>   ext4: switch to the new mount api
>
> 5. Cleanup the old unused structures and rearrange the parsing function.
>
>   ext4: Remove unused match_table_t tokens
>
> There is still a potential to do some cleanups and perhaps refactoring
> such as using the fsparam_flag_no to remove the separate negative
> options for example. However that can be done later after the conversion
> to the new mount API which is the main purpose of the patchset.
>
> Signed-off-by: Lukas Czerner <lczerner@redhat.com>
> Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
> ---

Is this the Git branch to pull from...?

https://github.com/lczerner/linux/tree/ext4_mount_api_rebase
https://github.com/lczerner/linux/commits/ext4_mount_api_rebase

Any other requirements or recommendations other than "ext4: ext4 mount
sanity test" (xfstests)?

Thanks.

- Sedat -

> v3 -> v4: Fix some typos, print exact quotafile type in log messages.
>           Remove explicit "Ext4:" from some log messages
> V2 -> V3: Rebase to the newer kernel, including new mount options.
> V1 -> V2: Rebase to the newer kernel
>
> Lukas Czerner (13):
>   fs_parse: allow parameter value to be empty
>   ext4: Add fs parameter specifications for mount options
>   ext4: move option validation to a separate function
>   ext4: Change handle_mount_opt() to use fs_parameter
>   ext4: Allow sb to be NULL in ext4_msg()
>   ext4: move quota configuration out of handle_mount_opt()
>   ext4: check ext2/3 compatibility outside handle_mount_opt()
>   ext4: get rid of super block and sbi from handle_mount_ops()
>   ext4: Completely separate options parsing and sb setup
>   ext4: clean up return values in handle_mount_opt()
>   ext4: change token2str() to use ext4_param_specs
>   ext4: switch to the new mount api
>   ext4: Remove unused match_table_t tokens
>
>  fs/ext4/super.c           | 1848 +++++++++++++++++++++++--------------
>  fs/fs_parser.c            |   31 +-
>  include/linux/fs_parser.h |    2 +-
>  3 files changed, 1189 insertions(+), 692 deletions(-)
>
> --
> 2.31.1
>

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-10-27 21:39 ` [PATCH v4 00/13] ext4: new mount API conversion Sedat Dilek
@ 2021-10-28 13:49   ` Lukas Czerner
  0 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-10-28 13:49 UTC (permalink / raw)
  To: Sedat Dilek; +Cc: linux-ext4, tytso, linux-fsdevel, Carlos Maiolino

On Wed, Oct 27, 2021 at 11:39:42PM +0200, Sedat Dilek wrote:
> On Wed, Oct 27, 2021 at 11:27 PM Lukas Czerner <lczerner@redhat.com> wrote:
> >
> > After some time I am once again resurrecting the patchset to convert 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 a new ext4 mount options regression test that
> > was sent to the fstests list. You can check it out on github as well.
> >
> > https://github.com/lczerner/xfstests/tree/ext4_mount_test
> >
> > Here is a high level description of the patchset
> >
> > 1. Prepare the ext4 mount parameters required by the new mount API and use
> >    it for parsing, while still using the old API to get the options
> >    string.
> >
> >   fs_parse: allow parameter value to be empty
> >   ext4: Add fs parameter specifications for mount options
> >   ext4: move option validation to a separate function
> >   ext4: Change handle_mount_opt() to use fs_parameter
> >
> > 2. Remove the use of ext4 super block from all the parsing code, because
> >    with the new mount API the parsing is going to be done before we even
> >    get the super block.
> >
> >   ext4: Allow sb to be NULL in ext4_msg()
> >   ext4: move quota configuration out of handle_mount_opt()
> >   ext4: check ext2/3 compatibility outside handle_mount_opt()
> >   ext4: get rid of super block and sbi from handle_mount_ops()
> >
> > 3. Actually finish the separation of the parsing and super block setup
> >    into distinct steps. This is where the new ext4_fill_super() and
> >    ext4_remount() functions are created temporarily before the actual
> >    transition to the new API.
> >
> >   ext4: Completely separate options parsing and sb setup
> >
> > 4. Make some last preparations and actually switch the ext4 to use the
> >    new mount API.
> >
> >   ext4: clean up return values in handle_mount_opt()
> >   ext4: change token2str() to use ext4_param_specs
> >   ext4: switch to the new mount api
> >
> > 5. Cleanup the old unused structures and rearrange the parsing function.
> >
> >   ext4: Remove unused match_table_t tokens
> >
> > There is still a potential to do some cleanups and perhaps refactoring
> > such as using the fsparam_flag_no to remove the separate negative
> > options for example. However that can be done later after the conversion
> > to the new mount API which is the main purpose of the patchset.
> >
> > Signed-off-by: Lukas Czerner <lczerner@redhat.com>
> > Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
> > ---
> 
> Is this the Git branch to pull from...?
> 
> https://github.com/lczerner/linux/tree/ext4_mount_api_rebase
> https://github.com/lczerner/linux/commits/ext4_mount_api_rebase

Hi,

yes that's the branch. I just updated it with the v4 changes.

> 
> Any other requirements or recommendations other than "ext4: ext4 mount
> sanity test" (xfstests)?

No special requirements I don't think. The xfstest patch is on the
fstests list with subject "ext4: add test for all ext4/ext3/ext2 mount
options", or you can checkout my github branch above.

My testing involved said test, regular xfstests run as well as different
ext4 kernel configuration (diabled quota, enabled debug and so on). In
order to test the "dax" options then dax capable device is needed of
course and the test shoud run that automatically with it.

Thanks a lot testing!
-Lukas

> 
> Thanks.
> 
> - Sedat -
> 
> > v3 -> v4: Fix some typos, print exact quotafile type in log messages.
> >           Remove explicit "Ext4:" from some log messages
> > V2 -> V3: Rebase to the newer kernel, including new mount options.
> > V1 -> V2: Rebase to the newer kernel
> >
> > Lukas Czerner (13):
> >   fs_parse: allow parameter value to be empty
> >   ext4: Add fs parameter specifications for mount options
> >   ext4: move option validation to a separate function
> >   ext4: Change handle_mount_opt() to use fs_parameter
> >   ext4: Allow sb to be NULL in ext4_msg()
> >   ext4: move quota configuration out of handle_mount_opt()
> >   ext4: check ext2/3 compatibility outside handle_mount_opt()
> >   ext4: get rid of super block and sbi from handle_mount_ops()
> >   ext4: Completely separate options parsing and sb setup
> >   ext4: clean up return values in handle_mount_opt()
> >   ext4: change token2str() to use ext4_param_specs
> >   ext4: switch to the new mount api
> >   ext4: Remove unused match_table_t tokens
> >
> >  fs/ext4/super.c           | 1848 +++++++++++++++++++++++--------------
> >  fs/fs_parser.c            |   31 +-
> >  include/linux/fs_parser.h |    2 +-
> >  3 files changed, 1189 insertions(+), 692 deletions(-)
> >
> > --
> > 2.31.1
> >
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 01/13] fs_parse: allow parameter value to be empty
  2021-10-27 14:18 ` [PATCH v4 01/13] fs_parse: allow parameter value to be empty Lukas Czerner
@ 2021-10-29  8:44   ` Christian Brauner
  2021-11-01  9:25     ` Lukas Czerner
  0 siblings, 1 reply; 22+ messages in thread
From: Christian Brauner @ 2021-10-29  8:44 UTC (permalink / raw)
  To: Lukas Czerner; +Cc: linux-ext4, tytso, linux-fsdevel, Al Viro, Carlos Maiolino

On Wed, Oct 27, 2021 at 04:18:45PM +0200, Lukas Czerner wrote:
> Allow parameter value to be empty by specifying fs_param_can_be_empty
> flag.

Hey Lukas,

what option is this for? Usually this should be handled by passing
FSCONFIG_SET_FLAG. Doesn't seem like a good idea to let the string value
be optionally empty. I'd rather have the guarantee that it has to be
something instead of having to be extra careful because it could be NULL.

> 
> Signed-off-by: Lukas Czerner <lczerner@redhat.com>
> Cc: Al Viro <viro@zeniv.linux.org.uk>
> Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
> ---
>  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 3df07c0e32b3..ed40ce5742fd 100644
> --- a/fs/fs_parser.c
> +++ b/fs/fs_parser.c
> @@ -199,6 +199,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);
> @@ -211,8 +213,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;
>  }
> @@ -221,8 +226,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;
>  }
> @@ -231,8 +239,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;
>  }
> @@ -244,6 +255,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);
> @@ -255,7 +268,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;
>  }
> @@ -275,7 +289,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 aab0ffc6bac6..f103c91139d4 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.31.1
> 

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 01/13] fs_parse: allow parameter value to be empty
  2021-10-29  8:44   ` Christian Brauner
@ 2021-11-01  9:25     ` Lukas Czerner
  0 siblings, 0 replies; 22+ messages in thread
From: Lukas Czerner @ 2021-11-01  9:25 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-ext4, tytso, linux-fsdevel, Al Viro, Carlos Maiolino

On Fri, Oct 29, 2021 at 10:44:11AM +0200, Christian Brauner wrote:
> On Wed, Oct 27, 2021 at 04:18:45PM +0200, Lukas Czerner wrote:
> > Allow parameter value to be empty by specifying fs_param_can_be_empty
> > flag.
> 
> Hey Lukas,
> 
> what option is this for? Usually this should be handled by passing
> FSCONFIG_SET_FLAG. Doesn't seem like a good idea to let the string value
> be optionally empty. I'd rather have the guarantee that it has to be
> something instead of having to be extra careful because it could be NULL.

Hi Christian,

this is for ext4 mount options usrjquota and grpjquota that will treat
empty parameter, that is "grpjquota=" and "usrjquota=", as a means to
disable those. I agree that it's not ideal, but if I don't want to break
compatibilily when converting ext4 to this new mount API I have to keep
that option the way it is.

I share your concern, but for the string to be empty, the
fs_param_can_be_empty flag must be used intentionaly and so the code
handling this must expect the string to be empty in some cases.

Another option would be to use a flag parameter with the name including
the egual sign. Not sure if that's currently possible. But that would
require us to use separtate parameter and it feels even more clunky to
me especially since we can easly detect empty string instead.

Thanks!
-Lukas

> 
> > 
> > Signed-off-by: Lukas Czerner <lczerner@redhat.com>
> > Cc: Al Viro <viro@zeniv.linux.org.uk>
> > Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
> > ---
> >  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 3df07c0e32b3..ed40ce5742fd 100644
> > --- a/fs/fs_parser.c
> > +++ b/fs/fs_parser.c
> > @@ -199,6 +199,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);
> > @@ -211,8 +213,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;
> >  }
> > @@ -221,8 +226,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;
> >  }
> > @@ -231,8 +239,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;
> >  }
> > @@ -244,6 +255,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);
> > @@ -255,7 +268,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;
> >  }
> > @@ -275,7 +289,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 aab0ffc6bac6..f103c91139d4 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.31.1
> > 
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (13 preceding siblings ...)
  2021-10-27 21:39 ` [PATCH v4 00/13] ext4: new mount API conversion Sedat Dilek
@ 2021-12-09 19:17 ` Theodore Y. Ts'o
  2021-12-09 19:55   ` Lukas Czerner
  2021-12-09 22:22 ` Theodore Ts'o
  15 siblings, 1 reply; 22+ messages in thread
From: Theodore Y. Ts'o @ 2021-12-09 19:17 UTC (permalink / raw)
  To: Lukas Czerner; +Cc: linux-ext4, linux-fsdevel, Carlos Maiolino

Hi Lukas,

I'm starting to process ext4 patches for the next merge window, and I
want to pull in the merge mount API conversions as one of the first
patches into the dev tree.

Should I use the v4 patch set or do you have a newer set of changes
that you'd like me to use?  There was a minor patch conflict in patch
#2, but that was pretty simple to fix up.

Thanks!

						- Ted

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-12-09 19:17 ` Theodore Y. Ts'o
@ 2021-12-09 19:55   ` Lukas Czerner
  2021-12-09 22:09     ` Theodore Y. Ts'o
  0 siblings, 1 reply; 22+ messages in thread
From: Lukas Czerner @ 2021-12-09 19:55 UTC (permalink / raw)
  To: Theodore Y. Ts'o; +Cc: linux-ext4, linux-fsdevel, Carlos Maiolino

On Thu, Dec 09, 2021 at 02:17:11PM -0500, Theodore Y. Ts'o wrote:
> Hi Lukas,
> 
> I'm starting to process ext4 patches for the next merge window, and I
> want to pull in the merge mount API conversions as one of the first
> patches into the dev tree.

Hi Ted,

that's great, thanks.

> 
> Should I use the v4 patch set or do you have a newer set of changes
> that you'd like me to use?  There was a minor patch conflict in patch
> #2, but that was pretty simple to fix up.

I don't have anything newer than this. I could rebase it if you'd like
me to, but it sounds like you've already done that pretty easily?

Thanks!
-Lukas

> 
> Thanks!
> 
> 						- Ted
> 


^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-12-09 19:55   ` Lukas Czerner
@ 2021-12-09 22:09     ` Theodore Y. Ts'o
  0 siblings, 0 replies; 22+ messages in thread
From: Theodore Y. Ts'o @ 2021-12-09 22:09 UTC (permalink / raw)
  To: Lukas Czerner; +Cc: linux-ext4, linux-fsdevel, Carlos Maiolino

On Thu, Dec 09, 2021 at 08:55:20PM +0100, Lukas Czerner wrote:
> > 
> > Should I use the v4 patch set or do you have a newer set of changes
> > that you'd like me to use?  There was a minor patch conflict in patch
> > #2, but that was pretty simple to fix up.
> 
> I don't have anything newer than this. I could rebase it if you'd like
> me to, but it sounds like you've already done that pretty easily?

Yep, I've done that and have run tests against it (including ext4/053
to test ext2/3/4 mount options).  I just haven't pushed it out pending
your confirmation that the v4 patchset was the one I should use.

Thanks!

						- Ted

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v4 00/13] ext4: new mount API conversion
  2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
                   ` (14 preceding siblings ...)
  2021-12-09 19:17 ` Theodore Y. Ts'o
@ 2021-12-09 22:22 ` Theodore Ts'o
  15 siblings, 0 replies; 22+ messages in thread
From: Theodore Ts'o @ 2021-12-09 22:22 UTC (permalink / raw)
  To: Lukas Czerner, linux-ext4
  Cc: Theodore Ts'o, Carlos Maiolino, linux-fsdevel

On Wed, 27 Oct 2021 16:18:44 +0200, Lukas Czerner wrote:
> After some time I am once again resurrecting the patchset to convert 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 a new ext4 mount options regression test that
> was sent to the fstests list. You can check it out on github as well.
> 
> [...]

Applied, thanks!

[01/13] fs_parse: allow parameter value to be empty
        commit: 6abfaaf124a81b7d2ab132cc2c9885baa14171e5
[02/13] ext4: Add fs parameter specifications for mount options
        commit: e5a185c26c11cbd1d386be8ee4c5e57b4f62273a
[03/13] ext4: move option validation to a separate function
        commit: 4c94bff967d90e91ace38a9886c1c7777a9c6f91
[04/13] ext4: Change handle_mount_opt() to use fs_parameter
        commit: 461c3af045d3ab949360fedbfb3ea1dcd9d8b22b
[05/13] ext4: Allow sb to be NULL in ext4_msg()
        commit: da812f611934bef16fe02d667a76df77ae9cf99a
[06/13] ext4: move quota configuration out of handle_mount_opt()
        commit: e6e268cb682290da29e3c8408493a4474307b8cc
[07/13] ext4: check ext2/3 compatibility outside handle_mount_opt()
        commit: b6bd243500b6024d92eaaacf592ed8588c2c75ea
[08/13] ext4: get rid of super block and sbi from handle_mount_ops()
        commit: 6e47a3cc68fc525428297a00524833361ebbb0e9
[09/13] ext4: Completely separate options parsing and sb setup
        commit: 7edfd85b1ffd36593011dec96ab395912a340418
[10/13] ext4: clean up return values in handle_mount_opt()
        commit: 02f960f8db1cd0aa9c182f8804b2b41ffd2c37b2
[11/13] ext4: change token2str() to use ext4_param_specs
        commit: 97d8a670b4531437d5b842cf68dafa6d1a932ddf
[12/13] ext4: switch to the new mount api
        commit: cebe85d570cf84804e848332d6721bc9e5300e07
[13/13] ext4: Remove unused match_table_t tokens
        commit: ba2e524d918ab72c0e5edc02354bd6cb43d005f8

Best regards,
-- 
Theodore Ts'o <tytso@mit.edu>

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2021-12-09 22:23 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-27 14:18 [PATCH v4 00/13] ext4: new mount API conversion Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 01/13] fs_parse: allow parameter value to be empty Lukas Czerner
2021-10-29  8:44   ` Christian Brauner
2021-11-01  9:25     ` Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 02/13] ext4: Add fs parameter specifications for mount options Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 03/13] ext4: move option validation to a separate function Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 04/13] ext4: Change handle_mount_opt() to use fs_parameter Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 05/13] ext4: Allow sb to be NULL in ext4_msg() Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 06/13] ext4: move quota configuration out of handle_mount_opt() Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 07/13] ext4: check ext2/3 compatibility outside handle_mount_opt() Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 08/13] ext4: get rid of super block and sbi from handle_mount_ops() Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 09/13] ext4: Completely separate options parsing and sb setup Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 10/13] ext4: clean up return values in handle_mount_opt() Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 11/13] ext4: change token2str() to use ext4_param_specs Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 12/13] ext4: switch to the new mount api Lukas Czerner
2021-10-27 14:18 ` [PATCH v4 13/13] ext4: Remove unused match_table_t tokens Lukas Czerner
2021-10-27 21:39 ` [PATCH v4 00/13] ext4: new mount API conversion Sedat Dilek
2021-10-28 13:49   ` Lukas Czerner
2021-12-09 19:17 ` Theodore Y. Ts'o
2021-12-09 19:55   ` Lukas Czerner
2021-12-09 22:09     ` Theodore Y. Ts'o
2021-12-09 22:22 ` Theodore Ts'o

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.