Re: [PATCH v4] erofs: accept source file descriptor via fsconfig
Gao Xiang <[email protected]> Thu, 23 Jul 2026 00:25:37 +0800
| Newsgroups | org.ozlabs.lists.linux-erofs,org.kernel.vger.linux-fsdevel |
|---|---|
| Message-ID | <[email protected]> |
Hi Christian, On 2026/7/23 00:11, Christian Brauner 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]> >> >> diff --git a/fs/erofs/super.c b/fs/erofs/super.c >> index 9d8f862f309f..bc55be84d945 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: { > > Afaict this is just fsparam_file_or_string()? > >> + 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; > > Hm, hm, hm... How is that reliable? So iirc this is then passed to: > > static int erofs_fc_get_tree(struct fs_context *fc) > { > 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) { > struct erofs_sb_info *sbi = fc->s_fs_info; > struct file *file; > > if (!fc->source) > return invalf(fc, "No source specified"); > file = filp_open(fc->source, O_RDONLY | O_LARGEFILE, 0); > 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; > } > > which then reopens the file or looks up the block device. So the only > way this is _vaguely_ (and really _very vaguely_) safe is if userspace > keeps at least the file descriptor open until the filesystem has been > mounted. I'm not quite sure if I catched the point, I think Giuseppe's patch here tried to record `file` into `sbi->dif0.file` (which indicates the primary "device" later.) And if `sbi->dif0.file` is set up by erofs_fc_parse_source(), erofs_fc_get_tree() will just use `sbi->dif0.file` instead of `fc->source` according to this patch. The reason why `fc->source` is set was discussed in the thread of the previous version suggested by Aleksa. > > If they close it before this means you can mount something completely > different. The other thing is even if they keep the fd open someone > could just rename the damn thing and fc->source ends up pointing > somwhere completely different. The could switch namespaces as well in > some circumstances and then it points again into wherever. > > I've played with that fd idea before. The only way to make this work > correctly is if you plumb this down into get_tree_nodev() fc->source in this case has no use in erofs_fc_get_tree() (`fc->source` is just used for mountinfo for example), `sbi->dif0.file` works instead I hope I don't misunderstand something. Thanks, Gao Xiang >