fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / super.c
index 87845e4..4c60290 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>
 #include <linux/writeback.h>           /* for the emergency remount stuff */
 #include <linux/idr.h>
 #include <linux/kobject.h>
+#include <linux/mutex.h>
 #include <linux/devpts_fs.h>
 #include <linux/proc_fs.h>
+#include <linux/vs_context.h>
 #include <asm/uaccess.h>
 
 
@@ -51,17 +52,17 @@ 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 = kmalloc(sizeof(struct super_block),  GFP_USER);
+       struct super_block *s = kzalloc(sizeof(struct super_block),  GFP_USER);
        static struct super_operations default_op;
 
        if (s) {
-               memset(s, 0, sizeof(struct super_block));
                if (security_sb_alloc(s)) {
                        kfree(s);
                        s = NULL;
@@ -75,12 +76,19 @@ 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);
-               sema_init(&s->s_vfs_rename_sem,1);
-               sema_init(&s->s_dquot.dqio_sem, 1);
-               sema_init(&s->s_dquot.dqonoff_sem, 1);
+               mutex_init(&s->s_vfs_rename_mutex);
+               mutex_init(&s->s_dquot.dqio_mutex);
+               mutex_init(&s->s_dquot.dqonoff_mutex);
                init_rwsem(&s->s_dquot.dqptr_sem);
                init_waitqueue_head(&s->s_wait_unfrozen);
                s->s_maxbytes = MAX_NON_LFS;
@@ -194,7 +202,7 @@ EXPORT_SYMBOL(deactivate_super);
  *     success, 0 if we had failed (superblock contents was already dead or
  *     dying when grab_super() had been called).
  */
-static int grab_super(struct super_block *s)
+static int grab_super(struct super_block *s) __releases(sb_lock)
 {
        s->s_count++;
        spin_unlock(&sb_lock);
@@ -215,6 +223,55 @@ static int grab_super(struct super_block *s)
        return 0;
 }
 
+/*
+ * Superblock locking.  We really ought to get rid of these two.
+ */
+void lock_super(struct super_block * sb)
+{
+       get_fs_excl();
+       mutex_lock(&sb->s_lock);
+}
+
+void unlock_super(struct super_block * sb)
+{
+       put_fs_excl();
+       mutex_unlock(&sb->s_lock);
+}
+
+EXPORT_SYMBOL(lock_super);
+EXPORT_SYMBOL(unlock_super);
+
+/*
+ * Write out and wait upon all dirty data associated with this
+ * superblock.  Filesystem data as well as the underlying block
+ * device.  Takes the superblock lock.  Requires a second blkdev
+ * flush by the caller to complete the operation.
+ */
+void __fsync_super(struct super_block *sb)
+{
+       sync_inodes_sb(sb, 0);
+       DQUOT_SYNC(sb);
+       lock_super(sb);
+       if (sb->s_dirt && sb->s_op->write_super)
+               sb->s_op->write_super(sb);
+       unlock_super(sb);
+       if (sb->s_op->sync_fs)
+               sb->s_op->sync_fs(sb, 1);
+       sync_blockdev(sb->s_bdev);
+       sync_inodes_sb(sb, 1);
+}
+
+/*
+ * Write out and wait upon all dirty data associated with this
+ * superblock.  Filesystem data as well as the underlying block
+ * device.  Takes the superblock lock.
+ */
+int fsync_super(struct super_block *sb)
+{
+       __fsync_super(sb);
+       return sync_blockdev(sb->s_bdev);
+}
+
 /**
  *     generic_shutdown_super  -       common helper for ->kill_sb()
  *     @sb: superblock to kill
@@ -224,17 +281,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 +355,7 @@ retry:
        }
        if (!s) {
                spin_unlock(&sb_lock);
-               s = alloc_super();
+               s = alloc_super(type);
                if (!s)
                        return ERR_PTR(-ENOMEM);
                goto retry;
@@ -383,9 +440,9 @@ restart:
 void sync_filesystems(int wait)
 {
        struct super_block *sb;
-       static DECLARE_MUTEX(mutex);
+       static DEFINE_MUTEX(mutex);
 
-       down(&mutex);           /* Could be down_interruptible */
+       mutex_lock(&mutex);             /* Could be down_interruptible */
        spin_lock(&sb_lock);
        list_for_each_entry(sb, &super_blocks, s_list) {
                if (!sb->s_op->sync_fs)
@@ -414,7 +471,7 @@ restart:
                        goto restart;
        }
        spin_unlock(&sb_lock);
-       up(&mutex);
+       mutex_unlock(&mutex);
 }
 
 /**
@@ -488,7 +545,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;
@@ -516,7 +573,7 @@ static void mark_files_ro(struct super_block *sb)
 
        file_list_lock();
        list_for_each_entry(f, &sb->s_files, f_u.fu_list) {
-               if (S_ISREG(f->f_dentry->d_inode->i_mode) && file_count(f))
+               if (S_ISREG(f->f_path.dentry->d_inode->i_mode) && file_count(f))
                        f->f_mode &= ~FMODE_WRITE;
        }
        file_list_unlock();
@@ -535,8 +592,10 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
 {
        int retval;
        
+#ifdef CONFIG_BLOCK
        if (!(flags & MS_RDONLY) && bdev_read_only(sb->s_bdev))
                return -EACCES;
+#endif
        if (flags & MS_RDONLY)
                acct_auto_close(sb);
        shrink_dcache_sb(sb);
@@ -656,6 +715,7 @@ void kill_litter_super(struct super_block *sb)
 
 EXPORT_SYMBOL(kill_litter_super);
 
+#ifdef CONFIG_BLOCK
 static int set_bdev_super(struct super_block *s, void *data)
 {
        s->s_bdev = data;
@@ -678,9 +738,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 +749,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,37 +760,42 @@ struct super_block *get_sb_bdev(struct file_system_type *fs_type,
        s = sget(fs_type, test_bdev_super, set_bdev_super, bdev);
        up(&bdev->bd_mount_sem);
        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];
 
                s->s_flags = flags;
                strlcpy(s->s_id, bdevname(bdev, b), sizeof(s->s_id));
                sb_set_blocksize(s, block_size(bdev));
-               error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+               error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
                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);
@@ -745,27 +811,29 @@ void kill_block_super(struct super_block *sb)
 }
 
 EXPORT_SYMBOL(kill_block_super);
+#endif
 
-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;
 
-       error = fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+       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;
-       return s;
+       return simple_set_mnt(mnt, s);
 }
 
 EXPORT_SYMBOL(get_sb_nodev);
@@ -775,107 +843,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_VERBOSE ? 1 : 0);
+               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 &&
+       if (!vx_capable(CAP_SYS_ADMIN, VXC_BINARY_MOUNT) && !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);