mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v7 0/4] nvmet: avoid recursive configfs open
@ 2026-09-27  8:20 Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

Several configfs store callbacks open user-configured paths while holding
the item's frag_sem.  If a configured path resolves into configfs, the open
can re-enter __configfs_open_file() and try to acquire the same
non-recursive semaphore again.

Add helpers in configfs for opening non-configfs paths, then use them in the
nvmet file-backed namespace and passthru paths.  The series also pins the
target-core db_root path and uses the root-relative helper for ALUA and APTPL
metadata, with a dedicated lock protecting the path and its configuration.

Changes since v6:
- Honor O_NOFOLLOW when resolving paths in configfs_file_open().
- Protect db_root and db_root_path against concurrent metadata writes and
  configfs updates.
- Return an error when a db_root update races with target device creation.

Checks:
- scripts/checkpatch.pl --strict: all 4 patches pass.
- Built the affected configfs, nvmet, and target-core objects.
- Applied the complete series to the stated base commit with git am.

Runyu Xiao (4):
  fs: configfs: add helpers for opening non-configfs paths
  nvmet: avoid recursive configfs open for file-backed namespaces
  nvmet: avoid recursive configfs open for passthru
  scsi: target: pin db_root for metadata writes

 drivers/nvme/target/io-cmd-file.c     |   3 +-
 drivers/nvme/target/passthru.c        |   3 +-
 drivers/target/target_core_alua.c     |  42 ++++----
 drivers/target/target_core_configfs.c | 142 +++++++++++++++++++++-----
 drivers/target/target_core_internal.h |   5 +
 drivers/target/target_core_pr.c       |  19 ++--
 fs/configfs/mount.c                   |  57 +++++++++++
 include/linux/configfs.h              |   5 +
 8 files changed, 219 insertions(+), 57 deletions(-)


base-commit: f741eaa40d2a8e199a31ceb683f30867537def70
-- 
2.34.1

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

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 <runyu.xiao@seu.edu.cn>
---
 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 <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,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


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

nvmet_ns_enable_store() runs as a configfs store callback while configfs
holds the item frag_sem. File-backed namespace enable used filp_open() on
the configured device_path, so a path into configfs could re-enter
__configfs_open_file() and try to acquire the same semaphore again.

Use configfs_file_open() so the path is resolved before opening,
configfs-backed paths are rejected, and the resolved path is opened with
file_open_root() while retaining the normal open-time permission and
security checks.

Fixes: d5eff33ee6f8 ("nvmet: add simple file backed ns support")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/nvme/target/io-cmd-file.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/io-cmd-file.c b/drivers/nvme/target/io-cmd-file.c
index 0b22d183f9279..2a4f25de94ba1 100644
--- a/drivers/nvme/target/io-cmd-file.c
+++ b/drivers/nvme/target/io-cmd-file.c
@@ -8,6 +8,7 @@
 #include <linux/uio.h>
 #include <linux/falloc.h>
 #include <linux/file.h>
+#include <linux/configfs.h>
 #include <linux/fs.h>
 #include "nvmet.h"
 
@@ -38,7 +39,7 @@ int nvmet_file_ns_enable(struct nvmet_ns *ns)
 	if (!ns->buffered_io)
 		flags |= O_DIRECT;
 
