summary refs log tree commit diff
path: root/fs/pnode.c
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2015-01-05 13:38:04 -0600
committerEric W. Biederman <ebiederm@xmission.com>2015-04-02 20:34:20 -0500
commit0c56fe31420ca599c90240315f7959bf1b4eb6ce (patch)
tree6b61d7457c3ee31429e70ede1ebabb07e2bbebbb /fs/pnode.c
parent5d88457eb5b86b475422dc882f089203faaeedb5 (diff)
downloadlinux-0c56fe31420ca599c90240315f7959bf1b4eb6ce.tar.gz
mnt: Don't propagate unmounts to locked mounts
If the first mount in shared subtree is locked don't unmount the
shared subtree.

This is ensured by walking through the mounts parents before children
and marking a mount as unmountable if it is not locked or it is locked
but it's parent is marked.

This allows recursive mount detach to propagate through a set of
mounts when unmounting them would not reveal what is under any locked
mount.

Cc: stable@vger.kernel.org
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'fs/pnode.c')
-rw-r--r--fs/pnode.c32
1 files changed, 29 insertions, 3 deletions
diff --git a/fs/pnode.c b/fs/pnode.c
index 89890293dd0a..6367e1e435c6 100644
--- a/fs/pnode.c
+++ b/fs/pnode.c
@@ -382,6 +382,26 @@ void propagate_mount_unlock(struct mount *mnt)
 }
 
 /*
+ * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted.
+ */
+static void mark_umount_candidates(struct mount *mnt)
+{
+	struct mount *parent = mnt->mnt_parent;
+	struct mount *m;
+
+	BUG_ON(parent == mnt);
+
+	for (m = propagation_next(parent, parent); m;
+			m = propagation_next(m, parent)) {
+		struct mount *child = __lookup_mnt_last(&m->mnt,
+						mnt->mnt_mountpoint);
+		if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) {
+			SET_MNT_MARK(child);
+		}
+	}
+}
+
+/*
  * NOTE: unmounting 'mnt' naturally propagates to all other mounts its
  * parent propagates to.
  */
@@ -398,10 +418,13 @@ static void __propagate_umount(struct mount *mnt)
 		struct mount *child = __lookup_mnt_last(&m->mnt,
 						mnt->mnt_mountpoint);
 		/*
-		 * umount the child only if the child has no
-		 * other children
+		 * umount the child only if the child has no children
+		 * and the child is marked safe to unmount.
 		 */
-		if (child && list_empty(&child->mnt_mounts)) {
+		if (!child || !IS_MNT_MARKED(child))
+			continue;
+		CLEAR_MNT_MARK(child);
+		if (list_empty(&child->mnt_mounts)) {
 			list_del_init(&child->mnt_child);
 			child->mnt.mnt_flags |= MNT_UMOUNT;
 			list_move_tail(&child->mnt_list, &mnt->mnt_list);
@@ -420,6 +443,9 @@ int propagate_umount(struct list_head *list)
 {
 	struct mount *mnt;
 
+	list_for_each_entry_reverse(mnt, list, mnt_list)
+		mark_umount_candidates(mnt);
+
 	list_for_each_entry(mnt, list, mnt_list)
 		__propagate_umount(mnt);
 	return 0;