2019-11-06 10:15:48

by Lukas Czerner

[permalink] [raw]
Subject: ext4: new mount API conversion

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

The series can be applied on top of the current mainline tree and the work
is based on the patches from David Howells (thank you David). It was built
and tested with xfstests and custom script to check all ext4 mount options
and some valid/invalid combinations.

This series also requires a small fix to the parsing infrastructure from
David Howells which has not been adopted yet - "vfs: Handle
fs_param_neg_with_empty"

I've tried to avoid big unrelated changes to the original ext4_fill_super()
and ext4_remount, though it could definitelly use some cleanup. This can
be done after the conversion with a separate patch set.

-Lukas

---

David Howells (1):
[PATCH 01/17] vfs: Handle fs_param_neg_with_empty

Lukas Czerner (16):
[PATCH 02/17] ext4: Add fs parameter description
[PATCH 03/17] ext4: Refactor parse_options
[PATCH 04/17] ext4: handle_mount_opt use fs_parameter
[PATCH 05/17] ext4: Allow sb to be NULL in ext4_msg()
[PATCH 06/17] ext4: move quota configuration out of
[PATCH 07/17] ext4: check ext2/3 compatibility outside
[PATCH 08/17] ext4: get rid of super block and sbi from
[PATCH 09/17] ext4: parse Opt_sb in handle_mount_opt()
[PATCH 10/17] ext4: clean up return values in handle_mount_opt()
[PATCH 11/17] ext4: mount api: add ext4_get_tree
[PATCH 12/17] ext4: refactor ext4_remount()
[PATCH 13/17] ext4: mount api: add ext4_reconfigure
[PATCH 14/17] ext4: mount api: add ext4_fc_free
[PATCH 15/17] ext4: mount api: switch to the new mount api
[PATCH 16/17] ext4: change token2str() to use ext4_param_specs
[PATCH 17/17] ext4: Remove unused code from old mount api


fs/ext4/super.c | 1858 ++++++++++++++++++++++--------------
fs/fs_parser.c | 5
include/linux/fs_parser.h | 6
3 files changed, 1147 insertions(+), 722 deletions(-)


2019-11-06 10:15:52

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH 02/17] ext4: Add fs parameter description

Signed-off-by: Lukas Czerner <[email protected]>
---
fs/ext4/super.c | 109 ++++++++++++++++++++++++++++++++++++++
include/linux/fs_parser.h | 6 +++
2 files changed, 115 insertions(+)

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

