]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
make commit_tree() usable in same-namespace move case
authorAl Viro <viro@zeniv.linux.org.uk>
Sat, 26 Apr 2025 02:34:33 +0000 (22:34 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Sun, 29 Jun 2025 22:13:41 +0000 (18:13 -0400)
Once attach_recursive_mnt() has created all copies of original subtree,
it needs to put them in place(s).

Steps needed for those are slightly different:
1) in 'move' case, original copy doesn't need any rbtree
manipulations (everything's already in the same namespace where it will
be), but it needs to be detached from the current location
2) in 'attach' case, original may be in anon namespace; if it is,
all those mounts need to removed from their current namespace before
insertion into the target one
3) additional copies have a couple of extra twists - in case
of cross-userns propagation we need to lock everything other the root of
subtree and in case when we end up inserting under an existing mount,
that mount needs to be found (for original copy we have it explicitly
passed by the caller).

Quite a bit of that can be unified; as the first step, make commit_tree()
helper (inserting mounts into namespace, hashing the root of subtree
and marking the namespace as updated) usable in all cases; (2) and (3)
are already using it and for (1) we only need to make the insertion of
mounts into namespace conditional.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/namespace.c

index f64895d47d70558c10c2eb7ff299ee0db8eecfd4..937c2a1825f2f3a3e5486dc21e16965a197847e2 100644 (file)
@@ -1172,15 +1172,17 @@ static void commit_tree(struct mount *mnt)
 
        BUG_ON(parent == mnt);
 
-       list_add_tail(&head, &mnt->mnt_list);
-       while (!list_empty(&head)) {
-               m = list_first_entry(&head, typeof(*m), mnt_list);
-               list_del(&m->mnt_list);
+       if (!mnt_ns_attached(mnt)) {
+               list_add_tail(&head, &mnt->mnt_list);
+               while (!list_empty(&head)) {
+                       m = list_first_entry(&head, typeof(*m), mnt_list);
+                       list_del(&m->mnt_list);
 
-               mnt_add_to_ns(n, m);
+                       mnt_add_to_ns(n, m);
+               }
+               n->nr_mounts += n->pending_mounts;
+               n->pending_mounts = 0;
        }
-       n->nr_mounts += n->pending_mounts;
-       n->pending_mounts = 0;
 
        make_visible(mnt);
        touch_mnt_namespace(n);
@@ -2691,12 +2693,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
 
        if (moving) {
                unhash_mnt(source_mnt);
-               mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
-               if (beneath)
-                       mnt_change_mountpoint(top, smp, top_mnt);
-               make_visible(source_mnt);
                mnt_notify_add(source_mnt);
-               touch_mnt_namespace(source_mnt->mnt_ns);
        } else {
                if (source_mnt->mnt_ns) {
                        LIST_HEAD(head);
@@ -2706,12 +2703,13 @@ static int attach_recursive_mnt(struct mount *source_mnt,
                                move_from_ns(p, &head);
                        list_del_init(&head);
                }
-               mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
-               if (beneath)
-                       mnt_change_mountpoint(top, smp, top_mnt);
-               commit_tree(source_mnt);
        }
 
+       mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
+       if (beneath)
+               mnt_change_mountpoint(top, smp, top_mnt);
+       commit_tree(source_mnt);
+
        hlist_for_each_entry_safe(child, n, &tree_list, mnt_hash) {
                struct mount *q;
                hlist_del_init(&child->mnt_hash);