]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/ext2/super.c
ext2: new export ops
[linux-2.6-omap-h63xx.git] / fs / ext2 / super.c
index 68579a0ed3f0a6617b8ecbe59e9ce826f5be2f2d..4a03d0c84df2a4276d3b688345ab211bf839261a 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/vfs.h>
 #include <linux/seq_file.h>
 #include <linux/mount.h>
+#include <linux/log2.h>
 #include <asm/uaccess.h>
 #include "ext2.h"
 #include "xattr.h"
@@ -148,6 +149,7 @@ static struct inode *ext2_alloc_inode(struct super_block *sb)
        ei->i_acl = EXT2_ACL_NOT_CACHED;
        ei->i_default_acl = EXT2_ACL_NOT_CACHED;
 #endif
+       ei->i_block_alloc_info = NULL;
        ei->vfs_inode.i_version = 1;
        return &ei->vfs_inode;
 }
@@ -157,7 +159,7 @@ static void ext2_destroy_inode(struct inode *inode)
        kmem_cache_free(ext2_inode_cachep, EXT2_I(inode));
 }
 
-static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flags)
+static void init_once(struct kmem_cache * cachep, void *foo)
 {
        struct ext2_inode_info *ei = (struct ext2_inode_info *) foo;
 
@@ -165,6 +167,7 @@ static void init_once(void * foo, struct kmem_cache * cachep, unsigned long flag
 #ifdef CONFIG_EXT2_FS_XATTR
        init_rwsem(&ei->xattr_sem);
 #endif
+       mutex_init(&ei->truncate_mutex);
        inode_init_once(&ei->vfs_inode);
 }
 
@@ -187,6 +190,7 @@ static void destroy_inodecache(void)
 
 static void ext2_clear_inode(struct inode *inode)
 {
+       struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info;
 #ifdef CONFIG_EXT2_FS_POSIX_ACL
        struct ext2_inode_info *ei = EXT2_I(inode);
 
@@ -199,14 +203,74 @@ static void ext2_clear_inode(struct inode *inode)
                ei->i_default_acl = EXT2_ACL_NOT_CACHED;
        }
 #endif
+       ext2_discard_reservation(inode);
+       EXT2_I(inode)->i_block_alloc_info = NULL;
+       if (unlikely(rsv))
+               kfree(rsv);
 }
 
 static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs)
 {
-       struct ext2_sb_info *sbi = EXT2_SB(vfs->mnt_sb);
+       struct super_block *sb = vfs->mnt_sb;
+       struct ext2_sb_info *sbi = EXT2_SB(sb);
+       struct ext2_super_block *es = sbi->s_es;
+       unsigned long def_mount_opts;
+
+       def_mount_opts = le32_to_cpu(es->s_default_mount_opts);
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_GRPID)
+       if (sbi->s_sb_block != 1)
+               seq_printf(seq, ",sb=%lu", sbi->s_sb_block);
+       if (test_opt(sb, MINIX_DF))
+               seq_puts(seq, ",minixdf");
+       if (test_opt(sb, GRPID))
                seq_puts(seq, ",grpid");
+       if (!test_opt(sb, GRPID) && (def_mount_opts & EXT2_DEFM_BSDGROUPS))
+               seq_puts(seq, ",nogrpid");
+       if (sbi->s_resuid != EXT2_DEF_RESUID ||
+           le16_to_cpu(es->s_def_resuid) != EXT2_DEF_RESUID) {
+               seq_printf(seq, ",resuid=%u", sbi->s_resuid);
+       }
+       if (sbi->s_resgid != EXT2_DEF_RESGID ||
+           le16_to_cpu(es->s_def_resgid) != EXT2_DEF_RESGID) {
+               seq_printf(seq, ",resgid=%u", sbi->s_resgid);
+       }
+       if (test_opt(sb, ERRORS_CONT)) {
+               int def_errors = le16_to_cpu(es->s_errors);
+
+               if (def_errors == EXT2_ERRORS_PANIC ||
+                   def_errors == EXT2_ERRORS_RO) {
+                       seq_puts(seq, ",errors=continue");
+               }
+       }
+       if (test_opt(sb, ERRORS_RO))
+               seq_puts(seq, ",errors=remount-ro");
+       if (test_opt(sb, ERRORS_PANIC))
+               seq_puts(seq, ",errors=panic");
+       if (test_opt(sb, NO_UID32))
+               seq_puts(seq, ",nouid32");
+       if (test_opt(sb, DEBUG))
+               seq_puts(seq, ",debug");
+       if (test_opt(sb, OLDALLOC))
+               seq_puts(seq, ",oldalloc");
+
+#ifdef CONFIG_EXT2_FS_XATTR
+       if (test_opt(sb, XATTR_USER))
+               seq_puts(seq, ",user_xattr");
+       if (!test_opt(sb, XATTR_USER) &&
+           (def_mount_opts & EXT2_DEFM_XATTR_USER)) {
+               seq_puts(seq, ",nouser_xattr");
+       }
+#endif
+
+#ifdef CONFIG_EXT2_FS_POSIX_ACL
+       if (test_opt(sb, POSIX_ACL))
+               seq_puts(seq, ",acl");
+       if (!test_opt(sb, POSIX_ACL) && (def_mount_opts & EXT2_DEFM_ACL))
+               seq_puts(seq, ",noacl");
+#endif
+
+       if (test_opt(sb, NOBH))
+               seq_puts(seq, ",nobh");
 
 #if defined(CONFIG_QUOTA)
        if (sbi->s_mount_opt & EXT2_MOUNT_USRQUOTA)