+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
+
#include "ext4.h"
#include "ext4_extents.h" /* Needed for trace points definition */
#include "ext4_jbd2.h"
@@ -1459,6 +1462,112 @@ enum {
Opt_dioread_nolock, Opt_dioread_lock,
Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
+ Opt_errors, Opt_data, Opt_data_err, Opt_jqfmt,
+};
+
+static const struct fs_parameter_enum ext4_param_enums[] = {
+ {Opt_errors, "continue", Opt_err_cont},
+ {Opt_errors, "panic", Opt_err_panic},
+ {Opt_errors, "remount-ro", Opt_err_ro},
+ {Opt_data, "journal", Opt_data_journal},
+ {Opt_data, "ordered", Opt_data_ordered},
+ {Opt_data, "writeback", Opt_data_writeback},
+ {Opt_data_err, "abort", Opt_data_err_abort},
+ {Opt_data_err, "ignore", Opt_data_err_ignore},
+ {Opt_jqfmt, "vfsold", Opt_jqfmt_vfsold},
+ {Opt_jqfmt, "vfsv0", Opt_jqfmt_vfsv0},
+ {Opt_jqfmt, "vfsv1", Opt_jqfmt_vfsv1},
+};
+
+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),
+ 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),
+ fsparam_enum ("data_err", Opt_data_err),
+ fsparam_string_empty
+ ("usrjquota", Opt_usrjquota),
+ fsparam_string_empty
+ ("grpjquota", Opt_grpjquota),
+ fsparam_enum ("jqfmt", Opt_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_bool ("barrier", Opt_barrier),
+ fsparam_flag ("nobarrier", Opt_nobarrier),
+ fsparam_flag ("i_version", Opt_i_version),
+ fsparam_flag ("dax", Opt_dax),
+ fsparam_u32 ("stripe", Opt_stripe),
+ fsparam_flag ("delalloc", Opt_delalloc),
+ fsparam_flag ("nodelalloc", Opt_nodelalloc),
+ fsparam_flag ("warn_on_error", Opt_warn_on_error),
+ fsparam_flag ("nowarn_on_error", Opt_nowarn_on_error),
+ fsparam_flag ("lazytime", Opt_lazytime),
+ fsparam_flag ("nolazytime", Opt_nolazytime),
+ fsparam_u32 ("debug_want_extra_isize",
+ Opt_debug_want_extra_isize),
+ fsparam_flag ("mblk_io_submit", Opt_removed),
+ fsparam_flag ("nomblk_io_submit", Opt_removed),
+ fsparam_flag ("block_validity", Opt_block_validity),
+ fsparam_flag ("noblock_validity", Opt_noblock_validity),
+ fsparam_u32 ("inode_readahead_blks",
+ Opt_inode_readahead_blks),
+ fsparam_u32 ("journal_ioprio", Opt_journal_ioprio),
+ fsparam_bool ("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 ("dioread_lock", Opt_dioread_lock),
+ fsparam_flag ("discard", Opt_discard),
+ fsparam_flag ("nodiscard", Opt_nodiscard),
+ fsparam_u32_opt ("init_itable", Opt_init_itable),
+ fsparam_flag ("noinit_itable", Opt_noinit_itable),
+ fsparam_u32 ("max_dir_size_kb", Opt_max_dir_size_kb),
+ fsparam_flag ("test_dummy_encryption",
+ Opt_test_dummy_encryption),
+ fsparam_flag ("nombcache", Opt_nombcache),
+ fsparam_flag ("no_mbcache", Opt_nombcache), /* for backward compatibility */
+ fsparam_string ("check", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("nocheck", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("reservation", Opt_removed), /* mount option from ext2/3 */
+ fsparam_flag ("noreservation", Opt_removed), /* mount option from ext2/3 */
+ fsparam_u32 ("journal", Opt_removed), /* mount option from ext2/3 */
+ {}
+};
+
+static const struct fs_parameter_description ext4_fs_parameters = {
+ .name = "ext4",
+ .specs = ext4_param_specs,
+ .enums = ext4_param_enums,
};

static const match_table_t tokens = {
diff --git a/include/linux/fs_parser.h b/include/linux/fs_parser.h
index dee140db6240..f704eb465cbd 100644
--- a/include/linux/fs_parser.h
+++ b/include/linux/fs_parser.h
@@ -133,11 +133,17 @@ static inline bool fs_validate_description(const struct fs_parameter_description
__fsparam(fs_param_is_u32_octal, NAME, OPT, 0)
#define fsparam_u32hex(NAME, OPT) \
__fsparam(fs_param_is_u32_hex, NAME, OPT, 0)
+#define fsparam_u32_opt(NAME, OPT) \
+ __fsparam(fs_param_is_u32, NAME, OPT, \
+ fs_param_v_optional)
#define fsparam_s32(NAME, OPT) __fsparam(fs_param_is_s32, NAME, OPT, 0)
#define fsparam_u64(NAME, OPT) __fsparam(fs_param_is_u64, NAME, OPT, 0)
#define fsparam_enum(NAME, OPT) __fsparam(fs_param_is_enum, NAME, OPT, 0)
#define fsparam_string(NAME, OPT) \
__fsparam(fs_param_is_string, NAME, OPT, 0)
+#define fsparam_string_empty(NAME, OPT) \
+ __fsparam(fs_param_is_string, NAME, OPT, \
+ fs_param_neg_with_empty)
#define fsparam_blob(NAME, OPT) __fsparam(fs_param_is_blob, NAME, OPT, 0)
#define fsparam_bdev(NAME, OPT) __fsparam(fs_param_is_blockdev, NAME, OPT, 0)
#define fsparam_path(NAME, OPT) __fsparam(fs_param_is_path, NAME, OPT, 0)
--
2.21.0

2019-11-06 10:15:56

by Lukas Czerner

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 44254179bd4f..b155257e2a4e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,6 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
unsigned int journal_inum);
+static int ext4_validate_options(struct super_block *sb);

/*
* Lock ordering
@@ -2160,10 +2161,9 @@ static int parse_options(char *options, struct super_block *sb,
unsigned int *journal_ioprio,
int is_remount)
{
- struct ext4_sb_info *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;
@@ -2181,7 +2181,14 @@ static int parse_options(char *options, struct super_block *sb,
journal_ioprio, is_remount) < 0)
return 0;
}
+ return ext4_validate_options(sb);
+}
+
+static int ext4_validate_options(struct super_block *sb)
+{
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
#ifdef CONFIG_QUOTA
+ char *usr_qf_name, *grp_qf_name;
/*
* We do the test below only for project quotas. 'usrquota' and
* 'grpquota' mount options are allowed even without quota feature
--
2.21.0

2019-11-06 10:15:57

by Lukas Czerner

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

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

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

#ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
+static int set_qf_name(struct super_block *sb, int qtype,
+ struct fs_parameter *param)
{
struct ext4_sb_info *sbi = EXT4_SB(sb);
char *qname, *old_qname = get_qf_name(sb, sbi, qtype);
@@ -1709,7 +1710,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");
@@ -1898,35 +1899,49 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
}
#endif

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

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

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

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

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

- if (args->from && !(m->flags & MOPT_STRING) && match_int(args, &arg))
- return -1;
- if (args->from && (m->flags & MOPT_GTE0) && (arg < 0))
- return -1;
if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
set_opt2(sb, EXPLICIT_DELALLOC);
@@ -1984,108 +2002,96 @@ 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) {
- 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 (result.has_value)
+ sbi->s_li_wait_mult = result.uint_32;
} else if (token == Opt_max_dir_size_kb) {
- sbi->s_max_dir_size_kb = arg;
+ sbi->s_max_dir_size_kb = result.uint_32;
} else if (token == Opt_stripe) {
- sbi->s_stripe = arg;
+ sbi->s_stripe = result.uint_32;
} else if (token == Opt_resuid) {
- uid = make_kuid(current_user_ns(), arg);
+ uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
- ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
+ ext4_msg(sb, KERN_ERR, "Invalid uid value %d",
+ result.uint_32);
return -1;
}
sbi->s_resuid = uid;
} else if (token == Opt_resgid) {
- gid = make_kgid(current_user_ns(), arg);
+ gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid)) {
- ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
+ ext4_msg(sb, KERN_ERR, "Invalid gid value %d",
+ result.uint_32);
return -1;
}
sbi->s_resgid = gid;
} else if (token == Opt_journal_dev) {
- if (is_remount) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(sb, KERN_ERR,
"Cannot specify journal on remount");
return -1;
}
- *journal_devnum = arg;
+ ctx->journal_devnum = result.uint_32;
} else if (token == Opt_journal_path) {
- char *journal_path;
struct inode *journal_inode;
struct path path;
int error;

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

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

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

if (!options)
return 1;

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

--
2.21.0

2019-11-06 10:16:01

by Lukas Czerner

[permalink] [raw]
Subject: [PATCH 06/17] ext4: move quota configuration out of handle_mount_opt()

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 12483f3b1f78..affcdaf63b6e 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -89,6 +89,10 @@ static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
unsigned int journal_inum);
static int ext4_validate_options(struct fs_context *fc);
+static int ext4_check_quota_consistency(struct fs_context *fc,
+ struct super_block *sb);
+static void ext4_apply_quota_options(struct fs_context *fc,
+ struct super_block *sb);

/*
* Lock ordering
@@ -1692,71 +1696,6 @@ static const char deprecated_msg[] =
"Mount option \"%s\" will be removed by %s\n"
"Contact [email protected] if you think we should keep it.\n";

-#ifdef CONFIG_QUOTA
-static int set_qf_name(struct super_block *sb, int qtype,
- struct fs_parameter *param)
-{
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- char *qname, *old_qname = get_qf_name(sb, sbi, qtype);
- int ret = -1;
-
- if (sb_any_quota_loaded(sb) && !old_qname) {
- ext4_msg(sb, KERN_ERR,
- "Cannot change journaled "
- "quota options when quota turned on");
- return -1;
- }
- if (ext4_has_feature_quota(sb)) {
- ext4_msg(sb, KERN_INFO, "Journaled quota options "
- "ignored when QUOTA feature is enabled");
- return 1;
- }
- qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
- if (!qname) {
- ext4_msg(sb, KERN_ERR,
- "Not enough memory for storing quotafile name");
- return -1;
- }
- if (old_qname) {
- if (strcmp(old_qname, qname) == 0)
- ret = 1;
- else
- ext4_msg(sb, KERN_ERR,
- "%s quota file already specified",
- QTYPE2NAME(qtype));
- goto errout;
- }
- if (strchr(qname, '/')) {
- ext4_msg(sb, KERN_ERR,
- "quotafile must be on filesystem root");
- goto errout;
- }
- rcu_assign_pointer(sbi->s_qf_names[qtype], qname);
- set_opt(sb, QUOTA);
- return 1;
-errout:
- kfree(qname);
- return ret;
-}
-
-static int clear_qf_name(struct super_block *sb, int qtype)
-{
-
- struct ext4_sb_info *sbi = EXT4_SB(sb);
- char *old_qname = get_qf_name(sb, sbi, qtype);
-
- if (sb_any_quota_loaded(sb) && old_qname) {
- ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
- " when quota turned on");
- return -1;
- }
- rcu_assign_pointer(sbi->s_qf_names[qtype], NULL);
- synchronize_rcu();
- kfree(old_qname);
- return 1;
-}
-#endif
-
#define MOPT_SET 0x0001
#define MOPT_CLEAR 0x0002
#define MOPT_NOSUPPORT 0x0004
@@ -1901,10 +1840,69 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
#endif

struct ext4_fs_context {
- unsigned long journal_devnum;
- unsigned int journal_ioprio;
+ char *s_qf_names[EXT4_MAXQUOTAS];
+ int s_jquota_fmt; /* Format of quota to use */
+ unsigned short qname_spec;
+ unsigned long journal_devnum;
+ unsigned int journal_ioprio;
};

+#ifdef CONFIG_QUOTA
+/*
+ * Note the name of the specified quota file.
+ */
+static int note_qf_name(struct fs_context *fc, int qtype,
+ struct fs_parameter *param)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
+ char *qname;
+
+ if (param->size < 1) {
+ ext4_msg(NULL, KERN_ERR, "EXT4-fs: Missing quota name");
+ return -EINVAL;
+ }
+ if (strchr(param->string, '/')) {
+ ext4_msg(NULL, KERN_ERR,
+ "quotafile must be on filesystem root");
+ return -EINVAL;
+ }
+ if (ctx->s_qf_names[qtype]) {
+ if (strcmp(ctx->s_qf_names[qtype], param->string) != 0) {
+ ext4_msg(NULL, KERN_ERR,
+ "EXT4-fs: Quota file already specified");
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+ if (!qname) {
+ ext4_msg(NULL, KERN_ERR,
+ "Not enough memory for storing quotafile name");
+ return -ENOMEM;
+ }
+ ctx->s_qf_names[qtype] = qname;
+ ctx->qname_spec |= 1 << qtype;
+ return 0;
+}
+
+/*
+ * Clear the name of the specified quota file.
+ */
+static int unnote_qf_name(struct fs_context *fc, int qtype)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
+
+ if (!ctx->s_qf_names[qtype])
+ return 0;
+
+ kfree(ctx->s_qf_names[qtype]);
+ ctx->s_qf_names[qtype] = NULL;
+ ctx->qname_spec |= 1 << qtype;
+ return 0;
+}
+#endif
+
static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
{
struct ext4_fs_context *ctx = fc->fs_private;
@@ -1923,14 +1921,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
#ifdef CONFIG_QUOTA
if (token == Opt_usrjquota) {
if (result.negated)
- return clear_qf_name(sb, USRQUOTA);
+ return unnote_qf_name(fc, USRQUOTA);
else
- return set_qf_name(sb, USRQUOTA, param);
+ return note_qf_name(fc, USRQUOTA, param);
} else if (token == Opt_grpjquota) {
if (result.negated)
- return clear_qf_name(sb, GRPQUOTA);
+ return unnote_qf_name(fc, GRPQUOTA);
else
- return set_qf_name(sb, GRPQUOTA, param);
+ return note_qf_name(fc, GRPQUOTA, param);
}
#endif
switch (token) {
@@ -1996,11 +1994,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
if (m->flags & MOPT_CLEAR_ERR)
clear_opt(sb, ERRORS_MASK);
- if (token == Opt_noquota && sb_any_quota_loaded(sb)) {
- ext4_msg(NULL, KERN_ERR, "Cannot change quota "
- "options when quota turned on");
- return -EINVAL;
- }

if (m->flags & MOPT_NOSUPPORT) {
ext4_msg(NULL, KERN_ERR, "%s option not supported",
@@ -2117,19 +2110,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
#ifdef CONFIG_QUOTA
} else if (m->flags & MOPT_QFMT) {
- if (sb_any_quota_loaded(sb) &&
- sbi->s_jquota_fmt != m->mount_opt) {
- ext4_msg(NULL, KERN_ERR, "Cannot change journaled "
- "quota options when quota turned on");
- return -EINVAL;
- }
- if (ext4_has_feature_quota(sb)) {
- ext4_msg(NULL, KERN_INFO,
- "Quota format mount options ignored "
- "when QUOTA feature is enabled");
- return -EINVAL;
- }
- sbi->s_jquota_fmt = m->mount_opt;
+ ctx->s_jquota_fmt = m->mount_opt;
#endif
} else if (token == Opt_dax) {
#ifdef CONFIG_FS_DAX
@@ -2231,9 +2212,99 @@ static int parse_options(char *options, struct super_block *sb,
if (ret < 0)
return 0;

+ ret = ext4_check_quota_consistency(&fc, sb);
+ if (ret < 0)
+ return 0;
+
+ if (ctx.qname_spec)
+ ext4_apply_quota_options(&fc, sb);
+
return 1;
}

+static void ext4_apply_quota_options(struct fs_context *fc,
+ struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+ struct ext4_fs_context *ctx = fc->fs_private;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ char *qname;
+ int i;
+
+ for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+ if (!(ctx->qname_spec & (1 << i)))
+ continue;
+ qname = ctx->s_qf_names[i]; /* May be NULL */
+ ctx->s_qf_names[i] = NULL;
+ kfree(sbi->s_qf_names[i]);
+ rcu_assign_pointer(sbi->s_qf_names[i], qname);
+ set_opt(sb, QUOTA);
+ }
+#endif
+}
+
+/*
+ * Check quota settings consistency.
+ */
+static int ext4_check_quota_consistency(struct fs_context *fc,
+ struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+ struct ext4_fs_context *ctx = fc->fs_private;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
+ bool quota_feature = ext4_has_feature_quota(sb);
+ bool quota_loaded = sb_any_quota_loaded(sb);
+ int i;
+
+ if (ctx->qname_spec && quota_loaded) {
+ if (quota_feature)
+ goto err_feature;
+
+ for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+ if (!(ctx->qname_spec & (1 << i)))
+ continue;
+
+ if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+ goto err_jquota_change;
+
+ if (sbi->s_qf_names[i] &&
+ strcmp(sbi->s_qf_names[i],
+ ctx->s_qf_names[i]) != 0)
+ goto err_jquota_specified;
+ }
+ }
+
+ if (ctx->s_jquota_fmt) {
+ if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
+ goto err_quota_change;
+ if (quota_feature) {
+ ext4_msg(NULL, KERN_INFO, "Quota format mount options "
+ "ignored when QUOTA feature is enabled");
+ return 0;
+ }
+ }
+ return 0;
+
+err_quota_change:
+ ext4_msg(NULL, KERN_ERR,
+ "Ext4: Cannot change quota options when quota turned on");
+ return -EINVAL;
+err_jquota_change:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Cannot change journaled quota "
+ "options when quota turned on");
+ return -EINVAL;
+err_jquota_specified:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Quota file already specified");
+ return -EINVAL;
+err_feature:
+ ext4_msg(NULL, KERN_ERR, "Ext4: Journaled quota options ignored "
+ "when QUOTA feature is enabled");
+ return -EINVAL;
+#else
+ return 0;
+#endif
+}
+
static int ext4_validate_options(struct fs_context *fc)
{
struct ext4_sb_info *sbi = fc->s_fs_info;
--
2.21.0

2019-11-06 10:16:01

by Lukas Czerner

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

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

Also return -EINVAL instead of -1

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 63a06dcb2807..12483f3b1f78 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -88,7 +88,7 @@ static void ext4_unregister_li_request(struct super_block *sb);
static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
unsigned int journal_inum);
-static int ext4_validate_options(struct super_block *sb);
+static int ext4_validate_options(struct fs_context *fc);

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

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

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

@@ -1935,12 +1936,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:
@@ -1967,22 +1968,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
break;

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

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

if (m->flags & MOPT_EXPLICIT) {
@@ -1991,28 +1992,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) {
@@ -2025,10 +2026,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) {
@@ -2043,24 +2044,24 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
} else if (token == Opt_resuid) {
uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
- ext4_msg(sb, KERN_ERR, "Invalid uid value %d",
+ ext4_msg(NULL, KERN_ERR, "Invalid uid value %d",
result.uint_32);
- return -1;
+ return -EINVAL;
}
sbi->s_resuid = uid;
} else if (token == Opt_resgid) {
gid = make_kgid(current_user_ns(), result.uint_32);
if (!gid_valid(gid)) {
- ext4_msg(sb, KERN_ERR, "Invalid gid value %d",
+ ext4_msg(NULL, KERN_ERR, "Invalid gid value %d",
result.uint_32);
- return -1;
+ return -EINVAL;
}
sbi->s_resgid = gid;
} else if (token == Opt_journal_dev) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
- ext4_msg(sb, KERN_ERR,
+ ext4_msg(NULL, KERN_ERR,
"Cannot specify journal on remount");
- return -1;
+ return -EINVAL;
}
ctx->journal_devnum = result.uint_32;
} else if (token == Opt_journal_path) {
@@ -2069,16 +2070,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
int error;

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

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

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

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

-static int ext4_validate_options(struct super_block *sb)
+static int ext4_validate_options(struct fs_context *fc)
{
- struct ext4_sb_info *sbi = EXT4_SB(sb);
+ struct ext4_sb_info *sbi = fc->s_fs_info;
+ struct super_block *sb = sbi->s_sb;
#ifdef CONFIG_QUOTA
char *usr_qf_name, *grp_qf_name;
/*
@@ -2240,9 +2246,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);
@@ -2254,15 +2260,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
@@ -2271,12 +2277,12 @@ static int ext4_validate_options(struct super_block *sb)
BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);

if (blocksize < PAGE_SIZE) {
- ext4_msg(sb, KERN_ERR, "can't mount with "
+ ext4_msg(NULL, KERN_ERR, "can't mount with "
"dioread_nolock if block size != PAGE_SIZE");
- return 0;
+ return -EINVAL;
}
}
- return 1;
+ return 0;
}

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

2019-11-06 10:16:05

by Lukas Czerner

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

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

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

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

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index f3dd82fef453..417a929cb0ab 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1667,29 +1667,6 @@ static const match_table_t tokens = {
{Opt_err, NULL},
};

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

struct ext4_fs_context {
char *s_qf_names[EXT4_MAXQUOTAS];
@@ -1881,6 +1860,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
@@ -1994,8 +1974,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
case Opt_nouser_xattr:
ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
break;
- case Opt_sb:
- return 1; /* handled by get_sb_block() */
case Opt_removed:
ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
param->key);
@@ -2108,6 +2086,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
ctx->s_resgid = gid;
ctx->spec |= EXT4_SPEC_s_resgid;
+ } else if (token == Opt_sb) {
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Ignoring %s option on remount", param->key);
+ } else {
+ ctx->s_sb_block = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_sb_block;
+ }
} else if (token == Opt_journal_dev) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(NULL, KERN_ERR,
@@ -2202,28 +2188,15 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
return 1;
}

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

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

while ((p = strsep(&options, ",")) != NULL) {

@@ -2250,27 +2223,63 @@ static int parse_options(char *options, struct super_block *sb,
}
}

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

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

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

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

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

static void ext4_apply_quota_options(struct fs_context *fc,
@@ -4020,21 +4029,53 @@ static void ext4_set_resv_clusters(struct super_block *sb)
atomic64_set(&sbi->s_resv_clusters, resv_clusters);
}

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

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

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

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

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

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

brelse(bh);
- logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
+ logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
offset = do_div(logical_sb_block, blocksize);
bh = sb_bread_unmovable(sb, logical_sb_block);
if (!bh) {
@@ -5004,15 +5041,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
ext4_msg(sb, KERN_INFO, "recovery complete");
ext4_mark_recovery_complete(sb, es);
}
- 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);
@@ -5022,13 +5050,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", descr,
- (int) sizeof(sbi->s_es->s_mount_opts),
- sbi->s_es->s_mount_opts,
- *sbi->s_es->s_mount_opts ? "; " : "", orig_data);
-
if (es->s_error_count)
mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */

@@ -5037,7 +5058,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
ratelimit_state_init(&sbi->s_warning_ratelimit_state, 5 * HZ, 10);
ratelimit_state_init(&sbi->s_msg_ratelimit_state, 5 * HZ, 10);

- kfree(orig_data);
return 0;

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

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

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

2019-11-06 10:16:10

by Lukas Czerner

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 76f7d742de8e..f863fddc3df3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -5801,8 +5801,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;
@@ -5815,10 +5817,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
int i, j;
char *to_free[EXT4_MAXQUOTAS];
#endif
- char *orig_data = kstrdup(data, GFP_KERNEL);
-
- if (data && !orig_data)
- return -ENOMEM;

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

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

ext4_clamp_want_extra_isize(sb);

@@ -6050,8 +6047,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
#endif

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

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

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

2019-11-06 10:16:10

by Lukas Czerner

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

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

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

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

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

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

2019-11-06 10:16:14

by Lukas Czerner

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

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

Now we can use is in fs_context_operations as .parse_param.

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 417a929cb0ab..dabb55bb999f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -92,6 +92,7 @@ static int ext4_validate_options(struct fs_context *fc);
static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb);
static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);
+static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);

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

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


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

