diff options
Diffstat (limited to 'fs/erofs/super.c')
| -rw-r--r-- | fs/erofs/super.c | 112 |
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) |
