ufs: NFS support
[safe/jmp/linux-2.6] / fs / ufs / super.c
index 506f724..143c20b 100644 (file)
@@ -41,7 +41,7 @@
  * Stefan Reinauer <stepan@home.culture.mipt.ru>
  *
  * Module usage counts added on 96/04/29 by
- * Gertjan van Wingerde <gertjan@cs.vu.nl>
+ * Gertjan van Wingerde <gwingerde@gmail.com>
  *
  * Clean swab support on 19970406 by
  * Francois-Rene Rideau <fare@tunes.org>
@@ -66,6 +66,7 @@
  */
 
 
+#include <linux/exportfs.h>
 #include <linux/module.h>
 #include <linux/bitops.h>
 
@@ -76,6 +77,7 @@
 
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/quotaops.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/stat.h>
 #include "swab.h"
 #include "util.h"
 
+static struct inode *ufs_nfs_get_inode(struct super_block *sb, u64 ino, u32 generation)
+{
+       struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+       struct inode *inode;
+
+       if (ino < UFS_ROOTINO || ino > uspi->s_ncg * uspi->s_ipg)
+               return ERR_PTR(-ESTALE);
+
+       inode = ufs_iget(sb, ino);
+       if (IS_ERR(inode))
+               return ERR_CAST(inode);
+       if (generation && inode->i_generation != generation) {
+               iput(inode);
+               return ERR_PTR(-ESTALE);
+       }
+       return inode;
+}
+
+static struct dentry *ufs_fh_to_dentry(struct super_block *sb, struct fid *fid,
+                                      int fh_len, int fh_type)
+{
+       return generic_fh_to_dentry(sb, fid, fh_len, fh_type, ufs_nfs_get_inode);
+}
+
+static struct dentry *ufs_fh_to_parent(struct super_block *sb, struct fid *fid,
+                                      int fh_len, int fh_type)
+{
+       return generic_fh_to_parent(sb, fid, fh_len, fh_type, ufs_nfs_get_inode);
+}
+
+static struct dentry *ufs_get_parent(struct dentry *child)
+{
+       struct qstr dot_dot = {
+               .name   = "..",
+               .len    = 2,
+       };
+       ino_t ino;
+
+       ino = ufs_inode_by_name(child->d_inode, &dot_dot);
+       if (!ino)
+               return ERR_PTR(-ENOENT);
+       return d_obtain_alias(ufs_iget(child->d_inode->i_sb, ino));
+}
+
+static const struct export_operations ufs_export_ops = {
+       .fh_to_dentry   = ufs_fh_to_dentry,
+       .fh_to_parent   = ufs_fh_to_parent,
+       .get_parent     = ufs_get_parent,
+};
+
 #ifdef CONFIG_UFS_DEBUG
 /*
  * Print contents of ufs_super_block, useful for debugging
@@ -262,6 +314,7 @@ void ufs_panic (struct super_block * sb, const char * function,
        struct ufs_super_block_first * usb1;
        va_list args;
        
+       lock_kernel();
        uspi = UFS_SB(sb)->s_uspi;
        usb1 = ubh_get_usb_first(uspi);
        
@@ -308,7 +361,7 @@ enum {
        Opt_err
 };
 
-static match_table_t tokens = {
+static const match_table_t tokens = {
        {Opt_type_old, "ufstype=old"},
        {Opt_type_sunx86, "ufstype=sunx86"},
        {Opt_type_sun, "ufstype=sun"},
@@ -593,6 +646,9 @@ static void ufs_put_super_internal(struct super_block *sb)
 
        
        UFSD("ENTER\n");
+
+       lock_kernel();
+
        ufs_put_cstotal(sb);
        size = uspi->s_cssize;
        blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
@@ -620,6 +676,9 @@ static void ufs_put_super_internal(struct super_block *sb)
                brelse (sbi->s_ucg[i]);
        kfree (sbi->s_ucg);
        kfree (base);
+
+       unlock_kernel();
+
        UFSD("EXIT\n");
 }
 
@@ -635,6 +694,7 @@ static int ufs_fill_super(struct super_block *sb, void *data, int silent)
        unsigned block_size, super_block_size;
        unsigned flags;
        unsigned super_block_offset;
+       unsigned maxsymlen;
        int ret = -EINVAL;
 
        uspi = NULL;
@@ -981,6 +1041,7 @@ magic_found:
         * Read ufs_super_block into internal data structures
         */
        sb->s_op = &ufs_super_ops;
