[PATCH] knfsd: nfsd: simplify exp_pseudoroot
[safe/jmp/linux-2.6] / fs / namespace.c
index c13072a..5ef336c 100644 (file)
@@ -8,36 +8,27 @@
  * Heavily rewritten.
  */
 
-#include <linux/config.h>
 #include <linux/syscalls.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
 #include <linux/init.h>
+#include <linux/kernel.h>
 #include <linux/quotaops.h>
 #include <linux/acct.h>
 #include <linux/capability.h>
 #include <linux/module.h>
+#include <linux/sysfs.h>
 #include <linux/seq_file.h>
-#include <linux/namespace.h>
+#include <linux/mnt_namespace.h>
 #include <linux/namei.h>
 #include <linux/security.h>
 #include <linux/mount.h>
+#include <linux/ramfs.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
 #include "pnode.h"
 
-extern int __init init_rootfs(void);
-
-#ifdef CONFIG_SYSFS
-extern int __init sysfs_init(void);
-#else
-static inline int sysfs_init(void)
-{
-       return 0;
-}
-#endif
-
 /* spinlock for vfsmount related operations, inplace of dcache_lock */
 __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock);
 
@@ -45,7 +36,7 @@ static int event;
 
 static struct list_head *mount_hashtable __read_mostly;
 static int hash_mask __read_mostly, hash_bits __read_mostly;
-static kmem_cache_t *mnt_cache __read_mostly;
+static struct kmem_cache *mnt_cache __read_mostly;
 static struct rw_semaphore namespace_sem;
 
 /* /sys/fs */
@@ -142,10 +133,10 @@ struct vfsmount *lookup_mnt(struct vfsmount *mnt, struct dentry *dentry)
 
 static inline int check_mnt(struct vfsmount *mnt)
 {
-       return mnt->mnt_namespace == current->namespace;
+       return mnt->mnt_ns == current->nsproxy->mnt_ns;
 }
 
-static void touch_namespace(struct namespace *ns)
+static void touch_mnt_namespace(struct mnt_namespace *ns)
 {
        if (ns) {
                ns->event = ++event;
@@ -153,7 +144,7 @@ static void touch_namespace(struct namespace *ns)
        }
 }
 
-static void __touch_namespace(struct namespace *ns)
+static void __touch_mnt_namespace(struct mnt_namespace *ns)
 {
        if (ns && ns->event != event) {
                ns->event = event;
@@ -196,19 +187,19 @@ static void commit_tree(struct vfsmount *mnt)
        struct vfsmount *parent = mnt->mnt_parent;
        struct vfsmount *m;
        LIST_HEAD(head);
-       struct namespace *n = parent->mnt_namespace;
+       struct mnt_namespace *n = parent->mnt_ns;
 
        BUG_ON(parent == mnt);
 
        list_add_tail(&head, &mnt->mnt_list);
        list_for_each_entry(m, &head, mnt_list)
-               m->mnt_namespace = n;
+               m->mnt_ns = n;
        list_splice(&head, n->list.prev);
 
        list_add_tail(&mnt->mnt_hash, mount_hashtable +
                                hash(parent, mnt->mnt_mountpoint));
        list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
-       touch_namespace(n);
+       touch_mnt_namespace(n);
 }
 
 static struct vfsmount *next_mnt(struct vfsmount *p, struct vfsmount *root)
@@ -329,7 +320,7 @@ EXPORT_SYMBOL(mnt_unpin);
 /* iterator */
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
-       struct namespace *n = m->private;
+       struct mnt_namespace *n = m->private;
        struct list_head *p;
        loff_t l = *pos;
 
@@ -342,7 +333,7 @@ static void *m_start(struct seq_file *m, loff_t *pos)
 
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
-       struct namespace *n = m->private;
+       struct mnt_namespace *n = m->private;
        struct list_head *p = ((struct vfsmount *)v)->mnt_list.next;
        (*pos)++;
        return p == &n->list ? NULL : list_entry(p, struct vfsmount, mnt_list);
@@ -377,6 +368,7 @@ static int show_vfsmnt(struct seq_file *m, void *v)
                { MNT_NOEXEC, ",noexec" },
                { MNT_NOATIME, ",noatime" },
                { MNT_NODIRATIME, ",nodiratime" },
+               { MNT_RELATIME, ",relatime" },
                { 0, NULL }
        };
        struct proc_fs_info *fs_infop;
