[PATCH] do shrink_submounts() for all fs types
[safe/jmp/linux-2.6] / fs / namespace.c
index ac19212..7bd74b2 100644 (file)
@@ -155,15 +155,15 @@ static void __touch_mnt_namespace(struct mnt_namespace *ns)
        }
 }
 
-static void detach_mnt(struct vfsmount *mnt, struct nameidata *old_nd)
+static void detach_mnt(struct vfsmount *mnt, struct path *old_path)
 {
-       old_nd->path.dentry = mnt->mnt_mountpoint;
-       old_nd->path.mnt = mnt->mnt_parent;
+       old_path->dentry = mnt->mnt_mountpoint;
+       old_path->mnt = mnt->mnt_parent;
        mnt->mnt_parent = mnt;
        mnt->mnt_mountpoint = mnt->mnt_root;
        list_del_init(&mnt->mnt_child);
        list_del_init(&mnt->mnt_hash);
-       old_nd->path.dentry->d_mounted--;
+       old_path->dentry->d_mounted--;
 }
 
 void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
@@ -174,12 +174,12 @@ void mnt_set_mountpoint(struct vfsmount *mnt, struct dentry *dentry,
        dentry->d_mounted++;
 }
 
-static void attach_mnt(struct vfsmount *mnt, struct nameidata *nd)
+static void attach_mnt(struct vfsmount *mnt, struct path *path)
 {
-       mnt_set_mountpoint(nd->path.mnt, nd->path.dentry, mnt);
+       mnt_set_mountpoint(path->mnt, path->dentry, mnt);
        list_add_tail(&mnt->mnt_hash, mount_hashtable +
-                       hash(nd->path.mnt, nd->path.dentry));
-       list_add_tail(&mnt->mnt_child, &nd->path.mnt->mnt_mounts);
+                       hash(path->mnt, path->dentry));
+       list_add_tail(&mnt->mnt_child, &path->mnt->mnt_mounts);
 }
 
 /*
@@ -408,10 +408,11 @@ static int show_vfsmnt(struct seq_file *m, void *v)
                { 0, NULL }
        };
        struct proc_fs_info *fs_infop;
+       struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
 
        mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none");
        seq_putc(m, ' ');
-       seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+       seq_path(m, &mnt_path, " \t\n\\");
        seq_putc(m, ' ');
        mangle(m, mnt->mnt_sb->s_type->name);
        if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) {
@@ -443,6 +444,7 @@ struct seq_operations mounts_op = {
 static int show_vfsstat(struct seq_file *m, void *v)
 {
        struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list);
+       struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
        int err = 0;
 
        /* device */
@@ -454,7 +456,7 @@ static int show_vfsstat(struct seq_file *m, void *v)
 
        /* mount point */
        seq_puts(m, " mounted on ");
-       seq_path(m, mnt, mnt->mnt_root, " \t\n\\");
+       seq_path(m, &mnt_path, " \t\n\\");
        seq_putc(m, ' ');
 
        /* file system type */
@@ -546,6 +548,7 @@ void release_mounts(struct list_head *head)
                        m = mnt->mnt_parent;
                        mnt->mnt_mountpoint = mnt->mnt_root;
                        mnt->mnt_parent = mnt;
+                       m->mnt_ghosts--;
                        spin_unlock(&vfsmount_lock);
                        dput(dentry);
                        mntput(m);
@@ -570,12 +573,16 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
                __touch_mnt_namespace(p->mnt_ns);
                p->mnt_ns = NULL;
                list_del_init(&p->mnt_child);
-               if (p->mnt_parent != p)
+               if (p->mnt_parent != p) {
+                       p->mnt_parent->mnt_ghosts++;
                        p->mnt_mountpoint->d_mounted--;
+               }
                change_mnt_propagation(p, MS_PRIVATE);
        }
 }
 
+static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts);
+
 static int do_umount(struct vfsmount *mnt, int flags)
 {
        struct super_block *sb = mnt->mnt_sb;
@@ -648,6 +655,9 @@ static int do_umount(struct vfsmount *mnt, int flags)
        spin_lock(&vfsmount_lock);
        event++;
 
+       if (!(flags & MNT_DETACH))
+               shrink_submounts(mnt, &umount_list);
+
        retval = -EBUSY;
        if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
                if (!list_empty(&mnt->mnt_list))
@@ -742,7 +752,7 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
                                        int flag)
 {
        struct vfsmount *res, *p, *q, *r, *s;
-       struct nameidata nd;
+       struct path path;
 
        if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
                return NULL;
@@ -767,14 +777,14 @@ struct vfsmount *copy_tree(struct vfsmount *mnt, struct dentry *dentry,
                                q = q->mnt_parent;
                        }
                        p = s;
-                       nd.path.mnt = q;
-                       nd.path.dentry = p->mnt_mountpoint;
+                       path.mnt = q;
+                       path.dentry = p->mnt_mountpoint;
                        q = clone_mnt(p, p->mnt_root, flag);
                        if (!q)
                                goto Enomem;
                        spin_lock(&vfsmount_lock);
                        list_add_tail(&q->mnt_list, &res->mnt_list);
-                       attach_mnt(q, &nd);
+                       attach_mnt(q, &path);
                        spin_unlock(&vfsmount_lock);
                }
        }
