Hi Giuseppe,
On Wed, Jul 08, 2026 at 11:34:26AM +0200, Giuseppe Scrivano wrote:
> Add fsparam_fd("source") so that userspace can pass an already-opened
> file descriptor instead of a path string. When the fd is provided via
> fsconfig(FSCONFIG_SET_FD, "source", NULL, fd), it is stored directly
> in sbi->dif0.file and erofs_fc_get_tree() skips the filp_open() call.
>
> This is useful for mount namespaces where the backing file may not be
> reachable by path, and for tools that already hold an fd to the image
> (e.g. composefs reusing an erofs mount's backing file).
>
> Signed-off-by: Giuseppe Scrivano <[email protected]>
> ---
> fs/erofs/super.c | 36 +++++++++++++++++++++++++-----------
> 1 file changed, 25 insertions(+), 11 deletions(-)
>
> diff --git a/fs/erofs/super.c b/fs/erofs/super.c
> index 86fa5c6a0c70..8ad1689f74b2 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_fd,
> };
>
> static const struct constant_table erofs_param_cache_strategy[] = {
> @@ -413,6 +414,7 @@ static const struct fs_parameter_spec
> erofs_fs_parameters[] = {
> fsparam_flag_no("directio", Opt_directio),
> fsparam_u64("fsoffset", Opt_fsoffset),
> fsparam_flag("inode_share", Opt_inode_share),
> + fsparam_fd("source", Opt_source_fd),
> {}
> };
>
> @@ -524,6 +526,15 @@ static int erofs_fc_parse_param(struct fs_context *fc,
> else
> set_opt(&sbi->opt, INODE_SHARE);
> break;
> + case Opt_source_fd:
> + if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE)) {
> + errorfc(fc, "source fd option not supported");
Thanks for the patch!
For this commit, it looks good to me overall, just some nits:
I guess we could just move this one into erofs_fc_get_tree(), see below.
> + return -EINVAL;
> + }
> + if (sbi->dif0.file)
Do we need to allow multi-shot source_fd?
I guess we could just bail out directly instead.
> + fput(sbi->dif0.file);
> + sbi->dif0.file = get_file(param->file);
> + break;
> }
> return 0;
> }
> @@ -752,14 +763,18 @@ static int erofs_fc_fill_super(struct super_block *sb,
> struct fs_context *fc)
>
> static int erofs_fc_get_tree(struct fs_context *fc)
> {
> - int ret;
> + struct erofs_sb_info *sbi = fc->s_fs_info;
Nit:
struct file *file = sbi->dif0.file;
>
> - 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;
> + if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) || !sbi->dif0.file) {
if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE) || !file) {
> struct file *file;
> + int ret;
> +
> + 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)
> + return ret;
>
> if (!fc->source)
> return invalf(fc, "No source specified");
> @@ -767,12 +782,11 @@ static int erofs_fc_get_tree(struct fs_context *fc)
> if (IS_ERR(file))
> return PTR_ERR(file);
> sbi->dif0.file = file;
> -
> - if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) &&
> - sbi->dif0.file->f_mapping->a_ops->read_folio)
> - return get_tree_nodev(fc, erofs_fc_fill_super);
> }
> - return ret;
> + if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) &&
> + sbi->dif0.file->f_mapping->a_ops->read_folio)
> + return get_tree_nodev(fc, erofs_fc_fill_super);
> + return -EINVAL;
Currently we don't support bdev-backed mounts for this, so I'm fine to
support file-backed mounts only for now.
So nit:
if (!S_ISREG(file_inode(file)->i_mode) ||
!file->f_mapping->a_ops->read_folio) {
errorfc(fc, "source is unsupported");
return -EINVAL;
}
return get_tree_nodev(fc, erofs_fc_fill_super);
Thanks,
Gao Xiang
> }
>
> static int erofs_fc_reconfigure(struct fs_context *fc)
> --
> 2.55.0
>
>