diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-16 02:08:44 +0100 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-01-16 19:47:01 +0100 |
commit | 7b8a53fd815deb39542085897743fa0063f9fe06 (patch) | |
tree | 1fe7d7f6192b3300a12d2e6af80b9601f23f94f8 /fs/namespace.c | |
parent | autofs4: Merge the remaining dentry ops tables (diff) | |
download | linux-7b8a53fd815deb39542085897743fa0063f9fe06.tar.xz linux-7b8a53fd815deb39542085897743fa0063f9fe06.zip |
fix old umount_tree() breakage
Expiry-related code calls umount_tree() several times with
the same list to collect vfsmounts to. Which is fine, except
that umount_tree() implicitly assumed that the list would
be empty on each call - it moves the victims over there and
then iterates through the list kicking them out. It's *almost*
idempotent, so everything nearly worked. However, mnt->ghosts
handling (and thus expirability checks) had been broken - that
part was not idempotent...
The fix is trivial - use local temporary list, splice it to
the the collector list when we are through.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to '')
-rw-r--r-- | fs/namespace.c | 8 |
1 files changed, 5 insertions, 3 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index bfcb701f9490..d7fc05fac753 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1226,15 +1226,16 @@ void release_mounts(struct list_head *head) */ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) { + LIST_HEAD(tmp_list); struct vfsmount *p; for (p = mnt; p; p = next_mnt(p, mnt)) - list_move(&p->mnt_hash, kill); + list_move(&p->mnt_hash, &tmp_list); if (propagate) - propagate_umount(kill); + propagate_umount(&tmp_list); - list_for_each_entry(p, kill, mnt_hash) { + list_for_each_entry(p, &tmp_list, mnt_hash) { list_del_init(&p->mnt_expire); list_del_init(&p->mnt_list); __touch_mnt_namespace(p->mnt_ns); @@ -1246,6 +1247,7 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill) } change_mnt_propagation(p, MS_PRIVATE); } + list_splice(&tmp_list, kill); } static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts); |