In handle_mount_opt, we use fs_parameter to pass each options. However we're still using the old API to get the options string. There are some change about parsering options: 1. For `active_logs`, `inline_xattr_size` and `fault_injection`, we use s32 type according the internal structure to record the option's value. 2. Introduce constant_table for `jqfmt` options. 3. Obtain the checkpoint disable cap (or percent) with private logic. Signed-off-by: Hongbo Li <lihongbo22@xxxxxxxxxx> --- fs/f2fs/super.c | 1191 ++++++++++++++++++++++++----------------------- 1 file changed, 614 insertions(+), 577 deletions(-) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 1bd923a73c1f..013b1078653f 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/ctype.h> #include <linux/fs_parser.h> #include "f2fs.h" @@ -460,7 +461,7 @@ static void init_once(void *foo) 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) + struct fs_parameter *param) { struct f2fs_sb_info *sbi = F2FS_SB(sb); char *qname; @@ -475,7 +476,7 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, 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; @@ -560,15 +561,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, + const 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; @@ -594,17 +590,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"); @@ -747,639 +743,680 @@ 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 handle_mount_opt(struct fs_context *fc, struct fs_parameter *param) { - struct f2fs_sb_info *sbi = F2FS_SB(sb); - substring_t args[MAX_OPT_ARGS]; + struct f2fs_sb_info *sbi = fc->s_fs_info; + struct super_block *sb = sbi->sb; #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 fs_parse_result result; + int is_remount; + char *name; kuid_t uid; kgid_t gid; - int ret; - - if (!options) - goto default_check; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - - 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); - - switch (token) { - case Opt_gc_background: - name = match_strdup(&args[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; - } + int token, ret; + + token = fs_parse(fc, f2fs_param_specs, param, &result); + if (token < 0) + return token; + + is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE; + + switch (token) { + case Opt_gc_background: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + + 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); - 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; + return -EINVAL; + } + kfree(name); + return 0; + case Opt_disable_roll_forward: + set_opt(sbi, DISABLE_ROLL_FORWARD); + return 0; + case Opt_norecovery: + /* this option mounts f2fs with ro */ + set_opt(sbi, NORECOVERY); + if (!f2fs_readonly(sb)) + return -EINVAL; + return 0; + case Opt_discard: + if (!f2fs_hw_support_discard(sbi)) { + f2fs_warn(sbi, "device does not support discard"); + return 0; + } + set_opt(sbi, DISCARD); + return 0; + 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); + return 0; + case Opt_noheap: + case Opt_heap: + f2fs_warn(sbi, "heap/no_heap options were deprecated"); + return 0; #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; + case Opt_user_xattr: + set_opt(sbi, XATTR_USER); + return 0; + case Opt_nouser_xattr: + clear_opt(sbi, XATTR_USER); + return 0; + case Opt_inline_xattr: + set_opt(sbi, INLINE_XATTR); + return 0; + case Opt_noinline_xattr: + clear_opt(sbi, INLINE_XATTR); + return 0; + case Opt_inline_xattr_size: + set_opt(sbi, INLINE_XATTR_SIZE); + F2FS_OPTION(sbi).inline_xattr_size = result.int_32; + return 0; #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; + case Opt_user_xattr: + f2fs_info(sbi, "user_xattr options not supported"); + return 0; + case Opt_nouser_xattr: + f2fs_info(sbi, "nouser_xattr options not supported"); + return 0; + case Opt_inline_xattr: + f2fs_info(sbi, "inline_xattr options not supported"); + return 0; + case Opt_noinline_xattr: + f2fs_info(sbi, "noinline_xattr options not supported"); + return 0; #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; + case Opt_acl: + set_opt(sbi, POSIX_ACL); + return 0; + case Opt_noacl: + clear_opt(sbi, POSIX_ACL); + return 0; #else - case Opt_acl: - f2fs_info(sbi, "acl options not supported"); - break; - case Opt_noacl: - f2fs_info(sbi, "noacl options not supported"); - break; + case Opt_acl: + f2fs_info(sbi, "acl options not supported"); + return 0; + case Opt_noacl: + f2fs_info(sbi, "noacl options not supported"); + return 0; #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 (!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; - } + case Opt_active_logs: + if (result.int_32 != 2 && result.int_32 != 4 && + result.int_32 != NR_CURSEG_PERSIST_TYPE) + return -EINVAL; + F2FS_OPTION(sbi).active_logs = result.int_32; + return 0; + case Opt_disable_ext_identify: + set_opt(sbi, DISABLE_EXT_IDENTIFY); + return 0; + case Opt_inline_data: + set_opt(sbi, INLINE_DATA); + return 0; + case Opt_inline_dentry: + set_opt(sbi, INLINE_DENTRY); + return 0; + case Opt_noinline_dentry: + clear_opt(sbi, INLINE_DENTRY); + return 0; + case Opt_flush_merge: + set_opt(sbi, FLUSH_MERGE); + return 0; + case Opt_noflush_merge: + clear_opt(sbi, FLUSH_MERGE); + return 0; + case Opt_nobarrier: + set_opt(sbi, NOBARRIER); + return 0; + case Opt_barrier: + clear_opt(sbi, NOBARRIER); + return 0; + case Opt_fastboot: + set_opt(sbi, FASTBOOT); + return 0; + case Opt_extent_cache: + set_opt(sbi, READ_EXTENT_CACHE); + return 0; + case Opt_noextent_cache: + clear_opt(sbi, READ_EXTENT_CACHE); + return 0; + case Opt_noinline_data: + clear_opt(sbi, INLINE_DATA); + return 0; + case Opt_data_flush: + set_opt(sbi, DATA_FLUSH); + return 0; + case Opt_reserve_root: + 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 = result.uint_32; + set_opt(sbi, RESERVE_ROOT); + } + return 0; + case Opt_resuid: + uid = make_kuid(current_user_ns(), result.uint_32); + if (!uid_valid(uid)) { + f2fs_err(sbi, "Invalid uid value %u", result.uint_32); + return -EINVAL; + } + F2FS_OPTION(sbi).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 gid value %u", result.uint_32); + return -EINVAL; + } + F2FS_OPTION(sbi).s_resgid = gid; + return 0; + case Opt_mode: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + + 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); - break; + return -EINVAL; + } + kfree(name); + return 0; #ifdef CONFIG_F2FS_FAULT_INJECTION - case Opt_fault_injection: - if (args->from && match_int(args, &arg)) - return -EINVAL; - if (f2fs_build_fault_attr(sbi, arg, - F2FS_ALL_FAULT_TYPE)) - return -EINVAL; - set_opt(sbi, FAULT_INJECTION); - break; + case Opt_fault_injection: + if (f2fs_build_fault_attr(sbi, result.int_32, + F2FS_ALL_FAULT_TYPE)) + return -EINVAL; + set_opt(sbi, FAULT_INJECTION); + return 0; - case Opt_fault_type: - if (args->from && match_int(args, &arg)) - return -EINVAL; - if (f2fs_build_fault_attr(sbi, 0, arg)) - return -EINVAL; - set_opt(sbi, FAULT_INJECTION); - break; + case Opt_fault_type: + if (f2fs_build_fault_attr(sbi, 0, result.uint_32)) + return -EINVAL; + set_opt(sbi, FAULT_INJECTION); + return 0; #else - case Opt_fault_injection: - f2fs_info(sbi, "fault_injection options not supported"); - break; + case Opt_fault_injection: + f2fs_info(sbi, "fault_injection options not supported"); + return 0; - case Opt_fault_type: - f2fs_info(sbi, "fault_type options not supported"); - break; + case Opt_fault_type: + f2fs_info(sbi, "fault_type options not supported"); + return 0; #endif #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: + case Opt_quota: + case Opt_usrquota: + set_opt(sbi, USRQUOTA); + return 0; + case Opt_grpquota: + set_opt(sbi, GRPQUOTA); + return 0; + case Opt_prjquota: + set_opt(sbi, PRJQUOTA); + return 0; + case Opt_usrjquota: + if (!*param->string) ret = f2fs_clear_qf_name(sb, USRQUOTA); - if (ret) - return ret; - break; - case Opt_offgrpjquota: + else + ret = f2fs_set_qf_name(sb, USRQUOTA, param); + if (ret) + return ret; + return 0; + case Opt_grpjquota: + if (!*param->string) ret = f2fs_clear_qf_name(sb, GRPQUOTA); - if (ret) - return ret; - break; - case Opt_offprjjquota: + else + ret = f2fs_set_qf_name(sb, GRPQUOTA, param); + if (ret) + return ret; + return 0; + case Opt_prjjquota: + if (!*param->string) 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 + ret = f2fs_set_qf_name(sb, PRJQUOTA, param); + if (ret) + return ret; + return 0; + case Opt_jqfmt: + F2FS_OPTION(sbi).s_jquota_fmt = result.uint_32; + return 0; + case Opt_noquota: + clear_opt(sbi, QUOTA); + clear_opt(sbi, USRQUOTA); + clear_opt(sbi, GRPQUOTA); + clear_opt(sbi, PRJQUOTA); + return 0; #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_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"); + 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; - } + case Opt_alloc: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - 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; - } + return -EINVAL; + } + kfree(name); + return 0; + case Opt_fsync: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - 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: + return -EINVAL; + } + kfree(name); + return 0; + case Opt_test_dummy_encryption: + ret = f2fs_set_test_dummy_encryption(sb, param, is_remount); + if (ret) + return ret; + return 0; + case Opt_inlinecrypt: #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT - sb->s_flags |= SB_INLINECRYPT; + sb->s_flags |= SB_INLINECRYPT; #else - f2fs_info(sbi, "inline encryption not supported"); + 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; + return 0; + case Opt_checkpoint: + { + const char *prefix = "disable:"; + size_t len = strlen(prefix); + const char *cp = param->string + len; + char *endp = (char *)cp; + unsigned long cap = 0; + + if (!strcmp(param->string, "enable")) { + clear_opt(sbi, DISABLE_CHECKPOINT); + return 0; + } + + if (!strcmp(param->string, "disable")) { set_opt(sbi, DISABLE_CHECKPOINT); - break; - case Opt_checkpoint_disable: + return 0; + } + + if (strlen(param->string) <= len) + return -EINVAL; + if (strncmp(prefix, param->string, len)) + return -EINVAL; + + while (isdigit(*endp)) { + cap = cap * 10 + (*endp - '0'); + endp++; + } + if (!strcmp(cp, endp)) + return -EINVAL; + if (strlen(endp) == 0) { + F2FS_OPTION(sbi).unusable_cap = cap; 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; + return 0; + } + if (strcmp(endp, "%")) + return -EINVAL; + if (cap > 100) + return -EINVAL; + F2FS_OPTION(sbi).unusable_cap_perc = cap; + set_opt(sbi, DISABLE_CHECKPOINT); + return 0; + } + case Opt_checkpoint_merge: + set_opt(sbi, MERGE_CHECKPOINT); + return 0; + case Opt_nocheckpoint_merge: + clear_opt(sbi, MERGE_CHECKPOINT); + return 0; #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")) { + case Opt_compress_algorithm: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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; + F2FS_OPTION(sbi).compress_level = 0; + F2FS_OPTION(sbi).compress_algorithm = + COMPRESS_LZO; #else - f2fs_info(sbi, "kernel doesn't support lzo compression"); + f2fs_info(sbi, "kernel doesn't support lzo compression"); #endif - } else if (!strncmp(name, "lz4", 3)) { + } 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; + 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"); + f2fs_info(sbi, "kernel doesn't support lz4 compression"); #endif - } else if (!strncmp(name, "zstd", 4)) { + } 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; + 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"); + f2fs_info(sbi, "kernel doesn't support zstd compression"); #endif - } else if (!strcmp(name, "lzo-rle")) { + } else if (!strcmp(name, "lzo-rle")) { #ifdef CONFIG_F2FS_FS_LZORLE - F2FS_OPTION(sbi).compress_level = 0; - F2FS_OPTION(sbi).compress_algorithm = - COMPRESS_LZORLE; + F2FS_OPTION(sbi).compress_level = 0; + F2FS_OPTION(sbi).compress_algorithm = + COMPRESS_LZORLE; #else - f2fs_info(sbi, "kernel doesn't support lzorle compression"); + f2fs_info(sbi, "kernel doesn't support lzorle compression"); #endif - } else { - kfree(name); - return -EINVAL; - } + } else { kfree(name); + return -EINVAL; + } + kfree(name); + return 0; + case Opt_compress_log_size: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + if (result.int_32 < MIN_COMPRESS_LOG_SIZE || + result.int_32 > MAX_COMPRESS_LOG_SIZE) { + f2fs_err(sbi, + "Compress cluster log size is out of range"); + return -EINVAL; + } + F2FS_OPTION(sbi).compress_log_size = result.int_32; + return 0; + case Opt_compress_extension: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); 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; + } + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + if (!name) + return -ENOMEM; - ext = F2FS_OPTION(sbi).extensions; - ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt; + 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 (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; - } + if (is_compress_extension_exist(sbi, name, true)) { + kfree(name); + return 0; + } - strcpy(ext[ext_cnt], name); - F2FS_OPTION(sbi).compress_ext_cnt++; + ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN); + if (ret < 0) { 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; + return ret; + } + F2FS_OPTION(sbi).compress_ext_cnt++; + kfree(name); + return 0; + case Opt_nocompress_extension: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + if (!name) + return -ENOMEM; - noext = F2FS_OPTION(sbi).noextensions; - noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt; + noext = F2FS_OPTION(sbi).noextensions; + noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt; - if (strlen(name) >= F2FS_EXTENSION_LEN || - noext_cnt >= COMPRESS_EXT_NUM) { - f2fs_err(sbi, - "invalid extension length/number"); - kfree(name); - return -EINVAL; - } + 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; - } + if (is_compress_extension_exist(sbi, name, false)) { + kfree(name); + return 0; + } - strcpy(noext[noext_cnt], name); - F2FS_OPTION(sbi).nocompress_ext_cnt++; + ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN); + if (ret < 0) { 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; - } + return ret; + } + F2FS_OPTION(sbi).nocompress_ext_cnt++; + kfree(name); + return 0; + case Opt_compress_chksum: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + F2FS_OPTION(sbi).compress_chksum = true; + return 0; + case Opt_compress_mode: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - 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; + return -EINVAL; + } + kfree(name); + return 0; + case Opt_compress_cache: + if (!f2fs_sb_has_compression(sbi)) { + f2fs_info(sbi, "Image doesn't support compression"); + return 0; + } + set_opt(sbi, COMPRESS_CACHE); + return 0; #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; + 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"); + return 0; #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; - } + case Opt_atgc: + set_opt(sbi, ATGC); + return 0; + case Opt_gc_merge: + set_opt(sbi, GC_MERGE); + return 0; + case Opt_nogc_merge: + clear_opt(sbi, GC_MERGE); + return 0; + case Opt_discard_unit: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - 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; - } + return -EINVAL; + } + kfree(name); + return 0; + case Opt_memory_mode: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - 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; - } + return -EINVAL; + } + kfree(name); + return 0; + case Opt_age_extent_cache: + set_opt(sbi, AGE_EXTENT_CACHE); + return 0; + case Opt_errors: + name = kmemdup_nul(param->string, param->size, GFP_KERNEL); + 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); - break; - default: - f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value", - p); return -EINVAL; } + kfree(name); + return 0; + default: + f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value", + param->key); + return -EINVAL; + } + + return 0; +} + +static int parse_options(struct super_block *sb, char *options, bool is_remount) +{ + struct f2fs_sb_info *sbi = F2FS_SB(sb); + struct fs_parameter param; + struct fs_context fc; + char *key; + int ret; + + if (!options) + goto default_check; + + memset(&fc, 0, sizeof(fc)); + fc.s_fs_info = sbi; + if (is_remount) + fc.purpose = FS_CONTEXT_FOR_RECONFIGURE; + + while ((key = strsep(&options, ",")) != NULL) { + if (*key) { + size_t v_len = 0; + char *value = strchr(key, '='); + + param.type = fs_value_is_flag; + param.string = NULL; + + if (value) { + if (value == key) + continue; + + *value++ = 0; + v_len = strlen(value); + param.string = kmemdup_nul(value, v_len, GFP_KERNEL); + if (!param.string) + return -ENOMEM; + param.type = fs_value_is_string; + } + + param.key = key; + param.size = v_len; + + ret = handle_mount_opt(&fc, ¶m); + kfree(param.string); + if (ret < 0) + return ret; + } } default_check: #ifdef CONFIG_QUOTA -- 2.34.1