mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 00/10] Towards safer path_* API
@ 2026-09-13 14:49 Mateusz Guzik
  2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
                   ` (11 more replies)
  0 siblings, 12 replies; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

Currently the handling is highly error-prone with consumers allowed to
arbitrarily manipulate references, notably with path_get().

In the past this resulted in preventable bugs.

The end goal, not yet achieved with this patchset, will provide an
invariant that a populated 'struct path' has precisely one set of
references on it.

While consumers can still cause leaks by neglecting to path_put(), the
footgun of path_get() can be eliminated.

With this patchset instead of hand-rolling a path copy and issuing
path_get() on it, callers can resort to path_clone() instead.

Almost all path_get() consumers get converted, most of which with
coccinelle.

Few instances are left until I figure out a nice way to sort them out.

Overall almost entirety of this patchset is a NOP.

Mateusz Guzik (10):
  fs: unexport backing_file_set_user_path() and make it take the ref on
    its own
  fs: add path_create(), path_move() and path_clone()
  fs: use path_clone() in path_init()
  fs: use path_clone() and path_move() in vfs_open*
  fs: use path_clone() in backing_file_set_user_path()
  Coccinelle-based conversion of path_* consumers to use the new
    primitives
  autofs: use path_create()
  fsnotify: use path_clone()
  proc: use path_clone()
  nfsd: use path_clone()

 drivers/block/loop.c               |  3 +-
 fs/autofs/dev-ioctl.c              |  3 +-
 fs/autofs/expire.c                 |  4 +--
 fs/backing-file.c                  |  2 --
 fs/devpts/inode.c                  |  6 ++--
 fs/failfs.c                        |  3 +-
 fs/fhandle.c                       |  3 +-
 fs/file_attr.c                     |  6 ++--
 fs/file_table.c                    |  3 +-
 fs/fs_struct.c                     |  6 ++--
 fs/namei.c                         | 44 ++++++++++++++++++++++++------
 fs/namespace.c                     |  3 +-
 fs/nfsd/export.c                   |  6 ++--
 fs/nfsd/nfs4xdr.c                  |  8 +++---
 fs/notify/fanotify/fanotify.c      |  6 ++--
 fs/notify/fanotify/fanotify_user.c |  3 +-
 fs/nsfs.c                          |  3 +-
 fs/open.c                          | 10 ++-----
 fs/overlayfs/params.c              |  3 +-
 fs/pidfs.c                         |  3 +-
 fs/proc/base.c                     |  6 ++--
 fs/proc/fd.c                       |  3 +-
 fs/smb/server/vfs.c                |  3 +-
 fs/xfs/xfs_handle.c                |  3 +-
 include/linux/path.h               | 14 ++++++++--
 kernel/trace/bpf_trace.c           |  3 +-
 security/apparmor/task.c           |  3 +-
 security/keys/big_key.c            |  3 +-
 security/landlock/fs.c             |  3 +-
 security/landlock/syscalls.c       |  3 +-
 30 files changed, 87 insertions(+), 85 deletions(-)

-- 
2.53.0


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

* [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:33   ` Jan Kara
  2026-09-14 23:41   ` NeilBrown
  2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

Prep for path_* API changes, which will force the ref change.

There are likely no users outside of the tree, but should there be some they
will no longer be caught by surprise.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/backing-file.c | 2 --
 fs/file_table.c   | 2 +-
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/fs/backing-file.c b/fs/backing-file.c
index cc101143f921..e92410a27982 100644
--- a/fs/backing-file.c
+++ b/fs/backing-file.c
@@ -43,7 +43,6 @@ struct file *backing_file_open(const struct file *user_file, int flags,
 	if (IS_ERR(f))
 		return f;
 
-	path_get(user_path);
 	backing_file_set_user_path(f, user_path);
 	error = vfs_open(real_path, f);
 	if (error) {
@@ -68,7 +67,6 @@ struct file *backing_tmpfile_open(const struct file *user_file, int flags,
 	if (IS_ERR(f))
 		return f;
 
-	path_get(user_path);
 	backing_file_set_user_path(f, user_path);
 	error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
 	if (error) {
diff --git a/fs/file_table.c b/fs/file_table.c
index 8dcd213c0251..f9d1b5edd6ae 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -71,8 +71,8 @@ EXPORT_SYMBOL_GPL(backing_file_user_path);
 void backing_file_set_user_path(struct file *f, const struct path *path)
 {
 	backing_file(f)->user_path = *path;
+	path_get(path);
 }
-EXPORT_SYMBOL_GPL(backing_file_set_user_path);
 
 #ifdef CONFIG_SECURITY
 void *backing_file_security(const struct file *f)
-- 
2.53.0


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

* [PATCH 02/10] fs: add path_create(), path_move() and path_clone()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
  2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-13 14:51   ` Mateusz Guzik
                     ` (2 more replies)
  2026-09-13 14:49 ` [PATCH 03/10] fs: use path_clone() in path_init() Mateusz Guzik
                   ` (9 subsequent siblings)
  11 siblings, 3 replies; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/namei.c           | 29 +++++++++++++++++++++++++++++
 include/linux/path.h | 14 ++++++++++++--
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index ca4f5e3be99a..c8754c7ba8be 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -698,6 +698,35 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
 	return security_inode_permission(inode, mask);
 }
 
+/**
+ * path_create - WRITEME
+ * @path: path to get the reference to
+ * @mnt: WRITEME
+ * @dentry: WRITEME
+ *
+ * Create a path object using the given vfsmount and dentry pair while incrementing
+ * the reference count on both.
+ */
+void path_create(struct path *path, struct vfsmount *mnt, struct dentry *dentry)
+{
+	path->mnt = mntget(mnt);
+	path->dentry = dget(dentry);
+}
+EXPORT_SYMBOL(path_create);
+
+/**
+ * path_clone - WRITEME
+ * @path: path to get the reference to
+ *
+ * Given a path increment the reference count to the dentry and the vfsmount.
+ */
+void path_clone(const struct path *src, struct path *dst)
+{
+	dst->mnt = mntget(src->mnt);
+	dst->dentry = dget(src->dentry);
+}
+EXPORT_SYMBOL(path_clone);
+
 /**
  * path_get - get a reference to a path
  * @path: path to get the reference to
diff --git a/include/linux/path.h b/include/linux/path.h
index 7ea389dc764b..8a621e0e1921 100644
--- a/include/linux/path.h
+++ b/include/linux/path.h
@@ -10,8 +10,18 @@ struct path {
 	struct dentry *dentry;
 } __randomize_layout;
 
-extern void path_get(const struct path *);
-extern void path_put(const struct path *);
+void path_create(struct path *, struct vfsmount *, struct dentry *);
+void path_clone(const struct path *, struct path *);
+void path_get(const struct path *);
+void path_put(const struct path *);
+
+static inline void path_move(struct path *src, struct path *dst)
+{
+	dst->mnt = src->mnt;
+	dst->dentry = src->dentry;
+	src->mnt = NULL;
+	src->dentry = NULL;
+}
 
 static inline int path_equal(const struct path *path1, const struct path *path2)
 {
-- 
2.53.0


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

* [PATCH 03/10] fs: use path_clone() in path_init()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
  2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
  2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14 23:52   ` NeilBrown
  2026-09-13 14:49 ` [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open* Mateusz Guzik
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/namei.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index c8754c7ba8be..11937cfa8c7f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2745,13 +2745,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 		struct inode *inode = root->d_inode;
 		if (*s && unlikely(!d_can_lookup(root)))
 			return ERR_PTR(-ENOTDIR);
-		nd->path = nd->root;
 		nd->inode = inode;
 		if (flags & LOOKUP_RCU) {
+			nd->path = nd->root;
 			nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
 			nd->root_seq = nd->seq;
 		} else {
-			path_get(&nd->path);
+			path_clone(&nd->root, &nd->path);
 		}
 		return s;
 	}
@@ -2801,23 +2801,23 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
 		if (*s && unlikely(!d_can_lookup(dentry)))
 			return ERR_PTR(-ENOTDIR);
 
-		nd->path = fd_file(f)->f_path;
 		if (flags & LOOKUP_RCU) {
+			nd->path = fd_file(f)->f_path;
 			nd->inode = nd->path.dentry->d_inode;
 			nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
 		} else {
-			path_get(&nd->path);
+			path_clone(&fd_file(f)->f_path, &nd->path);
 			nd->inode = nd->path.dentry->d_inode;
 		}
 	}
 
 	/* For scoped-lookups we need to set the root to the dirfd as well. */
 	if (unlikely(flags & LOOKUP_IS_SCOPED)) {
-		nd->root = nd->path;
 		if (flags & LOOKUP_RCU) {
+			nd->root = nd->path;
 			nd->root_seq = nd->seq;
 		} else {
-			path_get(&nd->root);
+			path_clone(&nd->path, &nd->root);
 			nd->state |= ND_ROOT_GRABBED;
 		}
 	}
-- 
2.53.0


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

* [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open*
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (2 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 03/10] fs: use path_clone() in path_init() Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:56   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path() Mateusz Guzik
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/open.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/fs/open.c b/fs/open.c
index a84b55301719..e11d1342ff74 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -1102,8 +1102,7 @@ int vfs_open(const struct path *path, struct file *file)
 {
 	int ret;
 
-	file->__f_path = *path;
-	path_get(&file->f_path);
+	path_clone(path, &file->__f_path);
 	ret = do_dentry_open(file, NULL);
 	if (!ret) {
 		/*
@@ -1125,9 +1124,7 @@ int vfs_open_consume(struct path *path, struct file *file)
 {
 	int ret;
 
-	file->__f_path = *path;
-	path->mnt = NULL;
-	path->dentry = NULL;
+	path_move(path, &file->__f_path);
 	ret = do_dentry_open(file, NULL);
 	if (!ret) {
 		fsnotify_open(file);
-- 
2.53.0


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

* [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (3 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open* Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:57   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives Mateusz Guzik
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/file_table.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/fs/file_table.c b/fs/file_table.c
index f9d1b5edd6ae..0dca0c6c991f 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -70,8 +70,7 @@ EXPORT_SYMBOL_GPL(backing_file_user_path);
 
 void backing_file_set_user_path(struct file *f, const struct path *path)
 {
-	backing_file(f)->user_path = *path;
-	path_get(path);
+	path_clone(path, &backing_file(f)->user_path);
 }
 
 #ifdef CONFIG_SECURITY
-- 
2.53.0


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

* [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (4 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path() Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:57   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 07/10] autofs: use path_create() Mateusz Guzik
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

The script:
@@
expression src, dst;
@@

- *dst = *src;
- path_get(dst);
+ path_clone(src, dst);

@@
expression src, dst;
@@

- *dst = src;
- path_get(dst);
+ path_clone(&src, dst);

@@
expression src, dst;
@@

- dst = src;
- path_get(&dst);
+ path_clone(&src, &dst);

@@
expression src, dst;
@@

- *dst = src;
- path_get(src);
+ path_clone(src, dst);

@@
expression src, dst;
@@

- path_get(&src);
- *dst = src;
+ path_clone(&src, dst);

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 drivers/block/loop.c               | 3 +--
 fs/autofs/dev-ioctl.c              | 3 +--
 fs/devpts/inode.c                  | 6 ++----
 fs/failfs.c                        | 3 +--
 fs/fhandle.c                       | 3 +--
 fs/file_attr.c                     | 6 ++----
 fs/fs_struct.c                     | 6 ++----
 fs/namei.c                         | 3 +--
 fs/namespace.c                     | 3 +--
 fs/notify/fanotify/fanotify_user.c | 3 +--
 fs/nsfs.c                          | 3 +--
 fs/open.c                          | 3 +--
 fs/overlayfs/params.c              | 3 +--
 fs/pidfs.c                         | 3 +--
 fs/proc/base.c                     | 3 +--
 fs/smb/server/vfs.c                | 3 +--
 fs/xfs/xfs_handle.c                | 3 +--
 kernel/trace/bpf_trace.c           | 3 +--
 security/apparmor/task.c           | 3 +--
 security/keys/big_key.c            | 3 +--
 security/landlock/fs.c             | 3 +--
 security/landlock/syscalls.c       | 3 +--
 22 files changed, 25 insertions(+), 50 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 758c20678bf6..cc5c11c57989 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1321,8 +1321,7 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
 	memcpy(info->lo_file_name, lo->lo_file_name, LO_NAME_SIZE);
 
 	/* Drop lo_mutex while we call into the filesystem. */
-	path = lo->lo_backing_file->f_path;
-	path_get(&path);
+	path_clone(&lo->lo_backing_file->f_path, &path);
 	mutex_unlock(&lo->lo_mutex);
 	ret = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
 	if (!ret) {
diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c
index 6743b3b64217..a2ec9659297f 100644
--- a/fs/autofs/dev-ioctl.c
+++ b/fs/autofs/dev-ioctl.c
@@ -200,8 +200,7 @@ static int find_autofs_mount(const char *pathname,
 	while (path.dentry == path.mnt->mnt_root) {
 		if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
 			if (test(&path, data)) {
-				path_get(&path);
-				*res = path;
+				path_clone(&path, res);
 				err = 0;
 				break;
 			}
diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
index 9844dcf354ee..2b5d273edaa7 100644
--- a/fs/devpts/inode.c
+++ b/fs/devpts/inode.c
@@ -152,8 +152,7 @@ struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
 	struct path path;
 	int err = 0;
 
-	path = filp->f_path;
-	path_get(&path);
+	path_clone(&filp->f_path, &path);
 
 	/* Walk upward while the start point is a bind mount of
 	 * a single file.
@@ -184,8 +183,7 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
 	struct path path;
 	struct super_block *sb;
 
-	path = filp->f_path;
-	path_get(&path);
+	path_clone(&filp->f_path, &path);
 
 	/* Has the devpts filesystem already been found? */
 	if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
diff --git a/fs/failfs.c b/fs/failfs.c
index 66a36da3d236..6459163de5b8 100644
--- a/fs/failfs.c
+++ b/fs/failfs.c
@@ -13,8 +13,7 @@ static struct path failfs_root_path = {};
 
 void failfs_get_root(struct path *path)
 {
-	*path = failfs_root_path;
-	path_get(path);
+	path_clone(&failfs_root_path, path);
 }
 
 bool failfs_mnt(const struct vfsmount *mnt)
diff --git a/fs/fhandle.c b/fs/fhandle.c
index f8829231e3d7..5c29a0a36c57 100644
--- a/fs/fhandle.c
+++ b/fs/fhandle.c
@@ -173,8 +173,7 @@ static int get_path_anchor(int fd, struct path *root)
 		CLASS(fd, f)(fd);
 		if (fd_empty(f))
 			return -EBADF;
-		*root = fd_file(f)->f_path;
-		path_get(root);
+		path_clone(&fd_file(f)->f_path, root);
 		return 0;
 	}
 
diff --git a/fs/file_attr.c b/fs/file_attr.c
index bfb00d256dd5..0a46fe39dfc7 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -402,8 +402,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
 		if (fd_empty(f))
 			return -EBADF;
 
-		filepath = fd_file(f)->f_path;
-		path_get(&filepath);
+		path_clone(&fd_file(f)->f_path, &filepath);
 	} else {
 		error = filename_lookup(dfd, name, lookup_flags, &filepath,
 					NULL);
@@ -464,8 +463,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
 		if (fd_empty(f))
 			return -EBADF;
 
-		filepath = fd_file(f)->f_path;
-		path_get(&filepath);
+		path_clone(&fd_file(f)->f_path, &filepath);
 	} else {
 		error = filename_lookup(dfd, name, lookup_flags, &filepath,
 					NULL);
diff --git a/fs/fs_struct.c b/fs/fs_struct.c
index 34699f3b6f88..330a15516787 100644
--- a/fs/fs_struct.c
+++ b/fs/fs_struct.c
@@ -120,10 +120,8 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
 		fs->umask = old->umask;
 
 		read_seqlock_excl(&old->seq);
-		fs->root = old->root;
-		path_get(&fs->root);
-		fs->pwd = old->pwd;
-		path_get(&fs->pwd);
+		path_clone(&old->root, &fs->root);
+		path_clone(&old->pwd, &fs->pwd);
 		read_sequnlock_excl(&old->seq);
 	}
 	return fs;
diff --git a/fs/namei.c b/fs/namei.c
index 11937cfa8c7f..44fd82ee45c2 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1182,8 +1182,7 @@ static int nd_jump_root(struct nameidata *nd)
 			return -ECHILD;
 	} else {
 		path_put(&nd->path);
-		nd->path = nd->root;
-		path_get(&nd->path);
+		path_clone(&nd->root, &nd->path);
 		nd->inode = nd->path.dentry->d_inode;
 	}
 	nd->state |= ND_JUMPED;
diff --git a/fs/namespace.c b/fs/namespace.c
index a36ea2cc733d..79f785fabfdc 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4614,8 +4614,7 @@ SYSCALL_DEFINE5(move_mount,
 		if (fd_empty(f_to))
 			return -EBADF;
 
-		to_path = fd_file(f_to)->f_path;
-		path_get(&to_path);
+		path_clone(&fd_file(f_to)->f_path, &to_path);
 	} else {
 		lflags = 0;
 		if (flags & MOVE_MOUNT_T_SYMLINKS)
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 63c9759fc3b0..3c49329543d9 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -1202,8 +1202,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
 		    !(S_ISDIR(file_inode(fd_file(f))->i_mode)))
 			return -ENOTDIR;
 
-		*path = fd_file(f)->f_path;
-		path_get(path);
+		path_clone(&fd_file(f)->f_path, path);
 		ret = 0;
 	} else {
 		unsigned int lookup_flags = 0;
diff --git a/fs/nsfs.c b/fs/nsfs.c
index c3b6ae76594a..577c9edcea32 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -29,8 +29,7 @@ static struct path nsfs_root_path = {};
 
 void nsfs_get_root(struct path *path)
 {
-	*path = nsfs_root_path;
-	path_get(path);
+	path_clone(&nsfs_root_path, path);
 }
 
 static long ns_ioctl(struct file *filp, unsigned int ioctl,
diff --git a/fs/open.c b/fs/open.c
index e11d1342ff74..fc602c44b3bc 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -653,8 +653,7 @@ SYSCALL_DEFINE2(fchroot, int, fd, unsigned int, flags)
 		if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
 			return -EPERM;
 
-		path = fd_file(f)->f_path;
-		path_get(&path);
+		path_clone(&fd_file(f)->f_path, &path);
 	}
 
 	error = security_path_chroot(&path);
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index c93fcaa45d4a..0c72e18dd625 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -474,8 +474,7 @@ static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
 		if (!buf)
 			return -ENOMEM;
 
-		layer_path = param->file->f_path;
-		path_get(&layer_path);
+		path_clone(&param->file->f_path, &layer_path);
 
 		layer_name = d_path(&layer_path, buf, PATH_MAX);
 		if (IS_ERR(layer_name))
diff --git a/fs/pidfs.c b/fs/pidfs.c
index a6a643f15d08..c70a997376bd 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -42,8 +42,7 @@ static struct simple_xattr_cache pidfs_xa_cache;
 
 void pidfs_get_root(struct path *path)
 {
-	*path = pidfs_root_path;
-	path_get(path);
+	path_clone(&pidfs_root_path, path);
 }
 
 enum pidfs_attr_mask_bits {
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 0f9efd25bb05..01d8bfe1c410 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2253,8 +2253,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path,
 	rc = -ENOENT;
 	vma = find_exact_vma(mm, vm_start, vm_end);
 	if (vma && vma->vm_file) {
-		*path = *file_user_path(vma->vm_file);
-		path_get(path);
+		path_clone(file_user_path(vma->vm_file), path);
 		rc = 0;
 	}
 	mmap_read_unlock(mm);
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 3a6f3139c6f5..c81b97a7356a 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -1349,8 +1349,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
 	path_len = strlen(filepath);
 	remain_len = path_len;
 
-	parent_path = share_conf->vfs_path;
-	path_get(&parent_path);
+	path_clone(&share_conf->vfs_path, &parent_path);
 
 	while (d_can_lookup(parent_path.dentry)) {
 		char *filename = filepath + path_len - remain_len;
diff --git a/fs/xfs/xfs_handle.c b/fs/xfs/xfs_handle.c
index 0689cade8f74..7e3ddf2967c6 100644
--- a/fs/xfs/xfs_handle.c
+++ b/fs/xfs/xfs_handle.c
@@ -94,8 +94,7 @@ xfs_find_handle(
 
 		if (fd_empty(f))
 			return -EBADF;
-		path = fd_file(f)->f_path;
-		path_get(&path);
+		path_clone(&fd_file(f)->f_path, &path);
 	} else {
 		error = user_path_at(AT_FDCWD, hreq->path, 0, &path);
 		if (error)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 29260951aa87..ebe957432213 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -3233,8 +3233,7 @@ static int bpf_uprobe_multi_get_path(const union bpf_attr *attr, struct path *pa
 		CLASS(fd, f)(path_fd);
 		if (fd_empty(f))
 			return -EBADF;
-		*path = fd_file(f)->f_path;
-		path_get(path);
+		path_clone(&fd_file(f)->f_path, path);
 		return 0;
 	}
 
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index e16ff4130bc2..36ca424760e2 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -337,8 +337,7 @@ static const char *get_current_exe_path(char *buffer, int buffer_size)
 	exe_file = get_task_exe_file(current);
 	if (!exe_file)
 		return ERR_PTR(-ENOENT);
-	p = exe_file->f_path;
-	path_get(&p);
+	path_clone(&exe_file->f_path, &p);
 
 	if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
 		path_str = ERR_PTR(-ENOMEM);
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 268f702df380..e20de015c626 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -121,8 +121,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
 		 * later
 		 */
 		payload->data = enckey;
-		payload->path = file->f_path;
-		path_get(&payload->path);
+		path_clone(&file->f_path, &payload->path);
 		fput(file);
 		kvfree_sensitive(buf, enclen);
 	} else {
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 330a1871bf94..3c614c56e3f8 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -892,8 +892,7 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
 		child2_is_directory = d_is_dir(dentry_child2);
 	}
 
-	walker_path = *path;
-	path_get(&walker_path);
+	path_clone(&*path, &walker_path);
 	/*
 	 * We need to walk through all the hierarchy to not miss any relevant
 	 * restriction.
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 1d02d57f4c48..003f4aa12e4b 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -349,8 +349,7 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
 	    IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry)))
 		return -EBADFD;
 
-	*path = fd_file(f)->f_path;
-	path_get(path);
+	path_clone(&fd_file(f)->f_path, path);
 	return 0;
 }
 
-- 
2.53.0


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

* [PATCH 07/10] autofs: use path_create()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (5 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:58   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 08/10] fsnotify: use path_clone() Mateusz Guzik
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/autofs/expire.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
index 5c2d459e1e48..9d19c47dc3cc 100644
--- a/fs/autofs/expire.c
+++ b/fs/autofs/expire.c
@@ -30,12 +30,12 @@ static int autofs_mount_busy(struct vfsmount *mnt,
 			     struct dentry *dentry, unsigned int how)
 {
 	struct dentry *top = dentry;
-	struct path path = {.mnt = mnt, .dentry = dentry};
+	struct path path;
 	int status = 1;
 
 	pr_debug("dentry %p %pd\n", dentry, dentry);
 
-	path_get(&path);
+	path_create(&path, mnt, dentry);
 
 	if (!follow_down_one(&path))
 		goto done;
-- 
2.53.0


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

* [PATCH 08/10] fsnotify: use path_clone()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (6 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 07/10] autofs: use path_create() Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:36   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 09/10] proc: " Mateusz Guzik
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/notify/fanotify/fanotify.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index a208a7ec1692..84b4c505dbe3 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -559,9 +559,8 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
 		return NULL;
 
 	pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH;
-	pevent->path = *path;
 	*hash ^= fanotify_hash_path(path);
-	path_get(path);
+	path_clone(path, &pevent->path);
 
 	return &pevent->fae;
 }
@@ -600,10 +599,9 @@ static struct fanotify_event *fanotify_alloc_perm_event(const void *data,
 	pevent->hdr.len = 0;
 	pevent->state = FAN_EVENT_INIT;
 	pevent->watchdog_cnt = 0;
-	pevent->path = *path;
 	pevent->pos = range ? range->pos : FANOTIFY_NO_RANGE;
 	pevent->count = range ? range->count : 0;
-	path_get(path);
+	path_clone(path, &pevent->path);
 
 	return &pevent->fae;
 }
-- 
2.53.0


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

* [PATCH 09/10] proc: use path_clone()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (7 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 08/10] fsnotify: use path_clone() Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  8:58   ` Jan Kara
  2026-09-13 14:49 ` [PATCH 10/10] nfsd: " Mateusz Guzik
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/proc/base.c | 3 +--
 fs/proc/fd.c   | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/fs/proc/base.c b/fs/proc/base.c
index 01d8bfe1c410..6b055352eabd 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1766,8 +1766,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
 
 	exe_file = get_task_exe_file(task);
 	if (exe_file) {
-		*exe_path = exe_file->f_path;
-		path_get(&exe_file->f_path);
+		path_clone(&exe_file->f_path, exe_path);
 		fput(exe_file);
 		return 0;
 	} else
diff --git a/fs/proc/fd.c b/fs/proc/fd.c
index 0f9a1556f2a3..9ab9bfd6e48d 100644
--- a/fs/proc/fd.c
+++ b/fs/proc/fd.c
@@ -180,8 +180,7 @@ static int proc_fd_link(struct dentry *dentry, struct path *path,
 
 	fd_file = fget_task(task, fd);
 	if (fd_file) {
-		*path = fd_file->f_path;
-		path_get(&fd_file->f_path);
+		path_clone(&fd_file->f_path, path);
 		ret = 0;
 		fput(fd_file);
 	}
-- 
2.53.0


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

* [PATCH 10/10] nfsd: use path_clone()
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (8 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 09/10] proc: " Mateusz Guzik
@ 2026-09-13 14:49 ` Mateusz Guzik
  2026-09-14  9:01   ` Jan Kara
  2026-09-14  8:53 ` [PATCH 00/10] Towards safer path_* API Jan Kara
  2026-09-14 23:36 ` NeilBrown
  11 siblings, 1 reply; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:49 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

No functional changes.

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---
 fs/nfsd/export.c  | 6 ++----
 fs/nfsd/nfs4xdr.c | 8 ++++----
 2 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index e5a0f1ababe6..84ea833816b5 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -233,8 +233,7 @@ static inline void expkey_update(struct cache_head *cnew,
 	struct svc_expkey *new = container_of(cnew, struct svc_expkey, h);
 	struct svc_expkey *item = container_of(citem, struct svc_expkey, h);
 
-	new->ek_path = item->ek_path;
-	path_get(&item->ek_path);
+	path_clone(&item->ek_path, &new->ek_path);
 }
 
 static struct cache_head *expkey_alloc(void)
@@ -1533,8 +1532,7 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
 
 	kref_get(&item->ex_client->ref);
 	new->ex_client = item->ex_client;
-	new->ex_path = item->ex_path;
-	path_get(&item->ex_path);
+	path_clone(&item->ex_path, &new->ex_path);
 	new->ex_fslocs.locations = NULL;
 	new->ex_fslocs.locations_count = 0;
 	new->ex_fslocs.migrated = 0;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 7d1b2d6f57f2..65a805501b75 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2966,14 +2966,14 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
 				     const struct path *root,
 				     const struct path *path)
 {
-	struct path cur = *path;
+	struct path cur;
 	struct dentry **components = NULL;
 	unsigned int ncomponents = 0;
 	__be32 err = nfserr_jukebox;
 
 	dprintk("nfsd4_encode_components(");
 
-	path_get(&cur);
+	path_clone(path, &cur);
 	/* First walk the path up to the nfsd root, and store the
 	 * dentries/path components in an array.
 	 */
@@ -3216,11 +3216,11 @@ static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32
 
 static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
 {
-	struct path path = exp->ex_path;
+	struct path path;
 	struct kstat stat;
 	int err;
 
-	path_get(&path);
+	path_clone(&exp->ex_path, &path);
 	while (follow_up(&path)) {
 		if (path.dentry != path.mnt->mnt_root)
 			break;
-- 
2.53.0


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

* Re: [PATCH 02/10] fs: add path_create(), path_move() and path_clone()
  2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
@ 2026-09-13 14:51   ` Mateusz Guzik
  2026-09-14 23:46   ` NeilBrown
  2026-09-15  0:05   ` NeilBrown
  2 siblings, 0 replies; 27+ messages in thread