@@ -526,10 +518,8 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
 {
        struct vfsmount *p;
 
-       for (p = mnt; p; p = next_mnt(p, mnt)) {
-               list_del(&p->mnt_hash);
-               list_add(&p->mnt_hash, kill);
-       }
+       for (p = mnt; p; p = next_mnt(p, mnt))
+               list_move(&p->mnt_hash, kill);
 
        if (propagate)
                propagate_umount(kill);
@@ -537,8 +527,8 @@ void umount_tree(struct vfsmount *mnt, int propagate, struct list_head *kill)
        list_for_each_entry(p, kill, mnt_hash) {
                list_del_init(&p->mnt_expire);
                list_del_init(&p->mnt_list);
-               __touch_namespace(p->mnt_namespace);
-               p->mnt_namespace = NULL;
+               __touch_mnt_namespace(p->mnt_ns);
+               p->mnt_ns = NULL;
                list_del_init(&p->mnt_child);
                if (p->mnt_parent != p)
                        p->mnt_mountpoint->d_mounted--;
@@ -585,8 +575,8 @@ static int do_umount(struct vfsmount *mnt, int flags)
         */
 
        lock_kernel();
-       if ((flags & MNT_FORCE) && sb->s_op->umount_begin)
-               sb->s_op->umount_begin(sb);
+       if (sb->s_op->umount_begin)
+               sb->s_op->umount_begin(mnt, flags);
        unlock_kernel();
 
        /*
@@ -841,7 +831,7 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt,
        if (parent_nd) {
                detach_mnt(source_mnt, parent_nd);
                attach_mnt(source_mnt, nd);
-               touch_namespace(current->namespace);
+               touch_mnt_namespace(current->nsproxy->mnt_ns);
        } else {
                mnt_set_mountpoint(dest_mnt, dest_dentry, source_mnt);
                commit_tree(source_mnt);
@@ -1156,9 +1146,9 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
         */
        if (!propagate_mount_busy(mnt, 2)) {
                /* delete from the namespace */
-               touch_namespace(mnt->mnt_namespace);
+               touch_mnt_namespace(mnt->mnt_ns);
                list_del_init(&mnt->mnt_list);
-               mnt->mnt_namespace = NULL;
+               mnt->mnt_ns = NULL;
                umount_tree(mnt, 1, umounts);
                spin_unlock(&vfsmount_lock);
        } else {
@@ -1172,13 +1162,46 @@ static void expire_mount(struct vfsmount *mnt, struct list_head *mounts,
 }
 
 /*
+ * 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_entry(graveyard->next, 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
  * here
  */
 void mark_mounts_for_expiry(struct list_head *mounts)
 {
-       struct namespace *namespace;
        struct vfsmount *mnt, *next;
        LIST_HEAD(graveyard);
 
@@ -1202,38 +1225,79 @@ void mark_mounts_for_expiry(struct list_head *mounts)
                list_move(&mnt->mnt_expire, &graveyard);
        }
 
-       /*
-        * 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
-        */
-       while (!list_empty(&graveyard)) {
-               LIST_HEAD(umounts);
-               mnt = list_entry(graveyard.next, struct vfsmount, mnt_expire);
-               list_del_init(&mnt->mnt_expire);
+       expire_mount_list(&graveyard, mounts);
 
-               /* don't do anything if the namespace is dead - all the
-                * vfsmounts from it are going away anyway */
-               namespace = mnt->mnt_namespace;
-               if (!namespace || !namespace->root)
+       spin_unlock(&vfsmount_lock);
+}
+
+EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
+
+/*
+ * Ripoff of 'select_parent()'
+ *
+ * search the list of submounts for a given mountpoint, and move any
+ * shrinkable submounts to the 'graveyard' list.
+ */
+static int select_submounts(struct vfsmount *parent, struct list_head *graveyard)
+{
+       struct vfsmount *this_parent = parent;
+       struct list_head *next;
+       int found = 0;
+
+repeat:
+       next = this_parent->mnt_mounts.next;
+resume:
+       while (next != &this_parent->mnt_mounts) {
+               struct list_head *tmp = next;
+               struct vfsmount *mnt = list_entry(tmp, struct vfsmount, mnt_child);
+
+               next = tmp->next;
+               if (!(mnt->mnt_flags & MNT_SHRINKABLE))
                        continue;
-               get_namespace(namespace);
+               /*
+                * Descend a level if the d_mounts list is non-empty.
+                */
+               if (!list_empty(&mnt->mnt_mounts)) {
+                       this_parent = mnt;
+                       goto repeat;
+               }
 
-               spin_unlock(&vfsmount_lock);
-               down_write(&namespace_sem);
-               expire_mount(mnt, mounts, &umounts);
-               up_write(&namespace_sem);
-               release_mounts(&umounts);
-               mntput(mnt);
-               put_namespace(namespace);
-               spin_lock(&vfsmount_lock);
+               if (!propagate_mount_busy(mnt, 1)) {
+                       mntget(mnt);
+                       list_move_tail(&mnt->mnt_expire, graveyard);
+                       found++;
+               }
        }
+       /*
+        * All done at this level ... ascend and resume the search
+        */
+       if (this_parent != parent) {
+               next = this_parent->mnt_child.next;
+               this_parent = this_parent->mnt_parent;
+               goto resume;
+       }
+       return found;
+}
+
+/*
+ * 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)
+{
+       LIST_HEAD(graveyard);
+       int found;
+
+       spin_lock(&vfsmount_lock);
+
+       /* extract submounts of 'mountpoint' from the expiration list */
+       while ((found = select_submounts(mountpoint, &graveyard)) != 0)
+               expire_mount_list(&graveyard, mounts);
 
        spin_unlock(&vfsmount_lock);
 }
 
-EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
+EXPORT_SYMBOL_GPL(shrink_submounts);
 
 /*
  * Some copy_from_user() implementations do not return the exact number of
@@ -1342,9 +1406,11 @@ long do_mount(char *dev_name, char *dir_name, char *type_page,
                mnt_flags |= MNT_NOATIME;
        if (flags & MS_NODIRATIME)
                mnt_flags |= MNT_NODIRATIME;
+       if (flags & MS_RELATIME)
+               mnt_flags |= MNT_RELATIME;
 
        flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE |
-                  MS_NOATIME | MS_NODIRATIME);
+                  MS_NOATIME | MS_NODIRATIME | MS_RELATIME);
 
        /* ... and get the mountpoint */
        retval = path_lookup(dir_name, LOOKUP_FOLLOW, &nd);
@@ -1376,14 +1442,15 @@ dput_out:
  * Allocate a new namespace structure and populate it with contents
  * copied from the namespace of the passed in task structure.
  */
-struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
+struct mnt_namespace *dup_mnt_ns(struct task_struct *tsk,
+               struct fs_struct *fs)
 {
-       struct namespace *namespace = tsk->namespace;
-       struct namespace *new_ns;
+       struct mnt_namespace *mnt_ns = tsk->nsproxy->mnt_ns;
+       struct mnt_namespace *new_ns;
        struct vfsmount *rootmnt = NULL, *pwdmnt = NULL, *altrootmnt = NULL;
        struct vfsmount *p, *q;
 
-       new_ns = kmalloc(sizeof(struct namespace), GFP_KERNEL);
+       new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
        if (!new_ns)
                return NULL;
 
@@ -1394,7 +1461,7 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
 
        down_write(&namespace_sem);
        /* First pass: copy the tree topology */
-       new_ns->root = copy_tree(namespace->root, namespace->root->mnt_root,
+       new_ns->root = copy_tree(mnt_ns->root, mnt_ns->root->mnt_root,
                                        CL_COPY_ALL | CL_EXPIRE);
        if (!new_ns->root) {
                up_write(&namespace_sem);
@@ -1410,10 +1477,10 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
         * as belonging to new namespace.  We have already acquired a private
         * fs_struct, so tsk->fs->lock is not needed.
         */
-       p = namespace->root;
+       p = mnt_ns->root;
        q = new_ns->root;
        while (p) {
-               q->mnt_namespace = new_ns;
+               q->mnt_ns = new_ns;
                if (fs) {
                        if (p == fs->rootmnt) {
                                rootmnt = p;
@@ -1428,7 +1495,7 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
                                fs->altrootmnt = mntget(q);
                        }
                }
-               p = next_mnt(p, namespace->root);
+               p = next_mnt(p, mnt_ns->root);
                q = next_mnt(q, new_ns->root);
        }
        up_write(&namespace_sem);
@@ -1443,16 +1510,16 @@ struct namespace *dup_namespace(struct task_struct *tsk, struct fs_struct *fs)
        return new_ns;
 }
 
-int copy_namespace(int flags, struct task_struct *tsk)
+int copy_mnt_ns(int flags, struct task_struct *tsk)
 {
-       struct namespace *namespace = tsk->namespace;
-       struct namespace *new_ns;
+       struct mnt_namespace *ns = tsk->nsproxy->mnt_ns;
+       struct mnt_namespace *new_ns;
        int err = 0;
 
-       if (!namespace)
+       if (!ns)
                return 0;
 
-       get_namespace(namespace);
+       get_mnt_ns(ns);
 
        if (!(flags & CLONE_NEWNS))
                return 0;
@@ -1462,16 +1529,16 @@ int copy_namespace(int flags, struct task_struct *tsk)
                goto out;
        }
 
-       new_ns = dup_namespace(tsk, tsk->fs);
+       new_ns = dup_mnt_ns(tsk, tsk->fs);
        if (!new_ns) {
                err = -ENOMEM;
                goto out;
        }
 
-       tsk->namespace = new_ns;
+       tsk->nsproxy->mnt_ns = new_ns;
 
 out:
-       put_namespace(namespace);
+       put_mnt_ns(ns);
        return err;
 }
 
@@ -1691,7 +1758,7 @@ asmlinkage long sys_pivot_root(const char __user * new_root,
        detach_mnt(user_nd.mnt, &root_parent);
        attach_mnt(user_nd.mnt, &old_nd);     /* mount old root on put_old */
        attach_mnt(new_nd.mnt, &root_parent); /* mount new_root on / */
-       touch_namespace(current->namespace);
+       touch_mnt_namespace(current->nsproxy->mnt_ns);
        spin_unlock(&vfsmount_lock);
        chroot_fs_refs(&user_nd, &new_nd);
        security_sb_post_pivotroot(&user_nd, &new_nd);
@@ -1716,33 +1783,27 @@ out3:
 static void __init init_mount_tree(void)
 {
        struct vfsmount *mnt;
-       struct namespace *namespace;
-       struct task_struct *g, *p;
+       struct mnt_namespace *ns;
 
        mnt = do_kern_mount("rootfs", 0, "rootfs", NULL);
        if (IS_ERR(mnt))
                panic("Can't create rootfs");
-       namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);
-       if (!namespace)
+       ns = kmalloc(sizeof(*ns), GFP_KERNEL);
+       if (!ns)
                panic("Can't allocate initial namespace");
-       atomic_set(&namespace->count, 1);
-       INIT_LIST_HEAD(&namespace->list);
-       init_waitqueue_head(&namespace->poll);
-       namespace->event = 0;
-       list_add(&mnt->mnt_list, &namespace->list);
-       namespace->root = mnt;
-       mnt->mnt_namespace = namespace;
-
-       init_task.namespace = namespace;
-       read_lock(&tasklist_lock);
-       do_each_thread(g, p) {
-               get_namespace(namespace);
-               p->namespace = namespace;
-       } while_each_thread(g, p);
-       read_unlock(&tasklist_lock);
-
-       set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);
-       set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);
+       atomic_set(&ns->count, 1);
+       INIT_LIST_HEAD(&ns->list);
+       init_waitqueue_head(&ns->poll);
+       ns->event = 0;
+       list_add(&mnt->mnt_list, &ns->list);
+       ns->root = mnt;
+       mnt->mnt_ns = ns;
+
+       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);
 }
 
 void __init mnt_init(unsigned long mempages)
