aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@xmission.com>2015-01-05 14:38:04 -0500
committerEric W. Biederman <ebiederm@xmission.com>2015-04-02 21:34:20 -0400
commit0c56fe31420ca599c90240315f7959bf1b4eb6ce (patch)
tree6b61d7457c3ee31429e70ede1ebabb07e2bbebbb /fs
parent5d88457eb5b86b475422dc882f089203faaeedb5 (diff)
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')
-rw-r--r--fs/pnode.c32
-rw-r--r--fs/pnode.h1
2 files changed, 30 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)
382} 382}
383 383
384/* 384/*
385 * Mark all mounts that the MNT_LOCKED logic will allow to be unmounted.
386 */
387static void mark_umount_candidates(struct mount *mnt)
388{
389 struct mount *parent = mnt->mnt_parent;
390 struct mount *m;
391
392 BUG_ON(parent == mnt);
393
394 for (m = propagation_next(parent, parent); m;
395 m = propagation_next(m, parent)) {
396 struct mount *child = __lookup_mnt_last(&m->mnt,
397 mnt->mnt_mountpoint);
398 if (child && (!IS_MNT_LOCKED(child) || IS_MNT_MARKED(m))) {
399 SET_MNT_MARK(child);
400 }
401 }
402}
403
404/*
385 * NOTE: unmounting 'mnt' naturally propagates to all other mounts its 405 * NOTE: unmounting 'mnt' naturally propagates to all other mounts its
386 * parent propagates to. 406 * parent propagates to.
387 */ 407 */
@@ -398,10 +418,13 @@ static void __propagate_umount(struct mount *mnt)
398 struct mount *child = __lookup_mnt_last(&m->mnt, 418 struct mount *child = __lookup_mnt_last(&m->mnt,
399 mnt->mnt_mountpoint); 419 mnt->mnt_mountpoint);
400 /* 420 /*
401 * umount the child only if the child has no 421 * umount the child only if the child has no children
402 * other children 422 * and the child is marked safe to unmount.
403 */ 423 */
404 if (child && list_empty(&child->mnt_mounts)) { 424 if (!child || !IS_MNT_MARKED(child))
425 continue;
426 CLEAR_MNT_MARK(child);
427 if (list_empty(&child->mnt_mounts)) {
405 list_del_init(&child->mnt_child); 428 list_del_init(&child->mnt_child);
406 child->mnt.mnt_flags |= MNT_UMOUNT; 429 child->mnt.mnt_flags |= MNT_UMOUNT;
407 list_move_tail(&child->mnt_list, &mnt->mnt_list); 430 list_move_tail(&child->mnt_list, &mnt->mnt_list);
@@ -420,6 +443,9 @@ int propagate_umount(struct list_head *list)
420{ 443{
421 struct mount *mnt; 444 struct mount *mnt;
422 445
446 list_for_each_entry_reverse(mnt, list, mnt_list)
447 mark_umount_candidates(mnt);
448
423 list_for_each_entry(mnt, list, mnt_list) 449 list_for_each_entry(mnt, list, mnt_list)
424 __propagate_umount(mnt); 450 __propagate_umount(mnt);
425 return 0; 451 return 0;
diff --git a/fs/pnode.h b/fs/pnode.h
index af47d4bd7b31..0fcdbe7ca648 100644
--- a/fs/pnode.h
+++ b/fs/pnode.h
@@ -19,6 +19,7 @@
19#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED) 19#define IS_MNT_MARKED(m) ((m)->mnt.mnt_flags & MNT_MARKED)
20#define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED) 20#define SET_MNT_MARK(m) ((m)->mnt.mnt_flags |= MNT_MARKED)
21#define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED) 21#define CLEAR_MNT_MARK(m) ((m)->mnt.mnt_flags &= ~MNT_MARKED)
22#define IS_MNT_LOCKED(m) ((m)->mnt.mnt_flags & MNT_LOCKED)
22 23
23#define CL_EXPIRE 0x01 24#define CL_EXPIRE 0x01
24#define CL_SLAVE 0x02 25#define CL_SLAVE 0x02