Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / super.c
index d325036..d3c66fb 100644 (file)
@@ -20,7 +20,6 @@
  *  Heavily rewritten for 'one fs - one tree' dcache architecture. AV, Mar 2000
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/init.h>
@@ -52,11 +51,12 @@ DEFINE_SPINLOCK(sb_lock);
 
 /**
  *     alloc_super     -       create new superblock
+ *     @type:  filesystem type superblock should belong to
  *
  *     Allocates and initializes a new &struct super_block.  alloc_super()
  *     returns a pointer new superblock or %NULL if allocation had failed.
  */
-static struct super_block *alloc_super(void)
+static struct super_block *alloc_super(struct file_system_type *type)
 {
        struct super_block *s = kzalloc(sizeof(struct super_block),  GFP_USER);
        static struct super_operations default_op;
@@ -75,6 +75,13 @@ static struct super_block *alloc_super(void)
                INIT_LIST_HEAD(&s->s_inodes);
                init_rwsem(&s->s_umount);
                mutex_init(&s->s_lock);
+               lockdep_set_class(&s->s_umount, &type->s_umount_key);
+               /*
+                * The locking rules for s_lock are up to the
+                * filesystem. For example ext3fs has different
+                * lock ordering than usbfs:
+                */
+               lockdep_set_class(&s->s_lock, &type->s_lock_key);
                down_write(&s->s_umount);
                s->s_count = S_BIAS;
                atomic_set(&s->s_active, 1);
@@ -224,17 +231,17 @@ static int grab_super(struct super_block *s)
  *     that need destruction out of superblock, call generic_shutdown_super()
  *     and release aforementioned objects.  Note: dentries and inodes _are_
  *     taken care of and do not need specific handling.
+ *
+ *     Upon calling this function, the filesystem may no longer alter or
+ *     rearrange the set of dentries belonging to this super_block, nor may it
+ *     change the attachments of dentries to inodes.
  */
 void generic_shutdown_super(struct super_block *sb)
 {
-       struct dentry *root = sb->s_root;
        struct super_operations *sop = sb->s_op;
 
-       if (root) {
-               sb->s_root = NULL;
-               shrink_dcache_parent(root);
-               shrink_dcache_anon(&sb->s_anon);
-               dput(root);
+       if (sb->s_root) {
+               shrink_dcache_for_umount(sb);
                fsync_super(sb);
                lock_super(sb);
                sb->s_flags &= ~MS_ACTIVE;
@@ -298,7 +305,7 @@ retry:
        }
        if (!s) {
                spin_unlock(&sb_lock);
-               s = alloc_super();
+               s = alloc_super(type);
                if (!s)
                        return ERR_PTR(-ENOMEM);
                goto retry;
@@ -488,7 +495,7 @@ asmlinkage long sys_ustat(unsigned dev, struct ustat __user * ubuf)
         s = user_get_super(new_decode_dev(dev));
         if (s == NULL)
                 goto out;
-       err = vfs_statfs(s, &sbuf);
+       err = vfs_statfs(s->s_root, &sbuf);
        drop_super(s);
        if (err)
                goto out;
@@ -678,9 +685,10 @@ static void bdev_uevent(struct block_device *bdev, enum kobject_action action)
        }
 }
 
-struct super_block *get_sb_bdev(struct file_system_type *fs_type,
+int get_sb_bdev(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data,
-       int (*fill_super)(struct super_block *, void *, int))
+       int (*fill_super)(struct super_block *, void *, int),
+       struct vfsmount *mnt)
 {
        struct block_device *bdev;
        struct super_block *s;
@@ -688,7 +696,7 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
 
        bdev = open_bdev_excl(dev_name, flags, fs_type);
        if (IS_ERR(bdev))
-               return (struct super_block *)bdev;
+               return PTR_ERR(bdev);
 
        /*
         * once the super is inserted into the list by sget, s_umount
@@ -699,15 +707,17 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
        s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
        mutex_unlock(&bdev->bd_mount_mutex);
        if (IS_ERR(s))
-               goto out;
+               goto error_s;
 
        if (s->s_root) {
                if ((flags ^ s->s_flags) & MS_RDONLY) {
                        up_write(&s->s_umount);
                        deactivate_super(s);
-                       s = ERR_PTR(-EBUSY);
+                       error = -EBUSY;
+                       goto error_bdev;
                }
-               goto out;
+
+               close_bdev_excl(bdev);
        } else {
                char b[BDEVNAME_SIZE];
 
@@ -718,18 +728,21 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
                if (error) {
                        up_write(&s->s_umount);
                        deactivate_super(s);
-                       s = ERR_PTR(error);
-               } else {
-                       s->s_flags |= MS_ACTIVE;
-                       bdev_uevent(bdev, KOBJ_MOUNT);
+                       goto error;
                }
+
+               s->s_flags |= MS_ACTIVE;
+               bdev_uevent(bdev, KOBJ_MOUNT);
        }
 
-       return s;
+       return simple_set_mnt(mnt, s);
 
-out:
+error_s:
+       error = PTR_ERR(s);
+error_bdev:
        close_bdev_excl(bdev);
-       return s;
+error:
+       return error;
 }
 
 EXPORT_SYMBOL(get_sb_bdev);
@@ -746,15 +759,16 @@ void kill_block_super(struct super_block *sb)
 
 EXPORT_SYMBOL(kill_block_super);
 
-struct super_block *get_sb_nodev(struct file_system_type *fs_type,
+int get_sb_nodev(struct file_system_type *fs_type,
        int flags, void *data,
-       int (*fill_super)(struct super_block *, void *, int))
+       int (*fill_super)(struct super_block *, void *, int),
+       struct vfsmount *mnt)
 {
        int error;
        struct super_block *s = sget(fs_type, NULL, set_anon_super, NULL);
 
        if (IS_ERR(s))
-               return s;
+               return PTR_ERR(s);
 
        s->s_flags = flags;
 
@@ -762,10 +776,10 @@ struct super_block *get_sb_nodev(struct file_system_type *fs_type,
        if (error) {
                up_write(&s->s_umount);
                deactivate_super(s);
-               return ERR_PTR(error);
+               return error;
        }
        s->s_flags |= MS_ACTIVE;
-       return s;
+       return simple_set_mnt(mnt, s);
 }
 
 EXPORT_SYMBOL(get_sb_nodev);
@@ -775,107 +789,116 @@ static int compare_single(struct super_block *s, void *p)
        return 1;
 }
 
-struct super_block *get_sb_single(struct file_system_type *fs_type,
+int get_sb_single(struct file_system_type *fs_type,
        int flags, void *data,
-       int (*fill_super)(struct super_block *, void *, int))
+       int (*fill_super)(struct super_block *, void *, int),
+       struct vfsmount *mnt)
 {
        struct super_block *s;
        int error;
 
        s = sget(fs_type, compare_single, set_anon_super, NULL);
        if (IS_ERR(s))
-               return s;
+               return PTR_ERR(s);
        if (!s->s_root) {
                s->s_flags = flags;
                error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                if (error) {
                        up_write(&s->s_umount);
                        deactivate_super(s);
-                       return ERR_PTR(error);
+                       return error;
                }
                s->s_flags |= MS_ACTIVE;
        }
        do_remount_sb(s, flags, data, 0);
-       return s;
+       return simple_set_mnt(mnt, s);
 }
 
 EXPORT_SYMBOL(get_sb_single);
 
 struct vfsmount *
-do_kern_mount(const char *fstype, int flags, const char *name, void *data)
+vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
 {
-       struct file_system_type *type = get_fs_type(fstype);
-       struct super_block *sb;
        struct vfsmount *mnt;
-       int error;
+       struct super_block *sb;
        char *secdata = NULL;
+       int error;
 
        if (!type)
                return ERR_PTR(-ENODEV);
 
-       sb = ERR_PTR(-EPERM);
-       if ((type->fs_flags & FS_BINARY_MOUNTDATA) &&
-               !vx_capable(CAP_SYS_ADMIN, VXC_BINARY_MOUNT))
-               goto out;
-
-       sb = ERR_PTR(-ENOMEM);
+       error = -ENOMEM;
        mnt = alloc_vfsmnt(name);
        if (!mnt)
                goto out;
 
        if (data) {
                secdata = alloc_secdata();
-               if (!secdata) {
-                       sb = ERR_PTR(-ENOMEM);
+               if (!secdata)
                        goto out_mnt;
-               }
 
                error = security_sb_copy_data(type, data, secdata);
-               if (error) {
-                       sb = ERR_PTR(error);
+               if (error)
                        goto out_free_secdata;
-               }
        }
 
-       sb = type->get_sb(type, flags, name, data);
-       if (IS_ERR(sb))
+       error = type->get_sb(type, flags, name, data, mnt);
+       if (error < 0)
                goto out_free_secdata;
 
+       sb = mnt->mnt_sb;
        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);
+       error = security_sb_kern_mount(sb, secdata);
        if (error)
                goto out_sb;
-       mnt->mnt_sb = sb;
-       mnt->mnt_root = dget(sb->s_root);
-       mnt->mnt_mountpoint = sb->s_root;
+
+       mnt->mnt_mountpoint = mnt->mnt_root;
        mnt->mnt_parent = mnt;
-       up_write(&sb->s_umount);
+       up_write(&mnt->mnt_sb->s_umount);
        free_secdata(secdata);
-       put_filesystem(type);
        return mnt;
 out_sb:
-       up_write(&sb->s_umount);
-       deactivate_super(sb);
-       sb = ERR_PTR(error);
+       dput(mnt->mnt_root);
+       up_write(&mnt->mnt_sb->s_umount);
+       deactivate_super(mnt->mnt_sb);
 out_free_secdata:
        free_secdata(secdata);
 out_mnt:
        free_vfsmnt(mnt);
 out:
-       put_filesystem(type);
-       return (struct vfsmount *)sb;
+       return ERR_PTR(error);
 }
 
-EXPORT_SYMBOL_GPL(do_kern_mount);
+EXPORT_SYMBOL_GPL(vfs_kern_mount);
+
+struct vfsmount *
+do_kern_mount(const char *fstype, int flags, const char *name, void *data)
+{
+       struct file_system_type *type = get_fs_type(fstype);
+       struct vfsmount *mnt;
+
+       if (!type)
+               return ERR_PTR(-ENODEV);
+
+       mnt = ERR_PTR(-EPERM);
+       if ((type->fs_flags & FS_BINARY_MOUNTDATA) &&
+               !vx_capable(CAP_SYS_ADMIN, VXC_BINARY_MOUNT))
+               goto out_put;
+
+       mnt = vfs_kern_mount(type, flags, name, data);
+out_put:
+       put_filesystem(type);
+       return mnt;
+}
 
 struct vfsmount *kern_mount(struct file_system_type *type)
 {
-       return do_kern_mount(type->name, 0, type->name, NULL);
+       return vfs_kern_mount(type, 0, type->name, NULL);
 }
 
 EXPORT_SYMBOL(kern_mount);