vserver 1.9.3
[linux-2.6.git] / fs / super.c
index 8d9bff1..998fc47 100644 (file)
@@ -35,6 +35,8 @@
 #include <linux/vfs.h>
 #include <linux/writeback.h>           /* for the emergency remount stuff */
 #include <linux/idr.h>
+#include <linux/devpts_fs.h>
+#include <linux/proc_fs.h>
 #include <asm/uaccess.h>
 
 
@@ -116,6 +118,27 @@ int __put_super(struct super_block *sb)
        return ret;
 }
 
+/*
+ * Drop a superblock's refcount.
+ * Returns non-zero if the superblock is about to be destroyed and
+ * at least is already removed from super_blocks list, so if we are
+ * making a loop through super blocks then we need to restart.
+ * The caller must hold sb_lock.
+ */
+int __put_super_and_need_restart(struct super_block *sb)
+{
+       /* check for race with generic_shutdown_super() */
+       if (list_empty(&sb->s_list)) {
+               /* super block is removed, need to restart... */
+               __put_super(sb);
+               return 1;
+       }
+       /* can't be the last, since s_list is still in use */
+       sb->s_count--;
+       BUG_ON(sb->s_count == 0);
+       return 0;
+}
+
 /**
  *     put_super       -       drop a temporary reference to superblock
  *     @s: superblock in question
@@ -229,7 +252,8 @@ void generic_shutdown_super(struct super_block *sb)
                unlock_super(sb);
        }
        spin_lock(&sb_lock);
-       list_del(&sb->s_list);
+       /* should be initialized for __put_super_and_need_restart() */
+       list_del_init(&sb->s_list);
        list_del(&sb->s_instances);
        spin_unlock(&sb_lock);
        up_write(&sb->s_umount);
@@ -282,7 +306,7 @@ retry:
        }
        s->s_type = type;
        strlcpy(s->s_id, type->name, sizeof(s->s_id));
-       list_add(&s->s_list, super_blocks.prev);
+       list_add_tail(&s->s_list, &super_blocks);
        list_add(&s->s_instances, &type->fs_supers);
        spin_unlock(&sb_lock);
        get_filesystem(type);
@@ -569,14 +593,19 @@ static spinlock_t unnamed_dev_lock = SPIN_LOCK_UNLOCKED;/* protects the above */
 int set_anon_super(struct super_block *s, void *data)
 {
        int dev;
+       int error;
 
-       spin_lock(&unnamed_dev_lock);
-       if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0) {
-               spin_unlock(&unnamed_dev_lock);
+ retry:
+       if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
                return -ENOMEM;
-       }
-       dev = idr_get_new(&unnamed_dev_idr, NULL);
+       spin_lock(&unnamed_dev_lock);
+       error = idr_get_new(&unnamed_dev_idr, NULL, &dev);
        spin_unlock(&unnamed_dev_lock);
+       if (error == -EAGAIN)
+               /* We raced and lost with another CPU. */
+               goto retry;
+       else if (error)
+               return -EAGAIN;
 
        if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
                spin_lock(&unnamed_dev_lock);
@@ -781,6 +810,13 @@ do_kern_mount(const char *fstype, int flags, const char *name, void *data)
        sb = type->get_sb(type, flags, name, data);
        if (IS_ERR(sb))
                goto out_free_secdata;
+
+       error = -EPERM;
+       if (!capable(CAP_SYS_ADMIN) && !sb->s_bdev &&
+               (sb->s_magic != PROC_SUPER_MAGIC) &&
+               (sb->s_magic != DEVPTS_SUPER_MAGIC))
+               goto out_sb;
+
        error = security_sb_kern_mount(sb, secdata);
        if (error)
                goto out_sb;
@@ -788,6 +824,7 @@ do_kern_mount(const char *fstype, int flags, const char *name, void *data)
        mnt->mnt_root = dget(sb->s_root);
        mnt->mnt_mountpoint = sb->s_root;
        mnt->mnt_parent = mnt;
+       mnt->mnt_namespace = current->namespace;
        up_write(&sb->s_umount);
        put_filesystem(type);
        return mnt;
@@ -804,6 +841,8 @@ out:
        return (struct vfsmount *)sb;
 }
 
+EXPORT_SYMBOL_GPL(do_kern_mount);
+
 struct vfsmount *kern_mount(struct file_system_type *type)
 {
        return do_kern_mount(type->name, 0, type->name, NULL);