linux-next: manual merge of the vfs-brauner tree with the vfs-fixes tree

From: Mark Brown

Date: Tue Feb 17 2026 - 08:42:33 EST


Hi all,

Today's linux-next merge of the vfs-brauner tree got a conflict in:

fs/namespace.c

between commits:

2aca1af473d26 ("mount: unmount copied tree")
85d88a3941593 ("mount: hold namespace_sem across copy in create_new_namespace()")

from the vfs-fixes tree and commit:

f4b0ce91e3795 ("mount: unmount copied tree")

from the vfs-brauner tree.

I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.

The below is a diff -c since a standard diff ended up showing empty
changes, it's the fixes version more or less:

diff --combined fs/namespace.c
index 188be116f6e63,cbb18285a6312..0000000000000
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@@ -2791,8 -2791,7 +2791,8 @@@ static inline void unlock_mount(struct
}

static void lock_mount_exact(const struct path *path,
- struct pinned_mountpoint *mp);
+ struct pinned_mountpoint *mp, bool copy_mount,
+ unsigned int copy_flags);

#define LOCK_MOUNT_MAYBE_BENEATH(mp, path, beneath) \
struct pinned_mountpoint mp __cleanup(unlock_mount) = {}; \
@@@ -2800,10 -2799,7 +2800,10 @@@
#define LOCK_MOUNT(mp, path) LOCK_MOUNT_MAYBE_BENEATH(mp, (path), false)
#define LOCK_MOUNT_EXACT(mp, path) \
struct pinned_mountpoint mp __cleanup(unlock_mount) = {}; \
- lock_mount_exact((path), &mp)
+ lock_mount_exact((path), &mp, false, 0)
+#define LOCK_MOUNT_EXACT_COPY(mp, path, copy_flags) \
+ struct pinned_mountpoint mp __cleanup(unlock_mount) = {}; \
+ lock_mount_exact((path), &mp, true, (copy_flags))

