diff options
author | Chao Yu <chao@kernel.org> | 2024-02-28 22:51:32 +0800 |
---|---|---|
committer | Chao Yu <chao@kernel.org> | 2024-02-27 09:30:33 +0800 |
commit | d4eac172f8f9f4594056366859b67e589e6124c3 (patch) | |
tree | 5dea0414b25208a7fa78a6eabea6535eec43032d | |
parent | b50650e50ba9d876469eed7315700ba64072ed4c (diff) | |
download | linux-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.h | 6 | ||||
-rw-r--r-- | fs/f2fs/super.c | 1284 |
2 files changed, 486 insertions, 804 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 85eb9a8a5ed30..01208db963706 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 78a76583a4aa8..975fa956b53e4 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(¶m, 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"); |