diff options
-rw-r--r-- | fs/mount.h | 1 | ||||
-rw-r--r-- | fs/namespace.c | 1 | ||||
-rw-r--r-- | fs/pnode.c | 35 |
3 files changed, 32 insertions, 5 deletions
diff --git a/fs/mount.h b/fs/mount.h index bf1fda6eed8f..ede5a1d5cf99 100644 --- a/fs/mount.h +++ b/fs/mount.h | |||
@@ -58,6 +58,7 @@ struct mount { | |||
58 | struct mnt_namespace *mnt_ns; /* containing namespace */ | 58 | struct mnt_namespace *mnt_ns; /* containing namespace */ |
59 | struct mountpoint *mnt_mp; /* where is it mounted */ | 59 | struct mountpoint *mnt_mp; /* where is it mounted */ |
60 | struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */ | 60 | struct hlist_node mnt_mp_list; /* list mounts with the same mountpoint */ |
61 | struct list_head mnt_reparent; /* reparent list entry */ | ||
61 | #ifdef CONFIG_FSNOTIFY | 62 | #ifdef CONFIG_FSNOTIFY |
62 | struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks; | 63 | struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks; |
63 | __u32 mnt_fsnotify_mask; | 64 | __u32 mnt_fsnotify_mask; |
diff --git a/fs/namespace.c b/fs/namespace.c index 8bd3e4d448b9..51e49866e1fe 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -236,6 +236,7 @@ static struct mount *alloc_vfsmnt(const char *name) | |||
236 | INIT_LIST_HEAD(&mnt->mnt_slave_list); | 236 | INIT_LIST_HEAD(&mnt->mnt_slave_list); |
237 | INIT_LIST_HEAD(&mnt->mnt_slave); | 237 | INIT_LIST_HEAD(&mnt->mnt_slave); |
238 | INIT_HLIST_NODE(&mnt->mnt_mp_list); | 238 | INIT_HLIST_NODE(&mnt->mnt_mp_list); |
239 | INIT_LIST_HEAD(&mnt->mnt_reparent); | ||
239 | init_fs_pin(&mnt->mnt_umount, drop_mountpoint); | 240 | init_fs_pin(&mnt->mnt_umount, drop_mountpoint); |
240 | } | 241 | } |
241 | return mnt; | 242 | return mnt; |
diff --git a/fs/pnode.c b/fs/pnode.c index 5bc7896d122a..52aca0a118ff 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
@@ -439,7 +439,7 @@ static void mark_umount_candidates(struct mount *mnt) | |||
439 | * NOTE: unmounting 'mnt' naturally propagates to all other mounts its | 439 | * NOTE: unmounting 'mnt' naturally propagates to all other mounts its |
440 | * parent propagates to. | 440 | * parent propagates to. |
441 | */ | 441 | */ |
442 | static void __propagate_umount(struct mount *mnt) | 442 | static void __propagate_umount(struct mount *mnt, struct list_head *to_reparent) |
443 | { | 443 | { |
444 | struct mount *parent = mnt->mnt_parent; | 444 | struct mount *parent = mnt->mnt_parent; |
445 | struct mount *m; | 445 | struct mount *m; |
@@ -464,17 +464,38 @@ static void __propagate_umount(struct mount *mnt) | |||
464 | */ | 464 | */ |
465 | topper = find_topper(child); | 465 | topper = find_topper(child); |
466 | if (topper) | 466 | if (topper) |
467 | mnt_change_mountpoint(child->mnt_parent, child->mnt_mp, | 467 | list_add_tail(&topper->mnt_reparent, to_reparent); |
468 | topper); | ||
469 | 468 | ||
470 | if (list_empty(&child->mnt_mounts)) { | 469 | if (topper || list_empty(&child->mnt_mounts)) { |
471 | list_del_init(&child->mnt_child); | 470 | list_del_init(&child->mnt_child); |
471 | list_del_init(&child->mnt_reparent); | ||
472 | child->mnt.mnt_flags |= MNT_UMOUNT; | 472 | child->mnt.mnt_flags |= MNT_UMOUNT; |
473 | list_move_tail(&child->mnt_list, &mnt->mnt_list); | 473 | list_move_tail(&child->mnt_list, &mnt->mnt_list); |
474 | } | 474 | } |
475 | } | 475 | } |
476 | } | 476 | } |
477 | 477 | ||
478 | static void reparent_mounts(struct list_head *to_reparent) | ||
479 | { | ||
480 | while (!list_empty(to_reparent)) { | ||
481 | struct mount *mnt, *parent; | ||
482 | struct mountpoint *mp; | ||
483 | |||
484 | mnt = list_first_entry(to_reparent, struct mount, mnt_reparent); | ||
485 | list_del_init(&mnt->mnt_reparent); | ||
486 | |||
487 | /* Where should this mount be reparented to? */ | ||
488 | mp = mnt->mnt_mp; | ||
489 | parent = mnt->mnt_parent; | ||
490 | while (parent->mnt.mnt_flags & MNT_UMOUNT) { | ||
491 | mp = parent->mnt_mp; | ||
492 | parent = parent->mnt_parent; | ||
493 | } | ||
494 | |||
495 | mnt_change_mountpoint(parent, mp, mnt); | ||
496 | } | ||
497 | } | ||
498 | |||
478 | /* | 499 | /* |
479 | * collect all mounts that receive propagation from the mount in @list, | 500 | * collect all mounts that receive propagation from the mount in @list, |
480 | * and return these additional mounts in the same list. | 501 | * and return these additional mounts in the same list. |
@@ -485,11 +506,15 @@ static void __propagate_umount(struct mount *mnt) | |||
485 | int propagate_umount(struct list_head *list) | 506 | int propagate_umount(struct list_head *list) |
486 | { | 507 | { |
487 | struct mount *mnt; | 508 | struct mount *mnt; |
509 | LIST_HEAD(to_reparent); | ||
488 | 510 | ||
489 | list_for_each_entry_reverse(mnt, list, mnt_list) | 511 | list_for_each_entry_reverse(mnt, list, mnt_list) |
490 | mark_umount_candidates(mnt); | 512 | mark_umount_candidates(mnt); |
491 | 513 | ||
492 | list_for_each_entry(mnt, list, mnt_list) | 514 | list_for_each_entry(mnt, list, mnt_list) |
493 | __propagate_umount(mnt); | 515 | __propagate_umount(mnt, &to_reparent); |
516 | |||
517 | reparent_mounts(&to_reparent); | ||
518 | |||
494 | return 0; | 519 | return 0; |
495 | } | 520 | } |