Configfs store callbacks hold frag_sem while they run. Reopening a path that resolves to configfs from such a callback can acquire the same non-recursive semaphore again. Add configfs_file_open() for configured pathnames and configfs_open_root() for paths relative to a resolved root. Reject configfs roots before using file_open_root() so the normal open checks remain in place. Assisted-by: LLM Codex Signed-off-by: Runyu Xiao --- fs/configfs/mount.c | 57 ++++++++++++++++++++++++++++++++++++++++ include/linux/configfs.h | 5 ++++ 2 files changed, 62 insertions(+) diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c index d8cac1cbf3bd5..aa79be05dd5bc 100644 --- a/fs/configfs/mount.c +++ b/fs/configfs/mount.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,62 @@ static struct file_system_type configfs_fs_type = { }; MODULE_ALIAS_FS("configfs"); +/** + * configfs_open_root - open a path below a non-configfs root + * @root: resolved root path + * @name: path relative to @root + * @flags: open flags + * @mode: mode for a newly created file + * + * Use this from configfs store callbacks with a resolved non-configfs root + * to avoid re-entering configfs while the callback holds its fragment + * semaphore. + * + * Return: opened file, or an ERR_PTR() value. Returns -EINVAL if @root + * is on configfs. + */ +struct file *configfs_open_root(const struct path *root, const char *name, + int flags, umode_t mode) +{ + if (root->dentry->d_sb->s_type == &configfs_fs_type) + return ERR_PTR(-EINVAL); + + return file_open_root(root, name, flags, mode); +} +EXPORT_SYMBOL_GPL(configfs_open_root); + +/** + * configfs_file_open - open an existing non-configfs pathname + * @filename: pathname to open; it must already exist + * @flags: open flags for the existing pathname + * @mode: unused; creation is not supported + * + * Resolve @filename and reject configfs paths. Use this from configfs + * store callbacks for existing configured paths. O_NOFOLLOW is honored + * when resolving the final path component. + * + * Return: opened file, or an ERR_PTR() value. Returns -EINVAL if the + * resolved path is on configfs. + */ +struct file *configfs_file_open(const char *filename, int flags, umode_t mode) +{ + struct file *file; + struct path path; + unsigned int lookup_flags = 0; + int ret; + + if (!(flags & O_NOFOLLOW)) + lookup_flags = LOOKUP_FOLLOW; + ret = kern_path(filename, lookup_flags, &path); + if (ret) + return ERR_PTR(ret); + + file = configfs_open_root(&path, "", flags, mode); + path_put(&path); + return file; +} +EXPORT_SYMBOL_GPL(configfs_file_open); + struct dentry *configfs_pin_fs(void) { int err = simple_pin_fs(&configfs_fs_type, &configfs_mount, diff --git a/include/linux/configfs.h b/include/linux/configfs.h index ef65c75beeaad..2a803bb836b4d 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -34,6 +34,8 @@ struct configfs_group_operations; struct configfs_attribute; struct configfs_bin_attribute; struct configfs_subsystem; +struct file; +struct path; struct config_item { char *ci_name; @@ -243,6 +245,9 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys); int configfs_register_group(struct config_group *parent_group, struct config_group *group); void configfs_unregister_group(struct config_group *group); +struct file *configfs_open_root(const struct path *root, const char *name, + int flags, umode_t mode); +struct file *configfs_file_open(const char *filename, int flags, umode_t mode); void configfs_remove_default_groups(struct config_group *group); -- 2.34.1