@@ -874,11 +884,11 @@ void drop_collected_mounts(struct vfsmount *mnt)
  * in allocations.
  */
 static int attach_recursive_mnt(struct vfsmount *source_mnt,
-                       struct nameidata *nd, struct nameidata *parent_nd)
+                       struct path *path, struct path *parent_path)
 {
        LIST_HEAD(tree_list);
-       struct vfsmount *dest_mnt = nd->path.mnt;
-       struct dentry *dest_dentry = nd->path.dentry;
+       struct vfsmount *dest_mnt = path->mnt;
+       struct dentry *dest_dentry = path->dentry;
        struct vfsmount *child, *p;
 
        if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list))
@@ -890,9 +900,9 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
        }
 
        spin_lock(&vfsmount_lock);
-       if (parent_nd) {
-               detach_mnt(source_mnt, parent_nd);
-               attach_mnt(source_mnt, nd);
+       if (parent_path) {
+               detach_mnt(source_mnt, parent_path);
+               attach_mnt(source_mnt, path);
                touch_mnt_namespace(current->nsproxy->mnt_ns);
        } else {
                mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
@@ -928,7 +938,7 @@ static int graft_tree(struct vfsmount *mnt, struct nameidata *nd)
 
        err = -ENOENT;
        if (IS_ROOT(nd->path.dentry) || !d_unhashed(nd->path.dentry))
-               err = attach_recursive_mnt(mnt, nd, NULL);
+               err = attach_recursive_mnt(mnt, &nd->path, NULL);
 out_unlock:
        mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
        if (!err)
@@ -1057,7 +1067,8 @@ static inline int tree_contains_unbindable(struct vfsmount *mnt)
  */
 static noinline int do_move_mount(struct nameidata *nd, char *old_name)
 {
-       struct nameidata old_nd, parent_nd;
+       struct nameidata old_nd;
+       struct path parent_path;
        struct vfsmount *p;
        int err = 0;
        if (!capable(CAP_SYS_ADMIN))
@@ -1112,7 +1123,7 @@ static noinline int do_move_mount(struct nameidata *nd, char *old_name)
                if (p == old_nd.path.mnt)
                        goto out1;
 
-       err = attach_recursive_mnt(old_nd.path.mnt, nd, &parent_nd);
+       err = attach_recursive_mnt(old_nd.path.mnt, &nd->path, &parent_path);
        if (err)
                goto out1;
 
@@ -1126,7 +1137,7 @@ out1:
 out:
        up_write(&namespace_sem);
        if (!err)
-               path_put(&parent_nd.path);
+               path_put(&parent_path);
        path_put(&old_nd.path);
        return err;
 }
@@ -1204,75 +1215,6 @@ unlock:
 
 EXPORT_SYMBOL_GPL(do_add_mount);
 
-static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
-                               struct list_head *umounts)
-{
-       spin_lock(&vfsmount_lock);
-
-       /*
-        * Check if mount is still attached, if not, let whoever holds it deal
-        * with the sucker
-        */
-       if (mnt->mnt_parent == mnt) {
-               spin_unlock(&vfsmount_lock);
-               return;
-       }
-
-       /*
-        * Check that it is still dead: the count should now be 2 - as
-        * contributed by the vfsmount parent and the mntget above
-        */
-       if (!propagate_mount_busy(mnt, 2)) {
-               /* delete from the namespace */
-               touch_mnt_namespace(mnt->mnt_ns);
-               list_del_init(&mnt->mnt_list);
-               mnt->mnt_ns = NULL;
-               umount_tree(mnt, 1, umounts);
-               spin_unlock(&vfsmount_lock);
-       } else {
-               /*
-                * Someone brought it back to life whilst we didn't have any
-                * locks held so return it to the expiration list
-                */
-               list_add_tail(&mnt->mnt_expire, mounts);
-               spin_unlock(&vfsmount_lock);
-       }
-}
-
-/*
- * go through the vfsmounts we've just consigned to the graveyard to
- * - check that they're still dead
- * - delete the vfsmount from the appropriate namespace under lock
- * - dispose of the corpse
- */
-static void expire_mount_list(struct list_head *graveyard, struct list_head *mounts)
-{
-       struct mnt_namespace *ns;
-       struct vfsmount *mnt;
-
-       while (!list_empty(graveyard)) {
-               LIST_HEAD(umounts);
-               mnt = list_first_entry(graveyard, struct vfsmount, mnt_expire);
-               list_del_init(&mnt->mnt_expire);
-
-               /* don't do anything if the namespace is dead - all the
-                * vfsmounts from it are going away anyway */
-               ns = mnt->mnt_ns;
-               if (!ns || !ns->root)
-                       continue;
-               get_mnt_ns(ns);
-
-               spin_unlock(&vfsmount_lock);
-               down_write(&namespace_sem);
-               expire_mount(mnt, mounts, &umounts);
-               up_write(&namespace_sem);
-               release_mounts(&umounts);
-               mntput(mnt);
-               put_mnt_ns(ns);
-               spin_lock(&vfsmount_lock);
-       }
-}
-
 /*
  * process a list of expirable mountpoints with the intent of discarding any
  * mountpoints that aren't in use and haven't been touched since last we came
@@ -1282,10 +1224,12 @@ void mark_mounts_for_expiry(struct list_head *mounts)
 {
        struct vfsmount *mnt, *next;
        LIST_HEAD(graveyard);
+       LIST_HEAD(umounts);
 
        if (list_empty(mounts))
                return;
 
+       down_write(&namespace_sem);
        spin_lock(&vfsmount_lock);
 
        /* extract from the expiration list every vfsmount that matches the
@@ -1296,16 +1240,19 @@ void mark_mounts_for_expiry(struct list_head *mounts)
         */
        list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
                if (!xchg(&mnt->mnt_expiry_mark, 1) ||
-                   atomic_read(&mnt->mnt_count) != 1)
+                       propagate_mount_busy(mnt, 1))
                        continue;