static int parse_options(struct fs_context *fc, char *options)
@@ -2223,7 +2228,7 @@ static int parse_options(struct fs_context *fc, char *options)
}
}

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

2019-11-06 10:16:15

by Lukas Czerner

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

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

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

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

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

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

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

2019-11-06 10:16:18

by Lukas Czerner

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

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

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

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

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

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

2019-11-06 10:16:19

by Lukas Czerner

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 815f86f1e047..f2f17c11b616 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -96,6 +96,8 @@ static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
static int ext4_get_tree(struct fs_context *fc);
static int ext4_reconfigure(struct fs_context *fc);
static void ext4_fc_free(struct fs_context *fc);
+static int ext4_init_fs_context(struct fs_context *fc);
+static const struct fs_parameter_description ext4_fs_parameters;

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

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


static struct file_system_type ext3_fs_type = {
- .owner = THIS_MODULE,
- .name = "ext3",
- .mount = ext4_mount,
- .kill_sb = kill_block_super,
- .fs_flags = FS_REQUIRES_DEV,
+ .owner = THIS_MODULE,
+ .name = "ext3",
+ .init_fs_context = ext4_init_fs_context,
+ .parameters = &ext4_fs_parameters,
+ .kill_sb = kill_block_super,
+ .fs_flags = FS_REQUIRES_DEV,
};
MODULE_ALIAS_FS("ext3");
MODULE_ALIAS("ext3");
@@ -1439,7 +1443,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,
@@ -1887,6 +1890,20 @@ static void ext4_fc_free(struct fs_context *fc)
kfree(ctx);
}

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

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

