aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChao Yu <chao@kernel.org>2024-02-28 22:51:32 +0800
committerChao Yu <chao@kernel.org>2024-02-27 09:30:33 +0800
commitd4eac172f8f9f4594056366859b67e589e6124c3 (patch)
tree5dea0414b25208a7fa78a6eabea6535eec43032d
parentb50650e50ba9d876469eed7315700ba64072ed4c (diff)
downloadlinux-feature/fs_param.tar.gz
[WIP] f2fs: use new fs_context interfacefeature/fs_param
TODO: - handle checkpoint_disabling* cases - handle compress_algorithm=lz4:xx | zstd:xx cases - retry mount once it fails Signed-off-by: Chao Yu <chao@kernel.org>
-rw-r--r--fs/f2fs/f2fs.h6
-rw-r--r--fs/f2fs/super.c1284
2 files changed, 486 insertions, 804 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 85eb9a8a5ed30d..01208db9637062 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -116,7 +116,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
#define F2FS_MOUNT_COMPRESS_CACHE 0x04000000
#define F2FS_MOUNT_AGE_EXTENT_CACHE 0x08000000
-#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
+#define F2FS_OPTION(sbi) ((sbi)->ctx)
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
#define set_opt(sbi, option) (F2FS_OPTION(sbi).opt |= F2FS_MOUNT_##option)
#define test_opt(sbi, option) (F2FS_OPTION(sbi).opt & F2FS_MOUNT_##option)
@@ -147,7 +147,7 @@ struct f2fs_rwsem {
#endif
};
-struct f2fs_mount_info {
+struct f2fs_fs_context {
unsigned int opt;
block_t root_reserved_blocks; /* root reserved blocks */
kuid_t s_resuid; /* reserved blocks for uid */
@@ -1655,7 +1655,7 @@ struct f2fs_sb_info {
/* valid inode count */
struct percpu_counter total_valid_inode_count;
- struct f2fs_mount_info mount_opt; /* mount options */
+ struct f2fs_fs_context ctx; /* mount options */
/* for cleaning operations */
struct f2fs_rwsem gc_lock; /*
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 78a76583a4aa80..975fa956b53e4e 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -28,6 +28,7 @@
#include <linux/part_stat.h>
#include <linux/zstd.h>
#include <linux/lz4.h>
+#include <linux/fs_parser.h>
#include "f2fs.h"
#include "node.h"
@@ -139,6 +140,7 @@ enum {
Opt_resgid,
Opt_resuid,
Opt_mode,
+ Opt_io_size_bits,
Opt_fault_injection,
Opt_fault_type,
Opt_lazytime,
@@ -154,9 +156,7 @@ enum {
Opt_offusrjquota,
Opt_offgrpjquota,
Opt_offprjjquota,
- Opt_jqfmt_vfsold,
- Opt_jqfmt_vfsv0,
- Opt_jqfmt_vfsv1,
+ Opt_jqfmt,
Opt_alloc,
Opt_fsync,
Opt_test_dummy_encryption,
@@ -184,83 +184,266 @@ enum {
Opt_err,
};
-static match_table_t f2fs_tokens = {
- {Opt_gc_background, "background_gc=%s"},
- {Opt_disable_roll_forward, "disable_roll_forward"},
- {Opt_norecovery, "norecovery"},
- {Opt_discard, "discard"},
- {Opt_nodiscard, "nodiscard"},
- {Opt_noheap, "no_heap"},
- {Opt_heap, "heap"},
- {Opt_user_xattr, "user_xattr"},
- {Opt_nouser_xattr, "nouser_xattr"},
- {Opt_acl, "acl"},
- {Opt_noacl, "noacl"},
- {Opt_active_logs, "active_logs=%u"},
- {Opt_disable_ext_identify, "disable_ext_identify"},
- {Opt_inline_xattr, "inline_xattr"},
- {Opt_noinline_xattr, "noinline_xattr"},
- {Opt_inline_xattr_size, "inline_xattr_size=%u"},
- {Opt_inline_data, "inline_data"},
- {Opt_inline_dentry, "inline_dentry"},
- {Opt_noinline_dentry, "noinline_dentry"},
- {Opt_flush_merge, "flush_merge"},
- {Opt_noflush_merge, "noflush_merge"},
- {Opt_barrier, "barrier"},
- {Opt_nobarrier, "nobarrier"},
- {Opt_fastboot, "fastboot"},
- {Opt_extent_cache, "extent_cache"},
- {Opt_noextent_cache, "noextent_cache"},
- {Opt_noinline_data, "noinline_data"},
- {Opt_data_flush, "data_flush"},
- {Opt_reserve_root, "reserve_root=%u"},
- {Opt_resgid, "resgid=%u"},
- {Opt_resuid, "resuid=%u"},
- {Opt_mode, "mode=%s"},
- {Opt_fault_injection, "fault_injection=%u"},
- {Opt_fault_type, "fault_type=%u"},
- {Opt_lazytime, "lazytime"},
- {Opt_nolazytime, "nolazytime"},
- {Opt_quota, "quota"},
- {Opt_noquota, "noquota"},
- {Opt_usrquota, "usrquota"},
- {Opt_grpquota, "grpquota"},
- {Opt_prjquota, "prjquota"},
- {Opt_usrjquota, "usrjquota=%s"},
- {Opt_grpjquota, "grpjquota=%s"},
- {Opt_prjjquota, "prjjquota=%s"},
- {Opt_offusrjquota, "usrjquota="},
- {Opt_offgrpjquota, "grpjquota="},
- {Opt_offprjjquota, "prjjquota="},
- {Opt_jqfmt_vfsold, "jqfmt=vfsold"},
- {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
- {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
- {Opt_alloc, "alloc_mode=%s"},
- {Opt_fsync, "fsync_mode=%s"},
- {Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
- {Opt_test_dummy_encryption, "test_dummy_encryption"},
- {Opt_inlinecrypt, "inlinecrypt"},
- {Opt_checkpoint_disable, "checkpoint=disable"},
- {Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
- {Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"},
- {Opt_checkpoint_enable, "checkpoint=enable"},
- {Opt_checkpoint_merge, "checkpoint_merge"},
- {Opt_nocheckpoint_merge, "nocheckpoint_merge"},
- {Opt_compress_algorithm, "compress_algorithm=%s"},
- {Opt_compress_log_size, "compress_log_size=%u"},
- {Opt_compress_extension, "compress_extension=%s"},
- {Opt_nocompress_extension, "nocompress_extension=%s"},
- {Opt_compress_chksum, "compress_chksum"},
- {Opt_compress_mode, "compress_mode=%s"},
- {Opt_compress_cache, "compress_cache"},
- {Opt_atgc, "atgc"},
- {Opt_gc_merge, "gc_merge"},
- {Opt_nogc_merge, "nogc_merge"},
- {Opt_discard_unit, "discard_unit=%s"},
- {Opt_memory_mode, "memory=%s"},
- {Opt_age_extent_cache, "age_extent_cache"},
- {Opt_errors, "errors=%s"},
- {Opt_err, NULL},
+static const struct mount_opts {
+ int token;
+ int opt;
+ int flags;
+};
+
+#define MO_SET 0x0001 /* set bit in opt */
+#define MO_CLEAR 0x0002 /* clear bit in opt */
+#define MO_ALONE 0x0004 /* handle alone */
+#define MO_ENUM 0x0008 /* set w/ enum */
+#define MO_NOTSUPP 0x0010 /* not supported */
+#define MO_DEPRECATED 0x0020 /* deprecated */
+
+#ifdef CONFIG_F2FS_FS_POSIX_ACL
+#define MO_ACL 0x0020
+#else
+#define MO_ACL MO_NOTSUPP
+#endif
+#ifdef CONFIG_F2FS_FS_XATTR
+#define MO_XATTR 0x0040
+#else
+#define MO_XATTR MO_NOTSUPP
+#endif
+#ifdef CONFIG_F2FS_FAULT_INJECTION
+#define MO_FAULT 0x0080
+#else
+#define MO_FAULT MO_NOTSUPP
+#endif
+#ifdef CONFIG_QUOTA
+#define MO_QUOTA 0x0100
+#else
+#define MO_QUOTA MO_NOTSUPP
+#endif
+#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
+#define MO_INLINECRYPT 0x0200
+#else
+#define MO_INLINECRYPT MO_NOTSUPP
+#endif
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+#define MO_COMPR 0x0400
+#else
+#define MO_COMPR MO_NOTSUPP
+#endif
+
+struct mount_opts f2fs_mount_opts[] = {
+ {Opt_gc_background, 0, MO_ENUM},
+ {Opt_disable_roll_forward, F2FS_MOUNT_DISABLE_ROLL_FORWARD,
+ MO_CLEAR},
+ {Opt_norecovery, F2FS_MOUNT_NORECOVERY, MO_SET},
+ {Opt_discard, F2FS_MOUNT_DISCARD, MO_SET},
+ {Opt_nodiscard, F2FS_MOUNT_DISCARD, MO_CLEAR},
+ {Opt_noheap, F2FS_MOUNT_NOHEAP, MO_DEPRECATED},
+ {Opt_heap, F2FS_MOUNT_NOHEAP, MO_DEPRECATED},
+ {Opt_acl, F2FS_MOUNT_POSIX_ACL, MO_SET | MO_ACL},
+ {Opt_noacl, F2FS_MOUNT_POSIX_ACL, MO_CLEAR | MO_ACL},
+ {Opt_user_xattr, F2FS_MOUNT_XATTR_USER, MO_SET | MO_XATTR},
+ {Opt_nouser_xattr, F2FS_MOUNT_XATTR_USER, MO_CLEAR | MO_XATTR},
+ {Opt_inline_xattr, F2FS_MOUNT_INLINE_XATTR, MO_SET | MO_XATTR},
+ {Opt_noinline_xattr, F2FS_MOUNT_INLINE_XATTR, MO_CLEAR | MO_XATTR},
+ {Opt_inline_xattr_size, 0, MO_SET | MO_XATTR},
+ {Opt_active_logs, 0, MO_ALONE},
+ {Opt_disable_ext_identify, F2FS_MOUNT_DISABLE_EXT_IDENTIFY,
+ MO_SET},
+ {Opt_inline_data, F2FS_MOUNT_INLINE_DATA, MO_SET},
+ {Opt_noinline_data, F2FS_MOUNT_INLINE_DATA, MO_CLEAR},
+ {Opt_inline_dentry, F2FS_MOUNT_INLINE_DENTRY, MO_SET},
+ {Opt_noinline_dentry, F2FS_MOUNT_INLINE_DENTRY, MO_CLEAR},
+ {Opt_flush_merge, F2FS_MOUNT_FLUSH_MERGE, MO_SET},
+ {Opt_noflush_merge, F2FS_MOUNT_FLUSH_MERGE, MO_CLEAR},
+ {Opt_barrier, F2FS_MOUNT_NOBARRIER, MO_CLEAR},
+ {Opt_nobarrier, F2FS_MOUNT_NOBARRIER, MO_SET},
+ {Opt_fastboot, F2FS_MOUNT_FASTBOOT, MO_SET},
+ {Opt_extent_cache, F2FS_MOUNT_READ_EXTENT_CACHE, MO_SET},
+ {Opt_noextent_cache, F2FS_MOUNT_READ_EXTENT_CACHE, MO_CLEAR},
+ {Opt_data_flush, F2FS_MOUNT_DATA_FLUSH, MO_SET},
+ {Opt_reserve_root, F2FS_MOUNT_RESERVE_ROOT, MO_SET},
+ {Opt_resgid, 0, MO_ALONE},
+ {Opt_resuid, 0, MO_ALONE},
+ {Opt_mode, 0, MO_ENUM},
+ {Opt_io_size_bits, 0, MO_DEPRECATED},
+ {Opt_fault_injection, 0, MO_ALONE | MO_FAULT},
+ {Opt_fault_type, 0, MO_ALONE | MO_FAULT},
+ {Opt_lazytime, 0, MO_ALONE},
+ {Opt_nolazytime, 0, MO_ALONE},
+ {Opt_quota, F2FS_MOUNT_USRQUOTA, MO_SET | MO_QUOTA},
+ {Opt_usrquota, F2FS_MOUNT_USRQUOTA, MO_SET | MO_QUOTA},
+ {Opt_grpquota, F2FS_MOUNT_GRPQUOTA, MO_SET | MO_QUOTA},
+ {Opt_prjquota, F2FS_MOUNT_PRJQUOTA, MO_SET | MO_QUOTA},
+ {Opt_usrjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_grpjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_prjjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_offusrjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_offgrpjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_offprjjquota, F2FS_MOUNT_QUOTA, MO_ALONE | MO_QUOTA},
+ {Opt_noquota, F2FS_MOUNT_QUOTA | F2FS_MOUNT_USRQUOTA |
+ F2FS_MOUNT_GRPQUOTA | F2FS_MOUNT_PRJQUOTA,
+ MO_CLEAR | MO_QUOTA},
+ {Opt_alloc, 0, MO_ALONE},
+ {Opt_fsync, 0, MO_ALONE},
+ {Opt_test_dummy_encryption, 0, MO_ALONE},
+ {Opt_inlinecrypt, 0, MO_ALONE | MO_INLINECRYPT},
+ {Opt_checkpoint_disable, F2FS_MOUNT_DISABLE_CHECKPOINT, MO_SET},
+ {Opt_checkpoint_enable, F2FS_MOUNT_DISABLE_CHECKPOINT, MO_CLEAR},
+ {Opt_checkpoint_merge, F2FS_MOUNT_MERGE_CHECKPOINT, MO_SET},
+ {Opt_nocheckpoint_merge, F2FS_MOUNT_MERGE_CHECKPOINT, MO_CLEAR},
+ {Opt_compress_algorithm, 0, MO_ALONE | MO_COMPR},
+ {Opt_compress_log_size, 0, MO_ALONE | MO_COMPR},
+ {Opt_compress_extension, 0, MO_ALONE | MO_COMPR},
+ {Opt_nocompress_extension, 0, MO_ALONE | MO_COMPR},
+ {Opt_compress_chksum, 0, MO_ALONE | MO_COMPR},
+ {Opt_compress_mode, 0, MO_ENUM | MO_COMPR},
+ {Opt_compress_cache, F2FS_MOUNT_COMPRESS_CACHE, MO_SET | MO_COMPR},
+ {Opt_atgc, F2FS_MOUNT_ATGC, MO_SET},
+ {Opt_gc_merge, F2FS_MOUNT_GC_MERGE, MO_SET},
+ {Opt_nogc_merge, F2FS_MOUNT_GC_MERGE, MO_CLEAR},
+ {Opt_discard_unit, 0, MO_ENUM},
+ {Opt_memory_mode, 0, MO_ENUM},
+ {Opt_age_extent_cache, F2FS_MOUNT_AGE_EXTENT_CACHE, MO_SET},
+ {Opt_errors, 0, MO_ENUM},
+ {Opt_err, 0, 0}
+};
+
+static const struct constant_table f2fs_param_bggc_mode[] = {
+ {"on", BGGC_MODE_ON},
+ {"off", BGGC_MODE_OFF},
+ {"sync", BGGC_MODE_SYNC},
+};
+
+static const struct constant_table f2fs_param_fs_mode[] = {
+ {"adaptive", FS_MODE_ADAPTIVE},
+ {"lfs", FS_MODE_LFS},
+ {"fragment:segment", FS_MODE_FRAGMENT_SEG},
+ {"fragment:block", FS_MODE_FRAGMENT_BLK},
+};
+
+static const struct constant_table f2fs_param_jqfmt[] = {
+ {"vfsold", QFMT_VFS_OLD},
+ {"vfsv0", QFMT_VFS_V0},
+ {"vfsv1", QFMT_VFS_V1},
+ {}
+};
+
+static const struct constant_table f2fs_param_alloc_mode[] = {
+ {"default", ALLOC_MODE_DEFAULT},
+ {"reuse", ALLOC_MODE_REUSE},
+};
+
+static const struct constant_table f2fs_param_fsync_mode[] = {
+ {"posix", FSYNC_MODE_POSIX},
+ {"strict", FSYNC_MODE_STRICT},
+ {"nobarrier", FSYNC_MODE_NOBARRIER},
+};
+
+/* should support zstd:xxx lz4:xxx mode*/
+static const struct constant_table f2fs_param_compress_algorithm[] = {
+ {"lzo", COMPRESS_LZO},
+ {"lz4", COMPRESS_LZ4},
+ {"zstd", COMPRESS_ZSTD},
+ {"lzo-rle", COMPRESS_LZORLE},
+};
+
+static const struct constant_table f2fs_param_compress_mode[] = {
+ {"fs", COMPR_MODE_FS},
+ {"user", COMPR_MODE_USER},
+};
+
+static const struct constant_table f2fs_param_discard_unit[] = {
+ {"block", DISCARD_UNIT_BLOCK},
+ {"segment", DISCARD_UNIT_SEGMENT},
+ {"section", DISCARD_UNIT_SECTION},
+};
+
+static const struct constant_table f2fs_param_memory_mode[] = {
+ {"normal", MEMORY_MODE_NORMAL},
+ {"low", MEMORY_MODE_LOW},
+};
+
+static const struct constant_table f2fs_param_error_mode[] = {
+ {"remount-ro", MOUNT_ERRORS_READONLY},
+ {"continue", MOUNT_ERRORS_CONTINUE},
+ {"panic", MOUNT_ERRORS_PANIC},
+};
+
+#define fsparam_string_empty(NAME, OPT) \
+ __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
+
+static const struct fs_parameter_spec f2fs_param_specs[] = {
+ fsparam_enum ("background_gc", Opt_gc_background,
+ f2fs_param_bggc_mode),
+ fsparam_flag ("disable_roll_forward",Opt_disable_roll_forward),
+ fsparam_flag ("norecovery", Opt_norecovery),
+ fsparam_flag_no ("discard", Opt_discard),
+ fsparam_flag ("no_heap", Opt_noheap),
+ fsparam_flag ("heap", Opt_heap),
+ fsparam_flag_no ("user_xattr", Opt_user_xattr),
+ fsparam_flag_no ("acl", Opt_acl),
+ fsparam_u32 ("active_logs", Opt_active_logs),
+ fsparam_flag ("disable_ext_identify",Opt_disable_ext_identify),
+ fsparam_flag_no ("inline_xattr", Opt_inline_xattr),
+ fsparam_u32 ("inline_xattr_size", Opt_inline_xattr_size),
+ fsparam_flag_no ("inline_data", Opt_inline_data),
+ fsparam_flag_no ("inline_dentry", Opt_inline_dentry),
+ fsparam_flag_no ("flush_merge", Opt_flush_merge),
+ fsparam_flag_no ("barrier", Opt_barrier),
+ fsparam_flag ("fastboot", Opt_fastboot),
+ fsparam_flag_no ("extent_cache", Opt_extent_cache),
+ fsparam_flag ("data_flush", Opt_data_flush),
+ fsparam_u32 ("reserve_root", Opt_reserve_root),
+ fsparam_u32 ("resgid", Opt_resgid),
+ fsparam_u32 ("resuid", Opt_resuid),
+ fsparam_enum ("mode", Opt_mode,
+ f2fs_param_fs_mode),
+ fsparam_u32 ("io_bits", Opt_io_size_bits),
+ fsparam_u32 ("fault_injection", Opt_fault_injection),
+ fsparam_u32 ("fault_type", Opt_fault_type),
+ fsparam_flag_no ("lazytime", Opt_lazytime),
+ fsparam_flag_no ("quota", Opt_quota),
+ fsparam_flag ("usrquota", Opt_usrquota),
+ fsparam_flag ("grpquota", Opt_grpquota),
+ fsparam_flag ("prjquota", Opt_prjquota),
+ fsparam_string_empty
+ ("usrjquota", Opt_usrjquota),
+ fsparam_string_empty
+ ("grpjquota", Opt_grpjquota),
+ fsparam_enum ("jqfmt", Opt_jqfmt,
+ f2fs_param_jqfmt),
+ fsparam_enum ("alloc_mode", Opt_alloc,
+ f2fs_param_alloc_mode),
+ fsparam_enum ("fsync_mode", Opt_fsync,
+ f2fs_param_fsync_mode),
+ fsparam_string ("test_dummy_encryption",
+ Opt_test_dummy_encryption),
+ fsparam_flag ("inlinecrypt", Opt_inlinecrypt),
+
+ //checkpoint
+ //{Opt_checkpoint_disable, "checkpoint=disable"},
+ //{Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
+ //{Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"},
+ //{Opt_checkpoint_enable, "checkpoint=enable"},
+
+ fsparam_flag_no ("checkpoint_merge", Opt_checkpoint_merge),
+ fsparam_enum ("compress_algorithm", Opt_compress_algorithm,
+ f2fs_param_compress_algorithm),
+ fsparam_string ("compress_log_size", Opt_compress_log_size),
+ fsparam_string ("compress_extension", Opt_compress_extension),
+ fsparam_string ("nocompress_extension",Opt_nocompress_extension),
+ fsparam_flag ("compress_chksum", Opt_compress_chksum),
+ fsparam_enum ("compress_mode", Opt_compress_mode,
+ f2fs_param_compress_mode),
+ fsparam_flag ("compress_cache", Opt_compress_cache),
+ fsparam_flag ("atgc", Opt_atgc),
+ fsparam_flag_no ("gc_merge", Opt_gc_merge),
+ fsparam_enum ("discard_unit", Opt_discard_unit,
+ f2fs_param_discard_unit),
+ fsparam_enum ("memory", Opt_memory_mode,
+ f2fs_param_memory_mode),
+ fsparam_flag ("age_extent_cache", Opt_age_extent_cache),
+ fsparam_enum ("errors", Opt_errors,
+ f2fs_param_error_mode),
+ {}
};
void f2fs_printk(struct f2fs_sb_info *sbi, bool limit_rate,
@@ -374,14 +557,14 @@ static void init_once(void *foo)
#ifdef CONFIG_QUOTA
static const char * const quotatypes[] = INITQFNAMES;
#define QTYPE2NAME(t) (quotatypes[t])
-static int f2fs_set_qf_name(struct super_block *sb, int qtype,
- substring_t *args)
+static int f2fs_set_qf_name(struct fs_context *fc, int qtype,
+ struct fs_parameter *param, int opt)
{
- struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ struct f2fs_fs_context *ctx = fc->fs_private;
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
char *qname;
- int ret = -EINVAL;
- if (sb_any_quota_loaded(sb) && !F2FS_OPTION(sbi).s_qf_names[qtype]) {
+ if (sb_any_quota_loaded(sbi->sb) && !ctx->s_qf_names[qtype]) {
f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
return -EINVAL;
}
@@ -389,42 +572,43 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype,
f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
return 0;
}
+ if (strchr(qname, '/')) {
+ f2fs_err(sbi, "quotafile must be on filesystem root");
+ return -EINVAL;
+ }
+
+ if (ctx->s_qf_names[qtype]) {
+ if (!strcmp(ctx->s_qf_names[qtype], param->string)) {
+ f2fs_err(sbi, "%s quota file already specified",
+ QTYPE2NAME(qtype));
+ return -EINVAL;
+ }
+ return 0;
+ }
- qname = match_strdup(args);
+ qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
if (!qname) {
f2fs_err(sbi, "Not enough memory for storing quotafile name");
return -ENOMEM;
}
- if (F2FS_OPTION(sbi).s_qf_names[qtype]) {
- if (strcmp(F2FS_OPTION(sbi).s_qf_names[qtype], qname) == 0)
- ret = 0;
- else
- f2fs_err(sbi, "%s quota file already specified",
- QTYPE2NAME(qtype));
- goto errout;
- }
- if (strchr(qname, '/')) {
- f2fs_err(sbi, "quotafile must be on filesystem root");
- goto errout;
- }
- F2FS_OPTION(sbi).s_qf_names[qtype] = qname;
- set_opt(sbi, QUOTA);
+
+ ctx->s_qf_names[qtype] = qname;
+ ctx->opt |= opt;
return 0;
-errout:
- kfree(qname);
- return ret;
}
-static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
+static int f2fs_clear_qf_name(struct fs_context *fc, int qtype, int opt)
{
- struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ struct f2fs_fs_context *ctx = fc->fs_private;
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
- if (sb_any_quota_loaded(sb) && F2FS_OPTION(sbi).s_qf_names[qtype]) {
+ if (sb_any_quota_loaded(sbi->sb) && ctx->s_qf_names[qtype]) {
f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
return -EINVAL;
}
- kfree(F2FS_OPTION(sbi).s_qf_names[qtype]);
- F2FS_OPTION(sbi).s_qf_names[qtype] = NULL;
+ kfree(ctx->s_qf_names[qtype]);
+ ctx->s_qf_names[qtype] = NULL;
+ ctx->opt &= ~opt;
return 0;
}
@@ -475,15 +659,10 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
#endif
static int f2fs_set_test_dummy_encryption(struct super_block *sb,
- const char *opt,
- const substring_t *arg,
+ struct fs_parameter *param,
bool is_remount)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
- struct fs_parameter param = {
- .type = fs_value_is_string,
- .string = arg->from ? arg->from : "",
- };
struct fscrypt_dummy_policy *policy =
&F2FS_OPTION(sbi).dummy_enc_policy;
int err;
@@ -509,17 +688,17 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
return -EINVAL;
}
- err = fscrypt_parse_test_dummy_encryption(&param, policy);
+ err = fscrypt_parse_test_dummy_encryption(param, policy);
if (err) {
if (err == -EEXIST)
f2fs_warn(sbi,
"Can't change test_dummy_encryption on remount");
else if (err == -EINVAL)
f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
- opt);
+ param->key);
else
f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
- opt, err);
+ param->key, err);
return -EINVAL;
}
f2fs_warn(sbi, "Test dummy encryption mode enabled");
@@ -662,644 +841,114 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
#endif
#endif
-static int parse_options(struct super_block *sb, char *options, bool is_remount)
+static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
{
- struct f2fs_sb_info *sbi = F2FS_SB(sb);
- substring_t args[MAX_OPT_ARGS];
-#ifdef CONFIG_F2FS_FS_COMPRESSION
- unsigned char (*ext)[F2FS_EXTENSION_LEN];
- unsigned char (*noext)[F2FS_EXTENSION_LEN];
- int ext_cnt, noext_cnt;
-#endif
- char *p, *name;
- int arg = 0;
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
+ struct f2fs_fs_context *ctx = fc->fs_private;
+ struct fs_parse_result result;
+ const struct mount_opts *m;
+ int is_remount;
kuid_t uid;
kgid_t gid;
- int ret;
-
- if (!options)
- goto default_check;
+ int token;
- while ((p = strsep(&options, ",")) != NULL) {
- int token;
+ token = fs_parse(fc, f2fs_param_specs, param, &result);
+ if (token < 0)
+ return token;
+ is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
- 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, f2fs_tokens, args);
+ for (m = f2fs_mount_opts; m->token != Opt_err; m++)
+ if (token == m->token)
+ break;
- switch (token) {
- case Opt_gc_background:
- name = match_strdup(&args[0]);
+ if (m->flags & MO_NOTSUPP) {
+ f2fs_info(sbi, "%s options not supported", param->key);
+ return 0;
+ }
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "on")) {
- F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
- } else if (!strcmp(name, "off")) {
- F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
- } else if (!strcmp(name, "sync")) {
- F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_disable_roll_forward:
- set_opt(sbi, DISABLE_ROLL_FORWARD);
- break;
- case Opt_norecovery:
- /* this option mounts f2fs with ro */
- set_opt(sbi, NORECOVERY);
- if (!f2fs_readonly(sb))
- return -EINVAL;
- break;
- case Opt_discard:
- if (!f2fs_hw_support_discard(sbi)) {
- f2fs_warn(sbi, "device does not support discard");
- break;
- }
- set_opt(sbi, DISCARD);
- break;
- case Opt_nodiscard:
- if (f2fs_hw_should_discard(sbi)) {
- f2fs_warn(sbi, "discard is required for zoned block devices");
- return -EINVAL;
- }
- clear_opt(sbi, DISCARD);
- break;
- case Opt_noheap:
- case Opt_heap:
- f2fs_warn(sbi, "heap/no_heap options were deprecated");
- break;
-#ifdef CONFIG_F2FS_FS_XATTR
- case Opt_user_xattr:
- set_opt(sbi, XATTR_USER);
- break;
- case Opt_nouser_xattr:
- clear_opt(sbi, XATTR_USER);
- break;
- case Opt_inline_xattr:
- set_opt(sbi, INLINE_XATTR);
- break;
- case Opt_noinline_xattr:
- clear_opt(sbi, INLINE_XATTR);
- break;
- case Opt_inline_xattr_size:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- set_opt(sbi, INLINE_XATTR_SIZE);
- F2FS_OPTION(sbi).inline_xattr_size = arg;
- break;
-#else
- case Opt_user_xattr:
- f2fs_info(sbi, "user_xattr options not supported");
- break;
- case Opt_nouser_xattr:
- f2fs_info(sbi, "nouser_xattr options not supported");
- break;
- case Opt_inline_xattr:
- f2fs_info(sbi, "inline_xattr options not supported");
- break;
- case Opt_noinline_xattr:
- f2fs_info(sbi, "noinline_xattr options not supported");
- break;
-#endif
-#ifdef CONFIG_F2FS_FS_POSIX_ACL
- case Opt_acl:
- set_opt(sbi, POSIX_ACL);
- break;
- case Opt_noacl:
- clear_opt(sbi, POSIX_ACL);
- break;
-#else
- case Opt_acl:
- f2fs_info(sbi, "acl options not supported");
- break;
- case Opt_noacl:
- f2fs_info(sbi, "noacl options not supported");
- break;
-#endif
- case Opt_active_logs:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- if (arg != 2 && arg != 4 &&
- arg != NR_CURSEG_PERSIST_TYPE)
- return -EINVAL;
- F2FS_OPTION(sbi).active_logs = arg;
- break;
- case Opt_disable_ext_identify:
- set_opt(sbi, DISABLE_EXT_IDENTIFY);
- break;
- case Opt_inline_data:
- set_opt(sbi, INLINE_DATA);
- break;
- case Opt_inline_dentry:
- set_opt(sbi, INLINE_DENTRY);
- break;
- case Opt_noinline_dentry:
- clear_opt(sbi, INLINE_DENTRY);
- break;
- case Opt_flush_merge:
- set_opt(sbi, FLUSH_MERGE);
- break;
- case Opt_noflush_merge:
- clear_opt(sbi, FLUSH_MERGE);
- break;
- case Opt_nobarrier:
- set_opt(sbi, NOBARRIER);
- break;
- case Opt_barrier:
- clear_opt(sbi, NOBARRIER);
- break;
- case Opt_fastboot:
- set_opt(sbi, FASTBOOT);
- break;
- case Opt_extent_cache:
- set_opt(sbi, READ_EXTENT_CACHE);
- break;
- case Opt_noextent_cache:
- clear_opt(sbi, READ_EXTENT_CACHE);
- break;
- case Opt_noinline_data:
- clear_opt(sbi, INLINE_DATA);
- break;
- case Opt_data_flush:
- set_opt(sbi, DATA_FLUSH);
- break;
- case Opt_reserve_root:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- if (test_opt(sbi, RESERVE_ROOT)) {
- f2fs_info(sbi, "Preserve previous reserve_root=%u",
- F2FS_OPTION(sbi).root_reserved_blocks);
- } else {
- F2FS_OPTION(sbi).root_reserved_blocks = arg;
- set_opt(sbi, RESERVE_ROOT);
- }
- break;
- case Opt_resuid:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- uid = make_kuid(current_user_ns(), arg);
- if (!uid_valid(uid)) {
- f2fs_err(sbi, "Invalid uid value %d", arg);
- return -EINVAL;
- }
- F2FS_OPTION(sbi).s_resuid = uid;
- break;
- case Opt_resgid:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- gid = make_kgid(current_user_ns(), arg);
- if (!gid_valid(gid)) {
- f2fs_err(sbi, "Invalid gid value %d", arg);
- return -EINVAL;
- }
- F2FS_OPTION(sbi).s_resgid = gid;
- break;
- case Opt_mode:
- name = match_strdup(&args[0]);
+ if (m->flags & MO_DEPRECATED) {
+ f2fs_info(sbi, "%s options is deprecated", param->key);
+ return 0;
+ }
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "adaptive")) {
- F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
- } else if (!strcmp(name, "lfs")) {
- F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
- } else if (!strcmp(name, "fragment:segment")) {
- F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_SEG;
- } else if (!strcmp(name, "fragment:block")) {
- F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_BLK;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
-#ifdef CONFIG_F2FS_FAULT_INJECTION
- case Opt_fault_injection:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- f2fs_build_fault_attr(sbi, arg, F2FS_ALL_FAULT_TYPE);
- set_opt(sbi, FAULT_INJECTION);
- break;
+ if (m->flags & MO_SET)
+ ctx->opt |= m->opt;
+ else if (m->flags & MO_CLEAR)
+ ctx->opt &= ~m->opt;
- case Opt_fault_type:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- f2fs_build_fault_attr(sbi, 0, arg);
- set_opt(sbi, FAULT_INJECTION);
- break;
-#else
- case Opt_fault_injection:
- f2fs_info(sbi, "fault_injection options not supported");
- break;
+ if (m->flags & MO_ALONE) {
+ /* handle alone */
+ }
- case Opt_fault_type:
- f2fs_info(sbi, "fault_type options not supported");
- break;
-#endif
- case Opt_lazytime:
- sb->s_flags |= SB_LAZYTIME;
- break;
- case Opt_nolazytime:
- sb->s_flags &= ~SB_LAZYTIME;
- break;
+ switch (token) {
+ case Opt_gc_background:
+ ctx->bggc_mode = result.uint_32;
+ return 0;
+ case Opt_norecovery:
+ if (!f2fs_readonly(sbi->sb))
+ return -EINVAL;
#ifdef CONFIG_QUOTA
- case Opt_quota:
- case Opt_usrquota:
- set_opt(sbi, USRQUOTA);
- break;
- case Opt_grpquota:
- set_opt(sbi, GRPQUOTA);
- break;
- case Opt_prjquota:
- set_opt(sbi, PRJQUOTA);
- break;
- case Opt_usrjquota:
- ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
- if (ret)
- return ret;
- break;
- case Opt_grpjquota:
- ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
- if (ret)
- return ret;
- break;
- case Opt_prjjquota:
- ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
- if (ret)
- return ret;
- break;
- case Opt_offusrjquota:
- ret = f2fs_clear_qf_name(sb, USRQUOTA);
- if (ret)
- return ret;
- break;
- case Opt_offgrpjquota:
- ret = f2fs_clear_qf_name(sb, GRPQUOTA);
- if (ret)
- return ret;
- break;
- case Opt_offprjjquota:
- ret = f2fs_clear_qf_name(sb, PRJQUOTA);
- if (ret)
- return ret;
- break;
- case Opt_jqfmt_vfsold:
- F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_OLD;
- break;
- case Opt_jqfmt_vfsv0:
- F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V0;
- break;
- case Opt_jqfmt_vfsv1:
- F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V1;
- break;
- case Opt_noquota:
- clear_opt(sbi, QUOTA);
- clear_opt(sbi, USRQUOTA);
- clear_opt(sbi, GRPQUOTA);
- clear_opt(sbi, PRJQUOTA);
- break;
-#else
- case Opt_quota:
- case Opt_usrquota:
- case Opt_grpquota:
- case Opt_prjquota:
- case Opt_usrjquota:
- case Opt_grpjquota:
- case Opt_prjjquota:
- case Opt_offusrjquota:
- case Opt_offgrpjquota:
- case Opt_offprjjquota:
- case Opt_jqfmt_vfsold:
- case Opt_jqfmt_vfsv0:
- case Opt_jqfmt_vfsv1:
- case Opt_noquota:
- f2fs_info(sbi, "quota operations not supported");
- break;
+ case Opt_usrjquota:
+ if (*param->string)
+ return f2fs_set_qf_name(fc, USRQUOTA, param, m->opt);
+ else
+ return f2fs_clear_qf_name(fc, USRQUOTA, m->opt);
+ case Opt_grpjquota:
+ if (*param->string)
+ return f2fs_set_qf_name(fc, GRPQUOTA, param, m->opt);
+ else
+ return f2fs_clear_qf_name(fc, GRPQUOTA, m->opt);
+ case Opt_prjjquota:
+ if (*param->string)
+ return f2fs_set_qf_name(fc, PRJQUOTA, param, m->opt);
+ else
+ return f2fs_clear_qf_name(fc, PRJQUOTA, m->opt);
+ case Opt_jqfmt:
+ ctx->s_jquota_fmt = result.uint_32;
+ return 0;
#endif
- case Opt_alloc:
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
-
- if (!strcmp(name, "default")) {
- F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
- } else if (!strcmp(name, "reuse")) {
- F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_fsync:
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "posix")) {
- F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
- } else if (!strcmp(name, "strict")) {
- F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
- } else if (!strcmp(name, "nobarrier")) {
- F2FS_OPTION(sbi).fsync_mode =
- FSYNC_MODE_NOBARRIER;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_test_dummy_encryption:
- ret = f2fs_set_test_dummy_encryption(sb, p, &args[0],
- is_remount);
- if (ret)
- return ret;
- break;
- case Opt_inlinecrypt:
+ case Opt_inlinecrypt:
#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
- sb->s_flags |= SB_INLINECRYPT;
-#else
- f2fs_info(sbi, "inline encryption not supported");
-#endif
- break;
- case Opt_checkpoint_disable_cap_perc:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- if (arg < 0 || arg > 100)
- return -EINVAL;
- F2FS_OPTION(sbi).unusable_cap_perc = arg;
- set_opt(sbi, DISABLE_CHECKPOINT);
- break;
- case Opt_checkpoint_disable_cap:
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- F2FS_OPTION(sbi).unusable_cap = arg;
- set_opt(sbi, DISABLE_CHECKPOINT);
- break;
- case Opt_checkpoint_disable:
- set_opt(sbi, DISABLE_CHECKPOINT);
- break;
- case Opt_checkpoint_enable:
- clear_opt(sbi, DISABLE_CHECKPOINT);
- break;
- case Opt_checkpoint_merge:
- set_opt(sbi, MERGE_CHECKPOINT);
- break;
- case Opt_nocheckpoint_merge:
- clear_opt(sbi, MERGE_CHECKPOINT);
- break;
-#ifdef CONFIG_F2FS_FS_COMPRESSION
- case Opt_compress_algorithm:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "lzo")) {
-#ifdef CONFIG_F2FS_FS_LZO
- F2FS_OPTION(sbi).compress_level = 0;
- F2FS_OPTION(sbi).compress_algorithm =
- COMPRESS_LZO;
-#else
- f2fs_info(sbi, "kernel doesn't support lzo compression");
-#endif
- } else if (!strncmp(name, "lz4", 3)) {
-#ifdef CONFIG_F2FS_FS_LZ4
- ret = f2fs_set_lz4hc_level(sbi, name);
- if (ret) {
- kfree(name);
- return -EINVAL;
- }
- F2FS_OPTION(sbi).compress_algorithm =
- COMPRESS_LZ4;
-#else
- f2fs_info(sbi, "kernel doesn't support lz4 compression");
-#endif
- } else if (!strncmp(name, "zstd", 4)) {
-#ifdef CONFIG_F2FS_FS_ZSTD
- ret = f2fs_set_zstd_level(sbi, name);
- if (ret) {
- kfree(name);
- return -EINVAL;
- }
- F2FS_OPTION(sbi).compress_algorithm =
- COMPRESS_ZSTD;
-#else
- f2fs_info(sbi, "kernel doesn't support zstd compression");
-#endif
- } else if (!strcmp(name, "lzo-rle")) {
-#ifdef CONFIG_F2FS_FS_LZORLE
- F2FS_OPTION(sbi).compress_level = 0;
- F2FS_OPTION(sbi).compress_algorithm =
- COMPRESS_LZORLE;
+ sbi->sb->s_flags |= SB_INLINECRYPT; //fixme
#else
- f2fs_info(sbi, "kernel doesn't support lzorle compression");
+ f2fs_info(sbi, "inline encryption not supported");
#endif
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_compress_log_size:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- if (args->from && match_int(args, &arg))
- return -EINVAL;
- if (arg < MIN_COMPRESS_LOG_SIZE ||
- arg > MAX_COMPRESS_LOG_SIZE) {
- f2fs_err(sbi,
- "Compress cluster log size is out of range");
- return -EINVAL;
- }
- F2FS_OPTION(sbi).compress_log_size = arg;
- break;
- case Opt_compress_extension:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
-
- ext = F2FS_OPTION(sbi).extensions;
- ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
-
- if (strlen(name) >= F2FS_EXTENSION_LEN ||
- ext_cnt >= COMPRESS_EXT_NUM) {
- f2fs_err(sbi,
- "invalid extension length/number");
- kfree(name);
- return -EINVAL;
- }
-
- if (is_compress_extension_exist(sbi, name, true)) {
- kfree(name);
- break;
- }
+ return 0;
+ case Opt_resuid:
+ uid = make_kuid(current_user_ns(), result.uint_32);
+ if (!uid_valid(uid)) {
+ f2fs_err(sbi, "Invalid uid value %d", result.uint_32);
+ return -EINVAL;
+ }
+ ctx->s_resuid = uid;
+ return 0;
+ case Opt_resgid:
+ gid = make_kgid(current_user_ns(), result.uint_32);
+ if (!gid_valid(gid)) {
+ f2fs_err(sbi, "Invalid uid value %d", result.uint_32);
+ return -EINVAL;
+ }
+ ctx->s_resgid = gid;
+ return 0;
+ case Opt_test_dummy_encryption:
+ return f2fs_set_test_dummy_encryption(sbi->sb, param,
+ is_remount);
+ }
- strcpy(ext[ext_cnt], name);
- F2FS_OPTION(sbi).compress_ext_cnt++;
- kfree(name);
- break;
- case Opt_nocompress_extension:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
+ if (m->token == Opt_err) {
+ f2fs_err(sbi, "buggy handling of option %s", param->key);
+ WARN_ON(1);
+ return -EINVAL;
+ }
- noext = F2FS_OPTION(sbi).noextensions;
- noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+ return 0;
- if (strlen(name) >= F2FS_EXTENSION_LEN ||
- noext_cnt >= COMPRESS_EXT_NUM) {
- f2fs_err(sbi,
- "invalid extension length/number");
- kfree(name);
- return -EINVAL;
- }
+}
- if (is_compress_extension_exist(sbi, name, false)) {
- kfree(name);
- break;
- }
+static int f2fs_validate_options(struct fs_context *fc)
+{
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
- strcpy(noext[noext_cnt], name);
- F2FS_OPTION(sbi).nocompress_ext_cnt++;
- kfree(name);
- break;
- case Opt_compress_chksum:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- F2FS_OPTION(sbi).compress_chksum = true;
- break;
- case Opt_compress_mode:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "fs")) {
- F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
- } else if (!strcmp(name, "user")) {
- F2FS_OPTION(sbi).compress_mode = COMPR_MODE_USER;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_compress_cache:
- if (!f2fs_sb_has_compression(sbi)) {
- f2fs_info(sbi, "Image doesn't support compression");
- break;
- }
- set_opt(sbi, COMPRESS_CACHE);
- break;
-#else
- case Opt_compress_algorithm:
- case Opt_compress_log_size:
- case Opt_compress_extension:
- case Opt_nocompress_extension:
- case Opt_compress_chksum:
- case Opt_compress_mode:
- case Opt_compress_cache:
- f2fs_info(sbi, "compression options not supported");
- break;
-#endif
- case Opt_atgc:
- set_opt(sbi, ATGC);
- break;
- case Opt_gc_merge:
- set_opt(sbi, GC_MERGE);
- break;
- case Opt_nogc_merge:
- clear_opt(sbi, GC_MERGE);
- break;
- case Opt_discard_unit:
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "block")) {
- F2FS_OPTION(sbi).discard_unit =
- DISCARD_UNIT_BLOCK;
- } else if (!strcmp(name, "segment")) {
- F2FS_OPTION(sbi).discard_unit =
- DISCARD_UNIT_SEGMENT;
- } else if (!strcmp(name, "section")) {
- F2FS_OPTION(sbi).discard_unit =
- DISCARD_UNIT_SECTION;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_memory_mode:
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "normal")) {
- F2FS_OPTION(sbi).memory_mode =
- MEMORY_MODE_NORMAL;
- } else if (!strcmp(name, "low")) {
- F2FS_OPTION(sbi).memory_mode =
- MEMORY_MODE_LOW;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- case Opt_age_extent_cache:
- set_opt(sbi, AGE_EXTENT_CACHE);
- break;
- case Opt_errors:
- name = match_strdup(&args[0]);
- if (!name)
- return -ENOMEM;
- if (!strcmp(name, "remount-ro")) {
- F2FS_OPTION(sbi).errors =
- MOUNT_ERRORS_READONLY;
- } else if (!strcmp(name, "continue")) {
- F2FS_OPTION(sbi).errors =
- MOUNT_ERRORS_CONTINUE;
- } else if (!strcmp(name, "panic")) {
- F2FS_OPTION(sbi).errors =
- MOUNT_ERRORS_PANIC;
- } else {
- kfree(name);
- return -EINVAL;
- }
- kfree(name);
- break;
- default:
- f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
- p);
- return -EINVAL;
- }
- }
-default_check:
#ifdef CONFIG_QUOTA
if (f2fs_check_quota_options(sbi))
return -EINVAL;
@@ -1375,6 +1024,11 @@ default_check:
}
}
+ if (test_opt(sbi, DISABLE_CHECKPOINT) && f2fs_lfs_mode(sbi)) {
+ f2fs_err(sbi, "LFS is not compatible with checkpoint=disable");
+ return -EINVAL;
+ }
+
if (test_opt(sbi, ATGC) && f2fs_lfs_mode(sbi)) {
f2fs_err(sbi, "LFS is not compatible with ATGC");
return -EINVAL;
@@ -1389,6 +1043,7 @@ default_check:
f2fs_err(sbi, "Allow to mount readonly mode only");
return -EROFS;
}
+
return 0;
}
@@ -2264,10 +1919,11 @@ static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
f2fs_flush_ckpt_thread(sbi);
}
-static int f2fs_remount(struct super_block *sb, int *flags, char *data)
+static int f2fs_remount(struct fs_context *fc, struct super_block *sb)
{
- struct f2fs_sb_info *sbi = F2FS_SB(sb);
- struct f2fs_mount_info org_mount_opt;
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
+ struct f2fs_fs_context old_ctx;
+ struct f2fs_fs_context *ctx = fc->fs_private;
unsigned long old_sb_flags;
int err;
bool need_restart_gc = false, need_stop_gc = false;
@@ -2289,29 +1945,29 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
* Save the old mount options in case we
* need to restore them.
*/
- org_mount_opt = sbi->mount_opt;
+ old_ctx = sbi->ctx;
old_sb_flags = sb->s_flags;
#ifdef CONFIG_QUOTA
- org_mount_opt.s_jquota_fmt = F2FS_OPTION(sbi).s_jquota_fmt;
+ old_ctx.s_jquota_fmt = F2FS_OPTION(sbi).s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++) {
if (F2FS_OPTION(sbi).s_qf_names[i]) {
- org_mount_opt.s_qf_names[i] =
+ old_ctx.s_qf_names[i] =
kstrdup(F2FS_OPTION(sbi).s_qf_names[i],
GFP_KERNEL);
- if (!org_mount_opt.s_qf_names[i]) {
+ if (!old_ctx.s_qf_names[i]) {
for (j = 0; j < i; j++)
- kfree(org_mount_opt.s_qf_names[j]);
+ kfree(old_ctx.s_qf_names[j]);
return -ENOMEM;
}
} else {
- org_mount_opt.s_qf_names[i] = NULL;
+ old_ctx.s_qf_names[i] = NULL;
}
}
#endif
/* recover superblocks we couldn't write due to previous RO mount */
- if (!(*flags & SB_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
+ if (!(fc->sb_flags & SB_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
err = f2fs_commit_super(sbi, false);
f2fs_info(sbi, "Try to recover all the superblocks, ret: %d",
err);
@@ -2319,12 +1975,12 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
clear_sbi_flag(sbi, SBI_NEED_SB_WRITE);
}
- default_options(sbi, true);
-
- /* parse mount options */
- err = parse_options(sb, data, true);
+ err = f2fs_validate_options(fc);
if (err)
- goto restore_opts;
+ goto skip;
+
+ sbi->ctx = *ctx;
+ sbi->sb->s_flags = fc->sb_flags;
/* flush outstanding errors before changing fs state */
flush_work(&sbi->s_error_work);
@@ -2333,20 +1989,20 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
* Previous and new state of filesystem is RO,
* so skip checking GC and FLUSH_MERGE conditions.
*/
- if (f2fs_readonly(sb) && (*flags & SB_RDONLY))
+ if (f2fs_readonly(sb) && (fc->sb_flags & SB_RDONLY))
goto skip;
- if (f2fs_dev_is_readonly(sbi) && !(*flags & SB_RDONLY)) {
+ if (f2fs_dev_is_readonly(sbi) && !(fc->sb_flags & SB_RDONLY)) {
err = -EROFS;
goto restore_opts;
}
#ifdef CONFIG_QUOTA
- if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) {
+ if (!f2fs_readonly(sb) && (fc->sb_flags & SB_RDONLY)) {
err = dquot_suspend(sb, -1);
if (err < 0)
goto restore_opts;
- } else if (f2fs_readonly(sb) && !(*flags & SB_RDONLY)) {
+ } else if (f2fs_readonly(sb) && !(fc->sb_flags & SB_RDONLY)) {
/* dquot_resume needs RW */
sb->s_flags &= ~SB_RDONLY;
if (sb_any_quota_suspended(sb)) {
@@ -2396,7 +2052,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
goto restore_opts;
}
- if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
+ if ((fc->sb_flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
err = -EINVAL;
f2fs_warn(sbi, "disabling checkpoint not compatible with read-only");
goto restore_opts;
@@ -2407,7 +2063,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
* or if background_gc = off is passed in mount
* option. Also sync the filesystem.
*/
- if ((*flags & SB_RDONLY) ||
+ if ((fc->sb_flags & SB_RDONLY) ||
(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
!test_opt(sbi, GC_MERGE))) {
if (sbi->gc_thread) {
@@ -2421,7 +2077,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
need_stop_gc = true;
}
- if (*flags & SB_RDONLY) {
+ if (fc->sb_flags & SB_RDONLY) {
sync_inodes_sb(sb);
set_sbi_flag(sbi, SBI_IS_DIRTY);
@@ -2434,7 +2090,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
* We stop issue flush thread if FS is mounted as RO
* or if flush_merge is not passed in mount option.
*/
- if ((*flags & SB_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
+ if ((fc->sb_flags & SB_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
clear_opt(sbi, FLUSH_MERGE);
f2fs_destroy_flush_cmd_control(sbi, false);
need_restart_flush = true;
@@ -2475,7 +2131,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
* triggered while remount and we need to take care of it before
* returning from remount.
*/
- if ((*flags & SB_RDONLY) || test_opt(sbi, DISABLE_CHECKPOINT) ||
+ if ((fc->sb_flags & SB_RDONLY) || test_opt(sbi, DISABLE_CHECKPOINT) ||
!test_opt(sbi, MERGE_CHECKPOINT)) {
f2fs_stop_ckpt_thread(sbi);
} else {
@@ -2495,7 +2151,7 @@ skip:
#ifdef CONFIG_QUOTA
/* Release old quota file names */
for (i = 0; i < MAXQUOTAS; i++)
- kfree(org_mount_opt.s_qf_names[i]);
+ kfree(old_ctx.s_qf_names[i]);
#endif
/* Update the POSIXACL Flag */
sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
@@ -2503,7 +2159,7 @@ skip:
limit_reserve_root(sbi);
adjust_unusable_cap_perc(sbi);
- *flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
+ fc->sb_flags = (fc->sb_flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
return 0;
restore_checkpoint:
if (need_enable_checkpoint) {
@@ -2536,13 +2192,13 @@ restore_gc:
}
restore_opts:
#ifdef CONFIG_QUOTA
- F2FS_OPTION(sbi).s_jquota_fmt = org_mount_opt.s_jquota_fmt;
+ F2FS_OPTION(sbi).s_jquota_fmt = old_ctx.s_jquota_fmt;
for (i = 0; i < MAXQUOTAS; i++) {
kfree(F2FS_OPTION(sbi).s_qf_names[i]);
- F2FS_OPTION(sbi).s_qf_names[i] = org_mount_opt.s_qf_names[i];
+ F2FS_OPTION(sbi).s_qf_names[i] = old_ctx.s_qf_names[i];
}
#endif
- sbi->mount_opt = org_mount_opt;
+ sbi->ctx = old_ctx;
sb->s_flags = old_sb_flags;
return err;
}
@@ -3145,7 +2801,6 @@ static const struct super_operations f2fs_sops = {
.freeze_fs = f2fs_freeze,
.unfreeze_fs = f2fs_unfreeze,
.statfs = f2fs_statfs,
- .remount_fs = f2fs_remount,
};
#ifdef CONFIG_FS_ENCRYPTION
@@ -4311,22 +3966,20 @@ static void f2fs_tuning_parameters(struct f2fs_sb_info *sbi)
sbi->readdir_ra = true;
}
-static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
+static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct f2fs_sb_info *sbi;
struct f2fs_super_block *raw_super;
+ struct f2fs_fs_context *ctx = fc->fs_private;
struct inode *root;
int err;
bool skip_recovery = false, need_fsck = false;
- char *options = NULL;
int recovery, i, valid_super_block;
struct curseg_info *seg_i;
- int retry_cnt = 1;
#ifdef CONFIG_QUOTA
bool quota_enabled = false;
#endif
-try_onemore:
err = -EINVAL;
raw_super = NULL;
valid_super_block = -1;
@@ -4389,17 +4042,9 @@ try_onemore:
sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid,
sizeof(raw_super->uuid));
- default_options(sbi, false);
- /* parse mount options */
- options = kstrdup((const char *)data, GFP_KERNEL);
- if (data && !options) {
- err = -ENOMEM;
- goto free_sb_buf;
- }
-
- err = parse_options(sb, options, false);
+ err = f2fs_validate_options(fc);
if (err)
- goto free_options;
+ goto free_sbi;
sb->s_maxbytes = max_file_blocks(NULL) <<
le32_to_cpu(raw_super->log_blocksize);
@@ -4709,7 +4354,6 @@ reset_checkpoint:
if (err)
goto sync_free_meta;
}
- kvfree(options);
/* recover broken superblock */
if (recovery) {
@@ -4732,7 +4376,6 @@ reset_checkpoint:
sync_free_meta:
/* safe to flush all the data */
sync_filesystem(sbi->sb);
- retry_cnt = 0;
free_meta:
#ifdef CONFIG_QUOTA
@@ -4802,30 +4445,15 @@ free_options:
kfree(F2FS_OPTION(sbi).s_qf_names[i]);
#endif
fscrypt_free_dummy_policy(&F2FS_OPTION(sbi).dummy_enc_policy);
- kvfree(options);
-free_sb_buf:
kfree(raw_super);
free_sbi:
if (sbi->s_chksum_driver)
crypto_free_shash(sbi->s_chksum_driver);
kfree(sbi);
sb->s_fs_info = NULL;
-
- /* give only one another chance */
- if (retry_cnt > 0 && skip_recovery) {
- retry_cnt--;
- shrink_dcache_sb(sb);
- goto try_onemore;
- }
return err;
}
-static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags,
- const char *dev_name, void *data)
-{
- return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super);
-}
-
static void kill_f2fs_super(struct super_block *sb)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
@@ -4865,12 +4493,66 @@ static void kill_f2fs_super(struct super_block *sb)
}
}
+static int f2fs_get_tree(struct fs_context *fc)
+{
+ return get_tree_bdev(fc, f2fs_fill_super);
+}
+
+static int f2fs_reconfigure(struct fs_context *fc)
+{
+ struct super_block *sb = fc->root->d_sb;
+
+ fc->s_fs_info = F2FS_SB(sb);
+
+ return f2fs_remount(fc, sb);
+}
+
+static void f2fs_fc_free(struct fs_context *fc)
+{
+ struct f2fs_fs_context *ctx = fc->fs_private;
+ int i;
+
+ if (!ctx)
+ return;
+
+ for (i = 0; i < MAXQUOTAS; i++)
+ kfree(ctx->s_qf_names[i]);
+
+ fscrypt_free_dummy_policy(&ctx->dummy_enc_policy);
+ kfree(ctx);
+}
+
+static const struct fs_context_operations f2fs_context_ops = {
+ .parse_param = f2fs_parse_param,
+ .get_tree = f2fs_get_tree,
+ .reconfigure = f2fs_reconfigure,
+ .free = f2fs_fc_free,
+};
+
+static int f2fs_init_fs_context(struct fs_context *fc)
+{
+ struct f2fs_sb_info *sbi = fc->s_fs_info;
+ struct f2fs_fs_context *ctx;
+ bool is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
+
+ ctx = kzalloc(sizeof(struct f2fs_fs_context), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ fc->fs_private = ctx;
+ fc->ops = &f2fs_context_ops;
+
+ default_options(sbi, is_remount);
+ return 0;
+}
+
static struct file_system_type f2fs_fs_type = {
- .owner = THIS_MODULE,
- .name = "f2fs",
- .mount = f2fs_mount,
- .kill_sb = kill_f2fs_super,
- .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
+ .owner = THIS_MODULE,
+ .name = "f2fs",
+ .init_fs_context = f2fs_init_fs_context,
+ .parameters = f2fs_param_specs,
+ .kill_sb = kill_f2fs_super,
+ .fs_flags = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
};
MODULE_ALIAS_FS("f2fs");