From 11c2b7ec2e1865bbc6a65e7d7312a5043e3cc1aa Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Mon, 25 Aug 2025 18:12:30 +0000 Subject: [PATCH 1/4] namei: move cross-device check to traverse_mounts This is preparation to RESOLVE_NO_XDEV fix in following commits. No functional change intended Signed-off-by: Askar Safin Link: https://lore.kernel.org/20250825181233.2464822-2-safinaskar@zohomail.com Signed-off-by: Christian Brauner --- fs/namei.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 138a693c2346..f81fdc7bbfed 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1518,6 +1518,7 @@ static inline int traverse_mounts(struct path *path, bool *jumped, int *count, unsigned lookup_flags) { unsigned flags = smp_load_acquire(&path->dentry->d_flags); + int ret; /* fastpath */ if (likely(!(flags & DCACHE_MANAGED_DENTRY))) { @@ -1526,7 +1527,11 @@ static inline int traverse_mounts(struct path *path, bool *jumped, return -ENOENT; return 0; } - return __traverse_mounts(path, flags, jumped, count, lookup_flags); + + ret = __traverse_mounts(path, flags, jumped, count, lookup_flags); + if (*jumped && unlikely(lookup_flags & LOOKUP_NO_XDEV)) + return -EXDEV; + return ret; } int follow_down_one(struct path *path) @@ -1631,9 +1636,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry, } ret = traverse_mounts(path, &jumped, &nd->total_link_count, nd->flags); if (jumped) { - if (unlikely(nd->flags & LOOKUP_NO_XDEV)) - ret = -EXDEV; - else + if (!unlikely(nd->flags & LOOKUP_NO_XDEV)) nd->state |= ND_JUMPED; } if (unlikely(ret)) { From 8b966d00b3ece6b1ffa4b6d73d484cf0ecf967e6 Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Mon, 25 Aug 2025 18:12:31 +0000 Subject: [PATCH 2/4] namei: remove LOOKUP_NO_XDEV check from handle_mounts This is preparation to RESOLVE_NO_XDEV fix in following commits. No functional change intended. The only place that ever looks at ND_JUMPED in nd->state is complete_walk() and we are not going to reach it if handle_mounts() returns an error Signed-off-by: Askar Safin Link: https://lore.kernel.org/20250825181233.2464822-3-safinaskar@zohomail.com Signed-off-by: Christian Brauner --- fs/namei.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index f81fdc7bbfed..6e34c3317421 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1635,10 +1635,8 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry, return -ECHILD; } ret = traverse_mounts(path, &jumped, &nd->total_link_count, nd->flags); - if (jumped) { - if (!unlikely(nd->flags & LOOKUP_NO_XDEV)) - nd->state |= ND_JUMPED; - } + if (jumped) + nd->state |= ND_JUMPED; if (unlikely(ret)) { dput(path->dentry); if (path->mnt != nd->path.mnt) From 8ded1fde0827e52f3962d7931193f5a16d87a52c Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Mon, 25 Aug 2025 18:12:32 +0000 Subject: [PATCH 3/4] namei: move cross-device check to __traverse_mounts This is preparation to RESOLVE_NO_XDEV fix in following commits. Also this commit makes LOOKUP_NO_XDEV logic more clear: now we immediately fail with EXDEV on first mount crossing instead of waiting for very end. No functional change intended Signed-off-by: Askar Safin Link: https://lore.kernel.org/20250825181233.2464822-4-safinaskar@zohomail.com Signed-off-by: Christian Brauner --- fs/namei.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 6e34c3317421..f0ca6f8d0a5f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1489,6 +1489,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped, // here we know it's positive flags = path->dentry->d_flags; need_mntput = true; + if (unlikely(lookup_flags & LOOKUP_NO_XDEV)) { + ret = -EXDEV; + break; + } continue; } } @@ -1518,7 +1522,6 @@ static inline int traverse_mounts(struct path *path, bool *jumped, int *count, unsigned lookup_flags) { unsigned flags = smp_load_acquire(&path->dentry->d_flags); - int ret; /* fastpath */ if (likely(!(flags & DCACHE_MANAGED_DENTRY))) { @@ -1527,11 +1530,7 @@ static inline int traverse_mounts(struct path *path, bool *jumped, return -ENOENT; return 0; } - - ret = __traverse_mounts(path, flags, jumped, count, lookup_flags); - if (*jumped && unlikely(lookup_flags & LOOKUP_NO_XDEV)) - return -EXDEV; - return ret; + return __traverse_mounts(path, flags, jumped, count, lookup_flags); } int follow_down_one(struct path *path) From 042a60680de43175eb4df0977ff04a4eba9da082 Mon Sep 17 00:00:00 2001 From: Askar Safin Date: Mon, 25 Aug 2025 18:12:33 +0000 Subject: [PATCH 4/4] openat2: don't trigger automounts with RESOLVE_NO_XDEV openat2 had a bug: if we pass RESOLVE_NO_XDEV, then openat2 doesn't traverse through automounts, but may still trigger them. (See the link for full bug report with reproducer.) This commit fixes this bug. Link: https://lore.kernel.org/linux-fsdevel/20250817075252.4137628-1-safinaskar@zohomail.com/ Fixes: fddb5d430ad9fa91b49b1 ("open: introduce openat2(2) syscall") Reviewed-by: Aleksa Sarai Cc: stable@vger.kernel.org Signed-off-by: Askar Safin Link: https://lore.kernel.org/20250825181233.2464822-5-safinaskar@zohomail.com Signed-off-by: Christian Brauner --- fs/namei.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/namei.c b/fs/namei.c index f0ca6f8d0a5f..44856b70ea3b 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1449,6 +1449,10 @@ static int follow_automount(struct path *path, int *count, unsigned lookup_flags dentry->d_inode) return -EISDIR; + /* No need to trigger automounts if mountpoint crossing is disabled. */ + if (lookup_flags & LOOKUP_NO_XDEV) + return -EXDEV; + if (count && (*count)++ >= MAXSYMLINKS) return -ELOOP; @@ -1472,6 +1476,10 @@ static int __traverse_mounts(struct path *path, unsigned flags, bool *jumped, /* Allow the filesystem to manage the transit without i_rwsem * being held. */ if (flags & DCACHE_MANAGE_TRANSIT) { + if (lookup_flags & LOOKUP_NO_XDEV) { + ret = -EXDEV; + break; + } ret = path->dentry->d_op->d_manage(path, false); flags = smp_load_acquire(&path->dentry->d_flags); if (ret < 0)