aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-08-10 03:44:55 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-08-11 12:28:10 -0400
commit12a5b5294cb1896e9a3c9fca8ff5a7e3def4e8c6 (patch)
tree9a3780d90c484e1a18e37b980d3d6cf2ac766711
parent60bb45297f7551833346c5cebc6d483ea17ea5f2 (diff)
fix copy_tree() regression
Since 3.14 we had copy_tree() get the shadowing wrong - if we had one vfsmount shadowing another (i.e. if A is a slave of B, C is mounted on A/foo, then D got mounted on B/foo creating D' on A/foo shadowed by C), copy_tree() of A would make a copy of D' shadow the the copy of C, not the other way around. It's easy to fix, fortunately - just make sure that mount follows the one that shadows it in mnt_child as well as in mnt_hash, and when copy_tree() decides to attach a new mount, check if the last child it has added to the same parent should be shadowing the new one. And if it should, just use the same logics commit_tree() has - put the new mount into the hash and children lists right after the one that should shadow it. Cc: stable@vger.kernel.org [3.14 and later] Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/namespace.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/fs/namespace.c b/fs/namespace.c
index 65af9d0e0d67..be3f6f23a47d 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -778,6 +778,20 @@ static void attach_mnt(struct mount *mnt,
778 list_add_tail(&mnt->mnt_child, &parent->mnt_mounts); 778 list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
779} 779}
780 780
781static void attach_shadowed(struct mount *mnt,
782 struct mount *parent,
783 struct mount *shadows)
784{
785 if (shadows) {
786 hlist_add_after_rcu(&shadows->mnt_hash, &mnt->mnt_hash);
787 list_add(&mnt->mnt_child, &shadows->mnt_child);
788 } else {
789 hlist_add_head_rcu(&mnt->mnt_hash,
790 m_hash(&parent->mnt, mnt->mnt_mountpoint));
791 list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
792 }
793}
794
781/* 795/*
782 * vfsmount lock must be held for write 796 * vfsmount lock must be held for write
783 */ 797 */
@@ -796,12 +810,7 @@ static void commit_tree(struct mount *mnt, struct mount *shadows)
796 810
797 list_splice(&head, n->list.prev); 811 list_splice(&head, n->list.prev);
798 812
799 if (shadows) 813 attach_shadowed(mnt, parent, shadows);
800 hlist_add_after_rcu(&shadows->mnt_hash, &mnt->mnt_hash);
801 else
802 hlist_add_head_rcu(&mnt->mnt_hash,
803 m_hash(&parent->mnt, mnt->mnt_mountpoint));
804 list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
805 touch_mnt_namespace(n); 814 touch_mnt_namespace(n);
806} 815}
807 816
@@ -1474,6 +1483,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
1474 continue; 1483 continue;
1475 1484
1476 for (s = r; s; s = next_mnt(s, r)) { 1485 for (s = r; s; s = next_mnt(s, r)) {
1486 struct mount *t = NULL;
1477 if (!(flag & CL_COPY_UNBINDABLE) && 1487 if (!(flag & CL_COPY_UNBINDABLE) &&
1478 IS_MNT_UNBINDABLE(s)) { 1488 IS_MNT_UNBINDABLE(s)) {
1479 s = skip_mnt_tree(s); 1489 s = skip_mnt_tree(s);
@@ -1495,7 +1505,14 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
1495 goto out; 1505 goto out;
1496 lock_mount_hash(); 1506 lock_mount_hash();
1497 list_add_tail(&q->mnt_list, &res->mnt_list); 1507 list_add_tail(&q->mnt_list, &res->mnt_list);
1498 attach_mnt(q, parent, p->mnt_mp); 1508 mnt_set_mountpoint(parent, p->mnt_mp, q);
1509 if (!list_empty(&parent->mnt_mounts)) {
1510 t = list_last_entry(&parent->mnt_mounts,
1511 struct mount, mnt_child);
1512 if (t->mnt_mp != p->mnt_mp)
1513 t = NULL;
1514 }
1515 attach_shadowed(q, parent, t);
1499 unlock_mount_hash(); 1516 unlock_mount_hash();
1500 } 1517 }
1501 } 1518 }