On 7/29/26 00:05, Giuseppe Scrivano wrote:
Allow userspace to pass an already-opened file descriptor as the mount
source instead of a path string.  This is useful for tools that already
hold an fd to the image, such as composefs reusing an existing erofs
backing file.

Signed-off-by: Giuseppe Scrivano <[email protected]>
---
v4: 
https://lore.kernel.org/linux-fsdevel/[email protected]/
v3: 
https://lore.kernel.org/linux-fsdevel/[email protected]/
v2: 
https://lore.kernel.org/linux-fsdevel/[email protected]/
v1: https://lore.kernel.org/linux-fsdevel/ak5GfvVfWLJU1EwK@debian/

  Documentation/filesystems/erofs.rst | 15 ++++++
  fs/erofs/super.c                    | 73 ++++++++++++++++++++++++-----
  2 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/Documentation/filesystems/erofs.rst 
b/Documentation/filesystems/erofs.rst
index 4230884fb359..774e8b236d09 100644
--- a/Documentation/filesystems/erofs.rst
+++ b/Documentation/filesystems/erofs.rst
@@ -139,6 +139,21 @@ inode_share            Enable inode page sharing for this 
filesystem.  Inodes wi
                         page cache.
  ===================    
=========================================================
+File-backed mounts
+==================
+
+When CONFIG_EROFS_FS_BACKED_BY_FILE is enabled, EROFS file-backed images
+can be mounted directly without a loopback block device.  The backing file
+can be given either as a path, or as an already-opened file descriptor.
+
+When a file descriptor is used, the kernel resolves its path and records it
+so that /proc/mounts and similar interfaces can still report the mount
+source.
+
+Only regular files are accepted as backing files; to mount an image that
+resides on a block device, use the traditional block device mount path
+instead.

Do we need to add an entry to describe the new mount option source= in
"Mount options" section in erofs.rst?

Thanks,

+
  Sysfs Entries
  =============
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 86fa5c6a0c70..558041011398 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;
  }
@@ -752,13 +790,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)


Reply via email to