-
-               mntget(mnt);
                list_move(&mnt->mnt_expire, &graveyard);
        }
-
-       expire_mount_list(&graveyard, mounts);
-
+       while (!list_empty(&graveyard)) {
+               mnt = list_first_entry(&graveyard, struct vfsmount, mnt_expire);
+               touch_mnt_namespace(mnt->mnt_ns);
+               umount_tree(mnt, 1, &umounts);
+       }
        spin_unlock(&vfsmount_lock);
+       up_write(&namespace_sem);
+
+       release_mounts(&umounts);
 }
 
 EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
@@ -1341,7 +1288,6 @@ resume:
                }
 
                if (!propagate_mount_busy(mnt, 1)) {
-                       mntget(mnt);
                        list_move_tail(&mnt->mnt_expire, graveyard);
                        found++;
                }
@@ -1361,22 +1307,22 @@ resume:
  * process a list of expirable mountpoints with the intent of discarding any
  * submounts of a specific parent mountpoint
  */
-void shrink_submounts(struct vfsmount *mountpoint, struct list_head *mounts)
+static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts)
 {
        LIST_HEAD(graveyard);
-       int found;
-
-       spin_lock(&vfsmount_lock);
+       struct vfsmount *m;
 
        /* extract submounts of 'mountpoint' from the expiration list */
-       while ((found = select_submounts(mountpoint, &graveyard)) != 0)
-               expire_mount_list(&graveyard, mounts);
-
-       spin_unlock(&vfsmount_lock);
+       while (select_submounts(mnt, &graveyard)) {
+               while (!list_empty(&graveyard)) {
+                       m = list_first_entry(&graveyard, struct vfsmount,
+                                               mnt_expire);
+                       touch_mnt_namespace(mnt->mnt_ns);
+                       umount_tree(mnt, 1, umounts);
+               }
+       }
 }
 
-EXPORT_SYMBOL_GPL(shrink_submounts);
-
 /*
  * Some copy_from_user() implementations do not return the exact number of
  * bytes remaining to copy on a fault.  But copy_mount_options() requires that.
@@ -1650,15 +1596,14 @@ out1:
  * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
  * It can block. Requires the big lock held.
  */