--
2.21.0

2019-11-06 10:16:30

by Lukas Czerner

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index f2f17c11b616..2f3296e81837 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2613,12 +2613,14 @@ 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 == fs_param_is_flag ||
+ spec->type == fs_param_is_bool))
break;
- return t->pattern;
+ return spec->name;
}

/*
--
2.21.0

2019-11-06 10:17:38

by Lukas Czerner

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index affcdaf63b6e..d515506d18a0 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -89,8 +89,8 @@ static void ext4_clear_request_list(void);
static struct inode *ext4_get_journal_inode(struct super_block *sb,
unsigned int journal_inum);
static int ext4_validate_options(struct fs_context *fc);
-static int ext4_check_quota_consistency(struct fs_context *fc,
- struct super_block *sb);
+static int ext4_check_opt_consistency(struct fs_context *fc,
+ struct super_block *sb);
static void ext4_apply_quota_options(struct fs_context *fc,
struct super_block *sb);

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

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

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

- if ((m->flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
- ext4_msg(NULL, KERN_ERR,
- "Mount option \"%s\" incompatible with ext2",
- param->string);
- return -EINVAL;
- }
- if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
- ext4_msg(NULL, KERN_ERR,
- "Mount option \"%s\" incompatible with ext3",
- param->string);
- return -EINVAL;
- }
-
if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
set_opt2(sb, EXPLICIT_DELALLOC);
@@ -2212,7 +2202,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;

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

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

2019-11-06 10:17:38

by Lukas Czerner

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

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

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

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d515506d18a0..f3dd82fef453 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -91,8 +91,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,
static int ext4_validate_options(struct fs_context *fc);
static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb);
-static void ext4_apply_quota_options(struct fs_context *fc,
- struct super_block *sb);
+static void ext4_apply_options(struct fs_context *fc, struct super_block *sb);

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

+#define EXT4_SPEC_JQUOTA (1 << 0)
+#define EXT4_SPEC_JQFMT (1 << 1)
+#define EXT4_SPEC_DATAJ (1 << 2)
+#define EXT4_SPEC_SB_BLOCK (1 << 3)
+#define EXT4_SPEC_JOURNAL_DEV (1 << 4)
+#define EXT4_SPEC_JOURNAL_IOPRIO (1 << 5)
+#define EXT4_SPEC_s_want_extra_isize (1 << 6)
+#define EXT4_SPEC_s_max_batch_time (1 << 7)
+#define EXT4_SPEC_s_min_batch_time (1 << 8)
+#define EXT4_SPEC_s_inode_readahead_blks (1 << 9)
+#define EXT4_SPEC_s_li_wait_mult (1 << 10)
+#define EXT4_SPEC_s_max_dir_size_kb (1 << 11)
+#define EXT4_SPEC_s_stripe (1 << 12)
+#define EXT4_SPEC_s_resuid (1 << 13)
+#define EXT4_SPEC_s_resgid (1 << 14)
+#define EXT4_SPEC_s_commit_interval (1 << 15)
+
struct ext4_fs_context {
char *s_qf_names[EXT4_MAXQUOTAS];
int s_jquota_fmt; /* Format of quota to use */
unsigned short qname_spec;
+ unsigned long vals_s_flags; /* Bits to set in s_flags */
+ unsigned long mask_s_flags; /* Bits changed in s_flags */
unsigned long journal_devnum;
+ unsigned long s_commit_interval;
+ unsigned long s_stripe;
+ unsigned int s_inode_readahead_blks;
+ unsigned int s_want_extra_isize;
+ unsigned int s_li_wait_mult;
+ unsigned int s_max_dir_size_kb;
unsigned int journal_ioprio;
+ unsigned int vals_s_mount_opt;
+ unsigned int mask_s_mount_opt;
+ unsigned int vals_s_mount_opt2;
+ unsigned int mask_s_mount_opt2;
+ unsigned int vals_s_mount_flags;
+ unsigned int mask_s_mount_flags;
unsigned int opt_flags; /* MOPT flags */
+ unsigned int spec;
+ u32 s_max_batch_time;
+ u32 s_min_batch_time;
+ kuid_t s_resuid;
+ kgid_t s_resgid;
};

