diff --git a/fs.c b/fs.c index a442005..09f0890 100644 --- a/fs.c +++ b/fs.c @@ -3,6 +3,7 @@ #if SIMPLEFS_AT_LEAST(6, 18, 0) #include +#include #endif #include #include @@ -14,14 +15,33 @@ static int simplefs_get_tree(struct fs_context *fc) { return get_tree_bdev(fc, simplefs_fill_super); } +static void simplefs_free_context(struct fs_context *fc) +{ + struct simplefs_fs_context *ctx = fc->fs_private; + + if (!ctx) + return; + + kfree(ctx->journal_path); + kfree(ctx); +} static const struct fs_context_operations simplefs_context_ops = { .get_tree = simplefs_get_tree, + .parse_param = simplefs_parse_param, + .free = simplefs_free_context, }; static int init_simplefs_context(struct fs_context *fc) { + struct simplefs_fs_context *ctx; + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + fc->fs_private = ctx; fc->ops = &simplefs_context_ops; + return 0; } +extern const struct fs_parameter_spec simplefs_param_specs; #else /* Mount a simplefs partition */ struct dentry *simplefs_mount(struct file_system_type *fs_type, @@ -60,6 +80,7 @@ static struct file_system_type simplefs_file_system_type = { .name = "simplefs", #if SIMPLEFS_AT_LEAST(6, 18, 0) .init_fs_context = init_simplefs_context, + .parameters = &simplefs_param_specs, #else .mount = simplefs_mount, #endif diff --git a/simplefs.h b/simplefs.h index 8bc1de3..1ee01c7 100644 --- a/simplefs.h +++ b/simplefs.h @@ -99,7 +99,12 @@ struct simplefs_dir_block { uint32_t nr_files; struct simplefs_file files[SIMPLEFS_FILES_PER_BLOCK]; }; - +#if SIMPLEFS_AT_LEAST(6, 18, 0) +struct simplefs_fs_context { + u32 journal_dev; + char *journal_path; +}; +#endif /* superblock functions */ #if SIMPLEFS_AT_LEAST(6, 18, 0) int simplefs_fill_super(struct super_block *sb, struct fs_context *fc); @@ -107,7 +112,11 @@ int simplefs_fill_super(struct super_block *sb, struct fs_context *fc); int simplefs_fill_super(struct super_block *sb, void *data, int silent); #endif void simplefs_kill_sb(struct super_block *sb); - +#if SIMPLEFS_AT_LEAST(6, 18, 0) +#include +#include +int simplefs_parse_param(struct fs_context *fc, struct fs_parameter *param); +#endif /* inode functions */ int simplefs_init_inode_cache(void); void simplefs_destroy_inode_cache(void); diff --git a/super.c b/super.c index c453603..a2c44b2 100644 --- a/super.c +++ b/super.c @@ -15,6 +15,7 @@ #include "simplefs.h" #if SIMPLEFS_AT_LEAST(6, 18, 0) #include +#include #endif struct dentry *simplefs_mount(struct file_system_type *fs_type, int flags, @@ -449,6 +450,39 @@ static const match_table_t tokens = { {SIMPLEFS_OPT_JOURNAL_DEV, "journal_dev=%u"}, {SIMPLEFS_OPT_JOURNAL_PATH, "journal_path=%s"}, }; +#if SIMPLEFS_AT_LEAST(6, 18, 0) +const struct fs_parameter_spec simplefs_param_specs[] = { + fsparam_u32("journal_dev", SIMPLEFS_OPT_JOURNAL_DEV), + fsparam_string("journal_path", SIMPLEFS_OPT_JOURNAL_PATH), + {}}; +int simplefs_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct simplefs_fs_context *ctx = fc->fs_private; + struct fs_parse_result result; + int opt; + + opt = fs_parse(fc, simplefs_param_specs, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case SIMPLEFS_OPT_JOURNAL_DEV: + ctx->journal_dev = result.uint_32; + break; + + case SIMPLEFS_OPT_JOURNAL_PATH: { + kfree(ctx->journal_path); + ctx->journal_path = kstrdup(param->string, GFP_KERNEL); + if (!ctx->journal_path) + return -ENOMEM; + break; + default: + return -EINVAL; + } + } + return 0; +} +#else static int simplefs_parse_options(struct super_block *sb, char *options) { substring_t args[MAX_OPT_ARGS]; @@ -496,12 +530,9 @@ static int simplefs_parse_options(struct super_block *sb, char *options) kfree(journal_path); return ret; } - - journal_inode = path.dentry->d_inode; - - path_put(&path); kfree(journal_path); + journal_inode = path.dentry->d_inode; if (S_ISBLK(journal_inode->i_mode)) { unsigned long journal_devnum = new_encode_dev(journal_inode->i_rdev); @@ -513,6 +544,7 @@ static int simplefs_parse_options(struct super_block *sb, char *options) return ret; } } + path_put(&path); break; } } @@ -520,7 +552,7 @@ static int simplefs_parse_options(struct super_block *sb, char *options) return 0; } - +#endif static struct super_operations simplefs_super_ops = { .put_super = simplefs_put_super, .alloc_inode = simplefs_alloc_inode, @@ -534,6 +566,7 @@ static struct super_operations simplefs_super_ops = { #if SIMPLEFS_AT_LEAST(6, 18, 0) int simplefs_fill_super(struct super_block *sb, struct fs_context *fc) { + struct simplefs_fs_context *ctx = fc->fs_private; #else int simplefs_fill_super(struct super_block *sb, void *data, int silent) { @@ -651,9 +684,41 @@ inode_init_owner(root_inode, NULL, root_inode->i_mode); ret = -ENOMEM; goto iput; } - /* Since parse_options is not available at fill_super stage at kernels - * v6.18+, it is disabled for now. */ -#if SIMPLEFS_LESS_EQUAL(6, 17, 0) + +#if SIMPLEFS_AT_LEAST(6, 18, 0) + if (ctx->journal_dev) { + ret = simplefs_load_journal(sb, ctx->journal_dev); + if (ret) { + pr_err( + "simplefs_parse_options: simplefs_load_journal failed with " + "%d\n", + ret); + return ret; + } + } + if (ctx->journal_path) { + struct path path; + struct inode *inode; + ret = kern_path(ctx->journal_path, LOOKUP_FOLLOW, &path); + if (ret) { + pr_err("simplefs_parse_options: kern_path failed with error %d\n", + ret); + return ret; + } + inode = d_inode(path.dentry); + path_put(&path); + if (S_ISBLK(inode->i_mode)) { + unsigned long journal_devnum = new_encode_dev(inode->i_rdev); + if ((ret = simplefs_load_journal(sb, journal_devnum))) { + pr_err( + "simplefs_parse_options: simplefs_load_journal failed " + "with %d\n", + ret); + return ret; + } + } + } +#else ret = simplefs_parse_options(sb, data); if (ret) { pr_err("simplefs_fill_super: Failed to parse options, error code: %d\n",