-void set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
-                struct dentry *dentry)
+void set_fs_root(struct fs_struct *fs, struct path *path)
 {
        struct path old_root;
 
        write_lock(&fs->lock);
        old_root = fs->root;
-       fs->root.mnt = mntget(mnt);
-       fs->root.dentry = dget(dentry);
+       fs->root = *path;
+       path_get(path);
        write_unlock(&fs->lock);
        if (old_root.dentry)
                path_put(&old_root);
@@ -1668,22 +1613,21 @@ void set_fs_root(struct fs_struct *fs, struct vfsmount *mnt,
  * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values.
  * It can block. Requires the big lock held.
  */
-void set_fs_pwd(struct fs_struct *fs, struct vfsmount *mnt,
-               struct dentry *dentry)
+void set_fs_pwd(struct fs_struct *fs, struct path *path)
 {
        struct path old_pwd;
 
        write_lock(&fs->lock);
        old_pwd = fs->pwd;
-       fs->pwd.mnt = mntget(mnt);
-       fs->pwd.dentry = dget(dentry);
+       fs->pwd = *path;
+       path_get(path);
        write_unlock(&fs->lock);
 
        if (old_pwd.dentry)
                path_put(&old_pwd);
 }
 
-static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
+static void chroot_fs_refs(struct path *old_root, struct path *new_root)
 {
        struct task_struct *g, *p;
        struct fs_struct *fs;
@@ -1695,14 +1639,12 @@ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
                if (fs) {
                        atomic_inc(&fs->count);
                        task_unlock(p);
-                       if (fs->root.dentry == old_nd->path.dentry
-                           && fs->root.mnt == old_nd->path.mnt)
-                               set_fs_root(fs, new_nd->path.mnt,
-                                           new_nd->path.dentry);
-                       if (fs->pwd.dentry == old_nd->path.dentry
-                           && fs->pwd.mnt == old_nd->path.mnt)
-                               set_fs_pwd(fs, new_nd->path.mnt,
-                                          new_nd->path.dentry);
+                       if (fs->root.dentry == old_root->dentry
+                           && fs->root.mnt == old_root->mnt)
+                               set_fs_root(fs, new_root);
+                       if (fs->pwd.dentry == old_root->dentry
+                           && fs->pwd.mnt == old_root->mnt)
+                               set_fs_pwd(fs, new_root);
                        put_fs_struct(fs);
                } else
                        task_unlock(p);
@@ -1739,7 +1681,8 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
                               const char __user * put_old)
 {
        struct vfsmount *tmp;
-       struct nameidata new_nd, old_nd, parent_nd, root_parent, user_nd;
+       struct nameidata new_nd, old_nd, user_nd;
+       struct path parent_path, root_parent;
        int error;
 
        if (!capable(CAP_SYS_ADMIN))
@@ -1813,19 +1756,19 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
                        goto out3;
        } else if (!is_subdir(old_nd.path.dentry, new_nd.path.dentry))
                goto out3;
-       detach_mnt(new_nd.path.mnt, &parent_nd);
+       detach_mnt(new_nd.path.mnt, &parent_path);
        detach_mnt(user_nd.path.mnt, &root_parent);
        /* mount old root on put_old */
-       attach_mnt(user_nd.path.mnt, &old_nd);
+       attach_mnt(user_nd.path.mnt, &old_nd.path);
        /* mount new_root on / */
        attach_mnt(new_nd.path.mnt, &root_parent);
        touch_mnt_namespace(current->nsproxy->mnt_ns);
        spin_unlock(&vfsmount_lock);
-       chroot_fs_refs(&user_nd, &new_nd);
+       chroot_fs_refs(&user_nd.path, &new_nd.path);
        security_sb_post_pivotroot(&user_nd, &new_nd);
        error = 0;
-       path_put(&root_parent.path);
-       path_put(&parent_nd.path);
+       path_put(&root_parent);
+       path_put(&parent_path);
 out2:
        mutex_unlock(&old_nd.path.dentry->d_inode->i_mutex);
        up_write(&namespace_sem);
@@ -1845,6 +1788,7 @@ static void __init init_mount_tree(void)
 {
        struct vfsmount *mnt;
        struct mnt_namespace *ns;
+       struct path root;
 
        mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
        if (IS_ERR(mnt))
@@ -1863,8 +1807,11 @@ static void __init init_mount_tree(void)
        init_task.nsproxy->mnt_ns = ns;
        get_mnt_ns(ns);
 
-       set_fs_pwd(current->fs, ns->root, ns->root->mnt_root);
-       set_fs_root(current->fs, ns->root, ns->root->mnt_root);
+       root.mnt = ns->root;
+       root.dentry = ns->root->mnt_root;
+
+       set_fs_pwd(current->fs, &root);
+       set_fs_root(current->fs, &root);
 }
 
 void __init mnt_init(void)