nfsd: nfsd should drop CAP_MKNOD for non-root
[safe/jmp/linux-2.6] / fs / super.c
index 0d77ac2..6ce5014 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/kobject.h>
 #include <linux/mutex.h>
 #include <linux/file.h>
+#include <linux/async.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -71,6 +72,7 @@ static struct super_block *alloc_super(struct file_system_type *type)
                INIT_HLIST_HEAD(&s->s_anon);
                INIT_LIST_HEAD(&s->s_inodes);
                INIT_LIST_HEAD(&s->s_dentry_lru);
+               INIT_LIST_HEAD(&s->s_async_list);
                init_rwsem(&s->s_umount);
                mutex_init(&s->s_lock);
                lockdep_set_class(&s->s_umount, &type->s_umount_key);
@@ -80,7 +82,22 @@ static struct super_block *alloc_super(struct file_system_type *type)
                 * lock ordering than usbfs:
                 */
                lockdep_set_class(&s->s_lock, &type->s_lock_key);
-               down_write(&s->s_umount);
+               /*
+                * sget() can have s_umount recursion.
+                *
+                * When it cannot find a suitable sb, it allocates a new
+                * one (this one), and tries again to find a suitable old
+                * one.
+                *
+                * In case that succeeds, it will acquire the s_umount
+                * lock of the old one. Since these are clearly distrinct
+                * locks, and this object isn't exposed yet, there's no
+                * risk of deadlocks.
+                *
+                * Annotate this by putting this lock in a different
+                * subclass.
+                */
+               down_write_nested(&s->s_umount, SINGLE_DEPTH_NESTING);
                s->s_count = S_BIAS;
                atomic_set(&s->s_active, 1);
                mutex_init(&s->s_vfs_rename_mutex);
@@ -289,11 +306,18 @@ void generic_shutdown_super(struct super_block *sb)
 {
        const struct super_operations *sop = sb->s_op;
 
+
        if (sb->s_root) {
                shrink_dcache_for_umount(sb);
                fsync_super(sb);
                lock_super(sb);
                sb->s_flags &= ~MS_ACTIVE;
+
+               /*
+                * wait for asynchronous fs operations to finish before going further
+                */
+               async_synchronize_full_domain(&sb->s_async_list);
+
                /* bad name - it should be evict_inodes() */
                invalidate_inodes(sb);
                lock_kernel();
@@ -347,8 +371,10 @@ retry:
                                continue;
                        if (!grab_super(old))
                                goto retry;
-                       if (s)
+                       if (s) {
+                               up_write(&s->s_umount);
                                destroy_super(s);
+                       }
                        return old;
                }
        }
@@ -363,6 +389,7 @@ retry:
        err = set(s, data);
        if (err) {
                spin_unlock(&sb_lock);
+               up_write(&s->s_umount);
                destroy_super(s);
                return ERR_PTR(err);
        }
@@ -461,6 +488,7 @@ restart:
                sb->s_count++;
                spin_unlock(&sb_lock);
                down_read(&sb->s_umount);
+               async_synchronize_full_domain(&sb->s_async_list);
                if (sb->s_root && (wait || sb->s_dirt))
                        sb->s_op->sync_fs(sb, wait);
                up_read(&sb->s_umount);
@@ -534,7 +562,7 @@ rescan:
        return NULL;
 }
 
-asmlinkage long sys_ustat(unsigned dev, struct ustat __user * ubuf)
+SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
 {
         struct super_block *s;
         struct ustat tmp;
@@ -682,7 +710,7 @@ void emergency_remount(void)
  * filesystems which don't use real block-devices.  -- jrs
  */
 
-static struct idr unnamed_dev_idr;
+static DEFINE_IDA(unnamed_dev_ida);
 static DEFINE_SPINLOCK(unnamed_dev_lock);/* protects the above */
 
 int set_anon_super(struct super_block *s, void *data)
@@ -691,10 +719,10 @@ int set_anon_super(struct super_block *s, void *data)
        int error;
 
  retry:
-       if (idr_pre_get(&unnamed_dev_idr, GFP_ATOMIC) == 0)
+       if (ida_pre_get(&unnamed_dev_ida, GFP_ATOMIC) == 0)
                return -ENOMEM;
        spin_lock(&unnamed_dev_lock);
-       error = idr_get_new(&unnamed_dev_idr, NULL, &dev);
+       error = ida_get_new(&unnamed_dev_ida, &dev);
        spin_unlock(&unnamed_dev_lock);
        if (error == -EAGAIN)
                /* We raced and lost with another CPU. */
@@ -704,7 +732,7 @@ int set_anon_super(struct super_block *s, void *data)
 
        if ((dev & MAX_ID_MASK) == (1 << MINORBITS)) {
                spin_lock(&unnamed_dev_lock);
-               idr_remove(&unnamed_dev_idr, dev);
+               ida_remove(&unnamed_dev_ida, dev);
                spin_unlock(&unnamed_dev_lock);
                return -EMFILE;
        }
@@ -720,17 +748,12 @@ void kill_anon_super(struct super_block *sb)
 
        generic_shutdown_super(sb);
        spin_lock(&unnamed_dev_lock);
-       idr_remove(&unnamed_dev_idr, slot);
+       ida_remove(&unnamed_dev_ida, slot);
        spin_unlock(&unnamed_dev_lock);
 }
 
 EXPORT_SYMBOL(kill_anon_super);
 
-void __init unnamed_dev_init(void)
-{
-       idr_init(&unnamed_dev_idr);
-}
-
 void kill_litter_super(struct super_block *sb)
 {
        if (sb->s_root)
@@ -805,6 +828,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
                }
 
                s->s_flags |= MS_ACTIVE;
+               bdev->bd_super = s;
        }
 
        return simple_set_mnt(mnt, s);
@@ -824,6 +848,7 @@ void kill_block_super(struct super_block *sb)
        struct block_device *bdev = sb->s_bdev;
        fmode_t mode = sb->s_mode;
 
+       bdev->bd_super = 0;
        generic_shutdown_super(sb);
        sync_blockdev(bdev);
        close_bdev_exclusive(bdev, mode);
@@ -919,7 +944,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
                goto out_free_secdata;
        BUG_ON(!mnt->mnt_sb);
 
-       error = security_sb_kern_mount(mnt->mnt_sb, secdata);
+       error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata);
        if (error)
                goto out_sb;