diff --git a/fs/pnode.c b/fs/pnode.c index 5bc7896..7aefd06 100644 --- a/fs/pnode.c +++ b/fs/pnode.c @@ -435,6 +435,17 @@ static void mark_umount_candidates(struct mount *mnt) } } +static void __marked_umount(struct mount *mnt, struct mount *child) +{ + 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); + } +} + /* * NOTE: unmounting 'mnt' naturally propagates to all other mounts its * parent propagates to. @@ -448,8 +459,13 @@ static void __propagate_umount(struct mount *mnt) for (m = propagation_next(parent, parent); m; m = propagation_next(m, parent)) { - struct mount *topper; - struct mount *child = __lookup_mnt(&m->mnt, + struct mount *topper, *topper_to_umount; + struct mount *child; + + if (m->mnt.mnt_flags & MNT_UMOUNT) + continue; + + child = __lookup_mnt(&m->mnt, mnt->mnt_mountpoint); /* * umount the child only if the child has no children @@ -457,21 +473,28 @@ static void __propagate_umount(struct mount *mnt) */ if (!child || !IS_MNT_MARKED(child)) continue; - CLEAR_MNT_MARK(child); - /* If there is exactly one mount covering all of child + /* If there is exactly one mount covering all of childV * replace child with that mount. */ - topper = find_topper(child); + topper = topper_to_umount = child; + while (1) { + topper = find_topper(topper); + if (topper == NULL) + break; + if (!IS_MNT_MARKED(topper)) + break; + topper_to_umount = topper; + } if (topper) - mnt_change_mountpoint(child->mnt_parent, child->mnt_mp, - topper); + mnt_change_mountpoint(child->mnt_parent, child->mnt_mp, topper); - 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); + while (topper_to_umount != child) { + __marked_umount(mnt, topper_to_umount); + topper_to_umount = topper_to_umount->mnt_parent; } + + __marked_umount(mnt, child); } }