@@ -1750,6 +1811,7 @@ void __init mnt_init(unsigned long mempages)
        struct list_head *d;
        unsigned int nr_hash;
        int i;
+       int err;
 
        init_rwsem(&namespace_sem);
 
@@ -1790,17 +1852,23 @@ void __init mnt_init(unsigned long mempages)
                d++;
                i--;
        } while (i);
-       sysfs_init();
-       subsystem_register(&fs_subsys);
+       err = sysfs_init();
+       if (err)
+               printk(KERN_WARNING "%s: sysfs_init error: %d\n",
+                       __FUNCTION__, err);
+       err = subsystem_register(&fs_subsys);
+       if (err)
+               printk(KERN_WARNING "%s: subsystem_register error: %d\n",
+                       __FUNCTION__, err);
        init_rootfs();
        init_mount_tree();
 }
 
-void __put_namespace(struct namespace *namespace)
+void __put_mnt_ns(struct mnt_namespace *ns)
 {
-       struct vfsmount *root = namespace->root;
+       struct vfsmount *root = ns->root;
        LIST_HEAD(umount_list);
-       namespace->root = NULL;
+       ns->root = NULL;
        spin_unlock(&vfsmount_lock);
        down_write(&namespace_sem);
        spin_lock(&vfsmount_lock);
@@ -1808,5 +1876,5 @@ void __put_namespace(struct namespace *namespace)
        spin_unlock(&vfsmount_lock);
        up_write(&namespace_sem);
        release_mounts(&umount_list);
-       kfree(namespace);
+       kfree(ns);
 }