#ifdef CONFIG_QUOTA
@@ -1884,6 +1919,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;
}

@@ -1900,15 +1936,36 @@ static int unnote_qf_name(struct fs_context *fc, int qtype)
kfree(ctx->s_qf_names[qtype]);
ctx->s_qf_names[qtype] = NULL;
ctx->qname_spec |= 1 << qtype;
+ ctx->spec |= EXT4_SPEC_JQUOTA;
return 0;
}
#endif

+#define EXT4_SET_CTX(name) \
+static inline void set_##name(struct ext4_fs_context *ctx, int flag) \
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name |= flag; \
+} \
+static inline void clear_##name(struct ext4_fs_context *ctx, int flag) \
+{ \
+ ctx->mask_s_##name |= flag; \
+ ctx->vals_s_##name &= ~flag; \
+} \
+static inline bool test_##name(struct ext4_fs_context *ctx, int flag) \
+{ \
+ return ((ctx->vals_s_##name & flag) != 0); \
+} \
+
+EXT4_SET_CTX(flags);
+EXT4_SET_CTX(mount_opt);
+EXT4_SET_CTX(mount_opt2);
+EXT4_SET_CTX(mount_flags);
+
+
static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
{
struct ext4_fs_context *ctx = fc->fs_private;
- struct ext4_sb_info *sbi = fc->s_fs_info;
- struct super_block *sb = sbi->s_sb;
const struct mount_opts *m;
struct fs_parse_result result;
kuid_t uid;
@@ -1944,16 +2001,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
param->key);
return 1;
case Opt_abort:
- sbi->s_mount_flags |= EXT4_MF_FS_ABORTED;
+ set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
return 1;
case Opt_i_version:
- sb->s_flags |= SB_I_VERSION;
+ set_flags(ctx, SB_I_VERSION);
return 1;
case Opt_lazytime:
- sb->s_flags |= SB_LAZYTIME;
+ set_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_nolazytime:
- sb->s_flags &= ~SB_LAZYTIME;
+ clear_flags(ctx, SB_LAZYTIME);
return 1;
case Opt_errors:
case Opt_data:
@@ -1976,21 +2033,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)

if (m->flags & MOPT_EXPLICIT) {
if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
- set_opt2(sb, EXPLICIT_DELALLOC);
+ set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
} else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
- set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
+ set_mount_opt2(ctx,
+ EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
} else
return -EINVAL;
}
if (m->flags & MOPT_CLEAR_ERR)
- clear_opt(sb, ERRORS_MASK);
+ clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);

