summaryrefslogtreecommitdiff
path: root/fs/erofs/super.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/erofs/super.c')
-rw-r--r--fs/erofs/super.c112
1 files changed, 88 insertions, 24 deletions
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 770808685934..0967aace9f7f 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -386,6 +386,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi)
enum {
Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,
Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,
+ Opt_source,
};
static const struct constant_table erofs_param_cache_strategy[] = {
@@ -402,17 +403,18 @@ static const struct constant_table erofs_dax_param_enums[] = {
};
static const struct fs_parameter_spec erofs_fs_parameters[] = {
- fsparam_flag_no("user_xattr", Opt_user_xattr),
- fsparam_flag_no("acl", Opt_acl),
- fsparam_enum("cache_strategy", Opt_cache_strategy,
+ fsparam_flag_no("user_xattr", Opt_user_xattr),
+ fsparam_flag_no("acl", Opt_acl),
+ fsparam_enum("cache_strategy", Opt_cache_strategy,
erofs_param_cache_strategy),
- fsparam_flag("dax", Opt_dax),
- fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums),
- fsparam_string("device", Opt_device),
- fsparam_string("domain_id", Opt_domain_id),
- fsparam_flag_no("directio", Opt_directio),
- fsparam_u64("fsoffset", Opt_fsoffset),
- fsparam_flag("inode_share", Opt_inode_share),
+ fsparam_flag("dax", Opt_dax),
+ fsparam_enum("dax", Opt_dax_enum, erofs_dax_param_enums),
+ fsparam_string("device", Opt_device),
+ fsparam_string("domain_id", Opt_domain_id),
+ fsparam_flag_no("directio", Opt_directio),
+ fsparam_u64("fsoffset", Opt_fsoffset),
+ fsparam_flag("inode_share", Opt_inode_share),
+ fsparam_file_or_string("source", Opt_source),
{}
};
@@ -437,6 +439,40 @@ static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode)
return false;
}
+static int erofs_fc_parse_source(struct fs_context *fc,
+ struct fs_parameter *param)
+{
+ struct erofs_sb_info *sbi = fc->s_fs_info;
+
+ if (fc->source || sbi->dif0.file)
+ return invalf(fc, "Multiple sources");
+
+ switch (param->type) {
+ case fs_value_is_string:
+ fc->source = param->string;
+ param->string = NULL;
+ return 0;
+ case fs_value_is_file: {
+ char *buf __free(kfree) = kmalloc(PATH_MAX, GFP_KERNEL);
+ char *p;
+
+ if (!buf)
+ return -ENOMEM;
+ p = file_path(param->file, buf, PATH_MAX);
+ if (IS_ERR(p))
+ return PTR_ERR(p);
+ fc->source = kstrdup(p, GFP_KERNEL);
+ if (!fc->source)
+ return -ENOMEM;
+ sbi->dif0.file = no_free_ptr(param->file);
+ return 0;
+ }
+ default:
+ WARN_ON_ONCE(true);
+ return -EINVAL;
+ }
+}
+
static int erofs_fc_parse_param(struct fs_context *fc,
struct fs_parameter *param)
{
@@ -524,6 +560,8 @@ static int erofs_fc_parse_param(struct fs_context *fc,
else
set_opt(&sbi->opt, INODE_SHARE);
break;
+ case Opt_source:
+ return erofs_fc_parse_source(fc, param);
}
return 0;
}
@@ -595,6 +633,21 @@ static const struct export_operations erofs_export_ops = {
.get_parent = erofs_get_parent,
};
+int erofs_setup_managed_cache(struct super_block *sb)
+{
+ if (!EROFS_SB(sb)->managed_cache) {
+ struct inode *inode = new_inode(sb);
+
+ if (!inode)
+ return -ENOMEM;
+ set_nlink(inode, 1);
+ inode->i_size = OFFSET_MAX;
+ mapping_set_gfp_mask(inode->i_mapping, GFP_KERNEL);
+ EROFS_SB(sb)->managed_cache = inode;
+ }
+ return 0;
+}
+
static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct inode *inode;
@@ -607,16 +660,16 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_op = &erofs_sops;
if (!sbi->domain_id && test_opt(&sbi->opt, INODE_SHARE)) {
- errorfc(fc, "domain_id is needed when inode_ishare is on");
+ errorfc(fc, "domain_id is needed when inode_share is on");
return -EINVAL;
}
if (test_opt(&sbi->opt, DAX_ALWAYS) && test_opt(&sbi->opt, INODE_SHARE)) {
- errorfc(fc, "FSDAX is not allowed when inode_ishare is on");
+ errorfc(fc, "FSDAX is not allowed when inode_share is on");
return -EINVAL;
}
sbi->blkszbits = PAGE_SHIFT;
- if (!sb->s_bdev) {
+ if (erofs_is_fileio_mode(sbi)) {
/*
* (File-backed mounts) EROFS claims it's safe to nest other
* fs contexts (including its own) due to self-controlled RO
@@ -631,14 +684,11 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
* It MUST change if another fs plans to support them, which
* may also require adjusting FILESYSTEM_MAX_STACK_DEPTH.
*/
- if (erofs_is_fileio_mode(sbi)) {
- inode = file_inode(sbi->dif0.file);
- if ((inode->i_sb->s_op == &erofs_sops &&
- !inode->i_sb->s_bdev) ||
- inode->i_sb->s_stack_depth) {
- erofs_err(sb, "file-backed mounts cannot be applied to stacked fses");
- return -ENOTBLK;
- }
+ inode = file_inode(sbi->dif0.file);
+ if ((inode->i_sb->s_op == &erofs_sops &&
+ !inode->i_sb->s_bdev) || inode->i_sb->s_stack_depth) {
+ erofs_err(sb, "file-backed mounts cannot be applied to stacked fses");
+ return -ENOTBLK;
}
sb->s_blocksize = PAGE_SIZE;
sb->s_blocksize_bits = PAGE_SHIFT;
@@ -646,6 +696,9 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
err = super_setup_bdi(sb);
if (err)
return err;
+ err = erofs_setup_managed_cache(sb);
+ if (err)
+ return err;
snprintf(sb->s_id, sizeof(sb->s_id),
"%u:%u", MAJOR(sb->s_dev), MINOR(sb->s_dev));
@@ -743,13 +796,26 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
static int erofs_fc_get_tree(struct fs_context *fc)
{
+ struct erofs_sb_info *sbi = fc->s_fs_info;
int ret;
+ if (sbi->dif0.file) {
+ if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE)) {
+ errorfc(fc, "source fd option not supported");
+ return -EINVAL;
+ }
+ if (!S_ISREG(file_inode(sbi->dif0.file)->i_mode) ||
+ !sbi->dif0.file->f_mapping->a_ops->read_folio) {
+ errorfc(fc, "source is unsupported");
+ return -EINVAL;
+ }
+ return get_tree_nodev(fc, erofs_fc_fill_super);
+ }
+
ret = get_tree_bdev_flags(fc, erofs_fc_fill_super,
IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) ?
GET_TREE_BDEV_QUIET_LOOKUP : 0);
if (IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) && ret == -ENOTBLK) {
- struct erofs_sb_info *sbi = fc->s_fs_info;
struct file *file;
if (!fc->source)
@@ -873,10 +939,8 @@ static void erofs_drop_internal_inodes(struct erofs_sb_info *sbi)
sbi->packed_inode = NULL;
iput(sbi->metabox_inode);
sbi->metabox_inode = NULL;
-#ifdef CONFIG_EROFS_FS_ZIP
iput(sbi->managed_cache);
sbi->managed_cache = NULL;
-#endif
}
static void erofs_kill_sb(struct super_block *sb)