From: Yonghong Song <yonghong.song@linux.dev>
To: Song Liu <song@kernel.org>,
bpf@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org
Cc: kernel-team@meta.com, andrii@kernel.org, eddyz87@gmail.com,
ast@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev,
viro@zeniv.linux.org.uk, brauner@kernel.org, jack@suse.cz,
kpsingh@kernel.org, mattbobrowski@google.com, m@maowtm.org,
neil@brown.name
Subject: Re: [PATCH v5 bpf-next 1/5] namei: Introduce new helper function path_walk_parent()
Date: Fri, 4 Jul 2025 10:40:18 -0700 [thread overview]
Message-ID: <2459c10e-d74c-4118-9b6d-c37d05ecec02@linux.dev> (raw)
In-Reply-To: <20250617061116.3681325-2-song@kernel.org>
On 6/16/25 11:11 PM, Song Liu wrote:
> This helper walks an input path to its parent. Logic are added to handle
> walking across mount tree.
>
> This will be used by landlock, and BPF LSM.
>
> Suggested-by: Neil Brown <neil@brown.name>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> fs/namei.c | 95 +++++++++++++++++++++++++++++++++++--------
> include/linux/namei.h | 2 +
> 2 files changed, 79 insertions(+), 18 deletions(-)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 4bb889fc980b..d0557c0b5cc8 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2048,36 +2048,95 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd)
> return nd->path.dentry;
> }
>
> -static struct dentry *follow_dotdot(struct nameidata *nd)
> +/**
> + * __path_walk_parent - Find the parent of the given struct path
> + * @path - The struct path to start from
> + * @root - A struct path which serves as a boundary not to be crosses.
> + * - If @root is zero'ed, walk all the way to global root.
> + * @flags - Some LOOKUP_ flags.
> + *
> + * Find and return the dentry for the parent of the given path
> + * (mount/dentry). If the given path is the root of a mounted tree, it
> + * is first updated to the mount point on which that tree is mounted.
> + *
> + * If %LOOKUP_NO_XDEV is given, then *after* the path is updated to a new
> + * mount, the error EXDEV is returned.
> + *
> + * If no parent can be found, either because the tree is not mounted or
> + * because the @path matches the @root, then @path->dentry is returned
> + * unless @flags contains %LOOKUP_BENEATH, in which case -EXDEV is returned.
> + *
> + * Returns: either an ERR_PTR() or the chosen parent which will have had
> + * the refcount incremented.
> + */
> +static struct dentry *__path_walk_parent(struct path *path, const struct path *root, int flags)
> {
> - struct dentry *parent;
> -
> - if (path_equal(&nd->path, &nd->root))
> + if (path_equal(path, root))
> goto in_root;
> - if (unlikely(nd->path.dentry == nd->path.mnt->mnt_root)) {
> - struct path path;
> + if (unlikely(path->dentry == path->mnt->mnt_root)) {
> + struct path new_path;
>
> - if (!choose_mountpoint(real_mount(nd->path.mnt),
> - &nd->root, &path))
> + if (!choose_mountpoint(real_mount(path->mnt),
> + root, &new_path))
> goto in_root;
> - path_put(&nd->path);
> - nd->path = path;
> - nd->inode = path.dentry->d_inode;
> - if (unlikely(nd->flags & LOOKUP_NO_XDEV))
> + path_put(path);
> + *path = new_path;
> + if (unlikely(flags & LOOKUP_NO_XDEV))
> return ERR_PTR(-EXDEV);
> }
> /* rare case of legitimate dget_parent()... */
> - parent = dget_parent(nd->path.dentry);
> + return dget_parent(path->dentry);
I have some confusion with this patch when crossing mount boundary.
In d_path.c, we have
static int __prepend_path(const struct dentry *dentry, const struct mount *mnt,
const struct path *root, struct prepend_buffer *p)
{
while (dentry != root->dentry || &mnt->mnt != root->mnt) {
const struct dentry *parent = READ_ONCE(dentry->d_parent);
if (dentry == mnt->mnt.mnt_root) {
struct mount *m = READ_ONCE(mnt->mnt_parent);
struct mnt_namespace *mnt_ns;
if (likely(mnt != m)) {
dentry = READ_ONCE(mnt->mnt_mountpoint);
mnt = m;
continue;
}
/* Global root */
mnt_ns = READ_ONCE(mnt->mnt_ns);
/* open-coded is_mounted() to use local mnt_ns */
if (!IS_ERR_OR_NULL(mnt_ns) && !is_anon_ns(mnt_ns))
return 1; // absolute root
else
return 2; // detached or not attached yet
}
if (unlikely(dentry == parent))
/* Escaped? */
return 3;
prefetch(parent);
if (!prepend_name(p, &dentry->d_name))
break;
dentry = parent;
}
return 0;
}
At the mount boundary and not at root mount, the code has
dentry = READ_ONCE(mnt->mnt_mountpoint);
mnt = m; /* 'mnt' will be parent mount */
continue;
After that, we have
const struct dentry *parent = READ_ONCE(dentry->d_parent);
if (dentry == mnt->mnt.mnt_root) {
/* assume this is false */
}
...
prefetch(parent);
if (!prepend_name(p, &dentry->d_name))
break;
dentry = parent;
So the prepend_name(p, &dentry->d_name) is actually from mnt->mnt_mountpoint.
In your above code, maybe we should return path->dentry in the below if statement?
if (unlikely(path->dentry == path->mnt->mnt_root)) {
struct path new_path;
if (!choose_mountpoint(real_mount(path->mnt),
root, &new_path))
goto in_root;
path_put(path);
*path = new_path;
if (unlikely(flags & LOOKUP_NO_XDEV))
return ERR_PTR(-EXDEV);
+ return path->dentry;
}
/* rare case of legitimate dget_parent()... */
return dget_parent(path->dentry);
Also, could you add some selftests cross mount points? This will
have more coverages with __path_walk_parent().
> +
> +in_root:
> + if (unlikely(flags & LOOKUP_BENEATH))
> + return ERR_PTR(-EXDEV);
> + return dget(path->dentry);
> +}
> +
> +/**
> + * path_walk_parent - Walk to the parent of path
> + * @path: input and output path.
> + * @root: root of the path walk, do not go beyond this root. If @root is
> + * zero'ed, walk all the way to real root.
> + *
> + * Given a path, find the parent path. Replace @path with the parent path.
> + * If we were already at the real root or a disconnected root, @path is
> + * not changed.
> + *
> + * Returns:
> + * 0 - if @path is updated to its parent.
> + * <0 - if @path is already the root (real root or @root).
> + */
> +int path_walk_parent(struct path *path, const struct path *root)
> +{
> + struct dentry *parent;
> +
> + parent = __path_walk_parent(path, root, LOOKUP_BENEATH);
> +
> + if (IS_ERR(parent))
> + return PTR_ERR(parent);
> +
> + if (parent == path->dentry) {
> + dput(parent);
> + return -ENOENT;
> + }
> + dput(path->dentry);
> + path->dentry = parent;
> + return 0;
> +}
> +
[...]
next prev parent reply other threads:[~2025-07-04 17:40 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-17 6:11 [PATCH v5 bpf-next 0/5] bpf path iterator Song Liu
2025-06-17 6:11 ` [PATCH v5 bpf-next 1/5] namei: Introduce new helper function path_walk_parent() Song Liu
2025-06-18 1:02 ` kernel test robot
2025-06-24 12:18 ` Jan Kara
2025-06-24 17:37 ` Song Liu
2025-06-25 10:30 ` Jan Kara
2025-07-04 17:40 ` Yonghong Song [this message]
2025-07-06 23:54 ` Song Liu
2025-07-07 17:53 ` Yonghong Song
2025-06-17 6:11 ` [PATCH v5 bpf-next 2/5] landlock: Use path_walk_parent() Song Liu
2025-07-03 18:29 ` Mickaël Salaün
2025-07-03 22:27 ` Song Liu
2025-07-04 9:00 ` Mickaël Salaün
2025-07-06 22:29 ` Song Liu
2025-07-07 10:28 ` Christian Brauner
2025-06-17 6:11 ` [PATCH v5 bpf-next 3/5] bpf: Introduce path iterator Song Liu
2025-06-17 6:11 ` [PATCH v5 bpf-next 4/5] selftests/bpf: Add tests for bpf " Song Liu
2025-06-17 6:11 ` [PATCH v5 bpf-next 5/5] selftests/bpf: Path walk test Song Liu
2025-06-20 21:59 ` [PATCH v5 bpf-next 0/5] bpf path iterator Song Liu
2025-06-24 18:45 ` Mickaël Salaün
2025-06-24 21:38 ` NeilBrown
2025-06-25 13:14 ` Mickaël Salaün
2025-06-25 23:04 ` NeilBrown
2025-06-25 23:17 ` Song Liu
2025-06-26 0:07 ` Tingmao Wang
2025-06-26 1:05 ` NeilBrown
2025-06-26 5:52 ` Song Liu
2025-06-26 9:43 ` Mickaël Salaün
2025-06-26 14:49 ` Song Liu
2025-06-26 10:22 ` NeilBrown
2025-06-26 14:28 ` Song Liu
2025-06-26 22:51 ` NeilBrown
2025-06-27 0:21 ` Song Liu
2025-07-07 10:46 ` Christian Brauner
2025-07-07 11:17 ` Christian Brauner
2025-07-07 18:50 ` Song Liu
2025-07-09 16:06 ` Mickaël Salaün
2025-07-09 17:31 ` Song Liu
2025-07-09 22:24 ` NeilBrown
2025-07-09 22:50 ` Song Liu
2025-07-10 0:58 ` NeilBrown
2025-07-10 6:28 ` Song Liu
2025-07-14 21:09 ` Song Liu
2025-07-24 17:35 ` Mickaël Salaün
2025-07-26 9:52 ` Song Liu
2025-07-09 22:14 ` NeilBrown
2025-07-09 22:41 ` Song Liu
2025-07-10 0:58 ` NeilBrown
2025-07-07 10:43 ` Christian Brauner
2025-07-03 5:04 ` Song Liu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=2459c10e-d74c-4118-9b6d-c37d05ecec02@linux.dev \
--to=yonghong.song@linux.dev \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=jack@suse.cz \
--cc=kernel-team@meta.com \
--cc=kpsingh@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=martin.lau@linux.dev \
--cc=mattbobrowski@google.com \
--cc=neil@brown.name \
--cc=song@kernel.org \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.