-	ns->file = filp_open(ns->device_path, flags, 0);
+	ns->file = configfs_file_open(ns->device_path, flags, 0);
 	if (IS_ERR(ns->file)) {
 		ret = PTR_ERR(ns->file);
 		pr_err("failed to open file %s: (%d)\n",
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
  2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig
  4 siblings, 0 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

nvmet_passthru_enable_store() runs as a configfs store callback while
configfs holds the item frag_sem. Passthru enable used filp_open() on the
configured controller path, so a path into configfs could re-enter
__configfs_open_file() and try to acquire the same semaphore again.

Use configfs_file_open() so the path is resolved before opening,
configfs-backed paths are rejected, and the resolved path is opened with
file_open_root() while retaining the normal open-time permission and
security checks.

Fixes: cae5b01a2afc ("nvmet: introduce the passthru configfs interface")
Cc: stable@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/nvme/target/passthru.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c
index fa6527c537e26..d60256004e6cf 100644
--- a/drivers/nvme/target/passthru.c
+++ b/drivers/nvme/target/passthru.c
@@ -9,6 +9,7 @@
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/module.h>
+#include <linux/configfs.h>
 
 #include "../host/nvme.h"
 #include "nvmet.h"
@@ -602,7 +603,7 @@ int nvmet_passthru_ctrl_enable(struct nvmet_subsys *subsys)
 		goto out_unlock;
 	}
 
-	file = filp_open(subsys->passthru_ctrl_path, O_RDWR, 0);
+	file = configfs_file_open(subsys->passthru_ctrl_path, O_RDWR, 0);
 	if (IS_ERR(file)) {
 		ret = PTR_ERR(file);
 		goto out_unlock;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v7 4/4] scsi: target: pin db_root for metadata writes
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
                   ` (2 preceding siblings ...)
  2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
@ 2026-09-27  8:20 ` Runyu Xiao
  2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig
  4 siblings, 0 replies; 6+ messages in thread
From: Runyu Xiao @ 2026-09-27  8:20 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Runyu Xiao, Jianhao Xu

ALUA and persistent reservation metadata files are derived from the
configurable db_root string and opened with filp_open().  If db_root
points at configfs, a metadata update from a configfs store callback can
re-enter configfs while the callback still holds frag_sem.

A one-time pathname check can also be bypassed by retargeting a symlink.
Resolve db_root once and retain the resulting path while target devices
use it.  Use configfs_open_root() both to reject configfs roots and to
open metadata files relative to the pinned root.

Protect db_root and db_root_path with a dedicated mutex while metadata
paths are opened or the configured root is replaced.  Return an error if
target devices appear before a validated root can be published.

Resolve a new root outside target_devices_lock and recheck the device count
before publishing it, so the path walk does not occur under that lock.

Fixes: fdddf932269a ("target: use new "dbroot" target attribute")
Link: https://lore.kernel.org/r/20260818051442.1523210-1-runyu.xiao@seu.edu.cn
Cc: stable@vger.kernel.org
Assisted-by: LLM Codex
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
 drivers/target/target_core_alua.c     |  42 ++++----
 drivers/target/target_core_configfs.c | 142 +++++++++++++++++++++-----
 drivers/target/target_core_internal.h |   5 +
 drivers/target/target_core_pr.c       |  19 ++--
 4 files changed, 153 insertions(+), 55 deletions(-)

diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 140154d93c430..c39e57c1ebf9e 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -18,7 +18,6 @@
 #include <linux/fcntl.h>
 #include <linux/file.h>
 #include <linux/fs.h>
-#include <linux/fs_struct.h>
 #include <linux/kthread.h>
 #include <scsi/scsi_proto.h>
 #include <linux/unaligned.h>
@@ -862,15 +861,18 @@ static int core_alua_write_tpg_metadata(
 	loff_t pos = 0;
 	int ret;
 
-	if (tsk_is_kthread(current)) {
-		scoped_with_init_fs()
-			file = filp_open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
-	} else {
-		file = filp_open(path, O_RDWR | O_CREAT | O_TRUNC, 0600);
+	mutex_lock(&db_root_lock);
+	if (!db_root_path.dentry) {
+		mutex_unlock(&db_root_lock);
+		pr_err("db_root is not initialized for ALUA metadata path: %s\n",
+		       path);
+		return -ENODEV;
 	}
-
+	file = configfs_open_root(&db_root_path, path,
+				  O_RDWR | O_CREAT | O_TRUNC, 0600);
+	mutex_unlock(&db_root_lock);
 	if (IS_ERR(file)) {
-		pr_err("filp_open(%s) for ALUA metadata failed\n", path);
+		pr_err("configfs_open_root(%s) for ALUA metadata failed\n", path);
 		return -ENODEV;
 	}
 	ret = kernel_write(file, md_buf, md_buf_len, &pos);
@@ -905,9 +907,9 @@ static int core_alua_update_tpg_primary_metadata(
 			tg_pt_gp->tg_pt_gp_alua_access_status);
 
 	rc = -ENOMEM;
-	path = kasprintf(GFP_KERNEL, "%s/alua/tpgs_%s/%s", db_root,
-			&wwn->unit_serial[0],
-			config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item));
+	path = kasprintf(GFP_KERNEL, "alua/tpgs_%s/%s",
+			 &wwn->unit_serial[0],
+			 config_item_name(&tg_pt_gp->tg_pt_gp_group.cg_item));
 	if (path) {
 		rc = core_alua_write_tpg_metadata(path, md_buf, len);
 		kfree(path);
@@ -1196,16 +1198,16 @@ static int core_alua_update_tpg_secondary_metadata(struct se_lun *lun)
 			lun->lun_tg_pt_secondary_stat);
 
 	if (se_tpg->se_tpg_tfo->tpg_get_tag != NULL) {
-		path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s+%hu/lun_%llu",
-				db_root, se_tpg->se_tpg_tfo->fabric_name,
-				se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
-				se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg),
-				lun->unpacked_lun);
+		path = kasprintf(GFP_KERNEL, "alua/%s/%s+%hu/lun_%llu",
+				 se_tpg->se_tpg_tfo->fabric_name,
+				 se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
+				 se_tpg->se_tpg_tfo->tpg_get_tag(se_tpg),
+				 lun->unpacked_lun);
 	} else {
-		path = kasprintf(GFP_KERNEL, "%s/alua/%s/%s/lun_%llu",
-				db_root, se_tpg->se_tpg_tfo->fabric_name,
-				se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
-				lun->unpacked_lun);
+		path = kasprintf(GFP_KERNEL, "alua/%s/%s/lun_%llu",
+				 se_tpg->se_tpg_tfo->fabric_name,
+				 se_tpg->se_tpg_tfo->tpg_get_wwn(se_tpg),
+				 lun->unpacked_lun);
 	}
 	if (!path) {
 		rc = -ENOMEM;
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 2b19a956007b7..31cd1ed42b135 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -96,57 +96,126 @@ static ssize_t target_core_item_version_show(struct config_item *item,
 CONFIGFS_ATTR_RO(target_core_item_, version);
 
 char db_root[DB_ROOT_LEN] = DB_ROOT_DEFAULT;
-static char db_root_stage[DB_ROOT_LEN];
+struct path db_root_path;
+/* Protect db_root and db_root_path. */
+DEFINE_MUTEX(db_root_lock);
+
+static int target_validate_db_root(const char *path_str, struct path *path)
+{
+	struct file *file;
+	int ret;
+
+	ret = kern_path(path_str, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, path);
+	if (ret) {
+		pr_err("db_root: cannot open: %s\n", path_str);
+		if (ret == -ENOTDIR)
+			pr_err("db_root: not a directory: %s\n", path_str);
+		return ret;
+	}
+
+	file = configfs_open_root(path, "", O_RDONLY, 0);
+	if (IS_ERR(file)) {
+		ret = PTR_ERR(file);
+		path_put(path);
+		*path = (struct path){};
+		if (ret != -EINVAL)
+			return ret;
+
+		pr_err("db_root: configfs is not a valid target database root: %s\n",
+		       path_str);
+		return -EINVAL;
+	}
+
+	path_put(path);
+	*path = file->f_path;
+	path_get(path);
+	fput(file);
+
+	return 0;
+}
 
 static ssize_t target_core_item_dbroot_show(struct config_item *item,
 					    char *page)
 {
-	return sprintf(page, "%s\n", db_root);
+	ssize_t ret;
+
+	mutex_lock(&db_root_lock);
+	ret = sprintf(page, "%s\n", db_root);
+	mutex_unlock(&db_root_lock);
+
+	return ret;
 }
 
 static ssize_t target_core_item_dbroot_store(struct config_item *item,
 					const char *page, size_t count)
 {
+	char *db_root_stage;
 	ssize_t read_bytes;
 	ssize_t r = -EINVAL;
 	struct path path = {};
+	struct path old_path = {};
+	bool have_old_path = false;
 
 	mutex_lock(&target_devices_lock);
 	if (target_devices) {
 		pr_err("db_root: cannot be changed because it's in use\n");
-		goto unlock;
+		mutex_unlock(&target_devices_lock);
+		return r;
 	}
+	mutex_unlock(&target_devices_lock);
 
 	if (count > (DB_ROOT_LEN - 1)) {
 		pr_err("db_root: count %d exceeds DB_ROOT_LEN-1: %u\n",
 		       (int)count, DB_ROOT_LEN - 1);
-		goto unlock;
+		return r;
 	}
 
+	db_root_stage = kmalloc(DB_ROOT_LEN, GFP_KERNEL);
+	if (!db_root_stage)
+		return -ENOMEM;
+
 	read_bytes = scnprintf(db_root_stage, DB_ROOT_LEN, "%s", page);
 	if (!read_bytes)
-		goto unlock;
+		goto free_stage;
 
 	if (db_root_stage[read_bytes - 1] == '\n')
 		db_root_stage[read_bytes - 1] = '\0';
 
 	/* validate new db root before accepting it */
-	r = kern_path(db_root_stage, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path);
-	if (r) {
-		pr_err("db_root: cannot open: %s\n", db_root_stage);
-		if (r == -ENOTDIR)
-			pr_err("db_root: not a directory: %s\n", db_root_stage);
-		goto unlock;
+	r = target_validate_db_root(db_root_stage, &path);
+	if (r)
+		goto free_stage;
+
+	mutex_lock(&target_devices_lock);
+	if (target_devices) {
+		pr_err("db_root: cannot be changed because it's in use\n");
+		r = -EINVAL;
+		goto unlock_put;
 	}
-	path_put(&path);
 
+	mutex_lock(&db_root_lock);
+	have_old_path = db_root_path.dentry;
+	if (have_old_path)
+		old_path = db_root_path;
+	db_root_path = path;
+	path = (struct path){};
 	strscpy(db_root, db_root_stage);
 	pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root);
+	mutex_unlock(&db_root_lock);
 
 	r = read_bytes;
 
-unlock:
+unlock_put:
 	mutex_unlock(&target_devices_lock);
+	if (path.dentry)
+		path_put(&path);
+	if (have_old_path)
+		path_put(&old_path);
+	kfree(db_root_stage);
+	return r;
+
+free_stage:
+	kfree(db_root_stage);
 	return r;
 }
 
@@ -3722,23 +3791,37 @@ void target_setup_backend_cits(struct target_backend *tb)
 
 static void target_init_dbroot(void)
 {
-	struct file *fp;
+	const char *db_root_stage;
+	struct path path = {};
+	int ret;
 
-	snprintf(db_root_stage, DB_ROOT_LEN, DB_ROOT_PREFERRED);
-	fp = filp_open(db_root_stage, O_RDONLY, 0);
-	if (IS_ERR(fp)) {
-		pr_err("db_root: cannot open: %s\n", db_root_stage);
-		return;
-	}
-	if (!S_ISDIR(file_inode(fp)->i_mode)) {
-		filp_close(fp, NULL);
-		pr_err("db_root: not a valid directory: %s\n", db_root_stage);
-		return;
+	db_root_stage = DB_ROOT_PREFERRED;
+	ret = target_validate_db_root(db_root_stage, &path);
+	if (ret) {
+		db_root_stage = DB_ROOT_DEFAULT;
+		ret = target_validate_db_root(db_root_stage, &path);
+		if (ret)
+			return;
 	}
-	filp_close(fp, NULL);
 
+	mutex_lock(&db_root_lock);
+	db_root_path = path;
 	strscpy(db_root, db_root_stage);
 	pr_debug("Target_Core_ConfigFS: db_root set to %s\n", db_root);
+	mutex_unlock(&db_root_lock);
+}
+
+static void target_release_db_root(void)
+{
+	struct path path;
+
+	mutex_lock(&db_root_lock);
+	path = db_root_path;
+	db_root_path = (struct path){};
+	mutex_unlock(&db_root_lock);
+
+	if (path.dentry)
+		path_put(&path);
 }
 
 static int __init target_core_init_configfs(void)
@@ -3797,6 +3880,10 @@ static int __init target_core_init_configfs(void)
 	/*
 	 * Register the target_core_mod subsystem with configfs.
 	 */
+	/* Resolve db_root before making the configfs attributes visible. */
+	scoped_with_kernel_creds()
+		target_init_dbroot();
+
 	ret = configfs_register_subsystem(subsys);
 	if (ret < 0) {
 		pr_err("Error %d while registering subsystem %s\n",
@@ -3821,9 +3908,6 @@ static int __init target_core_init_configfs(void)
 	if (ret < 0)
 		goto out;
 
-	scoped_with_kernel_creds()
-		target_init_dbroot();
-
 	return 0;
 
 out:
@@ -3832,6 +3916,7 @@ static int __init target_core_init_configfs(void)
 	core_dev_release_virtual_lun0();
 	rd_module_exit();
 out_global:
+	target_release_db_root();
 	if (default_lu_gp) {
 		core_alua_free_lu_gp(default_lu_gp);
 		default_lu_gp = NULL;
@@ -3861,6 +3946,7 @@ static void __exit target_core_exit_configfs(void)
 	core_dev_release_virtual_lun0();
 	rd_module_exit();
 	target_xcopy_release_pt();
+	target_release_db_root();
 	release_se_kmem_caches();
 }
 
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index f0886ea290345..502788ba732dd 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -4,6 +4,7 @@
 
 #include <linux/configfs.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
 #include <linux/types.h>
 #include <target/target_core_base.h>
 
@@ -171,6 +172,10 @@ extern struct se_portal_group xcopy_pt_tpg;
 #define	DB_ROOT_DEFAULT		"/var/target"
 #define	DB_ROOT_PREFERRED	"/etc/target"
 
+struct path;
+
 extern char db_root[];
+extern struct path db_root_path;
+extern struct mutex db_root_lock;
 
 #endif /* TARGET_CORE_INTERNAL_H */
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 25b1bcacc0c8f..36d20732382f1 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -18,7 +18,6 @@
 #include <linux/file.h>
 #include <linux/fcntl.h>
 #include <linux/fs.h>
-#include <linux/fs_struct.h>
 #include <scsi/scsi_proto.h>
 #include <linux/unaligned.h>
 
@@ -1965,16 +1964,22 @@ static int __core_scsi3_write_aptpl_to_file(
 	int ret;
 	loff_t pos = 0;
 
-	path = kasprintf(GFP_KERNEL, "%s/pr/aptpl_%s", db_root,
-			&wwn->unit_serial[0]);
+	path = kasprintf(GFP_KERNEL, "pr/aptpl_%s", &wwn->unit_serial[0]);
 	if (!path)
 		return -ENOMEM;
 
-	scoped_with_init_fs()
-		file = filp_open(path, flags, 0600);
+	mutex_lock(&db_root_lock);
+	if (!db_root_path.dentry) {
+		mutex_unlock(&db_root_lock);
+		pr_err("db_root is not initialized for APTPL metadata path: %s\n",
+		       path);
+		kfree(path);
+		return -ENODEV;
+	}
+	file = configfs_open_root(&db_root_path, path, flags, 0600);
+	mutex_unlock(&db_root_lock);
 	if (IS_ERR(file)) {
-		pr_err("filp_open(%s) for APTPL metadata"
-			" failed\n", path);
+		pr_err("configfs_open_root(%s) for APTPL metadata failed\n", path);
 		kfree(path);
 		return PTR_ERR(file);
 	}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v7 0/4] nvmet: avoid recursive configfs open
  2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
                   ` (3 preceding siblings ...)
  2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
@ 2026-09-28  5:12 ` Christoph Hellwig
  4 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2026-09-28  5:12 UTC (permalink / raw)
  To: Runyu Xiao
  Cc: Christoph Hellwig, Breno Leitao, Andreas Hindborg, Sagi Grimberg,
	Chaitanya Kulkarni, Keith Busch, Logan Gunthorpe,
	Martin K . Petersen, Lee Duncan, Hannes Reinecke,
	Nicholas Bellinger, linux-nvme, linux-scsi, target-devel,
	linux-kernel, stable, Jianhao Xu

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-28  5:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-27  8:20 [PATCH v7 0/4] nvmet: avoid recursive configfs open Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 1/4] fs: configfs: add helpers for opening non-configfs paths Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 2/4] nvmet: avoid recursive configfs open for file-backed namespaces Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 3/4] nvmet: avoid recursive configfs open for passthru Runyu Xiao
2026-09-27  8:20 ` [PATCH v7 4/4] scsi: target: pin db_root for metadata writes Runyu Xiao
2026-09-28  5:12 ` [PATCH v7 0/4] nvmet: avoid recursive configfs open Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®