if (m->flags & MOPT_NOSUPPORT) {
ext4_msg(NULL, KERN_ERR, "%s option not supported",
param->key);
} else if (token == Opt_commit) {
if (result.uint_32 == 0)
- sbi->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
+ ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
else if (result.uint_32 > INT_MAX / HZ) {
ext4_msg(NULL, KERN_ERR,
"Invalid commit interval %d, "
@@ -1998,13 +2056,17 @@ 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) {
- 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) ||
@@ -2014,16 +2076,20 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
"0 or a power of 2 smaller than 2^31");
return -EINVAL;
}
- sbi->s_inode_readahead_blks = result.uint_32;
+ ctx->s_inode_readahead_blks = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
} else if (token == Opt_init_itable) {
- set_opt(sb, INIT_INODE_TABLE);
- sbi->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
+ set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
+ ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
if (result.has_value)
- sbi->s_li_wait_mult = result.uint_32;
+ ctx->s_li_wait_mult = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_li_wait_mult;
} else if (token == Opt_max_dir_size_kb) {
- sbi->s_max_dir_size_kb = result.uint_32;
+ ctx->s_max_dir_size_kb = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
} else if (token == Opt_stripe) {
- sbi->s_stripe = result.uint_32;
+ ctx->s_stripe = result.uint_32;
+ ctx->spec |= EXT4_SPEC_s_stripe;
} else if (token == Opt_resuid) {
uid = make_kuid(current_user_ns(), result.uint_32);
if (!uid_valid(uid)) {
@@ -2031,7 +2097,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)) {
@@ -2039,7 +2106,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
result.uint_32);
return -EINVAL;
}
- sbi->s_resgid = gid;
+ ctx->s_resgid = gid;
+ ctx->spec |= EXT4_SPEC_s_resgid;
} else if (token == Opt_journal_dev) {
if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
ext4_msg(NULL, KERN_ERR,
@@ -2047,6 +2115,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;
@@ -2067,6 +2136,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) {
@@ -2076,9 +2146,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
}
ctx->journal_ioprio =
IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
+ ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
} else if (token == Opt_test_dummy_encryption) {
#ifdef CONFIG_FS_ENCRYPTION
- sbi->s_mount_flags |= EXT4_MF_TEST_DUMMY_ENCRYPTION;
+ set_mount_flags(ctx, EXT4_MF_TEST_DUMMY_ENCRYPTION);
ext4_msg(NULL, KERN_WARNING,
"Test dummy encryption mode enabled");
#else
@@ -2086,35 +2157,27 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
"Test dummy encryption mount option ignored");
#endif
} else if (m->flags & MOPT_DATAJ) {
- if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
- if (!sbi->s_journal)
- ext4_msg(NULL, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
- else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
- ext4_msg(NULL, KERN_ERR,
- "Cannot change data mode on remount");
- return -EINVAL;
- }
- } else {
- clear_opt(sb, DATA_FLAGS);
- sbi->s_mount_opt |= m->mount_opt;
- }
+ clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+ set_mount_opt(ctx, m->mount_opt);
+ ctx->spec |= EXT4_SPEC_DATAJ;
#ifdef CONFIG_QUOTA
} else if (m->flags & MOPT_QFMT) {
ctx->s_jquota_fmt = m->mount_opt;
+ ctx->spec |= EXT4_SPEC_JQFMT;
#endif
} else if (token == Opt_dax) {
#ifdef CONFIG_FS_DAX
ext4_msg(NULL, KERN_WARNING,
"DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
- sbi->s_mount_opt |= m->mount_opt;
+ set_mount_opt(ctx, m->mount_opt);
#else
ext4_msg(NULL, KERN_INFO, "dax option not supported");
return -EINVAL;
#endif
} else if (token == Opt_data_err_abort) {
- sbi->s_mount_opt |= m->mount_opt;
+ set_mount_opt(ctx, m->mount_opt);
} else if (token == Opt_data_err_ignore) {
- sbi->s_mount_opt &= ~m->mount_opt;
+ clear_mount_opt(ctx, m->mount_opt);
} else {
bool set;

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

- if (journal_devnum)
- *journal_devnum = ctx.journal_devnum;
- if (journal_ioprio)
- *journal_ioprio = ctx.journal_ioprio;
-
ret = ext4_validate_options(&fc);
if (ret < 0)
return 0;
@@ -2206,9 +2264,12 @@ static int parse_options(char *options, struct super_block *sb,
if (ret < 0)
return 0;

- if (ctx.qname_spec)
- ext4_apply_quota_options(&fc, sb);
+ if (ctx.spec & EXT4_SPEC_JOURNAL_DEV)
+ *journal_devnum = ctx.journal_devnum;
+ if (ctx.spec & EXT4_SPEC_JOURNAL_IOPRIO)
+ *journal_ioprio = ctx.journal_ioprio;

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

@@ -2216,20 +2277,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
}

@@ -2244,17 +2315,43 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
struct ext4_sb_info *sbi = EXT4_SB(sb);
bool quota_feature = ext4_has_feature_quota(sb);
bool quota_loaded = sb_any_quota_loaded(sb);
- int i;
+ bool usr_qf_name, grp_qf_name, usrquota, grpquota;
+ int quota_flags, i;

- if (ctx->qname_spec && quota_loaded) {
- if (quota_feature)
- goto err_feature;
+ /*
+ * We do the test below only for project quotas. 'usrquota' and
+ * 'grpquota' mount options are allowed even without quota feature
+ * to support legacy quotas in quota files.
+ */
+ if (test_mount_opt(ctx, EXT4_MOUNT_PRJQUOTA) &&
+ !ext4_has_feature_project(sb)) {
+ ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
+ "Cannot enable project quota enforcement.");
+ return -EINVAL;
+ }
+
+ quota_flags = EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
+ EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA;
+ if (quota_loaded &&
+ ctx->mask_s_mount_opt & quota_flags &&
+ !test_mount_opt( ctx, quota_flags))
+ goto err_quota_change;
+
+ if (ctx->spec & EXT4_SPEC_JQUOTA) {
+
+ if (quota_feature) {
+ ext4_msg(NULL, KERN_INFO,
+ "Ext4: Journaled quota options ignored when "
+ "QUOTA feature is enabled");
+ return 0;
+ }

for (i = 0; i < EXT4_MAXQUOTAS; i++) {
if (!(ctx->qname_spec & (1 << i)))
continue;

- if (!!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
+ if (quota_loaded &&
+ !!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
goto err_jquota_change;

if (sbi->s_qf_names[i] &&
@@ -2264,15 +2361,52 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
}
}

- if (ctx->s_jquota_fmt) {
+ if (ctx->spec & EXT4_SPEC_JQFMT) {
if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
- goto err_quota_change;
+ goto err_jquota_change;
if (quota_feature) {
ext4_msg(NULL, KERN_INFO, "Quota format mount options "
"ignored when QUOTA feature is enabled");
return 0;
}
}
+
+
+ /* Make sure we don't mix old and new quota format */
+ usr_qf_name = (get_qf_name(sb, sbi, USRQUOTA) ||
+ ctx->s_qf_names[USRQUOTA]);
+ grp_qf_name = (get_qf_name(sb, sbi, GRPQUOTA) ||
+ ctx->s_qf_names[GRPQUOTA]);
+
+ usrquota = (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ test_opt(sb, USRQUOTA));
+
+ grpquota = (test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
+ test_opt(sb, GRPQUOTA));
+
+ if (usr_qf_name) {
+ clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
+ usrquota = false;
+ }
+ if (grp_qf_name) {
+ clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
+ grpquota = false;
+ }
+
+ if (usr_qf_name || grp_qf_name) {
+ if (usrquota || grpquota) {
+ ext4_msg(NULL, KERN_ERR, "old and new quota "
+ "format mixing");
+ return -EINVAL;
+ }
+
+ if (!(ctx->spec & EXT4_SPEC_JQFMT || sbi->s_jquota_fmt)) {
+ ext4_msg(NULL, KERN_ERR, "journaled quota format "
+ "not specified");
+ return -EINVAL;
+ }
+ }
+
return 0;

err_quota_change:
@@ -2286,10 +2420,6 @@ static int ext4_check_quota_consistency(struct fs_context *fc,
err_jquota_specified:
ext4_msg(NULL, KERN_ERR, "Ext4: Quota file already specified");
return -EINVAL;
-err_feature:
- ext4_msg(NULL, KERN_ERR, "Ext4: Journaled quota options ignored "
- "when QUOTA feature is enabled");
- return -EINVAL;
#else
return 0;
#endif
@@ -2299,6 +2429,7 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
struct super_block *sb)
{
struct ext4_fs_context *ctx = fc->fs_private;
+ struct ext4_sb_info *sbi = fc->s_fs_info;

if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
ext4_msg(NULL, KERN_ERR,
@@ -2311,58 +2442,88 @@ static int ext4_check_opt_consistency(struct fs_context *fc,
return -EINVAL;
}

+ if (test_mount_opt(ctx, EXT4_MOUNT_DIOREAD_NOLOCK)) {
+ int blocksize =
+ BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
+
+ if (blocksize < PAGE_SIZE) {
+ ext4_msg(NULL, KERN_ERR,
+ "can't mount with dioread_nolock "
+ "if block size != PAGE_SIZE");
+ return -EINVAL;
+ }
+ }
+
+ if ((ctx->spec & EXT4_SPEC_DATAJ) &&
+ (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)) {
+ if (!sbi->s_journal) {
+ ext4_msg(NULL, KERN_WARNING,
+ "Remounting file system with no journal "
+ "so ignoring journalled data option");
+ clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
+ } else if (ctx->mask_s_mount_opt & EXT4_MOUNT_DATA_FLAGS) {
+ ext4_msg(NULL, KERN_ERR, "Cannot change data mode "
+ "on remount");
+ return -EINVAL;
+ }
+ }
+
return ext4_check_quota_consistency(fc, sb);
}

-static int ext4_validate_options(struct fs_context *fc)
+static void ext4_apply_options(struct fs_context *fc, struct super_block *sb)
{
+ struct ext4_fs_context *ctx = fc->fs_private;
struct ext4_sb_info *sbi = fc->s_fs_info;
- struct super_block *sb = sbi->s_sb;
+
+ sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
+ sbi->s_mount_opt |= ctx->vals_s_mount_opt;
+ sbi->s_mount_opt2 &= ~ctx->mask_s_mount_opt2;
+ sbi->s_mount_opt2 |= ctx->vals_s_mount_opt2;
+ sbi->s_mount_flags &= ~ctx->mask_s_mount_flags;
+ sbi->s_mount_flags |= ctx->vals_s_mount_flags;
+ sb->s_flags &= ~ctx->mask_s_flags;
+ sb->s_flags |= ctx->vals_s_flags;
+
+#define APPLY(X) if (ctx->spec & EXT4_SPEC_##X) sbi->X = ctx->X
+ APPLY(s_commit_interval);
+ APPLY(s_stripe);
+ APPLY(s_max_batch_time);
+ APPLY(s_min_batch_time);
+ APPLY(s_want_extra_isize);
+ APPLY(s_inode_readahead_blks);
+ APPLY(s_max_dir_size_kb);
+ APPLY(s_li_wait_mult);
+ APPLY(s_resgid);
+ APPLY(s_resuid);
+
+ ext4_apply_quota_options(fc, sb);
+}
+
+static int ext4_validate_options(struct fs_context *fc)
+{
+ struct ext4_fs_context *ctx = fc->fs_private;
#ifdef CONFIG_QUOTA
char *usr_qf_name, *grp_qf_name;
- /*
- * We do the test below only for project quotas. 'usrquota' and
- * 'grpquota' mount options are allowed even without quota feature
- * to support legacy quotas in quota files.
- */
- if (test_opt(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
- ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
- "Cannot enable project quota enforcement.");
- return -EINVAL;
- }
- usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
- grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
+
+ usr_qf_name = ctx->s_qf_names[USRQUOTA];
+ grp_qf_name = ctx->s_qf_names[GRPQUOTA];
if (usr_qf_name || grp_qf_name) {
- if (test_opt(sb, USRQUOTA) && usr_qf_name)
- clear_opt(sb, USRQUOTA);
+ if (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
+ clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);

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

- if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
+ if (test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
+ test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA)) {
ext4_msg(NULL, KERN_ERR, "old and new quota "
- "format mixing");
- return -EINVAL;
- }
-
- if (!sbi->s_jquota_fmt) {
- ext4_msg(NULL, KERN_ERR, "journaled quota format "
- "not specified");
+ "format mixing");
return -EINVAL;
}
}
#endif
- if (test_opt(sb, DIOREAD_NOLOCK)) {
- int blocksize =
- BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
-
- if (blocksize < PAGE_SIZE) {
- ext4_msg(NULL, KERN_ERR, "can't mount with "
- "dioread_nolock if block size != PAGE_SIZE");
- return -EINVAL;
- }
- }
- return 0;
+ return 1;
}

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

2019-11-06 10:18:25

by Lukas Czerner

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

Additionally rename ext4_fill_super_fc to ext4_fill_super

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

diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 2f3296e81837..ae7ee4a2c9b6 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -74,12 +74,9 @@ static void ext4_mark_recovery_complete(struct super_block *sb,
static void ext4_clear_journal_err(struct super_block *sb,
struct ext4_super_block *es);
static int ext4_sync_fs(struct super_block *sb, int wait);
-static int ext4_remount(struct super_block *sb, int *flags, char *data);
static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
static int ext4_unfreeze(struct super_block *sb);
static int ext4_freeze(struct super_block *sb);
-static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data);
static inline int ext2_feature_set_ok(struct super_block *sb);
static inline int ext3_feature_set_ok(struct super_block *sb);
static int ext4_feature_set_ok(struct super_block *sb, int readonly);
@@ -1589,98 +1586,6 @@ static const struct fs_parameter_description ext4_fs_parameters = {
.enums = ext4_param_enums,
};

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

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

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

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

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

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

2019-12-24 17:18:48

by Al Viro

[permalink] [raw]
Subject: Re: [PATCH 02/17] ext4: Add fs parameter description

On Tue, Dec 17, 2019 at 05:34:32PM +0100, Lukas Czerner wrote:

> > If so, I would prefer
> > fsparam_flag_no("barrier", Opt_barrier), // barrier | nobarrier
> > fsparam_u32("barrier", Opt_barrier), // barrier=<number>
> > as the solution, with fs_parse() having been taught to allow argument-bearing
> > and argument-less options with the same name, picking the right one. That
> > way Opt_nobarrier gets removed as well...
> >
> > I'll push a branch with that stuff later today; will post when it's out...
>
> That would be great, thanks.

It took longer than I hoped, sorry ;-/ The current patchset is in
#untested.fs_parse; the really interesting part is up to
"turn fs_param_is_... into functions".

One surprising source of PITA around your patchset is ext4_show_options().
It pretty much forces you into keeping "no..." forms separate, even though
normally you could just say
fsparam_flag_no("quota", Opt_quota),
and get rid of Opt_noquota, etc.

If you keep that dependency, it'll need to be documented - right in
fs/ext4/super.c, to make sure we don't get "optimizing" followups breaking
the hell out of things.

Said that, I really doubt that token2str() is a good idea. It might make
more sense to start with separating _ext4_show_options() from that
machinery.

Another thing is that all fsparam_bool() users are modifying user-visible
ABI; use fparam_flag_no() + fsparam_u32() with the same name and same
opt - that'll give you the existing behaviour.