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 --- 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"); + return -EINVAL; + } + if (sbi->dif0.file) + 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; - 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) { 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; } static int erofs_fc_reconfigure(struct fs_context *fc) -- 2.55.0 Add EROFS_IOC_GET_SOURCE_FD ioctl that returns a file descriptor to the backing image file for file-backed erofs mounts. Returns -ENOENT for block-device-backed erofs mounts where there is no backing file. The UAPI constant is defined in include/uapi/linux/erofs.h. Signed-off-by: Giuseppe Scrivano --- fs/erofs/inode.c | 25 +++++++++++++++++++++++++ include/uapi/linux/erofs.h | 9 +++++++++ 2 files changed, 34 insertions(+) create mode 100644 include/uapi/linux/erofs.h diff --git a/fs/erofs/inode.c b/fs/erofs/inode.c index 45afe5c50de8..0a38cb5cfc5d 100644 --- a/fs/erofs/inode.c +++ b/fs/erofs/inode.c @@ -6,6 +6,8 @@ */ #include "xattr.h" #include +#include +#include #include static int erofs_fill_symlink(struct inode *inode, void *bptr, unsigned int ofs) @@ -356,6 +358,27 @@ static int erofs_ioctl_get_volume_label(struct inode *inode, void __user *arg) return ret ? -EFAULT : 0; } +static int erofs_ioctl_get_source_fd(struct file *filp) +{ + struct erofs_sb_info *sbi = EROFS_I_SB(file_inode(filp)); + struct file *f; + int fd; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (!erofs_is_fileio_mode(sbi)) + return -ENOENT; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return fd; + + f = get_file(sbi->dif0.file); + fd_install(fd, f); + return fd; +} + long erofs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -364,6 +387,8 @@ long erofs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case FS_IOC_GETFSLABEL: return erofs_ioctl_get_volume_label(inode, argp); + case EROFS_IOC_GET_SOURCE_FD: + return erofs_ioctl_get_source_fd(filp); default: return -ENOTTY; } diff --git a/include/uapi/linux/erofs.h b/include/uapi/linux/erofs.h new file mode 100644 index 000000000000..17c835785ea9 --- /dev/null +++ b/include/uapi/linux/erofs.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_LINUX_EROFS_H +#define _UAPI_LINUX_EROFS_H + +#include + +#define EROFS_IOC_GET_SOURCE_FD _IO('e', 1) + +#endif /* _UAPI_LINUX_EROFS_H */ -- 2.55.0