@@ -234,7 +298,6 @@ static const struct super_operations ext2_sops = {
        .destroy_inode  = ext2_destroy_inode,
        .read_inode     = ext2_read_inode,
        .write_inode    = ext2_write_inode,
-       .put_inode      = ext2_put_inode,
        .delete_inode   = ext2_delete_inode,
        .put_super      = ext2_put_super,
        .write_super    = ext2_write_super,
@@ -248,13 +311,10 @@ static const struct super_operations ext2_sops = {
 #endif
 };
 
-static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp)
+static struct inode *ext2_nfs_get_inode(struct super_block *sb,
+               u64 ino, u32 generation)
 {
-       __u32 *objp = vobjp;
-       unsigned long ino = objp[0];
-       __u32 generation = objp[1];
        struct inode *inode;
-       struct dentry *result;
 
        if (ino < EXT2_FIRST_INO(sb) && ino != EXT2_ROOT_INO)
                return ERR_PTR(-ESTALE);
@@ -275,15 +335,21 @@ static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp)
                iput(inode);
                return ERR_PTR(-ESTALE);
        }
-       /* now to find a dentry.
-        * If possible, get a well-connected one
-        */
-       result = d_alloc_anon(inode);
-       if (!result) {
-               iput(inode);
-               return ERR_PTR(-ENOMEM);
-       }
-       return result;
+       return inode;
+}
+
+static struct dentry *ext2_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,
+                                   ext2_nfs_get_inode);
+}
+
+static struct dentry *ext2_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,
+                                   ext2_nfs_get_inode);
 }
 
 /* Yes, most of these are left as NULL!!
@@ -292,8 +358,9 @@ static struct dentry *ext2_get_dentry(struct super_block *sb, void *vobjp)
  * Currently only get_parent is required.
  */
 static struct export_operations ext2_export_ops = {
+       .fh_to_dentry = ext2_fh_to_dentry,
+       .fh_to_parent = ext2_fh_to_parent,
        .get_parent = ext2_get_parent,
-       .get_dentry = ext2_get_dentry,
 };
 
 static unsigned long get_sb_block(void **data)
