[PATCH v5 1/3] fs: configfs: add helpers for opening non-configfs paths
From: Runyu Xiao
Date: Tue Sep 22 2026 - 23:18:33 EST
Configfs store callbacks run with the fragment semaphore of the item they
belong to held for reading. A callback that opens a configfs path can
re-enter __configfs_open_file(), which takes that same semaphore again on
a non-recursive rwsem, and deadlock the task.
Add a pair of helpers for such callbacks. configfs_file_open() resolves a
configured pathname, rejects it if it resolves to configfs, and opens it.
configfs_open_root() rejects a configfs root and opens relative to an
already-resolved root, so a caller can validate a root once and keep
opening files below it. The helpers compare the actual filesystem type
and use file_open_root(), so the normal open-time permission and security
checks still apply.
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
---
fs/configfs/mount.c | 68 ++++++++++++++++++++++++++++++++++++++++
include/linux/configfs.h | 5 +++
2 files changed, 73 insertions(+)
diff --git a/fs/configfs/mount.c b/fs/configfs/mount.c
index d8cac1cbf3bd5..b2c14cf6a4053 100644
--- a/fs/configfs/mount.c
+++ b/fs/configfs/mount.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/fs_context.h>
+#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -118,6 +119,73 @@ static struct file_system_type configfs_fs_type = {
};
MODULE_ALIAS_FS("configfs");
+/**
+ * configfs_open_root - open a path relative to an already-resolved root
+ * @root: resolved root, which must not be on configfs
+ * @name: path to open relative to @root, or "" to open @root itself
+ * @flags: open flags as per the open(2) second argument
+ * @mode: mode argument passed to file_open_root()
+ *
+ * Open @name relative to @root, refusing a @root on configfs.
+ *
+ * Configfs store callbacks are called with the fragment semaphore of the
+ * item they belong to held for reading. Opening a configfs path from
+ * such a callback can re-enter __configfs_open_file() and take that same
+ * semaphore again, which is not recursive and deadlocks. Callers that
+ * open a user-configured path from a configfs store callback must
+ * therefore use this helper instead of filp_open() or a bare
+ * file_open_root().
+ *
+ * Resolving @name relative to @root also lets a caller pin and validate a
+ * root once and keep opening files below it, rather than re-resolving a
+ * pathname that can be retargeted in the meantime. The normal open-time
+ * permission and security checks still apply to the file being opened.
+ *
+ * Return: the opened file, or an ERR_PTR() value. -EINVAL is returned 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 a pathname that must not resolve to configfs
+ * @filename: existing pathname to resolve and open
+ * @flags: open flags as per the open(2) second argument
+ * @mode: mode argument passed to file_open_root()
+ *
+ * Resolve @filename and open the resulting file, refusing to open it if it
+ * resolves to configfs. @filename must already exist; this helper cannot
+ * create a missing path. Use this from configfs store callbacks that open
+ * a path configured by the user, in place of filp_open(). See
+ * configfs_open_root() for why opening configfs again from such a callback
+ * deadlocks.
+ *
+ * Return: the opened file, or an ERR_PTR() value. -EINVAL is returned if
+ * @filename resolves to configfs.
+ */
+struct file *configfs_file_open(const char *filename, int flags, umode_t mode)
+{
+ struct file *file;
+ struct path path;
+ int ret;
+
+ ret = kern_path(filename, LOOKUP_FOLLOW, &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