static int graft_tree(struct mount *mnt, const struct pinned_mountpoint *mp)
{
@@@ -3082,7 -3078,7 +3082,7 @@@ static struct mnt_namespace *create_new
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
struct user_namespace *user_ns = current_user_ns();
struct mnt_namespace *new_ns;
- struct mount *new_ns_root;
+ struct mount *new_ns_root, *old_ns_root;
struct path to_path;
struct mount *mnt;
unsigned int copy_flags = 0;
@@@ -3095,36 -3091,52 +3095,36 @@@
if (IS_ERR(new_ns))
return ERR_CAST(new_ns);

- scoped_guard(namespace_excl) {
- new_ns_root = clone_mnt(ns->root, ns->root->mnt.mnt_root, copy_flags);
- if (IS_ERR(new_ns_root)) {
- emptied_ns = new_ns;
- return ERR_CAST(new_ns_root);
- }
+ old_ns_root = ns->root;
+ to_path.mnt = &old_ns_root->mnt;
+ to_path.dentry = old_ns_root->mnt.mnt_root;

- /*
- * If the real rootfs had a locked mount on top of it somewhere
- * in the stack, lock the new mount tree as well so it can't be
- * exposed.
- */
- mnt = ns->root;
- while (mnt->overmount) {
- mnt = mnt->overmount;
- if (mnt->mnt.mnt_flags & MNT_LOCKED)
- locked = true;
- }
- }
+ VFS_WARN_ON_ONCE(old_ns_root->mnt_id_unique != 1);
+ VFS_WARN_ON_ONCE(old_ns_root->mnt.mnt_sb->s_type != &nullfs_fs_type);

- /*
- * We dropped the namespace semaphore so we can actually lock
- * the copy for mounting. The copied mount isn't attached to any
- * mount namespace and it is thus excluded from any propagation.
- * So realistically we're isolated and the mount can't be
- * overmounted.
- */
-
- /* Borrow the reference from clone_mnt(). */
- to_path.mnt = &new_ns_root->mnt;
- to_path.dentry = new_ns_root->mnt.mnt_root;
-
- /* Now lock for actual mounting. */
- LOCK_MOUNT_EXACT(mp, &to_path);
- if (unlikely(IS_ERR(mp.parent))) {
- guard(namespace_excl)();
- emptied_ns = new_ns;
- guard(mount_writer)();
- umount_tree(new_ns_root, 0);
+ LOCK_MOUNT_EXACT_COPY(mp, &to_path, copy_flags);
+ if (IS_ERR(mp.parent)) {
+ free_mnt_ns(new_ns);
return ERR_CAST(mp.parent);
}
+ new_ns_root = mp.parent;

/*
- * We don't emulate unshare()ing a mount namespace. We stick to the
- * restrictions of creating detached bind-mounts. It has a lot
- * saner and simpler semantics.
+ * If the real rootfs had a locked mount on top of it somewhere
+ * in the stack, lock the new mount tree as well so it can't be
+ * exposed.
+ */
+ mnt = old_ns_root;
+ while (mnt->overmount) {
+ mnt = mnt->overmount;
+ if (mnt->mnt.mnt_flags & MNT_LOCKED)
+ locked = true;
+ }
+
+ /*
+ * We don't emulate unshare()ing a mount namespace. We stick
+ * to the restrictions of creating detached bind-mounts. It
+ * has a lot saner and simpler semantics.
*/
mnt = __do_loopback(path, flags, copy_flags);
scoped_guard(mount_writer) {
@@@ -3137,20 -3149,21 +3137,20 @@@
if (locked)
mnt->mnt.mnt_flags |= MNT_LOCKED;
/*
- * Now mount the detached tree on top of the copy of the
- * real rootfs we created.
+ * now mount the detached tree on top of the copy
+ * of the real rootfs we created.
*/
attach_mnt(mnt, new_ns_root, mp.mp);
if (user_ns != ns->user_ns)
lock_mnt_tree(new_ns_root);
}

- /* Add all mounts to the new namespace. */
for (mnt = new_ns_root; mnt; mnt = next_mnt(mnt, new_ns_root)) {
mnt_add_to_ns(new_ns, mnt);
new_ns->nr_mounts++;
}

- new_ns->root = real_mount(to_path.mnt);
+ new_ns->root = new_ns_root;
ns_tree_add_raw(new_ns);
return new_ns;
}
@@@ -3834,20 -3847,16 +3834,20 @@@ static int do_new_mount(const struct pa
}

static void lock_mount_exact(const struct path *path,
- struct pinned_mountpoint *mp)
+ struct pinned_mountpoint *mp, bool copy_mount,
+ unsigned int copy_flags)
{
struct dentry *dentry = path->dentry;
int err;

+ /* Assert that inode_lock() locked the correct inode. */
+ VFS_WARN_ON_ONCE(copy_mount && !path_mounted(path));
+
inode_lock(dentry->d_inode);
namespace_lock();
if (unlikely(cant_mount(dentry)))
err = -ENOENT;
- else if (path_overmounted(path))
+ else if (!copy_mount && path_overmounted(path))
err = -EBUSY;
else
err = get_mountpoint(dentry, mp);
@@@ -3855,15 -3864,9 +3855,15 @@@
namespace_unlock();
inode_unlock(dentry->d_inode);
mp->parent = ERR_PTR(err);
- } else {
- mp->parent = real_mount(path->mnt);
+ return;
}
+
+ if (copy_mount)
+ mp->parent = clone_mnt(real_mount(path->mnt), dentry, copy_flags);
+ else
+ mp->parent = real_mount(path->mnt);
+ if (unlikely(IS_ERR(mp->parent)))
+ __unlock_mount(mp);
}

int finish_automount(struct vfsmount *__m, const struct path *path)
@@@ -5640,14 -5643,14 +5640,14 @@@ static int grab_requested_root(struct m
if (mnt_ns_empty(ns))
return -ENOENT;

- first = child = ns->root;
- for (;;) {
- child = listmnt_next(child, false);
- if (!child)
- return -ENOENT;
- if (child->mnt_parent == first)
+ first = ns->root;
+ for (child = node_to_mount(ns->mnt_first_node); child;
+ child = listmnt_next(child, false)) {
+ if (child != first && child->mnt_parent == first)
break;
}
+ if (!child)
+ return -ENOENT;

root->mnt = mntget(&child->mnt);
root->dentry = dget(root->mnt->mnt_root);

Attachment: signature.asc
Description: PGP signature