[PATCH] mount expiry fixes

- clean up the ugliness in may_umount_tree()

 - fix a bug in do_loopback().  after cloning a tree, do_loopback()
   unlinks only the topmost mount of the cloned tree, leaving behind the
   children mounts on their corresponding expiry list.

Signed-off-by: Ram Pai <linuxram@us.ibm.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
Ram Pai 2005-11-07 17:17:22 -05:00 committed by Linus Torvalds
parent 70fbcdf4d2
commit 36341f6456

View File

@ -27,6 +27,8 @@
extern int __init init_rootfs(void); extern int __init init_rootfs(void);
#define CL_EXPIRE 0x01
#ifdef CONFIG_SYSFS #ifdef CONFIG_SYSFS
extern int __init sysfs_init(void); extern int __init sysfs_init(void);
#else #else
@ -165,7 +167,8 @@ static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
return list_entry(next, struct vfsmount, mnt_child); return list_entry(next, struct vfsmount, mnt_child);
} }
static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root) static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root,
int flag)
{ {
struct super_block *sb = old->mnt_sb; struct super_block *sb = old->mnt_sb;
struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname);
@ -181,10 +184,12 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root)
/* stick the duplicate mount on the same expiry list /* stick the duplicate mount on the same expiry list
* as the original if that was on one */ * as the original if that was on one */
spin_lock(&vfsmount_lock); if (flag & CL_EXPIRE) {
if (!list_empty(&old->mnt_expire)) spin_lock(&vfsmount_lock);
list_add(&mnt->mnt_expire, &old->mnt_expire); if (!list_empty(&old->mnt_expire))
spin_unlock(&vfsmount_lock); list_add(&mnt->mnt_expire, &old->mnt_expire);
spin_unlock(&vfsmount_lock);
}
} }
return mnt; return mnt;
} }
@ -331,36 +336,14 @@ struct seq_operations mounts_op = {
*/ */
int may_umount_tree(struct vfsmount *mnt) int may_umount_tree(struct vfsmount *mnt)
{ {
struct list_head *next; int actual_refs = 0;
struct vfsmount *this_parent = mnt; int minimum_refs = 0;
int actual_refs; struct vfsmount *p;
int minimum_refs;
spin_lock(&vfsmount_lock); spin_lock(&vfsmount_lock);
actual_refs = atomic_read(&mnt->mnt_count); for (p = mnt; p; p = next_mnt(p, mnt)) {
minimum_refs = 2;
repeat:
next = this_parent->mnt_mounts.next;
resume:
while (next != &this_parent->mnt_mounts) {
struct vfsmount *p =
list_entry(next, struct vfsmount, mnt_child);
next = next->next;
actual_refs += atomic_read(&p->mnt_count); actual_refs += atomic_read(&p->mnt_count);
minimum_refs += 2; minimum_refs += 2;
if (!list_empty(&p->mnt_mounts)) {
this_parent = p;
goto repeat;
}
}
if (this_parent != mnt) {
next = this_parent->mnt_child.next;
this_parent = this_parent->mnt_parent;
goto resume;
} }
spin_unlock(&vfsmount_lock); spin_unlock(&vfsmount_lock);
@ -596,12 +579,13 @@ static int lives_below_in_same_fs(struct dentry *d, struct dentry *dentry)
} }
} }
static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry) static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
int flag)
{ {
struct vfsmount *res, *p, *q, *r, *s; struct vfsmount *res, *p, *q, *r, *s;
struct nameidata nd; struct nameidata nd;
res = q = clone_mnt(mnt, dentry); res = q = clone_mnt(mnt, dentry, flag);
if (!q) if (!q)
goto Enomem; goto Enomem;
q->mnt_mountpoint = mnt->mnt_mountpoint; q->mnt_mountpoint = mnt->mnt_mountpoint;
@ -619,7 +603,7 @@ static struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry)
p = s; p = s;
nd.mnt = q; nd.mnt = q;
nd.dentry = p->mnt_mountpoint; nd.dentry = p->mnt_mountpoint;
q = clone_mnt(p, p->mnt_root); q = clone_mnt(p, p->mnt_root, flag);
if (!q) if (!q)
goto Enomem; goto Enomem;
spin_lock(&vfsmount_lock); spin_lock(&vfsmount_lock);
@ -701,18 +685,13 @@ static int do_loopback(struct nameidata *nd, char *old_name, int recurse)
err = -ENOMEM; err = -ENOMEM;
if (recurse) if (recurse)
mnt = copy_tree(old_nd.mnt, old_nd.dentry); mnt = copy_tree(old_nd.mnt, old_nd.dentry, 0);
else else
mnt = clone_mnt(old_nd.mnt, old_nd.dentry); mnt = clone_mnt(old_nd.mnt, old_nd.dentry, 0);
if (!mnt) if (!mnt)
goto out; goto out;
/* stop bind mounts from expiring */
spin_lock(&vfsmount_lock);
list_del_init(&mnt->mnt_expire);
spin_unlock(&vfsmount_lock);
err = graft_tree(mnt, nd); err = graft_tree(mnt, nd);
if (err) { if (err) {
LIST_HEAD(umount_list); LIST_HEAD(umount_list);
@ -1155,7 +1134,8 @@ int copy_namespace(int flags, struct task_struct *tsk)
down_write(&tsk->namespace->sem); down_write(&tsk->namespace->sem);
/* First pass: copy the tree topology */ /* First pass: copy the tree topology */
new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root); new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root,
CL_EXPIRE);
if (!new_ns->root) { if (!new_ns->root) {
up_write(&tsk->namespace->sem); up_write(&tsk->namespace->sem);
kfree(new_ns); kfree(new_ns);