+       sb->s_export_op = &ufs_export_ops;
        sb->dq_op = NULL; /***/
        sb->s_magic = fs32_to_cpu(sb, usb3->fs_magic);
 
@@ -1068,6 +1129,16 @@ magic_found:
                uspi->s_maxsymlinklen =
                    fs32_to_cpu(sb, usb3->fs_un2.fs_44.fs_maxsymlinklen);
 
+       if (uspi->fs_magic == UFS2_MAGIC)
+               maxsymlen = 2 * 4 * (UFS_NDADDR + UFS_NINDIR);
+       else
+               maxsymlen = 4 * (UFS_NDADDR + UFS_NINDIR);
+       if (uspi->s_maxsymlinklen > maxsymlen) {
+               ufs_warning(sb, __func__, "ufs_read_super: excessive maximum "
+                           "fast symlink size (%u)\n", uspi->s_maxsymlinklen);
+               uspi->s_maxsymlinklen = maxsymlen;
+       }
+
        inode = ufs_iget(sb, UFS_ROOTINO);
        if (IS_ERR(inode)) {
                ret = PTR_ERR(inode);
@@ -1106,32 +1177,45 @@ failed_nomem:
        return -ENOMEM;
 }
 
-static void ufs_write_super(struct super_block *sb)
+static int ufs_sync_fs(struct super_block *sb, int wait)
 {
        struct ufs_sb_private_info * uspi;
        struct ufs_super_block_first * usb1;
        struct ufs_super_block_third * usb3;
        unsigned flags;
 
+       lock_super(sb);
        lock_kernel();
+
        UFSD("ENTER\n");
+
        flags = UFS_SB(sb)->s_flags;
        uspi = UFS_SB(sb)->s_uspi;
        usb1 = ubh_get_usb_first(uspi);
        usb3 = ubh_get_usb_third(uspi);
 
-       if (!(sb->s_flags & MS_RDONLY)) {
-               usb1->fs_time = cpu_to_fs32(sb, get_seconds());
-               if ((flags & UFS_ST_MASK) == UFS_ST_SUN 
-                 || (flags & UFS_ST_MASK) == UFS_ST_SUNOS
-                 || (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
-                       ufs_set_fs_state(sb, usb1, usb3,
-                                       UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
-               ufs_put_cstotal(sb);
-       }
+       usb1->fs_time = cpu_to_fs32(sb, get_seconds());
+       if ((flags & UFS_ST_MASK) == UFS_ST_SUN  ||
+           (flags & UFS_ST_MASK) == UFS_ST_SUNOS ||
+           (flags & UFS_ST_MASK) == UFS_ST_SUNx86)
+               ufs_set_fs_state(sb, usb1, usb3,
+                               UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time));
+       ufs_put_cstotal(sb);
        sb->s_dirt = 0;
+
        UFSD("EXIT\n");
        unlock_kernel();
+       unlock_super(sb);
+
+       return 0;
+}
+
+static void ufs_write_super(struct super_block *sb)
+{
+       if (!(sb->s_flags & MS_RDONLY))
+               ufs_sync_fs(sb, 1);
+       else
+               sb->s_dirt = 0;
 }
 
 static void ufs_put_super(struct super_block *sb)
@@ -1140,6 +1224,9 @@ static void ufs_put_super(struct super_block *sb)
                
        UFSD("ENTER\n");
 
+       if (sb->s_dirt)
+               ufs_write_super(sb);
+
        if (!(sb->s_flags & MS_RDONLY))
                ufs_put_super_internal(sb);
        
@@ -1159,7 +1246,9 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
        struct ufs_super_block_third * usb3;
        unsigned new_mount_opt, ufstype;
        unsigned flags;
-       
+
+       lock_kernel();
+       lock_super(sb);
        uspi = UFS_SB(sb)->s_uspi;
        flags = UFS_SB(sb)->s_flags;
        usb1 = ubh_get_usb_first(uspi);
@@ -1172,17 +1261,24 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
        ufstype = UFS_SB(sb)->s_mount_opt & UFS_MOUNT_UFSTYPE;
        new_mount_opt = 0;
        ufs_set_opt (new_mount_opt, ONERROR_LOCK);
-       if (!ufs_parse_options (data, &new_mount_opt))
+       if (!ufs_parse_options (data, &new_mount_opt)) {
+               unlock_super(sb);
+               unlock_kernel();
                return -EINVAL;
+       }
        if (!(new_mount_opt & UFS_MOUNT_UFSTYPE)) {
                new_mount_opt |= ufstype;
        } else if ((new_mount_opt & UFS_MOUNT_UFSTYPE) != ufstype) {
                printk("ufstype can't be changed during remount\n");
+               unlock_super(sb);
+               unlock_kernel();
                return -EINVAL;
        }
 
        if ((*mount_flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY)) {
                UFS_SB(sb)->s_mount_opt = new_mount_opt;
+               unlock_super(sb);
+               unlock_kernel();
                return 0;
        }
        
@@ -1207,6 +1303,8 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
 #ifndef CONFIG_UFS_FS_WRITE
                printk("ufs was compiled with read-only support, "
                "can't be mounted as read-write\n");
+               unlock_super(sb);
+               unlock_kernel();
                return -EINVAL;
 #else
                if (ufstype != UFS_MOUNT_UFSTYPE_SUN && 
@@ -1215,16 +1313,22 @@ static int ufs_remount (struct super_block *sb, int *mount_flags, char *data)
                    ufstype != UFS_MOUNT_UFSTYPE_SUNx86 &&
                    ufstype != UFS_MOUNT_UFSTYPE_UFS2) {
                        printk("this ufstype is read-only supported\n");
+                       unlock_super(sb);
+                       unlock_kernel();
                        return -EINVAL;
                }
                if (!ufs_read_cylinder_structures(sb)) {
                        printk("failed during remounting\n");
+                       unlock_super(sb);
+                       unlock_kernel();
                        return -EPERM;
                }
                sb->s_flags &= ~MS_RDONLY;
 #endif
        }
        UFS_SB(sb)->s_mount_opt = new_mount_opt;
+       unlock_super(sb);
+       unlock_kernel();
        return 0;
 }
 