@@ -322,7 +389,7 @@ enum {
        Opt_err_ro, Opt_nouid32, Opt_nocheck, Opt_debug,
        Opt_oldalloc, Opt_orlov, Opt_nobh, Opt_user_xattr, Opt_nouser_xattr,
        Opt_acl, Opt_noacl, Opt_xip, Opt_ignore, Opt_err, Opt_quota,
-       Opt_usrquota, Opt_grpquota
+       Opt_usrquota, Opt_grpquota, Opt_reservation, Opt_noreservation
 };
 
 static match_table_t tokens = {
@@ -354,6 +421,8 @@ static match_table_t tokens = {
        {Opt_ignore, "noquota"},
        {Opt_quota, "quota"},
        {Opt_usrquota, "usrquota"},
+       {Opt_reservation, "reservation"},
+       {Opt_noreservation, "noreservation"},
        {Opt_err, NULL}
 };
 
@@ -486,6 +555,14 @@ static int parse_options (char * options,
                        break;
 #endif
 
+               case Opt_reservation:
+                       set_opt(sbi->s_mount_opt, RESERVATION);
+                       printk("reservations ON\n");
+                       break;
+               case Opt_noreservation:
+                       clear_opt(sbi->s_mount_opt, RESERVATION);
+                       printk("reservations OFF\n");
+                       break;
                case Opt_ignore:
                        break;
                default:
@@ -580,7 +657,7 @@ static int ext2_check_descriptors (struct super_block * sb)
                        return 0;
                }
                if (le32_to_cpu(gdp->bg_inode_table) < first_block ||
-                   le32_to_cpu(gdp->bg_inode_table) + sbi->s_itb_per_group >
+                   le32_to_cpu(gdp->bg_inode_table) + sbi->s_itb_per_group - 1 >
                    last_block)
                {
                        ext2_error (sb, "ext2_check_descriptors",
@@ -653,11 +730,13 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        int db_count;
        int i, j;
        __le32 features;
+       int err;
 
        sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
        if (!sbi)
                return -ENOMEM;
        sb->s_fs_info = sbi;
+       sbi->s_sb_block = sb_block;
 
        /*
         * See what the current blocksize for the device is, and
@@ -725,6 +804,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_resuid = le16_to_cpu(es->s_def_resuid);
        sbi->s_resgid = le16_to_cpu(es->s_def_resgid);
        
+       set_opt(sbi->s_mount_opt, RESERVATION);
+
        if (!parse_options ((char *) data, sbi))
                goto failed_mount;
 
@@ -804,7 +885,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
                sbi->s_inode_size = le16_to_cpu(es->s_inode_size);
                sbi->s_first_ino = le32_to_cpu(es->s_first_ino);
                if ((sbi->s_inode_size < EXT2_GOOD_OLD_INODE_SIZE) ||
-                   (sbi->s_inode_size & (sbi->s_inode_size - 1)) ||
+                   !is_power_of_2(sbi->s_inode_size) ||
                    (sbi->s_inode_size > blocksize)) {
                        printk ("EXT2-fs: unsupported inode size: %d\n",
                                sbi->s_inode_size);
@@ -906,12 +987,35 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        get_random_bytes(&sbi->s_next_generation, sizeof(u32));
        spin_lock_init(&sbi->s_next_gen_lock);
 
-       percpu_counter_init(&sbi->s_freeblocks_counter,
+       /* per fileystem reservation list head & lock */
+       spin_lock_init(&sbi->s_rsv_window_lock);
+       sbi->s_rsv_window_root = RB_ROOT;
+       /*
+        * Add a single, static dummy reservation to the start of the
+        * reservation window list --- it gives us a placeholder for
+        * append-at-start-of-list which makes the allocation logic
+        * _much_ simpler.
+        */
+       sbi->s_rsv_window_head.rsv_start = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
+       sbi->s_rsv_window_head.rsv_end = EXT2_RESERVE_WINDOW_NOT_ALLOCATED;
+       sbi->s_rsv_window_head.rsv_alloc_hit = 0;
+       sbi->s_rsv_window_head.rsv_goal_size = 0;
+       ext2_rsv_window_add(sb, &sbi->s_rsv_window_head);
+
+       err = percpu_counter_init(&sbi->s_freeblocks_counter,
                                ext2_count_free_blocks(sb));
-       percpu_counter_init(&sbi->s_freeinodes_counter,
+       if (!err) {
+               err = percpu_counter_init(&sbi->s_freeinodes_counter,
                                ext2_count_free_inodes(sb));
-       percpu_counter_init(&sbi->s_dirs_counter,
+       }
+       if (!err) {
+               err = percpu_counter_init(&sbi->s_dirs_counter,
                                ext2_count_dirs(sb));
+       }
+       if (err) {
+               printk(KERN_ERR "EXT2-fs: insufficient memory\n");
+               goto failed_mount3;
+       }
        /*
         * set up enough so that it can read an inode
         */
@@ -1193,7 +1297,7 @@ static ssize_t ext2_quota_read(struct super_block *sb, int type, char *data,
 
                tmp_bh.b_state = 0;
                err = ext2_get_block(inode, blk, &tmp_bh, 0);
-               if (err)
+               if (err < 0)
                        return err;
                if (!buffer_mapped(&tmp_bh))    /* A hole? */
                        memset(data, 0, tocopy);
@@ -1232,7 +1336,7 @@ static ssize_t ext2_quota_write(struct super_block *sb, int type,
 
                tmp_bh.b_state = 0;
                err = ext2_get_block(inode, blk, &tmp_bh, 1);
-               if (err)
+               if (err < 0)
                        goto out;
                if (offset || tocopy != EXT2_BLOCK_SIZE(sb))
                        bh = sb_bread(sb, tmp_bh.b_blocknr);