From: Mateusz Guzik @ 2026-09-13 14:51 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel

On Sun, Sep 13, 2026 at 4:49 PM Mateusz Guzik <mjguzik@gmail.com> wrote:
>
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>  fs/namei.c           | 29 +++++++++++++++++++++++++++++
>  include/linux/path.h | 14 ++++++++++++--
>  2 files changed, 41 insertions(+), 2 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index ca4f5e3be99a..c8754c7ba8be 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -698,6 +698,35 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
>         return security_inode_permission(inode, mask);
>  }
>
> +/**
> + * path_create - WRITEME

sigh

I'll take care of it. for the purpose of review pretend this is actual text ;)

> + * @path: path to get the reference to
> + * @mnt: WRITEME
> + * @dentry: WRITEME
> + *
> + * Create a path object using the given vfsmount and dentry pair while incrementing
> + * the reference count on both.
> + */
> +void path_create(struct path *path, struct vfsmount *mnt, struct dentry *dentry)
> +{
> +       path->mnt = mntget(mnt);
> +       path->dentry = dget(dentry);
> +}
> +EXPORT_SYMBOL(path_create);
> +
> +/**
> + * path_clone - WRITEME
> + * @path: path to get the reference to
> + *
> + * Given a path increment the reference count to the dentry and the vfsmount.
> + */
> +void path_clone(const struct path *src, struct path *dst)
> +{
> +       dst->mnt = mntget(src->mnt);
> +       dst->dentry = dget(src->dentry);
> +}
> +EXPORT_SYMBOL(path_clone);
> +
>  /**
>   * path_get - get a reference to a path
>   * @path: path to get the reference to
> diff --git a/include/linux/path.h b/include/linux/path.h
> index 7ea389dc764b..8a621e0e1921 100644
> --- a/include/linux/path.h
> +++ b/include/linux/path.h
> @@ -10,8 +10,18 @@ struct path {
>         struct dentry *dentry;
>  } __randomize_layout;
>
> -extern void path_get(const struct path *);
> -extern void path_put(const struct path *);
> +void path_create(struct path *, struct vfsmount *, struct dentry *);
> +void path_clone(const struct path *, struct path *);
> +void path_get(const struct path *);
> +void path_put(const struct path *);
> +
> +static inline void path_move(struct path *src, struct path *dst)
> +{
> +       dst->mnt = src->mnt;
> +       dst->dentry = src->dentry;
> +       src->mnt = NULL;
> +       src->dentry = NULL;
> +}
>
>  static inline int path_equal(const struct path *path1, const struct path *path2)
>  {
> --
> 2.53.0
>

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

* Re: [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own
  2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
@ 2026-09-14  8:33   ` Jan Kara
  2026-09-14 23:41   ` NeilBrown
  1 sibling, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:33 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:08, Mateusz Guzik wrote:
> Prep for path_* API changes, which will force the ref change.
> 
> There are likely no users outside of the tree, but should there be some they
> will no longer be caught by surprise.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Nice cleanup. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/backing-file.c | 2 --
>  fs/file_table.c   | 2 +-
>  2 files changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/fs/backing-file.c b/fs/backing-file.c
> index cc101143f921..e92410a27982 100644
> --- a/fs/backing-file.c
> +++ b/fs/backing-file.c
> @@ -43,7 +43,6 @@ struct file *backing_file_open(const struct file *user_file, int flags,
>  	if (IS_ERR(f))
>  		return f;
>  
> -	path_get(user_path);
>  	backing_file_set_user_path(f, user_path);
>  	error = vfs_open(real_path, f);
>  	if (error) {
> @@ -68,7 +67,6 @@ struct file *backing_tmpfile_open(const struct file *user_file, int flags,
>  	if (IS_ERR(f))
>  		return f;
>  
> -	path_get(user_path);
>  	backing_file_set_user_path(f, user_path);
>  	error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
>  	if (error) {
> diff --git a/fs/file_table.c b/fs/file_table.c
> index 8dcd213c0251..f9d1b5edd6ae 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -71,8 +71,8 @@ EXPORT_SYMBOL_GPL(backing_file_user_path);
>  void backing_file_set_user_path(struct file *f, const struct path *path)
>  {
>  	backing_file(f)->user_path = *path;
> +	path_get(path);
>  }
> -EXPORT_SYMBOL_GPL(backing_file_set_user_path);
>  
>  #ifdef CONFIG_SECURITY
>  void *backing_file_security(const struct file *f)
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 08/10] fsnotify: use path_clone()
  2026-09-13 14:49 ` [PATCH 08/10] fsnotify: use path_clone() Mateusz Guzik
@ 2026-09-14  8:36   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:36 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:15, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

This looks good to me. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/notify/fanotify/fanotify.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> index a208a7ec1692..84b4c505dbe3 100644
> --- a/fs/notify/fanotify/fanotify.c
> +++ b/fs/notify/fanotify/fanotify.c
> @@ -559,9 +559,8 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
>  		return NULL;
>  
>  	pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH;
> -	pevent->path = *path;
>  	*hash ^= fanotify_hash_path(path);
> -	path_get(path);
> +	path_clone(path, &pevent->path);
>  
>  	return &pevent->fae;
>  }
> @@ -600,10 +599,9 @@ static struct fanotify_event *fanotify_alloc_perm_event(const void *data,
>  	pevent->hdr.len = 0;
>  	pevent->state = FAN_EVENT_INIT;
>  	pevent->watchdog_cnt = 0;
> -	pevent->path = *path;
>  	pevent->pos = range ? range->pos : FANOTIFY_NO_RANGE;
>  	pevent->count = range ? range->count : 0;
> -	path_get(path);
> +	path_clone(path, &pevent->path);
>  
>  	return &pevent->fae;
>  }
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 00/10] Towards safer path_* API
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (9 preceding siblings ...)
  2026-09-13 14:49 ` [PATCH 10/10] nfsd: " Mateusz Guzik
@ 2026-09-14  8:53 ` Jan Kara
  2026-09-14 23:36 ` NeilBrown
  11 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:53 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:07, Mateusz Guzik wrote:
> Currently the handling is highly error-prone with consumers allowed to
> arbitrarily manipulate references, notably with path_get().
> 
> In the past this resulted in preventable bugs.
> 
> The end goal, not yet achieved with this patchset, will provide an
> invariant that a populated 'struct path' has precisely one set of
> references on it.
> 
> While consumers can still cause leaks by neglecting to path_put(), the
> footgun of path_get() can be eliminated.
> 
> With this patchset instead of hand-rolling a path copy and issuing
> path_get() on it, callers can resort to path_clone() instead.
> 
> Almost all path_get() consumers get converted, most of which with
> coccinelle.
> 
> Few instances are left until I figure out a nice way to sort them out.
> 
> Overall almost entirety of this patchset is a NOP.

I like most of the changes. Just the changes to path_init() kind of leave a
bad taste because they leave struct path assignment there for the RCU
lookup mode. But I don't see a nicer way of dealing with it.

								Honza

> 
> Mateusz Guzik (10):
>   fs: unexport backing_file_set_user_path() and make it take the ref on
>     its own
>   fs: add path_create(), path_move() and path_clone()
>   fs: use path_clone() in path_init()
>   fs: use path_clone() and path_move() in vfs_open*
>   fs: use path_clone() in backing_file_set_user_path()
>   Coccinelle-based conversion of path_* consumers to use the new
>     primitives
>   autofs: use path_create()
>   fsnotify: use path_clone()
>   proc: use path_clone()
>   nfsd: use path_clone()
> 
>  drivers/block/loop.c               |  3 +-
>  fs/autofs/dev-ioctl.c              |  3 +-
>  fs/autofs/expire.c                 |  4 +--
>  fs/backing-file.c                  |  2 --
>  fs/devpts/inode.c                  |  6 ++--
>  fs/failfs.c                        |  3 +-
>  fs/fhandle.c                       |  3 +-
>  fs/file_attr.c                     |  6 ++--
>  fs/file_table.c                    |  3 +-
>  fs/fs_struct.c                     |  6 ++--
>  fs/namei.c                         | 44 ++++++++++++++++++++++++------
>  fs/namespace.c                     |  3 +-
>  fs/nfsd/export.c                   |  6 ++--
>  fs/nfsd/nfs4xdr.c                  |  8 +++---
>  fs/notify/fanotify/fanotify.c      |  6 ++--
>  fs/notify/fanotify/fanotify_user.c |  3 +-
>  fs/nsfs.c                          |  3 +-
>  fs/open.c                          | 10 ++-----
>  fs/overlayfs/params.c              |  3 +-
>  fs/pidfs.c                         |  3 +-
>  fs/proc/base.c                     |  6 ++--
>  fs/proc/fd.c                       |  3 +-
>  fs/smb/server/vfs.c                |  3 +-
>  fs/xfs/xfs_handle.c                |  3 +-
>  include/linux/path.h               | 14 ++++++++--
>  kernel/trace/bpf_trace.c           |  3 +-
>  security/apparmor/task.c           |  3 +-
>  security/keys/big_key.c            |  3 +-
>  security/landlock/fs.c             |  3 +-
>  security/landlock/syscalls.c       |  3 +-
>  30 files changed, 87 insertions(+), 85 deletions(-)
> 
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open*
  2026-09-13 14:49 ` [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open* Mateusz Guzik
@ 2026-09-14  8:56   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:56 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:11, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/open.c | 7 ++-----
>  1 file changed, 2 insertions(+), 5 deletions(-)
> 
> diff --git a/fs/open.c b/fs/open.c
> index a84b55301719..e11d1342ff74 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -1102,8 +1102,7 @@ int vfs_open(const struct path *path, struct file *file)
>  {
>  	int ret;
>  
> -	file->__f_path = *path;
> -	path_get(&file->f_path);
> +	path_clone(path, &file->__f_path);
>  	ret = do_dentry_open(file, NULL);
>  	if (!ret) {
>  		/*
> @@ -1125,9 +1124,7 @@ int vfs_open_consume(struct path *path, struct file *file)
>  {
>  	int ret;
>  
> -	file->__f_path = *path;
> -	path->mnt = NULL;
> -	path->dentry = NULL;
> +	path_move(path, &file->__f_path);
>  	ret = do_dentry_open(file, NULL);
>  	if (!ret) {
>  		fsnotify_open(file);
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path()
  2026-09-13 14:49 ` [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path() Mateusz Guzik
@ 2026-09-14  8:57   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:57 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:12, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/file_table.c | 3 +--
>  1 file changed, 1 insertion(+), 2 deletions(-)
> 
> diff --git a/fs/file_table.c b/fs/file_table.c
> index f9d1b5edd6ae..0dca0c6c991f 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -70,8 +70,7 @@ EXPORT_SYMBOL_GPL(backing_file_user_path);
>  
>  void backing_file_set_user_path(struct file *f, const struct path *path)
>  {
> -	backing_file(f)->user_path = *path;
> -	path_get(path);
> +	path_clone(path, &backing_file(f)->user_path);
>  }
>  
>  #ifdef CONFIG_SECURITY
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives
  2026-09-13 14:49 ` [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives Mateusz Guzik
@ 2026-09-14  8:57   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:57 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:13, Mateusz Guzik wrote:
> No functional changes.
> 
> The script:
> @@
> expression src, dst;
> @@
> 
> - *dst = *src;
> - path_get(dst);
> + path_clone(src, dst);
> 
> @@
> expression src, dst;
> @@
> 
> - *dst = src;
> - path_get(dst);
> + path_clone(&src, dst);
> 
> @@
> expression src, dst;
> @@
> 
> - dst = src;
> - path_get(&dst);
> + path_clone(&src, &dst);
> 
> @@
> expression src, dst;
> @@
> 
> - *dst = src;
> - path_get(src);
> + path_clone(src, dst);
> 
> @@
> expression src, dst;
> @@
> 
> - path_get(&src);
> - *dst = src;
> + path_clone(&src, dst);
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Looks good to me. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  drivers/block/loop.c               | 3 +--
>  fs/autofs/dev-ioctl.c              | 3 +--
>  fs/devpts/inode.c                  | 6 ++----
>  fs/failfs.c                        | 3 +--
>  fs/fhandle.c                       | 3 +--
>  fs/file_attr.c                     | 6 ++----
>  fs/fs_struct.c                     | 6 ++----
>  fs/namei.c                         | 3 +--
>  fs/namespace.c                     | 3 +--
>  fs/notify/fanotify/fanotify_user.c | 3 +--
>  fs/nsfs.c                          | 3 +--
>  fs/open.c                          | 3 +--
>  fs/overlayfs/params.c              | 3 +--
>  fs/pidfs.c                         | 3 +--
>  fs/proc/base.c                     | 3 +--
>  fs/smb/server/vfs.c                | 3 +--
>  fs/xfs/xfs_handle.c                | 3 +--
>  kernel/trace/bpf_trace.c           | 3 +--
>  security/apparmor/task.c           | 3 +--
>  security/keys/big_key.c            | 3 +--
>  security/landlock/fs.c             | 3 +--
>  security/landlock/syscalls.c       | 3 +--
>  22 files changed, 25 insertions(+), 50 deletions(-)
> 
> diff --git a/drivers/block/loop.c b/drivers/block/loop.c
> index 758c20678bf6..cc5c11c57989 100644
> --- a/drivers/block/loop.c
> +++ b/drivers/block/loop.c
> @@ -1321,8 +1321,7 @@ loop_get_status(struct loop_device *lo, struct loop_info64 *info)
>  	memcpy(info->lo_file_name, lo->lo_file_name, LO_NAME_SIZE);
>  
>  	/* Drop lo_mutex while we call into the filesystem. */
> -	path = lo->lo_backing_file->f_path;
> -	path_get(&path);
> +	path_clone(&lo->lo_backing_file->f_path, &path);
>  	mutex_unlock(&lo->lo_mutex);
>  	ret = vfs_getattr(&path, &stat, STATX_INO, AT_STATX_SYNC_AS_STAT);
>  	if (!ret) {
> diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c
> index 6743b3b64217..a2ec9659297f 100644
> --- a/fs/autofs/dev-ioctl.c
> +++ b/fs/autofs/dev-ioctl.c
> @@ -200,8 +200,7 @@ static int find_autofs_mount(const char *pathname,
>  	while (path.dentry == path.mnt->mnt_root) {
>  		if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
>  			if (test(&path, data)) {
> -				path_get(&path);
> -				*res = path;
> +				path_clone(&path, res);
>  				err = 0;
>  				break;
>  			}
> diff --git a/fs/devpts/inode.c b/fs/devpts/inode.c
> index 9844dcf354ee..2b5d273edaa7 100644
> --- a/fs/devpts/inode.c
> +++ b/fs/devpts/inode.c
> @@ -152,8 +152,7 @@ struct vfsmount *devpts_mntget(struct file *filp, struct pts_fs_info *fsi)
>  	struct path path;
>  	int err = 0;
>  
> -	path = filp->f_path;
> -	path_get(&path);
> +	path_clone(&filp->f_path, &path);
>  
>  	/* Walk upward while the start point is a bind mount of
>  	 * a single file.
> @@ -184,8 +183,7 @@ struct pts_fs_info *devpts_acquire(struct file *filp)
>  	struct path path;
>  	struct super_block *sb;
>  
> -	path = filp->f_path;
> -	path_get(&path);
> +	path_clone(&filp->f_path, &path);
>  
>  	/* Has the devpts filesystem already been found? */
>  	if (path.mnt->mnt_sb->s_magic != DEVPTS_SUPER_MAGIC) {
> diff --git a/fs/failfs.c b/fs/failfs.c
> index 66a36da3d236..6459163de5b8 100644
> --- a/fs/failfs.c
> +++ b/fs/failfs.c
> @@ -13,8 +13,7 @@ static struct path failfs_root_path = {};
>  
>  void failfs_get_root(struct path *path)
>  {
> -	*path = failfs_root_path;
> -	path_get(path);
> +	path_clone(&failfs_root_path, path);
>  }
>  
>  bool failfs_mnt(const struct vfsmount *mnt)
> diff --git a/fs/fhandle.c b/fs/fhandle.c
> index f8829231e3d7..5c29a0a36c57 100644
> --- a/fs/fhandle.c
> +++ b/fs/fhandle.c
> @@ -173,8 +173,7 @@ static int get_path_anchor(int fd, struct path *root)
>  		CLASS(fd, f)(fd);
>  		if (fd_empty(f))
>  			return -EBADF;
> -		*root = fd_file(f)->f_path;
> -		path_get(root);
> +		path_clone(&fd_file(f)->f_path, root);
>  		return 0;
>  	}
>  
> diff --git a/fs/file_attr.c b/fs/file_attr.c
> index bfb00d256dd5..0a46fe39dfc7 100644
> --- a/fs/file_attr.c
> +++ b/fs/file_attr.c
> @@ -402,8 +402,7 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
>  		if (fd_empty(f))
>  			return -EBADF;
>  
> -		filepath = fd_file(f)->f_path;
> -		path_get(&filepath);
> +		path_clone(&fd_file(f)->f_path, &filepath);
>  	} else {
>  		error = filename_lookup(dfd, name, lookup_flags, &filepath,
>  					NULL);
> @@ -464,8 +463,7 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
>  		if (fd_empty(f))
>  			return -EBADF;
>  
> -		filepath = fd_file(f)->f_path;
> -		path_get(&filepath);
> +		path_clone(&fd_file(f)->f_path, &filepath);
>  	} else {
>  		error = filename_lookup(dfd, name, lookup_flags, &filepath,
>  					NULL);
> diff --git a/fs/fs_struct.c b/fs/fs_struct.c
> index 34699f3b6f88..330a15516787 100644
> --- a/fs/fs_struct.c
> +++ b/fs/fs_struct.c
> @@ -120,10 +120,8 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old)
>  		fs->umask = old->umask;
>  
>  		read_seqlock_excl(&old->seq);
> -		fs->root = old->root;
> -		path_get(&fs->root);
> -		fs->pwd = old->pwd;
> -		path_get(&fs->pwd);
> +		path_clone(&old->root, &fs->root);
> +		path_clone(&old->pwd, &fs->pwd);
>  		read_sequnlock_excl(&old->seq);
>  	}
>  	return fs;
> diff --git a/fs/namei.c b/fs/namei.c
> index 11937cfa8c7f..44fd82ee45c2 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -1182,8 +1182,7 @@ static int nd_jump_root(struct nameidata *nd)
>  			return -ECHILD;
>  	} else {
>  		path_put(&nd->path);
> -		nd->path = nd->root;
> -		path_get(&nd->path);
> +		path_clone(&nd->root, &nd->path);
>  		nd->inode = nd->path.dentry->d_inode;
>  	}
>  	nd->state |= ND_JUMPED;
> diff --git a/fs/namespace.c b/fs/namespace.c
> index a36ea2cc733d..79f785fabfdc 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -4614,8 +4614,7 @@ SYSCALL_DEFINE5(move_mount,
>  		if (fd_empty(f_to))
>  			return -EBADF;
>  
> -		to_path = fd_file(f_to)->f_path;
> -		path_get(&to_path);
> +		path_clone(&fd_file(f_to)->f_path, &to_path);
>  	} else {
>  		lflags = 0;
>  		if (flags & MOVE_MOUNT_T_SYMLINKS)
> diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
> index 63c9759fc3b0..3c49329543d9 100644
> --- a/fs/notify/fanotify/fanotify_user.c
> +++ b/fs/notify/fanotify/fanotify_user.c
> @@ -1202,8 +1202,7 @@ static int fanotify_find_path(int dfd, const char __user *filename,
>  		    !(S_ISDIR(file_inode(fd_file(f))->i_mode)))
>  			return -ENOTDIR;
>  
> -		*path = fd_file(f)->f_path;
> -		path_get(path);
> +		path_clone(&fd_file(f)->f_path, path);
>  		ret = 0;
>  	} else {
>  		unsigned int lookup_flags = 0;
> diff --git a/fs/nsfs.c b/fs/nsfs.c
> index c3b6ae76594a..577c9edcea32 100644
> --- a/fs/nsfs.c
> +++ b/fs/nsfs.c
> @@ -29,8 +29,7 @@ static struct path nsfs_root_path = {};
>  
>  void nsfs_get_root(struct path *path)
>  {
> -	*path = nsfs_root_path;
> -	path_get(path);
> +	path_clone(&nsfs_root_path, path);
>  }
>  
>  static long ns_ioctl(struct file *filp, unsigned int ioctl,
> diff --git a/fs/open.c b/fs/open.c
> index e11d1342ff74..fc602c44b3bc 100644
> --- a/fs/open.c
> +++ b/fs/open.c
> @@ -653,8 +653,7 @@ SYSCALL_DEFINE2(fchroot, int, fd, unsigned int, flags)
>  		if (!ns_capable(current_user_ns(), CAP_SYS_CHROOT))
>  			return -EPERM;
>  
> -		path = fd_file(f)->f_path;
> -		path_get(&path);
> +		path_clone(&fd_file(f)->f_path, &path);
>  	}
>  
>  	error = security_path_chroot(&path);
> diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
> index c93fcaa45d4a..0c72e18dd625 100644
> --- a/fs/overlayfs/params.c
> +++ b/fs/overlayfs/params.c
> @@ -474,8 +474,7 @@ static int ovl_parse_layer(struct fs_context *fc, struct fs_parameter *param,
>  		if (!buf)
>  			return -ENOMEM;
>  
> -		layer_path = param->file->f_path;
> -		path_get(&layer_path);
> +		path_clone(&param->file->f_path, &layer_path);
>  
>  		layer_name = d_path(&layer_path, buf, PATH_MAX);
>  		if (IS_ERR(layer_name))
> diff --git a/fs/pidfs.c b/fs/pidfs.c
> index a6a643f15d08..c70a997376bd 100644
> --- a/fs/pidfs.c
> +++ b/fs/pidfs.c
> @@ -42,8 +42,7 @@ static struct simple_xattr_cache pidfs_xa_cache;
>  
>  void pidfs_get_root(struct path *path)
>  {
> -	*path = pidfs_root_path;
> -	path_get(path);
> +	path_clone(&pidfs_root_path, path);
>  }
>  
>  enum pidfs_attr_mask_bits {
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 0f9efd25bb05..01d8bfe1c410 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -2253,8 +2253,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path,
>  	rc = -ENOENT;
>  	vma = find_exact_vma(mm, vm_start, vm_end);
>  	if (vma && vma->vm_file) {
> -		*path = *file_user_path(vma->vm_file);
> -		path_get(path);
> +		path_clone(file_user_path(vma->vm_file), path);
>  		rc = 0;
>  	}
>  	mmap_read_unlock(mm);
> diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
> index 3a6f3139c6f5..c81b97a7356a 100644
> --- a/fs/smb/server/vfs.c
> +++ b/fs/smb/server/vfs.c
> @@ -1349,8 +1349,7 @@ int __ksmbd_vfs_kern_path(struct ksmbd_work *work, char *filepath,
>  	path_len = strlen(filepath);
>  	remain_len = path_len;
>  
> -	parent_path = share_conf->vfs_path;
> -	path_get(&parent_path);
> +	path_clone(&share_conf->vfs_path, &parent_path);
>  
>  	while (d_can_lookup(parent_path.dentry)) {
>  		char *filename = filepath + path_len - remain_len;
> diff --git a/fs/xfs/xfs_handle.c b/fs/xfs/xfs_handle.c
> index 0689cade8f74..7e3ddf2967c6 100644
> --- a/fs/xfs/xfs_handle.c
> +++ b/fs/xfs/xfs_handle.c
> @@ -94,8 +94,7 @@ xfs_find_handle(
>  
>  		if (fd_empty(f))
>  			return -EBADF;
> -		path = fd_file(f)->f_path;
> -		path_get(&path);
> +		path_clone(&fd_file(f)->f_path, &path);
>  	} else {
>  		error = user_path_at(AT_FDCWD, hreq->path, 0, &path);
>  		if (error)
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 29260951aa87..ebe957432213 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -3233,8 +3233,7 @@ static int bpf_uprobe_multi_get_path(const union bpf_attr *attr, struct path *pa
>  		CLASS(fd, f)(path_fd);
>  		if (fd_empty(f))
>  			return -EBADF;
> -		*path = fd_file(f)->f_path;
> -		path_get(path);
> +		path_clone(&fd_file(f)->f_path, path);
>  		return 0;
>  	}
>  
> diff --git a/security/apparmor/task.c b/security/apparmor/task.c
> index e16ff4130bc2..36ca424760e2 100644
> --- a/security/apparmor/task.c
> +++ b/security/apparmor/task.c
> @@ -337,8 +337,7 @@ static const char *get_current_exe_path(char *buffer, int buffer_size)
>  	exe_file = get_task_exe_file(current);
>  	if (!exe_file)
>  		return ERR_PTR(-ENOENT);
> -	p = exe_file->f_path;
> -	path_get(&p);
> +	path_clone(&exe_file->f_path, &p);
>  
>  	if (aa_path_name(&p, FLAG_VIEW_SUBNS, buffer, &path_str, NULL, NULL))
>  		path_str = ERR_PTR(-ENOMEM);
> diff --git a/security/keys/big_key.c b/security/keys/big_key.c
> index 268f702df380..e20de015c626 100644
> --- a/security/keys/big_key.c
> +++ b/security/keys/big_key.c
> @@ -121,8 +121,7 @@ int big_key_preparse(struct key_preparsed_payload *prep)
>  		 * later
>  		 */
>  		payload->data = enckey;
> -		payload->path = file->f_path;
> -		path_get(&payload->path);
> +		path_clone(&file->f_path, &payload->path);
>  		fput(file);
>  		kvfree_sensitive(buf, enclen);
>  	} else {
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 330a1871bf94..3c614c56e3f8 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -892,8 +892,7 @@ is_access_to_paths_allowed(const struct landlock_domain *const domain,
>  		child2_is_directory = d_is_dir(dentry_child2);
>  	}
>  
> -	walker_path = *path;
> -	path_get(&walker_path);
> +	path_clone(&*path, &walker_path);
>  	/*
>  	 * We need to walk through all the hierarchy to not miss any relevant
>  	 * restriction.
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 1d02d57f4c48..003f4aa12e4b 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -349,8 +349,7 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
>  	    IS_PRIVATE(d_backing_inode(fd_file(f)->f_path.dentry)))
>  		return -EBADFD;
>  
> -	*path = fd_file(f)->f_path;
> -	path_get(path);
> +	path_clone(&fd_file(f)->f_path, path);
>  	return 0;
>  }
>  
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 07/10] autofs: use path_create()
  2026-09-13 14:49 ` [PATCH 07/10] autofs: use path_create() Mateusz Guzik
@ 2026-09-14  8:58   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:58 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:14, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Looks good to me. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/autofs/expire.c | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
> index 5c2d459e1e48..9d19c47dc3cc 100644
> --- a/fs/autofs/expire.c
> +++ b/fs/autofs/expire.c
> @@ -30,12 +30,12 @@ static int autofs_mount_busy(struct vfsmount *mnt,
>  			     struct dentry *dentry, unsigned int how)
>  {
>  	struct dentry *top = dentry;
> -	struct path path = {.mnt = mnt, .dentry = dentry};
> +	struct path path;
>  	int status = 1;
>  
>  	pr_debug("dentry %p %pd\n", dentry, dentry);
>  
> -	path_get(&path);
> +	path_create(&path, mnt, dentry);
>  
>  	if (!follow_down_one(&path))
>  		goto done;
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 09/10] proc: use path_clone()
  2026-09-13 14:49 ` [PATCH 09/10] proc: " Mateusz Guzik
@ 2026-09-14  8:58   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  8:58 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:16, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/proc/base.c | 3 +--
>  fs/proc/fd.c   | 3 +--
>  2 files changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/proc/base.c b/fs/proc/base.c
> index 01d8bfe1c410..6b055352eabd 100644
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -1766,8 +1766,7 @@ static int proc_exe_link(struct dentry *dentry, struct path *exe_path,
>  
>  	exe_file = get_task_exe_file(task);
>  	if (exe_file) {
> -		*exe_path = exe_file->f_path;
> -		path_get(&exe_file->f_path);
> +		path_clone(&exe_file->f_path, exe_path);
>  		fput(exe_file);
>  		return 0;
>  	} else
> diff --git a/fs/proc/fd.c b/fs/proc/fd.c
> index 0f9a1556f2a3..9ab9bfd6e48d 100644
> --- a/fs/proc/fd.c
> +++ b/fs/proc/fd.c
> @@ -180,8 +180,7 @@ static int proc_fd_link(struct dentry *dentry, struct path *path,
>  
>  	fd_file = fget_task(task, fd);
>  	if (fd_file) {
> -		*path = fd_file->f_path;
> -		path_get(&fd_file->f_path);
> +		path_clone(&fd_file->f_path, path);
>  		ret = 0;
>  		fput(fd_file);
>  	}
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 10/10] nfsd: use path_clone()
  2026-09-13 14:49 ` [PATCH 10/10] nfsd: " Mateusz Guzik
@ 2026-09-14  9:01   ` Jan Kara
  0 siblings, 0 replies; 27+ messages in thread
From: Jan Kara @ 2026-09-14  9:01 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Sun 13-09-26 16:49:17, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

It would be good to CC NFSD folks on this so they are aware this is
happening (along with at least the cover letter and the patch introducing
the helpers). But the change looks good to me. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/nfsd/export.c  | 6 ++----
>  fs/nfsd/nfs4xdr.c | 8 ++++----
>  2 files changed, 6 insertions(+), 8 deletions(-)
> 
> diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
> index e5a0f1ababe6..84ea833816b5 100644
> --- a/fs/nfsd/export.c
> +++ b/fs/nfsd/export.c
> @@ -233,8 +233,7 @@ static inline void expkey_update(struct cache_head *cnew,
>  	struct svc_expkey *new = container_of(cnew, struct svc_expkey, h);
>  	struct svc_expkey *item = container_of(citem, struct svc_expkey, h);
>  
> -	new->ek_path = item->ek_path;
> -	path_get(&item->ek_path);
> +	path_clone(&item->ek_path, &new->ek_path);
>  }
>  
>  static struct cache_head *expkey_alloc(void)
> @@ -1533,8 +1532,7 @@ static void svc_export_init(struct cache_head *cnew, struct cache_head *citem)
>  
>  	kref_get(&item->ex_client->ref);
>  	new->ex_client = item->ex_client;
> -	new->ex_path = item->ex_path;
> -	path_get(&item->ex_path);
> +	path_clone(&item->ex_path, &new->ex_path);
>  	new->ex_fslocs.locations = NULL;
>  	new->ex_fslocs.locations_count = 0;
>  	new->ex_fslocs.migrated = 0;
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 7d1b2d6f57f2..65a805501b75 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -2966,14 +2966,14 @@ static __be32 nfsd4_encode_pathname4(struct xdr_stream *xdr,
>  				     const struct path *root,
>  				     const struct path *path)
>  {
> -	struct path cur = *path;
> +	struct path cur;
>  	struct dentry **components = NULL;
>  	unsigned int ncomponents = 0;
>  	__be32 err = nfserr_jukebox;
>  
>  	dprintk("nfsd4_encode_components(");
>  
> -	path_get(&cur);
> +	path_clone(path, &cur);
>  	/* First walk the path up to the nfsd root, and store the
>  	 * dentries/path components in an array.
>  	 */
> @@ -3216,11 +3216,11 @@ static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *bmval2, u32
>  
>  static int nfsd4_get_mounted_on_ino(struct svc_export *exp, u64 *pino)
>  {
> -	struct path path = exp->ex_path;
> +	struct path path;
>  	struct kstat stat;
>  	int err;
>  
> -	path_get(&path);
> +	path_clone(&exp->ex_path, &path);
>  	while (follow_up(&path)) {
>  		if (path.dentry != path.mnt->mnt_root)
>  			break;
> -- 
> 2.53.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

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

* Re: [PATCH 00/10] Towards safer path_* API
  2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
                   ` (10 preceding siblings ...)
  2026-09-14  8:53 ` [PATCH 00/10] Towards safer path_* API Jan Kara
@ 2026-09-14 23:36 ` NeilBrown
  11 siblings, 0 replies; 27+ messages in thread
From: NeilBrown @ 2026-09-14 23:36 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Mon, 14 Sep 2026, Mateusz Guzik wrote:
> Currently the handling is highly error-prone with consumers allowed to
> arbitrarily manipulate references, notably with path_get().
> 
> In the past this resulted in preventable bugs.

Can you provide a reference to one or two of these bugs.  That would be
useful context.  Thanks.

NeilBrown


> 
> The end goal, not yet achieved with this patchset, will provide an
> invariant that a populated 'struct path' has precisely one set of
> references on it.
> 
> While consumers can still cause leaks by neglecting to path_put(), the
> footgun of path_get() can be eliminated.
> 
> With this patchset instead of hand-rolling a path copy and issuing
> path_get() on it, callers can resort to path_clone() instead.
> 
> Almost all path_get() consumers get converted, most of which with
> coccinelle.
> 
> Few instances are left until I figure out a nice way to sort them out.
> 
> Overall almost entirety of this patchset is a NOP.
> 
> Mateusz Guzik (10):
>   fs: unexport backing_file_set_user_path() and make it take the ref on
>     its own
>   fs: add path_create(), path_move() and path_clone()
>   fs: use path_clone() in path_init()
>   fs: use path_clone() and path_move() in vfs_open*
>   fs: use path_clone() in backing_file_set_user_path()
>   Coccinelle-based conversion of path_* consumers to use the new
>     primitives
>   autofs: use path_create()
>   fsnotify: use path_clone()
>   proc: use path_clone()
>   nfsd: use path_clone()
> 
>  drivers/block/loop.c               |  3 +-
>  fs/autofs/dev-ioctl.c              |  3 +-
>  fs/autofs/expire.c                 |  4 +--
>  fs/backing-file.c                  |  2 --
>  fs/devpts/inode.c                  |  6 ++--
>  fs/failfs.c                        |  3 +-
>  fs/fhandle.c                       |  3 +-
>  fs/file_attr.c                     |  6 ++--
>  fs/file_table.c                    |  3 +-
>  fs/fs_struct.c                     |  6 ++--
>  fs/namei.c                         | 44 ++++++++++++++++++++++++------
>  fs/namespace.c                     |  3 +-
>  fs/nfsd/export.c                   |  6 ++--
>  fs/nfsd/nfs4xdr.c                  |  8 +++---
>  fs/notify/fanotify/fanotify.c      |  6 ++--
>  fs/notify/fanotify/fanotify_user.c |  3 +-
>  fs/nsfs.c                          |  3 +-
>  fs/open.c                          | 10 ++-----
>  fs/overlayfs/params.c              |  3 +-
>  fs/pidfs.c                         |  3 +-
>  fs/proc/base.c                     |  6 ++--
>  fs/proc/fd.c                       |  3 +-
>  fs/smb/server/vfs.c                |  3 +-
>  fs/xfs/xfs_handle.c                |  3 +-
>  include/linux/path.h               | 14 ++++++++--
>  kernel/trace/bpf_trace.c           |  3 +-
>  security/apparmor/task.c           |  3 +-
>  security/keys/big_key.c            |  3 +-
>  security/landlock/fs.c             |  3 +-
>  security/landlock/syscalls.c       |  3 +-
>  30 files changed, 87 insertions(+), 85 deletions(-)
> 
> -- 
> 2.53.0
> 
> 
> 


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

* Re: [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own
  2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
  2026-09-14  8:33   ` Jan Kara
@ 2026-09-14 23:41   ` NeilBrown
  1 sibling, 0 replies; 27+ messages in thread
From: NeilBrown @ 2026-09-14 23:41 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Mon, 14 Sep 2026, Mateusz Guzik wrote:
> Prep for path_* API changes, which will force the ref change.
> 
> There are likely no users outside of the tree, but should there be some they
> will no longer be caught by surprise.

It is encouraged to update Documentation/filesystems/porting.rst which
making externally visible changed to the VFS api.

It really looks like backing_file_set_user_path() should never have been
exported, and your change is a clear win.

Thanks,
NeilBrown


> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>  fs/backing-file.c | 2 --
>  fs/file_table.c   | 2 +-
>  2 files changed, 1 insertion(+), 3 deletions(-)
> 
> diff --git a/fs/backing-file.c b/fs/backing-file.c
> index cc101143f921..e92410a27982 100644
> --- a/fs/backing-file.c
> +++ b/fs/backing-file.c
> @@ -43,7 +43,6 @@ struct file *backing_file_open(const struct file *user_file, int flags,
>  	if (IS_ERR(f))
>  		return f;
>  
> -	path_get(user_path);
>  	backing_file_set_user_path(f, user_path);
>  	error = vfs_open(real_path, f);
>  	if (error) {
> @@ -68,7 +67,6 @@ struct file *backing_tmpfile_open(const struct file *user_file, int flags,
>  	if (IS_ERR(f))
>  		return f;
>  
> -	path_get(user_path);
>  	backing_file_set_user_path(f, user_path);
>  	error = vfs_tmpfile(real_idmap, real_parentpath, f, mode);
>  	if (error) {
> diff --git a/fs/file_table.c b/fs/file_table.c
> index 8dcd213c0251..f9d1b5edd6ae 100644
> --- a/fs/file_table.c
> +++ b/fs/file_table.c
> @@ -71,8 +71,8 @@ EXPORT_SYMBOL_GPL(backing_file_user_path);
>  void backing_file_set_user_path(struct file *f, const struct path *path)
>  {
>  	backing_file(f)->user_path = *path;
> +	path_get(path);
>  }
> -EXPORT_SYMBOL_GPL(backing_file_set_user_path);
>  
>  #ifdef CONFIG_SECURITY
>  void *backing_file_security(const struct file *f)
> -- 
> 2.53.0
> 
> 
> 


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

* Re: [PATCH 02/10] fs: add path_create(), path_move() and path_clone()
  2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
  2026-09-13 14:51   ` Mateusz Guzik
@ 2026-09-14 23:46   ` NeilBrown
  2026-09-15  0:05   ` NeilBrown
  2 siblings, 0 replies; 27+ messages in thread
From: NeilBrown @ 2026-09-14 23:46 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Mon, 14 Sep 2026, Mateusz Guzik wrote:

A few more words here wouldn't hurt.
Maybe just "These helpers encapsulate a common pattern and using them
will make the code easier to read".

When there are no words I have to guess the purpose myself and I am
terrible at guessing the thoughts of others.

Also I would probably make these static-inline, but maybe that isn't
important.

NeilBrown


> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>  fs/namei.c           | 29 +++++++++++++++++++++++++++++
>  include/linux/path.h | 14 ++++++++++++--
>  2 files changed, 41 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index ca4f5e3be99a..c8754c7ba8be 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -698,6 +698,35 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
>  	return security_inode_permission(inode, mask);
>  }
>  
> +/**
> + * path_create - WRITEME
> + * @path: path to get the reference to
> + * @mnt: WRITEME
> + * @dentry: WRITEME
> + *
> + * Create a path object using the given vfsmount and dentry pair while incrementing
> + * the reference count on both.
> + */
> +void path_create(struct path *path, struct vfsmount *mnt, struct dentry *dentry)
> +{
> +	path->mnt = mntget(mnt);
> +	path->dentry = dget(dentry);
> +}
> +EXPORT_SYMBOL(path_create);
> +
> +/**
> + * path_clone - WRITEME
> + * @path: path to get the reference to
> + *
> + * Given a path increment the reference count to the dentry and the vfsmount.
> + */
> +void path_clone(const struct path *src, struct path *dst)
> +{
> +	dst->mnt = mntget(src->mnt);
> +	dst->dentry = dget(src->dentry);
> +}
> +EXPORT_SYMBOL(path_clone);
> +
>  /**
>   * path_get - get a reference to a path
>   * @path: path to get the reference to
> diff --git a/include/linux/path.h b/include/linux/path.h
> index 7ea389dc764b..8a621e0e1921 100644
> --- a/include/linux/path.h
> +++ b/include/linux/path.h
> @@ -10,8 +10,18 @@ struct path {
>  	struct dentry *dentry;
>  } __randomize_layout;
>  
> -extern void path_get(const struct path *);
> -extern void path_put(const struct path *);
> +void path_create(struct path *, struct vfsmount *, struct dentry *);
> +void path_clone(const struct path *, struct path *);
> +void path_get(const struct path *);
> +void path_put(const struct path *);
> +
> +static inline void path_move(struct path *src, struct path *dst)
> +{
> +	dst->mnt = src->mnt;
> +	dst->dentry = src->dentry;
> +	src->mnt = NULL;
> +	src->dentry = NULL;
> +}
>  
>  static inline int path_equal(const struct path *path1, const struct path *path2)
>  {
> -- 
> 2.53.0
> 
> 
> 


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

* Re: [PATCH 03/10] fs: use path_clone() in path_init()
  2026-09-13 14:49 ` [PATCH 03/10] fs: use path_clone() in path_init() Mateusz Guzik
@ 2026-09-14 23:52   ` NeilBrown
  0 siblings, 0 replies; 27+ messages in thread
From: NeilBrown @ 2026-09-14 23:52 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Mon, 14 Sep 2026, Mateusz Guzik wrote:
> No functional changes.
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>  fs/namei.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index c8754c7ba8be..11937cfa8c7f 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2745,13 +2745,13 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
>  		struct inode *inode = root->d_inode;
>  		if (*s && unlikely(!d_can_lookup(root)))
>  			return ERR_PTR(-ENOTDIR);
> -		nd->path = nd->root;
>  		nd->inode = inode;
>  		if (flags & LOOKUP_RCU) {
> +			nd->path = nd->root;
>  			nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
>  			nd->root_seq = nd->seq;
>  		} else {
> -			path_get(&nd->path);
> +			path_clone(&nd->root, &nd->path);

If path_clone() we inline, the compiler possibly detect that both
branches of this if had the same assignment, and could move it before
the "if", restoring code size.

But I think the code does look a bit nicer this way.

NeilBrown


>  		}
>  		return s;
>  	}
> @@ -2801,23 +2801,23 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
>  		if (*s && unlikely(!d_can_lookup(dentry)))
>  			return ERR_PTR(-ENOTDIR);
>  
> -		nd->path = fd_file(f)->f_path;
>  		if (flags & LOOKUP_RCU) {
> +			nd->path = fd_file(f)->f_path;
>  			nd->inode = nd->path.dentry->d_inode;
>  			nd->seq = read_seqcount_begin(&nd->path.dentry->d_seq);
>  		} else {
> -			path_get(&nd->path);
> +			path_clone(&fd_file(f)->f_path, &nd->path);
>  			nd->inode = nd->path.dentry->d_inode;
>  		}
>  	}
>  
>  	/* For scoped-lookups we need to set the root to the dirfd as well. */
>  	if (unlikely(flags & LOOKUP_IS_SCOPED)) {
> -		nd->root = nd->path;
>  		if (flags & LOOKUP_RCU) {
> +			nd->root = nd->path;
>  			nd->root_seq = nd->seq;
>  		} else {
> -			path_get(&nd->root);
> +			path_clone(&nd->path, &nd->root);
>  			nd->state |= ND_ROOT_GRABBED;
>  		}
>  	}
> -- 
> 2.53.0
> 
> 
> 


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

* Re: [PATCH 02/10] fs: add path_create(), path_move() and path_clone()
  2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
  2026-09-13 14:51   ` Mateusz Guzik
  2026-09-14 23:46   ` NeilBrown
@ 2026-09-15  0:05   ` NeilBrown
  2026-09-15  0:10     ` NeilBrown
  2 siblings, 1 reply; 27+ messages in thread
From: NeilBrown @ 2026-09-15  0:05 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Mon, 14 Sep 2026, Mateusz Guzik wrote:
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>  fs/namei.c           | 29 +++++++++++++++++++++++++++++
>  include/linux/path.h | 14 ++++++++++++--
>  2 files changed, 41 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/namei.c b/fs/namei.c
> index ca4f5e3be99a..c8754c7ba8be 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -698,6 +698,35 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
>  	return security_inode_permission(inode, mask);
>  }
>  
> +/**
> + * path_create - WRITEME
> + * @path: path to get the reference to
> + * @mnt: WRITEME
> + * @dentry: WRITEME
> + *
> + * Create a path object using the given vfsmount and dentry pair while incrementing
> + * the reference count on both.
> + */
> +void path_create(struct path *path, struct vfsmount *mnt, struct dentry *dentry)
> +{
> +	path->mnt = mntget(mnt);
> +	path->dentry = dget(dentry);
> +}
> +EXPORT_SYMBOL(path_create);
> +
> +/**
> + * path_clone - WRITEME
> + * @path: path to get the reference to
> + *
> + * Given a path increment the reference count to the dentry and the vfsmount.
> + */
> +void path_clone(const struct path *src, struct path *dst)
> +{
> +	dst->mnt = mntget(src->mnt);
> +	dst->dentry = dget(src->dentry);
> +}
> +EXPORT_SYMBOL(path_clone);
> +
>  /**
>   * path_get - get a reference to a path
>   * @path: path to get the reference to
> diff --git a/include/linux/path.h b/include/linux/path.h
> index 7ea389dc764b..8a621e0e1921 100644
> --- a/include/linux/path.h
> +++ b/include/linux/path.h
> @@ -10,8 +10,18 @@ struct path {
>  	struct dentry *dentry;
>  } __randomize_layout;
>  
> -extern void path_get(const struct path *);
> -extern void path_put(const struct path *);
> +void path_create(struct path *, struct vfsmount *, struct dentry *);
> +void path_clone(const struct path *, struct path *);
> +void path_get(const struct path *);
> +void path_put(const struct path *);
> +
> +static inline void path_move(struct path *src, struct path *dst)

Oh ...  this is backwards.  At least is it backwards compared to
memmove().  I understand different people will read "move" differently
and would prefer to avoid it.
Can we do without it?  You only seem to use it in one place.

How would you feel about:

 struct path path_move_from(struct path *src)
 {
	struct path ret = *src;
	src->mnt = NULL;
	src->dentry = NULL;
	return ret;
 }

and then use

 file->__f_path = path_move_from(path);

??

NeilBrown


> +{
> +	dst->mnt = src->mnt;
> +	dst->dentry = src->dentry;
> +	src->mnt = NULL;
> +	src->dentry = NULL;
> +}
>  
>  static inline int path_equal(const struct path *path1, const struct path *path2)
>  {
> -- 
> 2.53.0
> 
> 
> 


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

* Re: [PATCH 02/10] fs: add path_create(), path_move() and path_clone()
  2026-09-15  0:05   ` NeilBrown
@ 2026-09-15  0:10     ` NeilBrown
  0 siblings, 0 replies; 27+ messages in thread
From: NeilBrown @ 2026-09-15  0:10 UTC (permalink / raw)
  To: Mateusz Guzik
  Cc: brauner, viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

On Tue, 15 Sep 2026, NeilBrown wrote:
> On Mon, 14 Sep 2026, Mateusz Guzik wrote:
> > Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> > ---
> >  fs/namei.c           | 29 +++++++++++++++++++++++++++++
> >  include/linux/path.h | 14 ++++++++++++--
> >  2 files changed, 41 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/namei.c b/fs/namei.c
> > index ca4f5e3be99a..c8754c7ba8be 100644
> > --- a/fs/namei.c
> > +++ b/fs/namei.c
> > @@ -698,6 +698,35 @@ static __always_inline int lookup_inode_permission_may_exec(struct mnt_idmap *id
> >  	return security_inode_permission(inode, mask);
> >  }
> >  
> > +/**
> > + * path_create - WRITEME
> > + * @path: path to get the reference to
> > + * @mnt: WRITEME
> > + * @dentry: WRITEME
> > + *
> > + * Create a path object using the given vfsmount and dentry pair while incrementing
> > + * the reference count on both.
> > + */
> > +void path_create(struct path *path, struct vfsmount *mnt, struct dentry *dentry)
> > +{
> > +	path->mnt = mntget(mnt);
> > +	path->dentry = dget(dentry);
> > +}
> > +EXPORT_SYMBOL(path_create);
> > +
> > +/**
> > + * path_clone - WRITEME
> > + * @path: path to get the reference to
> > + *
> > + * Given a path increment the reference count to the dentry and the vfsmount.
> > + */
> > +void path_clone(const struct path *src, struct path *dst)
> > +{
> > +	dst->mnt = mntget(src->mnt);
> > +	dst->dentry = dget(src->dentry);
> > +}
> > +EXPORT_SYMBOL(path_clone);
> > +
> >  /**
> >   * path_get - get a reference to a path
> >   * @path: path to get the reference to
> > diff --git a/include/linux/path.h b/include/linux/path.h
> > index 7ea389dc764b..8a621e0e1921 100644
> > --- a/include/linux/path.h
> > +++ b/include/linux/path.h
> > @@ -10,8 +10,18 @@ struct path {
> >  	struct dentry *dentry;
> >  } __randomize_layout;
> >  
> > -extern void path_get(const struct path *);
> > -extern void path_put(const struct path *);
> > +void path_create(struct path *, struct vfsmount *, struct dentry *);
> > +void path_clone(const struct path *, struct path *);
> > +void path_get(const struct path *);
> > +void path_put(const struct path *);
> > +
> > +static inline void path_move(struct path *src, struct path *dst)
> 
> Oh ...  this is backwards.  At least is it backwards compared to
> memmove().  I understand different people will read "move" differently
> and would prefer to avoid it.
> Can we do without it?  You only seem to use it in one place.
> 
> How would you feel about:
> 
>  struct path path_move_from(struct path *src)
>  {
> 	struct path ret = *src;
> 	src->mnt = NULL;
> 	src->dentry = NULL;
> 	return ret;
>  }
> 
> and then use
> 
>  file->__f_path = path_move_from(path);
> 
> ??

And then of course there is path_clone() which looks backwards to me.
Can we just change path_get() to return the path, like a lot of
FOO_get() functions do, and use
   newpath = path_clone(old_path);
??

Thanks,
NeilBrown


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

end of thread, other threads:[~2026-09-15  0:10 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-13 14:49 [PATCH 00/10] Towards safer path_* API Mateusz Guzik
2026-09-13 14:49 ` [PATCH 01/10] fs: unexport backing_file_set_user_path() and make it take the ref on its own Mateusz Guzik
2026-09-14  8:33   ` Jan Kara
2026-09-14 23:41   ` NeilBrown
2026-09-13 14:49 ` [PATCH 02/10] fs: add path_create(), path_move() and path_clone() Mateusz Guzik
2026-09-13 14:51   ` Mateusz Guzik
2026-09-14 23:46   ` NeilBrown
2026-09-15  0:05   ` NeilBrown
2026-09-15  0:10     ` NeilBrown
2026-09-13 14:49 ` [PATCH 03/10] fs: use path_clone() in path_init() Mateusz Guzik
2026-09-14 23:52   ` NeilBrown
2026-09-13 14:49 ` [PATCH 04/10] fs: use path_clone() and path_move() in vfs_open* Mateusz Guzik
2026-09-14  8:56   ` Jan Kara
2026-09-13 14:49 ` [PATCH 05/10] fs: use path_clone() in backing_file_set_user_path() Mateusz Guzik
2026-09-14  8:57   ` Jan Kara
2026-09-13 14:49 ` [PATCH 06/10] Coccinelle-based conversion of path_* consumers to use the new primitives Mateusz Guzik
2026-09-14  8:57   ` Jan Kara
2026-09-13 14:49 ` [PATCH 07/10] autofs: use path_create() Mateusz Guzik
2026-09-14  8:58   ` Jan Kara
2026-09-13 14:49 ` [PATCH 08/10] fsnotify: use path_clone() Mateusz Guzik
2026-09-14  8:36   ` Jan Kara
2026-09-13 14:49 ` [PATCH 09/10] proc: " Mateusz Guzik
2026-09-14  8:58   ` Jan Kara
2026-09-13 14:49 ` [PATCH 10/10] nfsd: " Mateusz Guzik
2026-09-14  9:01   ` Jan Kara
2026-09-14  8:53 ` [PATCH 00/10] Towards safer path_* API Jan Kara
2026-09-14 23:36 ` NeilBrown

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®