@@ -1256,6 +1360,7 @@ static int ufs_statfs(struct dentry *dentry, struct kstatfs *buf)
        struct ufs_super_block_first *usb1;
        struct ufs_super_block_second *usb2;
        struct ufs_super_block_third *usb3;
+       u64 id = huge_encode_dev(sb->s_bdev->bd_dev);
 
        lock_kernel();
 
@@ -1278,6 +1383,8 @@ static int ufs_statfs(struct dentry *dentry, struct kstatfs *buf)
                ? (buf->f_bfree - (((long)buf->f_blocks / 100) * uspi->s_minfree)) : 0;
        buf->f_files = uspi->s_ncg * uspi->s_ipg;
        buf->f_namelen = UFS_MAXNAMLEN;
+       buf->f_fsid.val[0] = (u32)id;
+       buf->f_fsid.val[1] = (u32)(id >> 32);
 
        unlock_kernel();
 
@@ -1301,7 +1408,7 @@ static void ufs_destroy_inode(struct inode *inode)
        kmem_cache_free(ufs_inode_cachep, UFS_I(inode));
 }
 
-static void init_once(struct kmem_cache * cachep, void *foo)
+static void init_once(void *foo)
 {
        struct ufs_inode_info *ei = (struct ufs_inode_info *) foo;
 
@@ -1337,6 +1444,7 @@ static const struct super_operations ufs_super_ops = {
        .delete_inode   = ufs_delete_inode,
        .put_super      = ufs_put_super,
        .write_super    = ufs_write_super,
+       .sync_fs        = ufs_sync_fs,
        .statfs         = ufs_statfs,
        .remount_fs     = ufs_remount,
        .show_options   = ufs_show_options,