Whamcloud - gitweb
Automatically detect when to apply the fixes patch.
authorgord-fig <gord-fig>
Wed, 25 Sep 2002 20:49:08 +0000 (20:49 +0000)
committergord-fig <gord-fig>
Wed, 25 Sep 2002 20:49:08 +0000 (20:49 +0000)
lustre/extN/Makefile.am
lustre/extN/extN.patch-2.4.18-40um [deleted file]

index 05ca190..340a550 100644 (file)
@@ -8,12 +8,13 @@ MODULE = extN
 modulefs_DATA = extN.o
 EXTRA_PROGRAMS = extN
 
-EXTNP =
+EXTNP = 
+EXTN_FIXES = ext3-2.4.18-fixes.diff
 # NOTE: If you are not using a RedHat 12.5 or later kernel, then you need to
 #       apply the following patch first, as it fixes a number of bugs in ext3.
 #       You can either apply it as part of the extN build by uncommenting the
 #       line below, or apply it to the source kernel tree and fix ext3 also.
-# EXTNP = ext3-2.4.18-fixes.diff
+# EXTNP = $(EXTN_FIXES)
 EXTNP+= htree-ext3-2.4.18.diff linux-2.4.18ea-0.8.26.diff
 EXTNP+= ext3-2.4.18-ino_sb_macro.diff extN-misc-fixup.diff
 EXTNC = balloc.c bitmap.c dir.c file.c fsync.c ialloc.c inode.c ioctl.c
@@ -97,7 +98,9 @@ patch-stamp: sed-stamp $(EXTNP)
          (cd $(top_srcdir) && patch -p0) < $(srcdir)/extN.patch-$(RELEASE);  \
        else                                                                  \
          echo "If first patch fails, read NOTE in extN/Makefile.am";         \
-         list='$(EXTNP)'; for p in $$list; do                                \
+         list='$(EXTNP)'; \
+         sed '/extN_mark_inode_dirty/q' $(srcdir)/extN.orig/namei.c | tail -2 | grep i_version >/dev/null || list="$(EXTN_FIXES) $$list"; \
+         for p in $$list; do                                \
            echo "applying patch $$p";                                        \
            sed $(SUB) $(srcdir)/$$p |                                        \
              (cd $(top_srcdir) && patch -p1) || exit $$?;                    \
diff --git a/lustre/extN/extN.patch-2.4.18-40um b/lustre/extN/extN.patch-2.4.18-40um
deleted file mode 100644 (file)
index 99c7fda..0000000
+++ /dev/null
@@ -1,4522 +0,0 @@
---- extN/extN.orig/balloc.c    Wed Sep 25 11:51:33 2002
-+++ extN/balloc.c      Wed Sep 25 14:22:53 2002
-@@ -46,18 +46,18 @@
-       unsigned long desc;
-       struct extN_group_desc * gdp;
--      if (block_group >= sb->u.extN_sb.s_groups_count) {
-+      if (block_group >= EXTN_SB(sb)->s_groups_count) {
-               extN_error (sb, "extN_get_group_desc",
-                           "block_group >= groups_count - "
-                           "block_group = %d, groups_count = %lu",
--                          block_group, sb->u.extN_sb.s_groups_count);
-+                          block_group, EXTN_SB(sb)->s_groups_count);
-               return NULL;
-       }
-       
-       group_desc = block_group / EXTN_DESC_PER_BLOCK(sb);
-       desc = block_group % EXTN_DESC_PER_BLOCK(sb);
--      if (!sb->u.extN_sb.s_group_desc[group_desc]) {
-+      if (!EXTN_SB(sb)->s_group_desc[group_desc]) {
-               extN_error (sb, "extN_get_group_desc",
-                           "Group descriptor not loaded - "
-                           "block_group = %d, group_desc = %lu, desc = %lu",
-@@ -66,9 +66,9 @@
-       }
-       
-       gdp = (struct extN_group_desc *) 
--            sb->u.extN_sb.s_group_desc[group_desc]->b_data;
-+            EXTN_SB(sb)->s_group_desc[group_desc]->b_data;
-       if (bh)
--              *bh = sb->u.extN_sb.s_group_desc[group_desc];
-+              *bh = EXTN_SB(sb)->s_group_desc[group_desc];
-       return gdp + desc;
- }
-@@ -104,8 +104,8 @@
-        * this group.  The IO will be retried next time.
-        */
- error_out:
--      sb->u.extN_sb.s_block_bitmap_number[bitmap_nr] = block_group;
--      sb->u.extN_sb.s_block_bitmap[bitmap_nr] = bh;
-+      EXTN_SB(sb)->s_block_bitmap_number[bitmap_nr] = block_group;
-+      EXTN_SB(sb)->s_block_bitmap[bitmap_nr] = bh;
-       return retval;
- }
-@@ -128,16 +128,17 @@
-       int i, j, retval = 0;
-       unsigned long block_bitmap_number;
-       struct buffer_head * block_bitmap;
-+      struct extN_sb_info *sbi = EXTN_SB(sb);
--      if (block_group >= sb->u.extN_sb.s_groups_count)
-+      if (block_group >= sbi->s_groups_count)
-               extN_panic (sb, "load_block_bitmap",
-                           "block_group >= groups_count - "
-                           "block_group = %d, groups_count = %lu",
--                          block_group, sb->u.extN_sb.s_groups_count);
-+                          block_group, EXTN_SB(sb)->s_groups_count);
--      if (sb->u.extN_sb.s_groups_count <= EXTN_MAX_GROUP_LOADED) {
--              if (sb->u.extN_sb.s_block_bitmap[block_group]) {
--                      if (sb->u.extN_sb.s_block_bitmap_number[block_group] ==
-+      if (sbi->s_groups_count <= EXTN_MAX_GROUP_LOADED) {
-+              if (sbi->s_block_bitmap[block_group]) {
-+                      if (sbi->s_block_bitmap_number[block_group] ==
-                           block_group)
-                               return block_group;
-                       extN_error (sb, "__load_block_bitmap",
-@@ -149,21 +150,20 @@
-               return block_group;
-       }
--      for (i = 0; i < sb->u.extN_sb.s_loaded_block_bitmaps &&
--                  sb->u.extN_sb.s_block_bitmap_number[i] != block_group; i++)
-+      for (i = 0; i < sbi->s_loaded_block_bitmaps &&
-+                  sbi->s_block_bitmap_number[i] != block_group; i++)
-               ;
--      if (i < sb->u.extN_sb.s_loaded_block_bitmaps &&
--          sb->u.extN_sb.s_block_bitmap_number[i] == block_group) {
--              block_bitmap_number = sb->u.extN_sb.s_block_bitmap_number[i];
--              block_bitmap = sb->u.extN_sb.s_block_bitmap[i];
-+      if (i < sbi->s_loaded_block_bitmaps &&
-+          sbi->s_block_bitmap_number[i] == block_group) {
-+              block_bitmap_number = sbi->s_block_bitmap_number[i];
-+              block_bitmap = sbi->s_block_bitmap[i];
-               for (j = i; j > 0; j--) {
--                      sb->u.extN_sb.s_block_bitmap_number[j] =
--                              sb->u.extN_sb.s_block_bitmap_number[j - 1];
--                      sb->u.extN_sb.s_block_bitmap[j] =
--                              sb->u.extN_sb.s_block_bitmap[j - 1];
-+                      sbi->s_block_bitmap_number[j] =
-+                              sbi->s_block_bitmap_number[j - 1];
-+                      sbi->s_block_bitmap[j] = sbi->s_block_bitmap[j - 1];
-               }
--              sb->u.extN_sb.s_block_bitmap_number[0] = block_bitmap_number;
--              sb->u.extN_sb.s_block_bitmap[0] = block_bitmap;
-+              sbi->s_block_bitmap_number[0] = block_bitmap_number;
-+              sbi->s_block_bitmap[0] = block_bitmap;
-               /*
-                * There's still one special case here --- if block_bitmap == 0
-@@ -173,17 +173,14 @@
-               if (!block_bitmap)
-                       retval = read_block_bitmap (sb, block_group, 0);
-       } else {
--              if (sb->u.extN_sb.s_loaded_block_bitmaps<EXTN_MAX_GROUP_LOADED)
--                      sb->u.extN_sb.s_loaded_block_bitmaps++;
-+              if (sbi->s_loaded_block_bitmaps<EXTN_MAX_GROUP_LOADED)
-+                      sbi->s_loaded_block_bitmaps++;
-               else
--                      brelse (sb->u.extN_sb.s_block_bitmap
--                                      [EXTN_MAX_GROUP_LOADED - 1]);
--              for (j = sb->u.extN_sb.s_loaded_block_bitmaps - 1;
--                                      j > 0;  j--) {
--                      sb->u.extN_sb.s_block_bitmap_number[j] =
--                              sb->u.extN_sb.s_block_bitmap_number[j - 1];
--                      sb->u.extN_sb.s_block_bitmap[j] =
--                              sb->u.extN_sb.s_block_bitmap[j - 1];
-+                      brelse(sbi->s_block_bitmap[EXTN_MAX_GROUP_LOADED - 1]);
-+              for (j = sbi->s_loaded_block_bitmaps - 1; j > 0;  j--) {
-+                      sbi->s_block_bitmap_number[j] =
-+                              sbi->s_block_bitmap_number[j - 1];
-+                      sbi->s_block_bitmap[j] = sbi->s_block_bitmap[j - 1];
-               }
-               retval = read_block_bitmap (sb, block_group, 0);
-       }
-@@ -206,24 +203,25 @@
- static inline int load_block_bitmap (struct super_block * sb,
-                                    unsigned int block_group)
- {
-+      struct extN_sb_info *sbi = EXTN_SB(sb);
-       int slot;
--      
-+
-       /*
-        * Do the lookup for the slot.  First of all, check if we're asking
-        * for the same slot as last time, and did we succeed that last time?
-        */
--      if (sb->u.extN_sb.s_loaded_block_bitmaps > 0 &&
--          sb->u.extN_sb.s_block_bitmap_number[0] == block_group &&
--          sb->u.extN_sb.s_block_bitmap[0]) {
-+      if (sbi->s_loaded_block_bitmaps > 0 &&
-+          sbi->s_block_bitmap_number[0] == block_group &&
-+          sbi->s_block_bitmap[0]) {
-               return 0;
-       }
-       /*
-        * Or can we do a fast lookup based on a loaded group on a filesystem
-        * small enough to be mapped directly into the superblock?
-        */
--      else if (sb->u.extN_sb.s_groups_count <= EXTN_MAX_GROUP_LOADED && 
--               sb->u.extN_sb.s_block_bitmap_number[block_group]==block_group
--                      && sb->u.extN_sb.s_block_bitmap[block_group]) {
-+      else if (sbi->s_groups_count <= EXTN_MAX_GROUP_LOADED &&
-+               sbi->s_block_bitmap_number[block_group] == block_group
-+                      && sbi->s_block_bitmap[block_group]) {
-               slot = block_group;
-       }
-       /*
-@@ -243,7 +241,7 @@
-        * If it's a valid slot, we may still have cached a previous IO error,
-        * in which case the bh in the superblock cache will be zero.
-        */
--      if (!sb->u.extN_sb.s_block_bitmap[slot])
-+      if (!sbi->s_block_bitmap[slot])
-               return -EIO;
-       
-       /*
-@@ -275,7 +273,7 @@
-               return;
-       }
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-+      es = EXTN_SB(sb)->s_es;
-       if (block < le32_to_cpu(es->s_first_data_block) || 
-           (block + count) > le32_to_cpu(es->s_blocks_count)) {
-               extN_error (sb, "extN_free_blocks",
-@@ -304,7 +302,7 @@
-       if (bitmap_nr < 0)
-               goto error_return;
-       
--      bitmap_bh = sb->u.extN_sb.s_block_bitmap[bitmap_nr];
-+      bitmap_bh = EXTN_SB(sb)->s_block_bitmap[bitmap_nr];
-       gdp = extN_get_group_desc (sb, block_group, &gd_bh);
-       if (!gdp)
-               goto error_return;
-@@ -312,9 +310,9 @@
-       if (in_range (le32_to_cpu(gdp->bg_block_bitmap), block, count) ||
-           in_range (le32_to_cpu(gdp->bg_inode_bitmap), block, count) ||
-           in_range (block, le32_to_cpu(gdp->bg_inode_table),
--                    sb->u.extN_sb.s_itb_per_group) ||
-+                    EXTN_SB(sb)->s_itb_per_group) ||
-           in_range (block + count - 1, le32_to_cpu(gdp->bg_inode_table),
--                    sb->u.extN_sb.s_itb_per_group))
-+                    EXTN_SB(sb)->s_itb_per_group))
-               extN_error (sb, "extN_free_blocks",
-                           "Freeing blocks in system zones - "
-                           "Block = %lu, count = %lu",
-@@ -340,8 +338,8 @@
-       if (err)
-               goto error_return;
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "get_write_access");
--      err = extN_journal_get_write_access(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(EXTN_SB(sb)->s_sbh, "get_write_access");
-+      err = extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh);
-       if (err)
-               goto error_return;
-@@ -410,8 +408,8 @@
-       if (!err) err = ret;
-       /* And the superblock */
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "dirtied superblock");
--      ret = extN_journal_dirty_metadata(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(EXTN_SB(sb)->s_sbh, "dirtied superblock");
-+      ret = extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh);
-       if (!err) err = ret;
-       if (overflow && !err) {
-@@ -564,12 +562,12 @@
-       }
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-+      es = EXTN_SB(sb)->s_es;
-       if (le32_to_cpu(es->s_free_blocks_count) <=
-                       le32_to_cpu(es->s_r_blocks_count) &&
--          ((sb->u.extN_sb.s_resuid != current->fsuid) &&
--           (sb->u.extN_sb.s_resgid == 0 ||
--            !in_group_p (sb->u.extN_sb.s_resgid)) && 
-+          ((EXTN_SB(sb)->s_resuid != current->fsuid) &&
-+           (EXTN_SB(sb)->s_resgid == 0 ||
-+            !in_group_p (EXTN_SB(sb)->s_resgid)) &&
-            !capable(CAP_SYS_RESOURCE)))
-               goto out;
-@@ -598,7 +596,7 @@
-               if (bitmap_nr < 0)
-                       goto io_error;
-               
--              bh = sb->u.extN_sb.s_block_bitmap[bitmap_nr];
-+              bh = EXTN_SB(sb)->s_block_bitmap[bitmap_nr];
-               extN_debug ("goal is at %d:%d.\n", i, j);
-@@ -621,9 +619,9 @@
-        * Now search the rest of the groups.  We assume that 
-        * i and gdp correctly point to the last group visited.
-        */
--      for (k = 0; k < sb->u.extN_sb.s_groups_count; k++) {
-+      for (k = 0; k < EXTN_SB(sb)->s_groups_count; k++) {
-               i++;
--              if (i >= sb->u.extN_sb.s_groups_count)
-+              if (i >= EXTN_SB(sb)->s_groups_count)
-                       i = 0;
-               gdp = extN_get_group_desc (sb, i, &bh2);
-               if (!gdp) {
-@@ -635,7 +633,7 @@
-                       if (bitmap_nr < 0)
-                               goto io_error;
-       
--                      bh = sb->u.extN_sb.s_block_bitmap[bitmap_nr];
-+                      bh = EXTN_SB(sb)->s_block_bitmap[bitmap_nr];
-                       j = find_next_usable_block(-1, bh, 
-                                                  EXTN_BLOCKS_PER_GROUP(sb));
-                       if (j >= 0) 
-@@ -674,8 +672,8 @@
-       fatal = extN_journal_get_write_access(handle, bh2);
-       if (fatal) goto out;
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "get_write_access");
--      fatal = extN_journal_get_write_access(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(EXTN_SB(sb)->s_sbh, "get_write_access");
-+      fatal = extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh);
-       if (fatal) goto out;
-       tmp = j + i * EXTN_BLOCKS_PER_GROUP(sb)
-@@ -684,7 +682,7 @@
-       if (tmp == le32_to_cpu(gdp->bg_block_bitmap) ||
-           tmp == le32_to_cpu(gdp->bg_inode_bitmap) ||
-           in_range (tmp, le32_to_cpu(gdp->bg_inode_table),
--                    sb->u.extN_sb.s_itb_per_group))
-+                    EXTN_SB(sb)->s_itb_per_group))
-               extN_error (sb, "extN_new_block",
-                           "Allocating block in system zone - "
-                           "block = %u", tmp);
-@@ -796,7 +794,7 @@
-       if (!fatal) fatal = err;
-       
-       BUFFER_TRACE(bh, "journal_dirty_metadata for superblock");
--      err = extN_journal_dirty_metadata(handle, sb->u.extN_sb.s_sbh);
-+      err = extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh);
-       if (!fatal) fatal = err;
-       sb->s_dirt = 1;
-@@ -829,11 +827,11 @@
-       int i;
-       
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-+      es = EXTN_SB(sb)->s_es;
-       desc_count = 0;
-       bitmap_count = 0;
-       gdp = NULL;
--      for (i = 0; i < sb->u.extN_sb.s_groups_count; i++) {
-+      for (i = 0; i < EXTN_SB(sb)->s_groups_count; i++) {
-               gdp = extN_get_group_desc (sb, i, NULL);
-               if (!gdp)
-                       continue;
-@@ -842,7 +840,7 @@
-               if (bitmap_nr < 0)
-                       continue;
-               
--              x = extN_count_free (sb->u.extN_sb.s_block_bitmap[bitmap_nr],
-+              x = extN_count_free (EXTN_SB(sb)->s_block_bitmap[bitmap_nr],
-                                    sb->s_blocksize);
-               printk ("group %d: stored = %d, counted = %lu\n",
-                       i, le16_to_cpu(gdp->bg_free_blocks_count), x);
-@@ -853,7 +851,7 @@
-       unlock_super (sb);
-       return bitmap_count;
- #else
--      return le32_to_cpu(sb->u.extN_sb.s_es->s_free_blocks_count);
-+      return le32_to_cpu(EXTN_SB(sb)->s_es->s_free_blocks_count);
- #endif
- }
-@@ -862,7 +860,7 @@
-                               unsigned char * map)
- {
-       return extN_test_bit ((block -
--              le32_to_cpu(sb->u.extN_sb.s_es->s_first_data_block)) %
-+              le32_to_cpu(EXTN_SB(sb)->s_es->s_first_data_block)) %
-                        EXTN_BLOCKS_PER_GROUP(sb), map);
- }
-@@ -930,11 +928,11 @@
-       struct extN_group_desc * gdp;
-       int i;
--      es = sb->u.extN_sb.s_es;
-+      es = EXTN_SB(sb)->s_es;
-       desc_count = 0;
-       bitmap_count = 0;
-       gdp = NULL;
--      for (i = 0; i < sb->u.extN_sb.s_groups_count; i++) {
-+      for (i = 0; i < EXTN_SB(sb)->s_groups_count; i++) {
-               gdp = extN_get_group_desc (sb, i, NULL);
-               if (!gdp)
-                       continue;
-@@ -968,7 +966,7 @@
-                                   "Inode bitmap for group %d is marked free",
-                                   i);
--              for (j = 0; j < sb->u.extN_sb.s_itb_per_group; j++)
-+              for (j = 0; j < EXTN_SB(sb)->s_itb_per_group; j++)
-                       if (!block_in_use (le32_to_cpu(gdp->bg_inode_table) + j,
-                                                       sb, bh->b_data))
-                               extN_error (sb, "extN_check_blocks_bitmap",
---- extN/extN.orig/dir.c       Wed Sep 25 11:51:33 2002
-+++ extN/dir.c Wed Sep 25 11:53:07 2002
-@@ -52,7 +52,7 @@
-       else if (((char *) de - bh->b_data) + rlen > dir->i_sb->s_blocksize)
-               error_msg = "directory entry across blocks";
-       else if (le32_to_cpu(de->inode) >
--                      le32_to_cpu(dir->i_sb->u.extN_sb.s_es->s_inodes_count))
-+                      le32_to_cpu(EXTN_SB(dir->i_sb)->s_es->s_inodes_count))
-               error_msg = "inode out of bounds";
-       if (error_msg != NULL)
---- extN/extN.orig/ialloc.c    Wed Sep 25 11:51:33 2002
-+++ extN/ialloc.c      Wed Sep 25 11:53:07 2002
-@@ -17,6 +17,7 @@
- #include <linux/jbd.h>
- #include <linux/extN_fs.h>
- #include <linux/extN_jbd.h>
-+#include <linux/extN_xattr.h>
- #include <linux/stat.h>
- #include <linux/string.h>
- #include <linux/locks.h>
-@@ -73,8 +74,8 @@
-        * this group.  The IO will be retried next time.
-        */
- error_out:
--      sb->u.extN_sb.s_inode_bitmap_number[bitmap_nr] = block_group;
--      sb->u.extN_sb.s_inode_bitmap[bitmap_nr] = bh;
-+      EXTN_SB(sb)->s_inode_bitmap_number[bitmap_nr] = block_group;
-+      EXTN_SB(sb)->s_inode_bitmap[bitmap_nr] = bh;
-       return retval;
- }
-@@ -216,6 +217,7 @@
-        * as writing the quota to disk may need the lock as well.
-        */
-       DQUOT_INIT(inode);
-+      extN_xattr_drop_inode(handle, inode);
-       DQUOT_FREE_INODE(inode);
-       DQUOT_DROP(inode);
-@@ -225,7 +227,7 @@
-       clear_inode (inode);
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-+      es = EXTN_SB(sb)->s_es;
-       if (ino < EXTN_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) {
-               extN_error (sb, "extN_free_inode",
-                           "reserved or nonexistent inode %lu", ino);
-@@ -237,7 +239,7 @@
-       if (bitmap_nr < 0)
-               goto error_return;
--      bh = sb->u.extN_sb.s_inode_bitmap[bitmap_nr];
-+      bh = EXTN_SB(sb)->s_inode_bitmap[bitmap_nr];
-       BUFFER_TRACE(bh, "get_write_access");
-       fatal = extN_journal_get_write_access(handle, bh);
-@@ -255,8 +257,8 @@
-               fatal = extN_journal_get_write_access(handle, bh2);
-               if (fatal) goto error_return;
--              BUFFER_TRACE(sb->u.extN_sb.s_sbh, "get write access");
--              fatal = extN_journal_get_write_access(handle, sb->u.extN_sb.s_sbh);
-+              BUFFER_TRACE(EXTN_SB(sb)->s_sbh, "get write access");
-+              fatal = extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh);
-               if (fatal) goto error_return;
-               if (gdp) {
-@@ -271,9 +273,9 @@
-               if (!fatal) fatal = err;
-               es->s_free_inodes_count =
-                       cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) + 1);
--              BUFFER_TRACE(sb->u.extN_sb.s_sbh,
-+              BUFFER_TRACE(EXTN_SB(sb)->s_sbh,
-                                       "call extN_journal_dirty_metadata");
--              err = extN_journal_dirty_metadata(handle, sb->u.extN_sb.s_sbh);
-+              err = extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh);
-               if (!fatal) fatal = err;
-       }
-       BUFFER_TRACE(bh, "call extN_journal_dirty_metadata");
-@@ -305,6 +307,8 @@
-       int i, j, avefreei;
-       struct inode * inode;
-       int bitmap_nr;
-+      struct extN_inode_info *ei;
-+      struct extN_sb_info *sbi;
-       struct extN_group_desc * gdp;
-       struct extN_group_desc * tmp;
-       struct extN_super_block * es;
-@@ -318,19 +322,21 @@
-       inode = new_inode(sb);
-       if (!inode)
-               return ERR_PTR(-ENOMEM);
--      init_rwsem(&inode->u.extN_i.truncate_sem);
-+      sbi = EXTN_SB(sb);
-+      ei = EXTN_I(inode);
-+      init_rwsem(&ei->truncate_sem);
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-+      es = sbi->s_es;
- repeat:
-       gdp = NULL;
-       i = 0;
-       if (S_ISDIR(mode)) {
-               avefreei = le32_to_cpu(es->s_free_inodes_count) /
--                      sb->u.extN_sb.s_groups_count;
-+                      sbi->s_groups_count;
-               if (!gdp) {
--                      for (j = 0; j < sb->u.extN_sb.s_groups_count; j++) {
-+                      for (j = 0; j < sbi->s_groups_count; j++) {
-                               struct buffer_head *temp_buffer;
-                               tmp = extN_get_group_desc (sb, j, &temp_buffer);
-                               if (tmp &&
-@@ -350,7 +356,7 @@
-               /*
-                * Try to place the inode in its parent directory
-                */
--              i = dir->u.extN_i.i_block_group;
-+              i = EXTN_I(dir)->i_block_group;
-               tmp = extN_get_group_desc (sb, i, &bh2);
-               if (tmp && le16_to_cpu(tmp->bg_free_inodes_count))
-                       gdp = tmp;
-@@ -360,10 +366,10 @@
-                        * Use a quadratic hash to find a group with a
-                        * free inode
-                        */
--                      for (j = 1; j < sb->u.extN_sb.s_groups_count; j <<= 1) {
-+                      for (j = 1; j < sbi->s_groups_count; j <<= 1) {
-                               i += j;
--                              if (i >= sb->u.extN_sb.s_groups_count)
--                                      i -= sb->u.extN_sb.s_groups_count;
-+                              if (i >= sbi->s_groups_count)
-+                                      i -= sbi->s_groups_count;
-                               tmp = extN_get_group_desc (sb, i, &bh2);
-                               if (tmp &&
-                                   le16_to_cpu(tmp->bg_free_inodes_count)) {
-@@ -376,9 +382,9 @@
-                       /*
-                        * That failed: try linear search for a free inode
-                        */
--                      i = dir->u.extN_i.i_block_group + 1;
--                      for (j = 2; j < sb->u.extN_sb.s_groups_count; j++) {
--                              if (++i >= sb->u.extN_sb.s_groups_count)
-+                      i = EXTN_I(dir)->i_block_group + 1;
-+                      for (j = 2; j < sbi->s_groups_count; j++) {
-+                              if (++i >= sbi->s_groups_count)
-                                       i = 0;
-                               tmp = extN_get_group_desc (sb, i, &bh2);
-                               if (tmp &&
-@@ -399,11 +405,11 @@
-       if (bitmap_nr < 0)
-               goto fail;
--      bh = sb->u.extN_sb.s_inode_bitmap[bitmap_nr];
-+      bh = sbi->s_inode_bitmap[bitmap_nr];
-       if ((j = extN_find_first_zero_bit ((unsigned long *) bh->b_data,
--                                    EXTN_INODES_PER_GROUP(sb))) <
--          EXTN_INODES_PER_GROUP(sb)) {
-+                                    sbi->s_inodes_per_group)) <
-+          sbi->s_inodes_per_group) {
-               BUFFER_TRACE(bh, "get_write_access");
-               err = extN_journal_get_write_access(handle, bh);
-               if (err) goto fail;
-@@ -436,8 +442,8 @@
-               }
-               goto repeat;
-       }
--      j += i * EXTN_INODES_PER_GROUP(sb) + 1;
--      if (j < EXTN_FIRST_INO(sb) || j > le32_to_cpu(es->s_inodes_count)) {
-+      j += i * sbi->s_inodes_per_group + 1;
-+      if (j < sbi->s_first_ino || j > le32_to_cpu(es->s_inodes_count)) {
-               extN_error (sb, "extN_new_inode",
-                           "reserved inode or inode > inodes count - "
-                           "block_group = %d,inode=%d", i, j);
-@@ -457,13 +463,13 @@
-       err = extN_journal_dirty_metadata(handle, bh2);
-       if (err) goto fail;
-       
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "get_write_access");
--      err = extN_journal_get_write_access(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(sbi->s_sbh, "get_write_access");
-+      err = extN_journal_get_write_access(handle, sbi->s_sbh);
-       if (err) goto fail;
-       es->s_free_inodes_count =
-               cpu_to_le32(le32_to_cpu(es->s_free_inodes_count) - 1);
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "call extN_journal_dirty_metadata");
--      err = extN_journal_dirty_metadata(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(sbi->s_sbh, "call extN_journal_dirty_metadata");
-+      err = extN_journal_dirty_metadata(handle, sbi->s_sbh);
-       sb->s_dirt = 1;
-       if (err) goto fail;
-@@ -483,31 +489,31 @@
-       inode->i_blksize = PAGE_SIZE;
-       inode->i_blocks = 0;
-       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
--      inode->u.extN_i.i_flags = dir->u.extN_i.i_flags & ~EXTN_INDEX_FL;
-+      ei->i_flags = EXTN_I(dir)->i_flags & ~EXTN_INDEX_FL;
-       if (S_ISLNK(mode))
--              inode->u.extN_i.i_flags &= ~(EXTN_IMMUTABLE_FL|EXTN_APPEND_FL);
-+              ei->i_flags &= ~(EXTN_IMMUTABLE_FL|EXTN_APPEND_FL);
- #ifdef EXTN_FRAGMENTS
--      inode->u.extN_i.i_faddr = 0;
--      inode->u.extN_i.i_frag_no = 0;
--      inode->u.extN_i.i_frag_size = 0;
-+      ei->i_faddr = 0;
-+      ei->i_frag_no = 0;
-+      ei->i_frag_size = 0;
- #endif
--      inode->u.extN_i.i_file_acl = 0;
--      inode->u.extN_i.i_dir_acl = 0;
--      inode->u.extN_i.i_dtime = 0;
--      INIT_LIST_HEAD(&inode->u.extN_i.i_orphan);
-+      ei->i_file_acl = 0;
-+      ei->i_dir_acl = 0;
-+      ei->i_dtime = 0;
-+      INIT_LIST_HEAD(&ei->i_orphan);
- #ifdef EXTN_PREALLOCATE
--      inode->u.extN_i.i_prealloc_count = 0;
-+      ei->i_prealloc_count = 0;
- #endif
--      inode->u.extN_i.i_block_group = i;
-+      ei->i_block_group = i;
-       
--      if (inode->u.extN_i.i_flags & EXTN_SYNC_FL)
-+      if (ei->i_flags & EXTN_SYNC_FL)
-               inode->i_flags |= S_SYNC;
-       if (IS_SYNC(inode))
-               handle->h_sync = 1;
-       insert_inode_hash(inode);
--      inode->i_generation = sb->u.extN_sb.s_next_generation++;
-+      inode->i_generation = sbi->s_next_generation++;
--      inode->u.extN_i.i_state = EXTN_STATE_NEW;
-+      ei->i_state = EXTN_STATE_NEW;
-       err = extN_mark_inode_dirty(handle, inode);
-       if (err) goto fail;
-       
-@@ -585,19 +591,19 @@
- unsigned long extN_count_free_inodes (struct super_block * sb)
- {
-+      struct extN_sb_info *sbi = EXTN_SB(sb);
-+      struct extN_super_block *es = sbi->s_es;
- #ifdef EXTNFS_DEBUG
--      struct extN_super_block * es;
-       unsigned long desc_count, bitmap_count, x;
-       int bitmap_nr;
-       struct extN_group_desc * gdp;
-       int i;
-       lock_super (sb);
--      es = sb->u.extN_sb.s_es;
-       desc_count = 0;
-       bitmap_count = 0;
-       gdp = NULL;
--      for (i = 0; i < sb->u.extN_sb.s_groups_count; i++) {
-+      for (i = 0; i < sbi->s_groups_count; i++) {
-               gdp = extN_get_group_desc (sb, i, NULL);
-               if (!gdp)
-                       continue;
-@@ -606,8 +612,8 @@
-               if (bitmap_nr < 0)
-                       continue;
--              x = extN_count_free (sb->u.extN_sb.s_inode_bitmap[bitmap_nr],
--                                   EXTN_INODES_PER_GROUP(sb) / 8);
-+              x = extN_count_free(sbi->s_inode_bitmap[bitmap_nr],
-+                                  sbi->s_inodes_per_group / 8);
-               printk ("group %d: stored = %d, counted = %lu\n",
-                       i, le16_to_cpu(gdp->bg_free_inodes_count), x);
-               bitmap_count += x;
-@@ -617,7 +623,7 @@
-       unlock_super (sb);
-       return desc_count;
- #else
--      return le32_to_cpu(sb->u.extN_sb.s_es->s_free_inodes_count);
-+      return le32_to_cpu(es->s_free_inodes_count);
- #endif
- }
-@@ -626,16 +632,18 @@
- void extN_check_inodes_bitmap (struct super_block * sb)
- {
-       struct extN_super_block * es;
-+      struct extN_sb_info *sbi;
-       unsigned long desc_count, bitmap_count, x;
-       int bitmap_nr;
-       struct extN_group_desc * gdp;
-       int i;
--      es = sb->u.extN_sb.s_es;
-+      sbi = EXTN_SB(sb);
-+      es = sbi->s_es;
-       desc_count = 0;
-       bitmap_count = 0;
-       gdp = NULL;
--      for (i = 0; i < sb->u.extN_sb.s_groups_count; i++) {
-+      for (i = 0; i < sbi->s_groups_count; i++) {
-               gdp = extN_get_group_desc (sb, i, NULL);
-               if (!gdp)
-                       continue;
-@@ -644,7 +652,7 @@
-               if (bitmap_nr < 0)
-                       continue;
--              x = extN_count_free (sb->u.extN_sb.s_inode_bitmap[bitmap_nr],
-+              x = extN_count_free (sbi->s_inode_bitmap[bitmap_nr],
-                                    EXTN_INODES_PER_GROUP(sb) / 8);
-               if (le16_to_cpu(gdp->bg_free_inodes_count) != x)
-                       extN_error (sb, "extN_check_inodes_bitmap",
---- extN/extN.orig/inode.c     Wed Sep 25 11:51:33 2002
-+++ extN/inode.c       Wed Sep 25 11:53:07 2002
-@@ -39,6 +39,18 @@
-  */
- #undef SEARCH_FROM_ZERO
-+/*
-+ * Test whether an inode is a fast symlink.
-+ */
-+static inline int extN_inode_is_fast_symlink(struct inode *inode)
-+{
-+      int ea_blocks = EXTN_I(inode)->i_file_acl ?
-+              (inode->i_sb->s_blocksize >> 9) : 0;
-+
-+      return (S_ISLNK(inode->i_mode) &&
-+              inode->i_blocks - ea_blocks == 0);
-+}
-+
- /* The extN forget function must perform a revoke if we are freeing data
-  * which has been journaled.  Metadata (eg. indirect blocks) must be
-  * revoked in all cases. 
-@@ -48,7 +60,7 @@
-  * still needs to be revoked.
-  */
--static int extN_forget(handle_t *handle, int is_metadata,
-+int extN_forget(handle_t *handle, int is_metadata,
-                      struct inode *inode, struct buffer_head *bh,
-                      int blocknr)
- {
-@@ -164,9 +176,7 @@
- {
-       handle_t *handle;
-       
--      if (is_bad_inode(inode) ||
--          inode->i_ino == EXTN_ACL_IDX_INO ||
--          inode->i_ino == EXTN_ACL_DATA_INO)
-+      if (is_bad_inode(inode))
-               goto no_delete;
-       lock_kernel();
-@@ -196,7 +206,7 @@
-        * (Well, we could do this if we need to, but heck - it works)
-        */
-       extN_orphan_del(handle, inode);
--      inode->u.extN_i.i_dtime = CURRENT_TIME;
-+      EXTN_I(inode)->i_dtime = CURRENT_TIME;
-       /* 
-        * One subtle ordering requirement: if anything has gone wrong
-@@ -220,13 +230,14 @@
- void extN_discard_prealloc (struct inode * inode)
- {
- #ifdef EXTN_PREALLOCATE
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       lock_kernel();
-       /* Writer: ->i_prealloc* */
--      if (inode->u.extN_i.i_prealloc_count) {
--              unsigned short total = inode->u.extN_i.i_prealloc_count;
--              unsigned long block = inode->u.extN_i.i_prealloc_block;
--              inode->u.extN_i.i_prealloc_count = 0;
--              inode->u.extN_i.i_prealloc_block = 0;
-+      if (ei->i_prealloc_count) {
-+              unsigned short total = ei->i_prealloc_count;
-+              unsigned long block = ei->i_prealloc_block;
-+              ei->i_prealloc_count = 0;
-+              ei->i_prealloc_block = 0;
-               /* Writer: end */
-               extN_free_blocks (inode, block, total);
-       }
-@@ -243,13 +254,15 @@
-       unsigned long result;
- #ifdef EXTN_PREALLOCATE
-+      struct extN_inode_info *ei = EXTN_I(inode);
-+
-       /* Writer: ->i_prealloc* */
--      if (inode->u.extN_i.i_prealloc_count &&
--          (goal == inode->u.extN_i.i_prealloc_block ||
--           goal + 1 == inode->u.extN_i.i_prealloc_block))
-+      if (ei->i_prealloc_count &&
-+          (goal == ei->i_prealloc_block ||
-+           goal + 1 == ei->i_prealloc_block))
-       {
--              result = inode->u.extN_i.i_prealloc_block++;
--              inode->u.extN_i.i_prealloc_count--;
-+              result = ei->i_prealloc_block++;
-+              ei->i_prealloc_count--;
-               /* Writer: end */
-               extN_debug ("preallocation hit (%lu/%lu).\n",
-                           ++alloc_hits, ++alloc_attempts);
-@@ -259,8 +272,8 @@
-                           alloc_hits, ++alloc_attempts);
-               if (S_ISREG(inode->i_mode))
-                       result = extN_new_block (inode, goal, 
--                               &inode->u.extN_i.i_prealloc_count,
--                               &inode->u.extN_i.i_prealloc_block, err);
-+                               &ei->i_prealloc_count,
-+                               &ei->i_prealloc_block, err);
-               else
-                       result = extN_new_block (inode, goal, 0, 0, err);
-               /*
-@@ -394,7 +407,7 @@
-       *err = 0;
-       /* i_data is not going away, no lock needed */
--      add_chain (chain, NULL, inode->u.extN_i.i_data + *offsets);
-+      add_chain (chain, NULL, EXTN_I(inode)->i_data + *offsets);
-       if (!p->key)
-               goto no_block;
-       while (--depth) {
-@@ -437,7 +450,8 @@
- static inline unsigned long extN_find_near(struct inode *inode, Indirect *ind)
- {
--      u32 *start = ind->bh ? (u32*) ind->bh->b_data : inode->u.extN_i.i_data;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-+      u32 *start = ind->bh ? (u32*) ind->bh->b_data : ei->i_data;
-       u32 *p;
-       /* Try to find previous block */
-@@ -453,9 +467,8 @@
-        * It is going to be refered from inode itself? OK, just put it into
-        * the same cylinder group then.
-        */
--      return (inode->u.extN_i.i_block_group * 
--              EXTN_BLOCKS_PER_GROUP(inode->i_sb)) +
--             le32_to_cpu(inode->i_sb->u.extN_sb.s_es->s_first_data_block);
-+      return (ei->i_block_group * EXTN_BLOCKS_PER_GROUP(inode->i_sb)) +
-+             le32_to_cpu(EXTN_SB(inode->i_sb)->s_es->s_first_data_block);
- }
- /**
-@@ -474,14 +487,15 @@
- static int extN_find_goal(struct inode *inode, long block, Indirect chain[4],
-                         Indirect *partial, unsigned long *goal)
- {
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       /* Writer: ->i_next_alloc* */
--      if (block == inode->u.extN_i.i_next_alloc_block + 1) {
--              inode->u.extN_i.i_next_alloc_block++;
--              inode->u.extN_i.i_next_alloc_goal++;
-+      if (block == ei->i_next_alloc_block + 1) {
-+              ei->i_next_alloc_block++;
-+              ei->i_next_alloc_goal++;
-       }
- #ifdef SEARCH_FROM_ZERO
--      inode->u.extN_i.i_next_alloc_block = 0;
--      inode->u.extN_i.i_next_alloc_goal = 0;
-+      ei->i_next_alloc_block = 0;
-+      ei->i_next_alloc_goal = 0;
- #endif
-       /* Writer: end */
-       /* Reader: pointers, ->i_next_alloc* */
-@@ -490,8 +504,8 @@
-                * try the heuristic for sequential allocation,
-                * failing that at least try to get decent locality.
-                */
--              if (block == inode->u.extN_i.i_next_alloc_block)
--                      *goal = inode->u.extN_i.i_next_alloc_goal;
-+              if (block == ei->i_next_alloc_block)
-+                      *goal = ei->i_next_alloc_goal;
-               if (!*goal)
-                       *goal = extN_find_near(inode, partial);
- #ifdef SEARCH_FROM_ZERO
-@@ -619,6 +633,7 @@
- {
-       int i;
-       int err = 0;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       /*
-        * If we're splicing into a [td]indirect block (as opposed to the
-@@ -641,11 +656,11 @@
-       /* That's it */
-       *where->p = where->key;
--      inode->u.extN_i.i_next_alloc_block = block;
--      inode->u.extN_i.i_next_alloc_goal = le32_to_cpu(where[num-1].key);
-+      ei->i_next_alloc_block = block;
-+      ei->i_next_alloc_goal = le32_to_cpu(where[num-1].key);
- #ifdef SEARCH_FROM_ZERO
--      inode->u.extN_i.i_next_alloc_block = 0;
--      inode->u.extN_i.i_next_alloc_goal = 0;
-+      ei->i_next_alloc_block = 0;
-+      ei->i_next_alloc_goal = 0;
- #endif
-       /* Writer: end */
-@@ -729,6 +744,7 @@
-       unsigned long goal;
-       int left;
-       int depth = extN_block_to_path(inode, iblock, offsets);
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       loff_t new_size;
-       J_ASSERT(handle != NULL || create == 0);
-@@ -782,7 +798,7 @@
-       /*
-        * Block out extN_truncate while we alter the tree
-        */
--      down_read(&inode->u.extN_i.truncate_sem);
-+      down_read(&ei->truncate_sem);
-       err = extN_alloc_branch(handle, inode, left, goal,
-                                       offsets+(partial-chain), partial);
-@@ -794,7 +810,7 @@
-       if (!err)
-               err = extN_splice_branch(handle, inode, iblock, chain,
-                                        partial, left);
--      up_read(&inode->u.extN_i.truncate_sem);
-+      up_read(&ei->truncate_sem);
-       if (err == -EAGAIN)
-               goto changed;
-       if (err)
-@@ -807,8 +823,8 @@
-        * truncate is in progress.  It is racy between multiple parallel
-        * instances of get_block, but we have the BKL.
-        */
--      if (new_size > inode->u.extN_i.i_disksize)
--              inode->u.extN_i.i_disksize = new_size;
-+      if (new_size > ei->i_disksize)
-+              ei->i_disksize = new_size;
-       bh_result->b_state |= (1UL << BH_New);
-       goto got_it;
-@@ -921,7 +937,7 @@
-               struct buffer_head *tmp_bh;
-               for (i = 1;
--                   inode->u.extN_i.i_prealloc_count &&
-+                   EXTN_I(inode)->i_prealloc_count &&
-                    i < EXTN_SB(inode->i_sb)->s_es->s_prealloc_dir_blocks;
-                    i++) {
-                       /*
-@@ -1131,8 +1147,8 @@
-                       kunmap(page);
-               }
-       }
--      if (inode->i_size > inode->u.extN_i.i_disksize) {
--              inode->u.extN_i.i_disksize = inode->i_size;
-+      if (inode->i_size > EXTN_I(inode)->i_disksize) {
-+              EXTN_I(inode)->i_disksize = inode->i_size;
-               ret2 = extN_mark_inode_dirty(handle, inode);
-               if (!ret) 
-                       ret = ret2;
-@@ -1832,7 +1848,8 @@
- void extN_truncate(struct inode * inode)
- {
-       handle_t *handle;
--      u32 *i_data = inode->u.extN_i.i_data;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-+      u32 *i_data = EXTN_I(inode)->i_data;
-       int addr_per_block = EXTN_ADDR_PER_BLOCK(inode->i_sb);
-       int offsets[4];
-       Indirect chain[4];
-@@ -1845,6 +1862,8 @@
-       if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-           S_ISLNK(inode->i_mode)))
-               return;
-+      if (extN_inode_is_fast_symlink(inode))
-+              return;
-       if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
-               return;
-@@ -1884,13 +1903,13 @@
-        * on-disk inode. We do this via i_disksize, which is the value which
-        * extN *really* writes onto the disk inode.
-        */
--      inode->u.extN_i.i_disksize = inode->i_size;
-+      ei->i_disksize = inode->i_size;
-       /*
-        * From here we block out all extN_get_block() callers who want to
-        * modify the block allocation tree.
-        */
--      down_write(&inode->u.extN_i.truncate_sem);
-+      down_write(&ei->truncate_sem);
-       if (n == 1) {           /* direct blocks */
-               extN_free_data(handle, inode, NULL, i_data+offsets[0],
-@@ -1954,7 +1973,7 @@
-               case EXTN_TIND_BLOCK:
-                       ;
-       }
--      up_write(&inode->u.extN_i.truncate_sem);
-+      up_write(&ei->truncate_sem);
-       inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-       extN_mark_inode_dirty(handle, inode);
-@@ -1983,6 +2002,8 @@
- int extN_get_inode_loc (struct inode *inode, struct extN_iloc *iloc)
- {
-+      struct super_block *sb = inode->i_sb;
-+      struct extN_sb_info *sbi = EXTN_SB(sb);
-       struct buffer_head *bh = 0;
-       unsigned long block;
-       unsigned long block_group;
-@@ -1992,28 +2013,22 @@
-       struct extN_group_desc * gdp;
-               
-       if ((inode->i_ino != EXTN_ROOT_INO &&
--              inode->i_ino != EXTN_ACL_IDX_INO &&
--              inode->i_ino != EXTN_ACL_DATA_INO &&
-               inode->i_ino != EXTN_JOURNAL_INO &&
--              inode->i_ino < EXTN_FIRST_INO(inode->i_sb)) ||
--              inode->i_ino > le32_to_cpu(
--                      inode->i_sb->u.extN_sb.s_es->s_inodes_count)) {
--              extN_error (inode->i_sb, "extN_get_inode_loc",
--                          "bad inode number: %lu", inode->i_ino);
-+              inode->i_ino < EXTN_FIRST_INO(sb)) ||
-+              inode->i_ino > le32_to_cpu(sbi->s_es->s_inodes_count)) {
-+              extN_error (sb, __FUNCTION__, "bad inode #%lu", inode->i_ino);
-               goto bad_inode;
-       }
--      block_group = (inode->i_ino - 1) / EXTN_INODES_PER_GROUP(inode->i_sb);
--      if (block_group >= inode->i_sb->u.extN_sb.s_groups_count) {
--              extN_error (inode->i_sb, "extN_get_inode_loc",
--                          "group >= groups count");
-+      block_group = (inode->i_ino - 1) / sbi->s_inodes_per_group;
-+      if (block_group >= sbi->s_groups_count) {
-+              extN_error(sb, __FUNCTION__, "group >= groups count");
-               goto bad_inode;
-       }
--      group_desc = block_group >> EXTN_DESC_PER_BLOCK_BITS(inode->i_sb);
--      desc = block_group & (EXTN_DESC_PER_BLOCK(inode->i_sb) - 1);
--      bh = inode->i_sb->u.extN_sb.s_group_desc[group_desc];
-+      group_desc = block_group >> sbi->s_desc_per_block_bits;
-+      desc = block_group & (sbi->s_desc_per_block - 1);
-+      bh = sbi->s_group_desc[group_desc];
-       if (!bh) {
--              extN_error (inode->i_sb, "extN_get_inode_loc",
--                          "Descriptor not loaded");
-+              extN_error(sb, __FUNCTION__, "Descriptor not loaded");
-               goto bad_inode;
-       }
-@@ -2021,17 +2036,17 @@
-       /*
-        * Figure out the offset within the block group inode table
-        */
--      offset = ((inode->i_ino - 1) % EXTN_INODES_PER_GROUP(inode->i_sb)) *
--              EXTN_INODE_SIZE(inode->i_sb);
-+      offset = ((inode->i_ino - 1) % sbi->s_inodes_per_group) *
-+              sbi->s_inode_size;
-       block = le32_to_cpu(gdp[desc].bg_inode_table) +
--              (offset >> EXTN_BLOCK_SIZE_BITS(inode->i_sb));
--      if (!(bh = sb_bread(inode->i_sb, block))) {
--              extN_error (inode->i_sb, "extN_get_inode_loc",
-+              (offset >> EXTN_BLOCK_SIZE_BITS(sb));
-+      if (!(bh = sb_bread(sb, block))) {
-+              extN_error (sb, __FUNCTION__,
-                           "unable to read inode block - "
-                           "inode=%lu, block=%lu", inode->i_ino, block);
-               goto bad_inode;
-       }
--      offset &= (EXTN_BLOCK_SIZE(inode->i_sb) - 1);
-+      offset &= (EXTN_BLOCK_SIZE(sb) - 1);
-       iloc->bh = bh;
-       iloc->raw_inode = (struct extN_inode *) (bh->b_data + offset);
-@@ -2047,6 +2062,7 @@
- {
-       struct extN_iloc iloc;
-       struct extN_inode *raw_inode;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       struct buffer_head *bh;
-       int block;
-       
-@@ -2054,7 +2070,7 @@
-               goto bad_inode;
-       bh = iloc.bh;
-       raw_inode = iloc.raw_inode;
--      init_rwsem(&inode->u.extN_i.truncate_sem);
-+      init_rwsem(&ei->truncate_sem);
-       inode->i_mode = le16_to_cpu(raw_inode->i_mode);
-       inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
-       inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
-@@ -2067,7 +2083,7 @@
-       inode->i_atime = le32_to_cpu(raw_inode->i_atime);
-       inode->i_ctime = le32_to_cpu(raw_inode->i_ctime);
-       inode->i_mtime = le32_to_cpu(raw_inode->i_mtime);
--      inode->u.extN_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
-+      ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);
-       /* We now have enough fields to check if the inode was active or not.
-        * This is needed because nfsd might try to access dead inodes
-        * the test is that same one that e2fsck uses
-@@ -2075,7 +2091,7 @@
-        */
-       if (inode->i_nlink == 0) {
-               if (inode->i_mode == 0 ||
--                  !(inode->i_sb->u.extN_sb.s_mount_state & EXTN_ORPHAN_FS)) {
-+                  !(EXTN_SB(inode->i_sb)->s_mount_state & EXTN_ORPHAN_FS)) {
-                       /* this inode is deleted */
-                       brelse (bh);
-                       goto bad_inode;
-@@ -2090,40 +2106,37 @@
-                                        * size */  
-       inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
-       inode->i_version = ++event;
--      inode->u.extN_i.i_flags = le32_to_cpu(raw_inode->i_flags);
-+      ei->i_flags = le32_to_cpu(raw_inode->i_flags);
- #ifdef EXTN_FRAGMENTS
--      inode->u.extN_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
--      inode->u.extN_i.i_frag_no = raw_inode->i_frag;
--      inode->u.extN_i.i_frag_size = raw_inode->i_fsize;
-+      ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
-+      ei->i_frag_no = raw_inode->i_frag;
-+      ei->i_frag_size = raw_inode->i_fsize;
- #endif
--      inode->u.extN_i.i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
-+      ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
-       if (!S_ISREG(inode->i_mode)) {
--              inode->u.extN_i.i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
-+              ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
-       } else {
-               inode->i_size |=
-                       ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
-       }
--      inode->u.extN_i.i_disksize = inode->i_size;
-+      ei->i_disksize = inode->i_size;
-       inode->i_generation = le32_to_cpu(raw_inode->i_generation);
- #ifdef EXTN_PREALLOCATE
--      inode->u.extN_i.i_prealloc_count = 0;
-+      ei->i_prealloc_count = 0;
- #endif
--      inode->u.extN_i.i_block_group = iloc.block_group;
-+      ei->i_block_group = iloc.block_group;
-       /*
-        * NOTE! The in-memory inode i_data array is in little-endian order
-        * even on big-endian machines: we do NOT byteswap the block numbers!
-        */
-       for (block = 0; block < EXTN_N_BLOCKS; block++)
--              inode->u.extN_i.i_data[block] = iloc.raw_inode->i_block[block];
--      INIT_LIST_HEAD(&inode->u.extN_i.i_orphan);
-+              ei->i_data[block] = iloc.raw_inode->i_block[block];
-+      INIT_LIST_HEAD(&ei->i_orphan);
-       brelse (iloc.bh);
--      if (inode->i_ino == EXTN_ACL_IDX_INO ||
--          inode->i_ino == EXTN_ACL_DATA_INO)
--              /* Nothing to do */ ;
--      else if (S_ISREG(inode->i_mode)) {
-+      if (S_ISREG(inode->i_mode)) {
-               inode->i_op = &extN_file_inode_operations;
-               inode->i_fop = &extN_file_operations;
-               inode->i_mapping->a_ops = &extN_aops;
-@@ -2131,7 +2144,7 @@
-               inode->i_op = &extN_dir_inode_operations;
-               inode->i_fop = &extN_dir_operations;
-       } else if (S_ISLNK(inode->i_mode)) {
--              if (!inode->i_blocks)
-+              if (extN_inode_is_fast_symlink(inode))
-                       inode->i_op = &extN_fast_symlink_inode_operations;
-               else {
-                       inode->i_op = &page_symlink_inode_operations;
-@@ -2141,19 +2154,19 @@
-               init_special_inode(inode, inode->i_mode,
-                                  le32_to_cpu(iloc.raw_inode->i_block[0]));
-       /* inode->i_attr_flags = 0;                             unused */
--      if (inode->u.extN_i.i_flags & EXTN_SYNC_FL) {
-+      if (ei->i_flags & EXTN_SYNC_FL) {
-               /* inode->i_attr_flags |= ATTR_FLAG_SYNCRONOUS; unused */
-               inode->i_flags |= S_SYNC;
-       }
--      if (inode->u.extN_i.i_flags & EXTN_APPEND_FL) {
-+      if (ei->i_flags & EXTN_APPEND_FL) {
-               /* inode->i_attr_flags |= ATTR_FLAG_APPEND;     unused */
-               inode->i_flags |= S_APPEND;
-       }
--      if (inode->u.extN_i.i_flags & EXTN_IMMUTABLE_FL) {
-+      if (ei->i_flags & EXTN_IMMUTABLE_FL) {
-               /* inode->i_attr_flags |= ATTR_FLAG_IMMUTABLE;  unused */
-               inode->i_flags |= S_IMMUTABLE;
-       }
--      if (inode->u.extN_i.i_flags & EXTN_NOATIME_FL) {
-+      if (ei->i_flags & EXTN_NOATIME_FL) {
-               /* inode->i_attr_flags |= ATTR_FLAG_NOATIME;    unused */
-               inode->i_flags |= S_NOATIME;
-       }
-@@ -2175,6 +2188,7 @@
-                               struct extN_iloc *iloc)
- {
-       struct extN_inode *raw_inode = iloc->raw_inode;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       struct buffer_head *bh = iloc->bh;
-       int err = 0, rc, block;
-@@ -2192,7 +2206,7 @@
-  * Fix up interoperability with old kernels. Otherwise, old inodes get
-  * re-used with the upper 16 bits of the uid/gid intact
-  */
--              if(!inode->u.extN_i.i_dtime) {
-+              if(!ei->i_dtime) {
-                       raw_inode->i_uid_high =
-                               cpu_to_le16(high_16_bits(inode->i_uid));
-                       raw_inode->i_gid_high =
-@@ -2210,34 +2224,33 @@
-               raw_inode->i_gid_high = 0;
-       }
-       raw_inode->i_links_count = cpu_to_le16(inode->i_nlink);
--      raw_inode->i_size = cpu_to_le32(inode->u.extN_i.i_disksize);
-+      raw_inode->i_size = cpu_to_le32(ei->i_disksize);
-       raw_inode->i_atime = cpu_to_le32(inode->i_atime);
-       raw_inode->i_ctime = cpu_to_le32(inode->i_ctime);
-       raw_inode->i_mtime = cpu_to_le32(inode->i_mtime);
-       raw_inode->i_blocks = cpu_to_le32(inode->i_blocks);
--      raw_inode->i_dtime = cpu_to_le32(inode->u.extN_i.i_dtime);
--      raw_inode->i_flags = cpu_to_le32(inode->u.extN_i.i_flags);
-+      raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
-+      raw_inode->i_flags = cpu_to_le32(ei->i_flags);
- #ifdef EXTN_FRAGMENTS
--      raw_inode->i_faddr = cpu_to_le32(inode->u.extN_i.i_faddr);
--      raw_inode->i_frag = inode->u.extN_i.i_frag_no;
--      raw_inode->i_fsize = inode->u.extN_i.i_frag_size;
-+      raw_inode->i_faddr = cpu_to_le32(ei->i_faddr);
-+      raw_inode->i_frag = ei->i_frag_no;
-+      raw_inode->i_fsize = ei->i_frag_size;
- #else
-       /* If we are not tracking these fields in the in-memory inode,
-        * then preserve them on disk, but still initialise them to zero
-        * for new inodes. */
--      if (EXTN_I(inode)->i_state & EXTN_STATE_NEW) {
-+      if (ei->i_state & EXTN_STATE_NEW) {
-               raw_inode->i_faddr = 0;
-               raw_inode->i_frag = 0;
-               raw_inode->i_fsize = 0;
-       }
- #endif
--      raw_inode->i_file_acl = cpu_to_le32(inode->u.extN_i.i_file_acl);
-+      raw_inode->i_file_acl = cpu_to_le32(ei->i_file_acl);
-       if (!S_ISREG(inode->i_mode)) {
--              raw_inode->i_dir_acl = cpu_to_le32(inode->u.extN_i.i_dir_acl);
-+              raw_inode->i_dir_acl = cpu_to_le32(ei->i_dir_acl);
-       } else {
--              raw_inode->i_size_high =
--                      cpu_to_le32(inode->u.extN_i.i_disksize >> 32);
--              if (inode->u.extN_i.i_disksize > 0x7fffffffULL) {
-+              raw_inode->i_size_high = cpu_to_le32(ei->i_disksize >> 32);
-+              if (ei->i_disksize > MAX_NON_LFS) {
-                       struct super_block *sb = inode->i_sb;
-                       if (!EXTN_HAS_RO_COMPAT_FEATURE(sb,
-                                       EXTN_FEATURE_RO_COMPAT_LARGE_FILE) ||
-@@ -2247,7 +2260,7 @@
-                               * created, add a flag to the superblock.
-                               */
-                               err = extN_journal_get_write_access(handle,
--                                              sb->u.extN_sb.s_sbh);
-+                                              EXTN_SB(sb)->s_sbh);
-                               if (err)
-                                       goto out_brelse;
-                               extN_update_dynamic_rev(sb);
-@@ -2256,7 +2269,7 @@
-                               sb->s_dirt = 1;
-                               handle->h_sync = 1;
-                               err = extN_journal_dirty_metadata(handle,
--                                              sb->u.extN_sb.s_sbh);
-+                                              EXTN_SB(sb)->s_sbh);
-                       }
-               }
-       }
-@@ -2265,13 +2278,13 @@
-               raw_inode->i_block[0] =
-                       cpu_to_le32(kdev_t_to_nr(inode->i_rdev));
-       else for (block = 0; block < EXTN_N_BLOCKS; block++)
--              raw_inode->i_block[block] = inode->u.extN_i.i_data[block];
-+              raw_inode->i_block[block] = ei->i_data[block];
-       BUFFER_TRACE(bh, "call extN_journal_dirty_metadata");
-       rc = extN_journal_dirty_metadata(handle, bh);
-       if (!err)
-               err = rc;
--      EXTN_I(inode)->i_state &= ~EXTN_STATE_NEW;
-+      ei->i_state &= ~EXTN_STATE_NEW;
- out_brelse:
-       brelse (bh);
-@@ -2379,7 +2392,7 @@
-               }
-               
-               error = extN_orphan_add(handle, inode);
--              inode->u.extN_i.i_disksize = attr->ia_size;
-+              EXTN_I(inode)->i_disksize = attr->ia_size;
-               rc = extN_mark_inode_dirty(handle, inode);
-               if (!error)
-                       error = rc;
-@@ -2622,9 +2635,9 @@
-        */
-       if (val)
--              inode->u.extN_i.i_flags |= EXTN_JOURNAL_DATA_FL;
-+              EXTN_I(inode)->i_flags |= EXTN_JOURNAL_DATA_FL;
-       else
--              inode->u.extN_i.i_flags &= ~EXTN_JOURNAL_DATA_FL;
-+              EXTN_I(inode)->i_flags &= ~EXTN_JOURNAL_DATA_FL;
-       journal_unlock_updates(journal);
---- extN/extN.orig/ioctl.c     Wed Sep 25 11:51:33 2002
-+++ extN/ioctl.c       Wed Sep 25 11:53:07 2002
-@@ -18,13 +18,14 @@
- int extN_ioctl (struct inode * inode, struct file * filp, unsigned int cmd,
-               unsigned long arg)
- {
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       unsigned int flags;
-       extN_debug ("cmd = %u, arg = %lu\n", cmd, arg);
-       switch (cmd) {
-       case EXTN_IOC_GETFLAGS:
--              flags = inode->u.extN_i.i_flags & EXTN_FL_USER_VISIBLE;
-+              flags = ei->i_flags & EXTN_FL_USER_VISIBLE;
-               return put_user(flags, (int *) arg);
-       case EXTN_IOC_SETFLAGS: {
-               handle_t *handle = NULL;
-@@ -42,7 +43,7 @@
-               if (get_user(flags, (int *) arg))
-                       return -EFAULT;
--              oldflags = inode->u.extN_i.i_flags;
-+              oldflags = ei->i_flags;
-               /* The JOURNAL_DATA flag is modifiable only by root */
-               jflag = flags & EXTN_JOURNAL_DATA_FL;
-@@ -79,7 +80,7 @@
-               
-               flags = flags & EXTN_FL_USER_MODIFIABLE;
-               flags |= oldflags & ~EXTN_FL_USER_MODIFIABLE;
--              inode->u.extN_i.i_flags = flags;
-+              ei->i_flags = flags;
-               if (flags & EXTN_SYNC_FL)
-                       inode->i_flags |= S_SYNC;
-@@ -155,12 +156,12 @@
-                       int ret = 0;
-                       set_current_state(TASK_INTERRUPTIBLE);
--                      add_wait_queue(&sb->u.extN_sb.ro_wait_queue, &wait);
--                      if (timer_pending(&sb->u.extN_sb.turn_ro_timer)) {
-+                      add_wait_queue(&EXTN_SB(sb)->ro_wait_queue, &wait);
-+                      if (timer_pending(&EXTN_SB(sb)->turn_ro_timer)) {
-                               schedule();
-                               ret = 1;
-                       }
--                      remove_wait_queue(&sb->u.extN_sb.ro_wait_queue, &wait);
-+                      remove_wait_queue(&EXTN_SB(sb)->ro_wait_queue, &wait);
-                       return ret;
-               }
- #endif
---- extN/extN.orig/namei.c     Wed Sep 25 11:51:34 2002
-+++ extN/namei.c       Wed Sep 25 14:24:17 2002
-@@ -16,6 +16,10 @@
-  *        David S. Miller (davem@caip.rutgers.edu), 1995
-  *  Directory entry file type support and forward compatibility hooks
-  *    for B-tree directories by Theodore Ts'o (tytso@mit.edu), 1998
-+ *  Hash Tree Directory indexing (c)
-+ *    Daniel Phillips, 2001
-+ *  Hash Tree Directory indexing porting
-+ *    Christopher Li, 2002
-  */
- #include <linux/fs.h>
-@@ -23,12 +27,13 @@
- #include <linux/sched.h>
- #include <linux/extN_fs.h>
- #include <linux/extN_jbd.h>
-+#include <linux/extN_xattr.h>
- #include <linux/fcntl.h>
- #include <linux/stat.h>
- #include <linux/string.h>
- #include <linux/locks.h>
- #include <linux/quotaops.h>
--
-+#include <linux/slab.h>
- /*
-  * define how far ahead to read directories while searching them.
-@@ -38,6 +43,432 @@
- #define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS)
- #define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))
-+static struct buffer_head *extN_append(handle_t *handle,
-+                                      struct inode *inode,
-+                                      u32 *block, int *err)
-+{
-+      struct buffer_head *bh;
-+
-+      *block = inode->i_size >> inode->i_sb->s_blocksize_bits;
-+
-+      if ((bh = extN_bread(handle, inode, *block, 1, err))) {
-+              inode->i_size += inode->i_sb->s_blocksize;
-+              EXTN_I(inode)->i_disksize = inode->i_size;
-+              extN_journal_get_write_access(handle,bh);
-+      }
-+      return bh;
-+}
-+
-+#ifndef assert
-+#define assert(test) J_ASSERT(test)
-+#endif
-+
-+#ifndef swap
-+#define swap(x, y) do { typeof(x) z = x; x = y; y = z; } while (0)
-+#endif
-+
-+typedef struct { u32 v; } le_u32;
-+typedef struct { u16 v; } le_u16;
-+
-+#define dxtrace_on(command) command
-+#define dxtrace_off(command)
-+#define dxtrace dxtrace_off
-+
-+struct fake_dirent
-+{
-+      /*le*/u32 inode;
-+      /*le*/u16 rec_len;
-+      u8 name_len;
-+      u8 file_type;
-+};
-+
-+struct dx_countlimit
-+{
-+      le_u16 limit;
-+      le_u16 count;
-+};
-+
-+struct dx_entry
-+{
-+      le_u32 hash;
-+      le_u32 block;
-+};
-+
-+/*
-+ * dx_root_info is laid out so that if it should somehow get overlaid by a
-+ * dirent the two low bits of the hash version will be zero.  Therefore, the
-+ * hash version mod 4 should never be 0.  Sincerely, the paranoia department.
-+ */
-+
-+struct dx_root
-+{
-+      struct fake_dirent dot;
-+      char dot_name[4];
-+      struct fake_dirent dotdot;
-+      char dotdot_name[4];
-+      struct dx_root_info
-+      {
-+              le_u32 reserved_zero;
-+              u8 hash_version; /* 0 now, 1 at release */
-+              u8 info_length; /* 8 */
-+              u8 indirect_levels;
-+              u8 unused_flags;
-+      }
-+      info;
-+      struct dx_entry entries[0];
-+};
-+
-+struct dx_node
-+{
-+      struct fake_dirent fake;
-+      struct dx_entry entries[0];
-+};
-+
-+
-+struct dx_frame
-+{
-+      struct buffer_head *bh;
-+      struct dx_entry *entries;
-+      struct dx_entry *at;
-+};
-+
-+struct dx_map_entry
-+{
-+      u32 hash;
-+      u32 offs;
-+};
-+
-+typedef struct extN_dir_entry_2 extN_dirent;
-+static inline unsigned dx_get_block (struct dx_entry *entry);
-+static void dx_set_block (struct dx_entry *entry, unsigned value);
-+static inline unsigned dx_get_hash (struct dx_entry *entry);
-+static void dx_set_hash (struct dx_entry *entry, unsigned value);
-+static unsigned dx_get_count (struct dx_entry *entries);
-+static unsigned dx_get_limit (struct dx_entry *entries);
-+static void dx_set_count (struct dx_entry *entries, unsigned value);
-+static void dx_set_limit (struct dx_entry *entries, unsigned value);
-+static unsigned dx_root_limit (struct inode *dir, unsigned infosize);
-+static unsigned dx_node_limit (struct inode *dir);
-+static unsigned dx_hack_hash (const u8 *name, int len);
-+static struct dx_frame *dx_probe (struct inode *dir, u32 hash, struct dx_frame *frame);
-+static void dx_release (struct dx_frame *frames);
-+static int dx_make_map (extN_dirent *de, int size, struct dx_map_entry map[]);
-+static void dx_sort_map(struct dx_map_entry *map, unsigned count);
-+static extN_dirent *dx_copy_dirents (char *from, char *to,
-+     struct dx_map_entry *map, int count);
-+static void dx_insert_block (struct dx_frame *frame, u32 hash, u32 block);
-+
-+
-+#ifdef CONFIG_EXTN_INDEX
-+/*
-+ * Future: use high four bits of block for coalesce-on-delete flags
-+ * Mask them off for now.
-+ */
-+
-+static inline unsigned dx_get_block (struct dx_entry *entry)
-+{
-+      return le32_to_cpu(entry->block.v) & 0x00ffffff;
-+}
-+
-+static inline void dx_set_block (struct dx_entry *entry, unsigned value)
-+{
-+      entry->block.v = cpu_to_le32(value);
-+}
-+
-+static inline unsigned dx_get_hash (struct dx_entry *entry)
-+{
-+      return le32_to_cpu(entry->hash.v);
-+}
-+
-+static inline void dx_set_hash (struct dx_entry *entry, unsigned value)
-+{
-+      entry->hash.v = cpu_to_le32(value);
-+}
-+
-+static inline unsigned dx_get_count (struct dx_entry *entries)
-+{
-+      return le16_to_cpu(((struct dx_countlimit *) entries)->count.v);
-+}
-+
-+static inline unsigned dx_get_limit (struct dx_entry *entries)
-+{
-+      return le16_to_cpu(((struct dx_countlimit *) entries)->limit.v);
-+}
-+
-+static inline void dx_set_count (struct dx_entry *entries, unsigned value)
-+{
-+      ((struct dx_countlimit *) entries)->count.v = cpu_to_le16(value);
-+}
-+
-+static inline void dx_set_limit (struct dx_entry *entries, unsigned value)
-+{
-+      ((struct dx_countlimit *) entries)->limit.v = cpu_to_le16(value);
-+}
-+
-+static inline unsigned dx_root_limit (struct inode *dir, unsigned infosize)
-+{
-+      unsigned entry_space = dir->i_sb->s_blocksize - EXTN_DIR_REC_LEN(1) -
-+              EXTN_DIR_REC_LEN(2) - infosize;
-+      return 0? 20: entry_space / sizeof(struct dx_entry);
-+}
-+
-+static inline unsigned dx_node_limit (struct inode *dir)
-+{
-+      unsigned entry_space = dir->i_sb->s_blocksize - EXTN_DIR_REC_LEN(0);
-+      return 0? 22: entry_space / sizeof(struct dx_entry);
-+}
-+
-+/* Hash function - not bad, but still looking for an ideal default */
-+
-+static unsigned dx_hack_hash (const u8 *name, int len)
-+{
-+      u32 hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9;
-+      while (len--)
-+      {
-+              u32 hash = hash1 + (hash0 ^ (*name++ * 7152373));
-+              if (hash & 0x80000000) hash -= 0x7fffffff;
-+              hash1 = hash0;
-+              hash0 = hash;
-+      }
-+      return hash0;
-+}
-+
-+#define dx_hash(s,n) (dx_hack_hash(s,n) << 1)
-+
-+/*
-+ * Debug
-+ */
-+static void dx_show_index (char * label, struct dx_entry *entries)
-+{
-+      int i, n = dx_get_count (entries);
-+      printk("%s index ", label);
-+      for (i = 0; i < n; i++)
-+      {
-+              printk("%x->%u ", i? dx_get_hash(entries + i): 0, dx_get_block(entries + i));
-+      }
-+      printk("\n");
-+}
-+
-+struct stats
-+{ 
-+      unsigned names;
-+      unsigned space;
-+      unsigned bcount;
-+};
-+
-+static struct stats dx_show_leaf (extN_dirent *de, int size, int show_names)
-+{
-+      unsigned names = 0, space = 0;
-+      char *base = (char *) de;
-+      printk("names: ");
-+      while ((char *) de < base + size)
-+      {
-+              if (de->inode)
-+              {
-+                      if (show_names)
-+                      {
-+                              int len = de->name_len;
-+                              char *name = de->name;
-+                              while (len--) printk("%c", *name++);
-+                              printk(":%x.%u ", dx_hash (de->name, de->name_len), ((char *) de - base));
-+                      }
-+                      space += EXTN_DIR_REC_LEN(de->name_len);
-+                      names++;
-+              }
-+              de = (extN_dirent *) ((char *) de + le16_to_cpu(de->rec_len));
-+      }
-+      printk("(%i)\n", names);
-+      return (struct stats) { names, space, 1 };
-+}
-+
-+struct stats dx_show_entries (struct inode *dir, struct dx_entry *entries, int levels)
-+{
-+      unsigned blocksize = dir->i_sb->s_blocksize;
-+      unsigned count = dx_get_count (entries), names = 0, space = 0, i;
-+      unsigned bcount = 0;
-+      struct buffer_head *bh;
-+      int err;
-+      printk("%i indexed blocks...\n", count);
-+      for (i = 0; i < count; i++, entries++)
-+      {
-+              u32 block = dx_get_block(entries), hash = i? dx_get_hash(entries): 0;
-+              u32 range = i < count - 1? (dx_get_hash(entries + 1) - hash): ~hash;
-+              struct stats stats;
-+              printk("%s%3u:%03u hash %8x/%8x ",levels?"":"   ", i, block, hash, range);
-+              if (!(bh = extN_bread (NULL,dir, block, 0,&err))) continue;
-+              stats = levels?
-+                 dx_show_entries (dir, ((struct dx_node *) bh->b_data)->entries, levels - 1):
-+                 dx_show_leaf ((extN_dirent *) bh->b_data, blocksize, 0);
-+              names += stats.names;
-+              space += stats.space;
-+              bcount += stats.bcount;
-+              brelse (bh);
-+      }
-+      if (bcount)
-+              printk("%snames %u, fullness %u (%u%%)\n", levels?"":"   ",
-+                      names, space/bcount,(space/bcount)*100/blocksize);
-+      return (struct stats) { names, space, bcount};
-+}
-+
-+/*
-+ * Probe for a directory leaf block to search
-+ */
-+
-+static struct dx_frame *
-+dx_probe(struct inode *dir, u32 hash, struct dx_frame *frame_in)
-+{
-+      unsigned count, indirect;
-+      struct dx_entry *at, *entries, *p, *q, *m;
-+      struct dx_root *root;
-+      struct buffer_head *bh;
-+      struct dx_frame *frame = frame_in;
-+      int err;
-+
-+      frame->bh = NULL;
-+      if (!(bh = extN_bread(NULL, dir, 0, 0, &err)))
-+              goto fail;
-+      root = (struct dx_root *) bh->b_data;
-+      if (root->info.hash_version > 0 || root->info.unused_flags & 1) {
-+              brelse(bh);
-+              goto fail;
-+      }
-+      if ((indirect = root->info.indirect_levels) > 1) {
-+              brelse(bh);
-+              goto fail;
-+      }
-+      entries = (struct dx_entry *) (((char *) &root->info) + root->info.info_length);
-+      assert (dx_get_limit(entries) == dx_root_limit(dir, root->info.info_length));
-+      dxtrace (printk("Look up %x", hash));
-+      while (1)
-+      {
-+              count = dx_get_count(entries);
-+              assert (count && count <= dx_get_limit(entries));
-+              p = entries + 1;
-+              q = entries + count - 1;
-+              while (p <= q)
-+              {
-+                      m = p + (q - p)/2;
-+                      dxtrace(printk("."));
-+                      if (dx_get_hash(m) > hash)
-+                              q = m - 1;
-+                      else
-+                              p = m + 1;
-+              }
-+
-+              if (0) // linear search cross check
-+              {
-+                      unsigned n = count - 1;
-+                      at = entries;
-+                      while (n--)
-+                      {
-+                              dxtrace(printk(","));
-+                              if (dx_get_hash(++at) > hash)
-+                              {
-+                                      at--;
-+                                      break;
-+                              }
-+                      }
-+                      assert (at == p - 1);
-+              }
-+
-+              at = p - 1;
-+              dxtrace(printk(" %x->%u\n", at == entries? 0: dx_get_hash(at), dx_get_block(at)));
-+              frame->bh = bh;
-+              frame->entries = entries;
-+              frame->at = at;
-+              if (!indirect--) return frame;
-+              if (!(bh = extN_bread (NULL,dir, dx_get_block(at), 0,&err)))
-+                      goto fail2;
-+              at = entries = ((struct dx_node *) bh->b_data)->entries;
-+              assert (dx_get_limit(entries) == dx_node_limit (dir));
-+              frame++;
-+      }
-+fail2:
-+      while (frame >= frame_in) {
-+              brelse(frame->bh);
-+              frame--;
-+      }
-+fail:
-+      return NULL;
-+}
-+
-+static void dx_release (struct dx_frame *frames)
-+{
-+      if (frames[0].bh == NULL)
-+              return;
-+
-+      if (((struct dx_root *)frames[0].bh->b_data)->info.indirect_levels)
-+              brelse (frames[1].bh);
-+      brelse (frames[0].bh);
-+}
-+
-+/*
-+ * Directory block splitting, compacting
-+ */
-+
-+static int dx_make_map (extN_dirent *de, int size, struct dx_map_entry map[])
-+{
-+      int count = 0;
-+      char *base = (char *) de;
-+      while ((char *) de < base + size)
-+      {
-+              map[count].hash = dx_hash (de->name, de->name_len);
-+              map[count].offs = (u32) ((char *) de - base);
-+              de = (extN_dirent *) ((char *) de + le16_to_cpu(de->rec_len));
-+              count++;
-+      }
-+      return count;
-+}
-+
-+static void dx_sort_map (struct dx_map_entry *map, unsigned count)
-+{
-+        struct dx_map_entry *p, *q, *top = map + count - 1;
-+        int more;
-+        /* Combsort until bubble sort doesn't suck */
-+        while (count > 2)
-+      {
-+                count = count*10/13;
-+                if (count - 9 < 2) /* 9, 10 -> 11 */
-+                        count = 11;
-+                for (p = top, q = p - count; q >= map; p--, q--)
-+                        if (p->hash < q->hash)
-+                                swap(*p, *q);
-+        }
-+        /* Garden variety bubble sort */
-+        do {
-+                more = 0;
-+                q = top;
-+                while (q-- > map)
-+              {
-+                        if (q[1].hash >= q[0].hash)
-+                              continue;
-+                        swap(*(q+1), *q);
-+                        more = 1;
-+              }
-+      } while(more);
-+}
-+
-+static void dx_insert_block(struct dx_frame *frame, u32 hash, u32 block)
-+{
-+      struct dx_entry *entries = frame->entries;
-+      struct dx_entry *old = frame->at, *new = old + 1;
-+      int count = dx_get_count(entries);
-+
-+      assert(count < dx_get_limit(entries));
-+      assert(old < entries + count);
-+      memmove(new + 1, new, (char *)(entries + count) - (char *)(new));
-+      dx_set_hash(new, hash);
-+      dx_set_block(new, block);
-+      dx_set_count(entries, count + 1);
-+}
-+#endif
-+
-+static void extN_update_dx_flag(struct inode *inode)
-+{
-+      if (!test_opt(inode->i_sb, INDEX))
-+              EXTN_I(inode)->i_flags &= ~EXTN_INDEX_FL;
-+}
-+
- /*
-  * NOTE! unlike strncmp, extN_match returns 1 for success, 0 for failure.
-  *
-@@ -95,6 +526,15 @@
- }
- /*
-+ * p is at least 6 bytes before the end of page
-+ */
-+static inline extN_dirent *extN_next_entry(extN_dirent *p)
-+{
-+      return (extN_dirent *)((char*)p + le16_to_cpu(p->rec_len));
-+}
-+
-+
-+/*
-  *    extN_find_entry()
-  *
-  * finds an entry in the specified directory with the wanted name. It
-@@ -105,6 +545,8 @@
-  * The returned buffer_head has ->b_count elevated.  The caller is expected
-  * to brelse() it when appropriate.
-  */
-+
-+      
- static struct buffer_head * extN_find_entry (struct dentry *dentry,
-                                       struct extN_dir_entry_2 ** res_dir)
- {
-@@ -119,12 +561,78 @@
-       int num = 0;
-       int nblocks, i, err;
-       struct inode *dir = dentry->d_parent->d_inode;
-+      int namelen;
-+      const u8 *name;
-+      unsigned blocksize;
-+      extN_dirent *de, *top;
-       *res_dir = NULL;
-       sb = dir->i_sb;
-+      blocksize = sb->s_blocksize;
-+      namelen = dentry->d_name.len;
-+      name = dentry->d_name.name;
-+      if (namelen > EXTN_NAME_LEN)
-+              return NULL;
-+      if (extN_dx && is_dx(dir)) {
-+              u32 hash = dx_hash (name, namelen);
-+              struct dx_frame frames[2], *frame;
-+              if (!(frame = dx_probe (dir, hash, frames)))
-+                      return NULL;
-+dxnext:
-+              block = dx_get_block(frame->at);
-+              if (!(bh = extN_bread (NULL,dir, block, 0, &err)))
-+                      goto dxfail;
-+              de = (extN_dirent *) bh->b_data;
-+              top = (extN_dirent *) ((char *) de + blocksize -
-+                              EXTN_DIR_REC_LEN(0));
-+              for (; de < top; de = extN_next_entry(de))
-+                      if (extN_match (namelen, name, de)) {
-+                              if (!extN_check_dir_entry("extN_find_entry",
-+                                        dir, de, bh,
-+                                        (block<<EXTN_BLOCK_SIZE_BITS(sb))
-+                                         +((char *)de - bh->b_data))) {
-+                                      brelse (bh);
-+                                      goto dxfail;
-+                              }
-+                              *res_dir = de;
-+                              goto dxfound;
-+                      }
-+              brelse (bh);
-+              /* Same hash continues in next block?  Search on. */
-+              if (++(frame->at) == frame->entries + dx_get_count(frame->entries))
-+              {
-+                      struct buffer_head *bh2;
-+                      if (frame == frames)
-+                              goto dxfail;
-+                      if (++(frames->at) == frames->entries + dx_get_count(frames->entries))
-+                              goto dxfail;
-+                      /* should omit read if not continued */
-+                      if (!(bh2 = extN_bread (NULL, dir,
-+                                              dx_get_block(frames->at),
-+                                              0, &err)))
-+                              goto dxfail;
-+                      brelse (frame->bh);
-+                      frame->bh = bh2;
-+                      frame->at = frame->entries = ((struct dx_node *) bh2->b_data)->entries;
-+                      /* Subtle: the 0th entry has the count, find the hash in frame above */
-+                      if ((dx_get_hash(frames->at) & -2) == hash)
-+                              goto dxnext;
-+                      goto dxfail;
-+              }
-+              if ((dx_get_hash(frame->at) & -2) == hash)
-+                      goto dxnext;
-+dxfail:
-+              dxtrace(printk("%s not found\n", name));
-+              dx_release (frames);
-+              return NULL;
-+dxfound:
-+              dx_release (frames);
-+              return bh;
-+      }
-+      
-       nblocks = dir->i_size >> EXTN_BLOCK_SIZE_BITS(sb);
--      start = dir->u.extN_i.i_dir_start_lookup;
-+      start = EXTN_I(dir)->i_dir_start_lookup;
-       if (start >= nblocks)
-               start = 0;
-       block = start;
-@@ -165,7 +673,7 @@
-               i = search_dirblock(bh, dir, dentry,
-                           block << EXTN_BLOCK_SIZE_BITS(sb), res_dir);
-               if (i == 1) {
--                      dir->u.extN_i.i_dir_start_lookup = block;
-+                      EXTN_I(dir)->i_dir_start_lookup = block;
-                       ret = bh;
-                       goto cleanup_and_exit;
-               } else {
-@@ -237,6 +745,92 @@
-               de->file_type = extN_type_by_mode[(mode & S_IFMT)>>S_SHIFT];
- }
-+static extN_dirent *
-+dx_copy_dirents (char *from, char *to, struct dx_map_entry *map, int count)
-+{
-+      unsigned rec_len = 0;
-+
-+      while (count--) {
-+              extN_dirent *de = (extN_dirent *) (from + map->offs);
-+              rec_len = EXTN_DIR_REC_LEN(de->name_len);
-+              memcpy (to, de, rec_len);
-+              ((extN_dirent *) to)->rec_len = rec_len;
-+              to += rec_len;
-+              map++;
-+      }
-+      return (extN_dirent *) (to - rec_len);
-+}
-+
-+#ifdef CONFIG_EXTN_INDEX
-+static extN_dirent *do_split(handle_t *handle, struct inode *dir,
-+                      struct buffer_head **bh,struct dx_frame *frame,
-+                      u32 hash, int *error)
-+{
-+      unsigned blocksize = dir->i_sb->s_blocksize;
-+      unsigned count, continued;
-+      struct buffer_head *bh2;
-+      u32 newblock;
-+      unsigned MAX_DX_MAP = PAGE_CACHE_SIZE/EXTN_DIR_REC_LEN(1) + 1;
-+      u32 hash2;
-+      struct dx_map_entry *map;
-+      char *data1 = (*bh)->b_data, *data2, *data3;
-+      unsigned split;
-+      extN_dirent *de, *de2;
-+
-+      bh2 = extN_append (handle, dir, &newblock, error);
-+      if (!(bh2))
-+      {
-+              brelse(*bh);
-+              *bh = NULL;
-+              return (extN_dirent *)bh2;
-+      }
-+
-+      BUFFER_TRACE(*bh, "get_write_access");
-+      extN_journal_get_write_access(handle, *bh);
-+      BUFFER_TRACE(frame->bh, "get_write_access");
-+      extN_journal_get_write_access(handle, frame->bh);
-+
-+      data2 = bh2->b_data;
-+
-+      map = kmalloc(sizeof(*map) * MAX_DX_MAP, GFP_KERNEL);
-+      if (!map)
-+              panic("no memory for do_split\n");
-+      count = dx_make_map ((extN_dirent *) data1, blocksize, map);
-+      split = count/2; // need to adjust to actual middle
-+      dx_sort_map (map, count);
-+      hash2 = map[split].hash;
-+      continued = hash2 == map[split - 1].hash;
-+      dxtrace(printk("Split block %i at %x, %i/%i\n",
-+              dx_get_block(frame->at), hash2, split, count-split));
-+
-+      /* Fancy dance to stay within two buffers */
-+      de2 = dx_copy_dirents (data1, data2, map + split, count - split);
-+      data3 = (char *) de2 + de2->rec_len;
-+      de = dx_copy_dirents (data1, data3, map, split);
-+      memcpy(data1, data3, (char *) de + de->rec_len - data3);
-+      de = (extN_dirent *) ((char *) de - data3 + data1); // relocate de
-+      de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
-+      de2->rec_len = cpu_to_le16(data2 + blocksize - (char *) de2);
-+      dxtrace(dx_show_leaf ((extN_dirent *) data1, blocksize, 1));
-+      dxtrace(dx_show_leaf ((extN_dirent *) data2, blocksize, 1));
-+
-+      /* Which block gets the new entry? */
-+      if (hash >= hash2)
-+      {
-+              swap(*bh, bh2);
-+              de = de2;
-+      }
-+      dx_insert_block (frame, hash2 + continued, newblock);
-+      extN_journal_dirty_metadata (handle, bh2);
-+      brelse (bh2);
-+      extN_journal_dirty_metadata (handle, frame->bh);
-+      dxtrace(dx_show_index ("frame", frame->entries));
-+      kfree(map);
-+      return de;
-+}
-+#endif
-+
-+
- /*
-  *    extN_add_entry()
-  *
-@@ -251,6 +845,7 @@
- /*
-  * AKPM: the journalling code here looks wrong on the error paths
-  */
-+
- static int extN_add_entry (handle_t *handle, struct dentry *dentry,
-       struct inode *inode)
- {
-@@ -258,117 +853,281 @@
-       const char *name = dentry->d_name.name;
-       int namelen = dentry->d_name.len;
-       unsigned long offset;
--      unsigned short rec_len;
-       struct buffer_head * bh;
--      struct extN_dir_entry_2 * de, * de1;
--      struct super_block * sb;
-+      extN_dirent *de;
-+      struct super_block * sb = dir->i_sb;
-       int     retval;
-+      unsigned short reclen = EXTN_DIR_REC_LEN(namelen);
--      sb = dir->i_sb;
-+      unsigned blocksize = sb->s_blocksize;
-+      unsigned nlen, rlen;
-+      u32 block, blocks;
-+      char *top;
-       if (!namelen)
-               return -EINVAL;
--      bh = extN_bread (handle, dir, 0, 0, &retval);
--      if (!bh)
--              return retval;
--      rec_len = EXTN_DIR_REC_LEN(namelen);
--      offset = 0;
--      de = (struct extN_dir_entry_2 *) bh->b_data;
--      while (1) {
--              if ((char *)de >= sb->s_blocksize + bh->b_data) {
--                      brelse (bh);
--                      bh = NULL;
--                      bh = extN_bread (handle, dir,
--                              offset >> EXTN_BLOCK_SIZE_BITS(sb), 1, &retval);
--                      if (!bh)
--                              return retval;
--                      if (dir->i_size <= offset) {
--                              if (dir->i_size == 0) {
--                                      brelse(bh);
--                                      return -ENOENT;
-+      if (extN_dx && is_dx(dir)) {
-+              struct dx_frame frames[2], *frame;
-+              struct dx_entry *entries, *at;
-+              u32 hash;
-+              char *data1;
-+
-+              hash = dx_hash(name, namelen);
-+              /* FIXME: do something if dx_probe() fails here */
-+              frame = dx_probe(dir, hash, frames);
-+              entries = frame->entries;
-+              at = frame->at;
-+
-+              if (!(bh = extN_bread(handle,dir, dx_get_block(at), 0,&retval)))
-+                      goto dxfail1;
-+
-+              BUFFER_TRACE(bh, "get_write_access");
-+              extN_journal_get_write_access(handle, bh);
-+
-+              data1 = bh->b_data;
-+              de = (extN_dirent *) data1;
-+              top = data1 + (0? 200: blocksize);
-+              while ((char *) de < top)
-+              {
-+                      /* FIXME: check EEXIST and dir */
-+                      nlen = EXTN_DIR_REC_LEN(de->name_len);
-+                      rlen = le16_to_cpu(de->rec_len);
-+                      if ((de->inode? rlen - nlen: rlen) >= reclen)
-+                              goto dx_add;
-+                      de = (extN_dirent *) ((char *) de + rlen);
-+              }
-+              /* Block full, should compress but for now just split */
-+              dxtrace(printk("using %u of %u node entries\n",
-+                      dx_get_count(entries), dx_get_limit(entries)));
-+              /* Need to split index? */
-+              if (dx_get_count(entries) == dx_get_limit(entries))
-+              {
-+                      u32 newblock;
-+                      unsigned icount = dx_get_count(entries);
-+                      int levels = frame - frames;
-+                      struct dx_entry *entries2;
-+                      struct dx_node *node2;
-+                      struct buffer_head *bh2;
-+                      if (levels && dx_get_count(frames->entries) == dx_get_limit(frames->entries))
-+                              goto dxfull;
-+                      bh2 = extN_append (handle, dir, &newblock, &retval);
-+                      if (!(bh2))
-+                              goto dxfail2;
-+                      node2 = (struct dx_node *)(bh2->b_data);
-+                      entries2 = node2->entries;
-+                      node2->fake.rec_len = cpu_to_le16(blocksize);
-+                      node2->fake.inode = 0;
-+                      BUFFER_TRACE(frame->bh, "get_write_access");
-+                      extN_journal_get_write_access(handle, frame->bh);
-+                      if (levels)
-+                      {
-+                              unsigned icount1 = icount/2, icount2 = icount - icount1;
-+                              unsigned hash2 = dx_get_hash(entries + icount1);
-+                              dxtrace(printk("Split index %i/%i\n", icount1, icount2));
-+                              
-+                              BUFFER_TRACE(frame->bh, "get_write_access"); /* index root */
-+                              extN_journal_get_write_access(handle, frames[0].bh);
-+                              
-+                              memcpy ((char *) entries2, (char *) (entries + icount1),
-+                                      icount2 * sizeof(struct dx_entry));
-+                              dx_set_count (entries, icount1);
-+                              dx_set_count (entries2, icount2);
-+                              dx_set_limit (entries2, dx_node_limit(dir));
-+
-+                              /* Which index block gets the new entry? */
-+                              if (at - entries >= icount1) {
-+                                      frame->at = at = at - entries - icount1 + entries2;
-+                                      frame->entries = entries = entries2;
-+                                      swap(frame->bh, bh2);
-                               }
--
--                              extN_debug ("creating next block\n");
--
--                              BUFFER_TRACE(bh, "get_write_access");
--                              extN_journal_get_write_access(handle, bh);
--                              de = (struct extN_dir_entry_2 *) bh->b_data;
--                              de->inode = 0;
--                              de->rec_len = le16_to_cpu(sb->s_blocksize);
--                              dir->u.extN_i.i_disksize =
--                                      dir->i_size = offset + sb->s_blocksize;
--                              dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
--                              extN_mark_inode_dirty(handle, dir);
-+                              dx_insert_block (frames + 0, hash2, newblock);
-+                              dxtrace(dx_show_index ("node", frames[1].entries));
-+                              dxtrace(dx_show_index ("node",
-+                                      ((struct dx_node *) bh2->b_data)->entries));
-+                              extN_journal_dirty_metadata(handle, bh2);
-+                              brelse (bh2);
-                       } else {
--
--                              extN_debug ("skipping to next block\n");
--
--                              de = (struct extN_dir_entry_2 *) bh->b_data;
-+                              dxtrace(printk("Creating second level index...\n"));
-+                              memcpy((char *) entries2, (char *) entries,
-+                                      icount * sizeof(struct dx_entry));
-+                              dx_set_limit(entries2, dx_node_limit(dir));
-+
-+                              /* Set up root */
-+                              dx_set_count(entries, 1);
-+                              dx_set_block(entries + 0, newblock);
-+                              ((struct dx_root *) frames[0].bh->b_data)->info.indirect_levels = 1;
-+
-+                              /* Add new access path frame */
-+                              frame = frames + 1;
-+                              frame->at = at = at - entries + entries2;
-+                              frame->entries = entries = entries2;
-+                              frame->bh = bh2;
-+                              extN_journal_get_write_access(handle, frame->bh);
-                       }
-+                      extN_journal_dirty_metadata(handle, frames[0].bh);
-               }
--              if (!extN_check_dir_entry ("extN_add_entry", dir, de, bh,
--                                         offset)) {
--                      brelse (bh);
--                      return -ENOENT;
--              }
--              if (extN_match (namelen, name, de)) {
-+              de = do_split(handle, dir, &bh, frame, hash, &retval);
-+              dx_release (frames);
-+              if (!(de))
-+                      goto fail;
-+              nlen = EXTN_DIR_REC_LEN(de->name_len);
-+              rlen = le16_to_cpu(de->rec_len);
-+              goto add;
-+
-+dx_add:
-+              dx_release (frames);
-+              goto add;
-+
-+dxfull:
-+              extN_warning(sb, __FUNCTION__, "Directory index full!\n");
-+              retval = -ENOSPC;
-+dxfail2:
-+              brelse(bh);
-+dxfail1:
-+              dx_release (frames);
-+              goto fail1;
-+      }
-+
-+      blocks = dir->i_size >> sb->s_blocksize_bits;
-+      for (block = 0, offset = 0; block < blocks; block++) {
-+              bh = extN_bread(handle, dir, block, 0, &retval);
-+              if(!bh)
-+                      return retval;
-+              de = (extN_dirent *)bh->b_data;
-+              top = bh->b_data + blocksize - reclen;
-+              while ((char *) de <= top) {
-+                      if (!extN_check_dir_entry("extN_add_entry", dir, de,
-+                                                bh, offset)) {
-+                              brelse (bh);
-+                              return -EIO;
-+                      }
-+                      if (extN_match (namelen, name, de)) {
-                               brelse (bh);
-                               return -EEXIST;
--              }
--              if ((le32_to_cpu(de->inode) == 0 &&
--                              le16_to_cpu(de->rec_len) >= rec_len) ||
--                  (le16_to_cpu(de->rec_len) >=
--                              EXTN_DIR_REC_LEN(de->name_len) + rec_len)) {
--                      BUFFER_TRACE(bh, "get_write_access");
--                      extN_journal_get_write_access(handle, bh);
--                      /* By now the buffer is marked for journaling */
--                      offset += le16_to_cpu(de->rec_len);
--                      if (le32_to_cpu(de->inode)) {
--                              de1 = (struct extN_dir_entry_2 *) ((char *) de +
--                                      EXTN_DIR_REC_LEN(de->name_len));
--                              de1->rec_len =
--                                      cpu_to_le16(le16_to_cpu(de->rec_len) -
--                                      EXTN_DIR_REC_LEN(de->name_len));
--                              de->rec_len = cpu_to_le16(
--                                              EXTN_DIR_REC_LEN(de->name_len));
--                              de = de1;
-                       }
--                      de->file_type = EXTN_FT_UNKNOWN;
--                      if (inode) {
--                              de->inode = cpu_to_le32(inode->i_ino);
--                              extN_set_de_type(dir->i_sb, de, inode->i_mode);
--                      } else
--                              de->inode = 0;
--                      de->name_len = namelen;
--                      memcpy (de->name, name, namelen);
--                      /*
--                       * XXX shouldn't update any times until successful
--                       * completion of syscall, but too many callers depend
--                       * on this.
--                       *
--                       * XXX similarly, too many callers depend on
--                       * extN_new_inode() setting the times, but error
--                       * recovery deletes the inode, so the worst that can
--                       * happen is that the times are slightly out of date
--                       * and/or different from the directory change time.
--                       */
--                      dir->i_mtime = dir->i_ctime = CURRENT_TIME;
--                      dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
--                      extN_mark_inode_dirty(handle, dir);
--                      dir->i_version = ++event;
--                      BUFFER_TRACE(bh, "call extN_journal_dirty_metadata");
--                      extN_journal_dirty_metadata(handle, bh);
-+                      nlen = EXTN_DIR_REC_LEN(de->name_len);
-+                      rlen = le16_to_cpu(de->rec_len);
-+                      if ((de->inode? rlen - nlen: rlen) >= reclen)
-+                              goto add;
-+                      de = (extN_dirent *)((char *)de + rlen);
-+                      offset += rlen;
-+              }
-+              if (extN_dx && blocks == 1 && test_opt(sb, INDEX))
-+                      goto dx_make_index;
-+              brelse(bh);
-+      }
-+      bh = extN_append(handle, dir, &block, &retval);
-+      if (!bh)
-+              return retval;
-+      de = (extN_dirent *) bh->b_data;
-+      de->inode = 0;
-+      de->rec_len = cpu_to_le16(rlen = blocksize);
-+      nlen = 0;
-+      goto add;
-+
-+add:
-+      BUFFER_TRACE(bh, "get_write_access");
-+      extN_journal_get_write_access(handle, bh);
-+      /* By now the buffer is marked for journaling */
-+      if (de->inode) {
-+              extN_dirent *de1 = (extN_dirent *)((char *)de + nlen);
-+              de1->rec_len = cpu_to_le16(rlen - nlen);
-+              de->rec_len = cpu_to_le16(nlen);
-+              de = de1;
-+      }
-+      de->file_type = EXTN_FT_UNKNOWN;
-+      if (inode) {
-+              de->inode = cpu_to_le32(inode->i_ino);
-+              extN_set_de_type(dir->i_sb, de, inode->i_mode);
-+      } else
-+              de->inode = 0;
-+      de->name_len = namelen;
-+      memcpy (de->name, name, namelen);
-+      /*
-+       * XXX shouldn't update any times until successful
-+       * completion of syscall, but too many callers depend
-+       * on this.
-+       *
-+       * XXX similarly, too many callers depend on
-+       * extN_new_inode() setting the times, but error
-+       * recovery deletes the inode, so the worst that can
-+       * happen is that the times are slightly out of date
-+       * and/or different from the directory change time.
-+       */
-+      dir->i_mtime = dir->i_ctime = CURRENT_TIME;
-+      extN_update_dx_flag(dir);
-+      dir->i_version = ++event;
-+      extN_mark_inode_dirty(handle, dir);
-+      BUFFER_TRACE(bh, "call extN_journal_dirty_metadata");
-+      extN_journal_dirty_metadata(handle, bh);
-+      brelse(bh);
-+      return 0;
-+
-+dx_make_index:
-+      {
-+              struct buffer_head *bh2;
-+              struct dx_root *root;
-+              struct dx_frame frames[2], *frame;
-+              struct dx_entry *entries;
-+              extN_dirent *de2;
-+              char *data1;
-+              unsigned len;
-+              u32 hash;
-+              
-+              dxtrace(printk("Creating index\n"));
-+              extN_journal_get_write_access(handle, bh);
-+              root = (struct dx_root *) bh->b_data;
-+              
-+              EXTN_I(dir)->i_flags |= EXTN_INDEX_FL;
-+              bh2 = extN_append (handle, dir, &block, &retval);
-+              if (!(bh2))
-+              {
-                       brelse(bh);
--                      return 0;
-+                      return retval;
-               }
--              offset += le16_to_cpu(de->rec_len);
--              de = (struct extN_dir_entry_2 *)
--                      ((char *) de + le16_to_cpu(de->rec_len));
-+              data1 = bh2->b_data;
-+
-+              /* The 0th block becomes the root, move the dirents out */
-+              de = (extN_dirent *) &root->info;
-+              len = ((char *) root) + blocksize - (char *) de;
-+              memcpy (data1, de, len);
-+              de = (extN_dirent *) data1;
-+              top = data1 + len;
-+              while (((char *) de2=(char*)de+le16_to_cpu(de->rec_len)) < top)
-+                      de = de2;
-+              de->rec_len = cpu_to_le16(data1 + blocksize - (char *) de);
-+              /* Initialize the root; the dot dirents already exist */
-+              de = (extN_dirent *) (&root->dotdot);
-+              de->rec_len = cpu_to_le16(blocksize - EXTN_DIR_REC_LEN(2));
-+              memset (&root->info, 0, sizeof(root->info));
-+              root->info.info_length = sizeof(root->info);
-+              entries = root->entries;
-+              dx_set_block (entries, 1);
-+              dx_set_count (entries, 1);
-+              dx_set_limit (entries, dx_root_limit(dir, sizeof(root->info)));
-+
-+              /* Initialize as for dx_probe */
-+              hash = dx_hash (name, namelen);
-+              frame = frames;
-+              frame->entries = entries;
-+              frame->at = entries;
-+              frame->bh = bh;
-+              bh = bh2;
-+              de = do_split(handle,dir, &bh, frame, hash, &retval);
-+              dx_release (frames);
-+              if (!(de))
-+                      return retval;
-+              nlen = EXTN_DIR_REC_LEN(de->name_len);
-+              rlen = le16_to_cpu(de->rec_len);
-+              goto add;
-       }
--      brelse (bh);
--      return -ENOSPC;
-+fail1:
-+      return retval;
-+fail:
-+      return -ENOENT;
- }
-+
- /*
-  * extN_delete_entry deletes a directory entry by merging it with the
-  * previous entry
-@@ -451,7 +1210,8 @@
-       struct inode * inode;
-       int err;
--      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS + 3);
-+      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS + 3);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-@@ -464,8 +1224,10 @@
-               inode->i_op = &extN_file_inode_operations;
-               inode->i_fop = &extN_file_operations;
-               inode->i_mapping->a_ops = &extN_aops;
--              extN_mark_inode_dirty(handle, inode);
-               err = extN_add_nondir(handle, dentry, inode);
-+              if (err)
-+                      extN_xattr_drop_inode(handle, inode);
-+              extN_mark_inode_dirty(handle, inode);
-       }
-       extN_journal_stop(handle, dir);
-       return err;
-@@ -478,7 +1240,8 @@
-       struct inode *inode;
-       int err;
--      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS + 3);
-+      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS + 3);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-@@ -489,8 +1252,10 @@
-       err = PTR_ERR(inode);
-       if (!IS_ERR(inode)) {
-               init_special_inode(inode, mode, rdev);
--              extN_mark_inode_dirty(handle, inode);
-               err = extN_add_nondir(handle, dentry, inode);
-+              if (err)
-+                      extN_xattr_drop_inode(handle, inode);
-+              extN_mark_inode_dirty(handle, inode);
-       }
-       extN_journal_stop(handle, dir);
-       return err;
-@@ -507,22 +1272,22 @@
-       if (dir->i_nlink >= EXTN_LINK_MAX)
-               return -EMLINK;
--      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS + 3);
-+      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS + 3);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-       if (IS_SYNC(dir))
-               handle->h_sync = 1;
--      inode = extN_new_inode (handle, dir, S_IFDIR);
-+      inode = extN_new_inode (handle, dir, S_IFDIR | mode);
-       err = PTR_ERR(inode);
-       if (IS_ERR(inode))
-               goto out_stop;
-       inode->i_op = &extN_dir_inode_operations;
-       inode->i_fop = &extN_dir_operations;
--      inode->i_size = inode->u.extN_i.i_disksize = inode->i_sb->s_blocksize;
--      inode->i_blocks = 0;    
-+      inode->i_size = EXTN_I(inode)->i_disksize = inode->i_sb->s_blocksize;
-       dir_block = extN_bread (handle, inode, 0, 1, &err);
-       if (!dir_block) {
-               inode->i_nlink--; /* is this nlink == 0? */
-@@ -549,15 +1314,12 @@
-       BUFFER_TRACE(dir_block, "call extN_journal_dirty_metadata");
-       extN_journal_dirty_metadata(handle, dir_block);
-       brelse (dir_block);
--      inode->i_mode = S_IFDIR | mode;
--      if (dir->i_mode & S_ISGID)
--              inode->i_mode |= S_ISGID;
-       extN_mark_inode_dirty(handle, inode);
-       err = extN_add_entry (handle, dentry, inode);
-       if (err)
-               goto out_no_entry;
-       dir->i_nlink++;
--      dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
-+      extN_update_dx_flag(dir);
-       extN_mark_inode_dirty(handle, dir);
-       d_instantiate(dentry, inode);
- out_stop:
-@@ -565,6 +1327,7 @@
-       return err;
- out_no_entry:
-+      extN_xattr_drop_inode(handle, inode);
-       inode->i_nlink = 0;
-       extN_mark_inode_dirty(handle, inode);
-       iput (inode);
-@@ -655,7 +1418,7 @@
-       int err = 0, rc;
-       
-       lock_super(sb);
--      if (!list_empty(&inode->u.extN_i.i_orphan))
-+      if (!list_empty(&EXTN_I(inode)->i_orphan))
-               goto out_unlock;
-       /* Orphan handling is only valid for files with data blocks
-@@ -670,8 +1433,8 @@
-       J_ASSERT ((S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
-               S_ISLNK(inode->i_mode)) || inode->i_nlink == 0);
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "get_write_access");
--      err = extN_journal_get_write_access(handle, sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(EXTN_SB(sb)->s_sbh, "get_write_access");
-+      err = extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh);
-       if (err)
-               goto out_unlock;
-       
-@@ -682,7 +1445,7 @@
-       /* Insert this inode at the head of the on-disk orphan list... */
-       NEXT_ORPHAN(inode) = le32_to_cpu(EXTN_SB(sb)->s_es->s_last_orphan);
-       EXTN_SB(sb)->s_es->s_last_orphan = cpu_to_le32(inode->i_ino);
--      err = extN_journal_dirty_metadata(handle, sb->u.extN_sb.s_sbh);
-+      err = extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh);
-       rc = extN_mark_iloc_dirty(handle, inode, &iloc);
-       if (!err)
-               err = rc;
-@@ -696,7 +1459,7 @@
-        * This is safe: on error we're going to ignore the orphan list
-        * anyway on the next recovery. */
-       if (!err)
--              list_add(&inode->u.extN_i.i_orphan, &EXTN_SB(sb)->s_orphan);
-+              list_add(&EXTN_I(inode)->i_orphan, &EXTN_SB(sb)->s_orphan);
-       jbd_debug(4, "superblock will point to %ld\n", inode->i_ino);
-       jbd_debug(4, "orphan inode %ld will point to %d\n",
-@@ -714,25 +1477,25 @@
- int extN_orphan_del(handle_t *handle, struct inode *inode)
- {
-       struct list_head *prev;
-+      struct extN_inode_info *ei = EXTN_I(inode);
-       struct extN_sb_info *sbi;
-       ino_t ino_next; 
-       struct extN_iloc iloc;
-       int err = 0;
-       
-       lock_super(inode->i_sb);
--      if (list_empty(&inode->u.extN_i.i_orphan)) {
-+      if (list_empty(&ei->i_orphan)) {
-               unlock_super(inode->i_sb);
-               return 0;
-       }
-       ino_next = NEXT_ORPHAN(inode);
--      prev = inode->u.extN_i.i_orphan.prev;
-+      prev = ei->i_orphan.prev;
-       sbi = EXTN_SB(inode->i_sb);
-       jbd_debug(4, "remove inode %ld from orphan list\n", inode->i_ino);
--      list_del(&inode->u.extN_i.i_orphan);
--      INIT_LIST_HEAD(&inode->u.extN_i.i_orphan);
-+      list_del_init(&ei->i_orphan);
-       /* If we're on an error path, we may not have a valid
-        * transaction handle with which to update the orphan list on
-@@ -755,9 +1518,8 @@
-               err = extN_journal_dirty_metadata(handle, sbi->s_sbh);
-       } else {
-               struct extN_iloc iloc2;
--              struct inode *i_prev =
--                      list_entry(prev, struct inode, u.extN_i.i_orphan);
--              
-+              struct inode *i_prev = orphan_list_entry(prev);
-+
-               jbd_debug(4, "orphan inode %ld will point to %ld\n",
-                         i_prev->i_ino, ino_next);
-               err = extN_reserve_inode_write(handle, i_prev, &iloc2);
-@@ -832,7 +1594,7 @@
-       extN_mark_inode_dirty(handle, inode);
-       dir->i_nlink--;
-       inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
--      dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
-+      extN_update_dx_flag(dir);
-       extN_mark_inode_dirty(handle, dir);
- end_rmdir:
-@@ -878,7 +1640,7 @@
-       if (retval)
-               goto end_unlink;
-       dir->i_ctime = dir->i_mtime = CURRENT_TIME;
--      dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
-+      extN_update_dx_flag(dir);
-       extN_mark_inode_dirty(handle, dir);
-       inode->i_nlink--;
-       if (!inode->i_nlink)
-@@ -904,7 +1666,8 @@
-       if (l > dir->i_sb->s_blocksize)
-               return -ENAMETOOLONG;
--      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS + 5);
-+      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS + 5);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-@@ -916,7 +1679,7 @@
-       if (IS_ERR(inode))
-               goto out_stop;
--      if (l > sizeof (inode->u.extN_i.i_data)) {
-+      if (l > sizeof(EXTN_I(inode)->i_data)) {
-               inode->i_op = &page_symlink_inode_operations;
-               inode->i_mapping->a_ops = &extN_aops;
-               /*
-@@ -929,10 +1692,10 @@
-                       goto out_no_entry;
-       } else {
-               inode->i_op = &extN_fast_symlink_inode_operations;
--              memcpy((char*)&inode->u.extN_i.i_data,symname,l);
-+              memcpy((char*)&EXTN_I(inode)->i_data,symname,l);
-               inode->i_size = l-1;
-       }
--      inode->u.extN_i.i_disksize = inode->i_size;
-+      EXTN_I(inode)->i_disksize = inode->i_size;
-       extN_mark_inode_dirty(handle, inode);
-       err = extN_add_nondir(handle, dentry, inode);
- out_stop:
-@@ -959,7 +1722,8 @@
-       if (inode->i_nlink >= EXTN_LINK_MAX)
-               return -EMLINK;
--      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS);
-+      handle = extN_journal_start(dir, EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-@@ -995,7 +1759,8 @@
-       old_bh = new_bh = dir_bh = NULL;
--      handle = extN_journal_start(old_dir, 2 * EXTN_DATA_TRANS_BLOCKS + 2);
-+      handle = extN_journal_start(old_dir, 2 * EXTN_DATA_TRANS_BLOCKS +
-+                                      EXTN_INDEX_EXTRA_TRANS_BLOCKS + 2);
-       if (IS_ERR(handle))
-               return PTR_ERR(handle);
-@@ -1077,7 +1842,7 @@
-               new_inode->i_ctime = CURRENT_TIME;
-       }
-       old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
--      old_dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
-+      extN_update_dx_flag(old_dir);
-       if (dir_bh) {
-               BUFFER_TRACE(dir_bh, "get_write_access");
-               extN_journal_get_write_access(handle, dir_bh);
-@@ -1089,7 +1854,7 @@
-                       new_inode->i_nlink--;
-               } else {
-                       new_dir->i_nlink++;
--                      new_dir->u.extN_i.i_flags &= ~EXTN_INDEX_FL;
-+                      extN_update_dx_flag(new_dir);
-                       extN_mark_inode_dirty(handle, new_dir);
-               }
-       }
---- extN/extN.orig/super.c     Wed Sep 25 11:51:34 2002
-+++ extN/super.c       Wed Sep 25 11:53:07 2002
-@@ -24,6 +24,7 @@
- #include <linux/jbd.h>
- #include <linux/extN_fs.h>
- #include <linux/extN_jbd.h>
-+#include <linux/extN_xattr.h>
- #include <linux/slab.h>
- #include <linux/init.h>
- #include <linux/locks.h>
-@@ -121,7 +122,7 @@
-       /* If no overrides were specified on the mount, then fall back
-        * to the default behaviour set in the filesystem's superblock
-        * on disk. */
--      switch (le16_to_cpu(sb->u.extN_sb.s_es->s_errors)) {
-+      switch (le16_to_cpu(EXTN_SB(sb)->s_es->s_errors)) {
-       case EXTN_ERRORS_PANIC:
-               return EXTN_ERRORS_PANIC;
-       case EXTN_ERRORS_RO:
-@@ -269,9 +270,9 @@
-               return;
-       
-       printk (KERN_CRIT "Remounting filesystem read-only\n");
--      sb->u.extN_sb.s_mount_state |= EXTN_ERROR_FS;
-+      EXTN_SB(sb)->s_mount_state |= EXTN_ERROR_FS;
-       sb->s_flags |= MS_RDONLY;
--      sb->u.extN_sb.s_mount_opt |= EXTN_MOUNT_ABORT;
-+      EXTN_SB(sb)->s_mount_opt |= EXTN_MOUNT_ABORT;
-       journal_abort(EXTN_SB(sb)->s_journal, -EIO);
- }
-@@ -377,8 +378,6 @@
-       return ret;
- }
--#define orphan_list_entry(l) list_entry((l), struct inode, u.extN_i.i_orphan)
--
- static void dump_orphan_list(struct super_block *sb, struct extN_sb_info *sbi)
- {
-       struct list_head *l;
-@@ -404,6 +403,7 @@
-       kdev_t j_dev = sbi->s_journal->j_dev;
-       int i;
-+      extN_xattr_put_super(sb);
-       journal_destroy(sbi->s_journal);
-       if (!(sb->s_flags & MS_RDONLY)) {
-               EXTN_CLEAR_INCOMPAT_FEATURE(sb, EXTN_FEATURE_INCOMPAT_RECOVER);
-@@ -529,6 +529,12 @@
-                                      "EXTN Check option not supported\n");
- #endif
-               }
-+              else if (!strcmp (this_char, "index"))
-+#ifdef CONFIG_EXTN_INDEX
-+                      set_opt (*mount_options, INDEX);
-+#else
-+                      printk("EXTN index option not supported\n");
-+#endif
-               else if (!strcmp (this_char, "debug"))
-                       set_opt (*mount_options, DEBUG);
-               else if (!strcmp (this_char, "errors")) {
-@@ -702,6 +708,12 @@
-       es->s_mtime = cpu_to_le32(CURRENT_TIME);
-       extN_update_dynamic_rev(sb);
-       EXTN_SET_INCOMPAT_FEATURE(sb, EXTN_FEATURE_INCOMPAT_RECOVER);
-+
-+      if (test_opt(sb, INDEX))
-+              EXTN_SET_COMPAT_FEATURE(sb, EXTN_FEATURE_COMPAT_DIR_INDEX);
-+      else if (EXTN_HAS_COMPAT_FEATURE(sb, EXTN_FEATURE_COMPAT_DIR_INDEX))
-+              set_opt (EXTN_SB(sb)->s_mount_opt, INDEX);
-+
-       extN_commit_super (sb, es, 1);
-       if (test_opt (sb, DEBUG))
-               printk (KERN_INFO
-@@ -818,7 +830,7 @@
-               sb->s_flags &= ~MS_RDONLY;
-       }
--      if (sb->u.extN_sb.s_mount_state & EXTN_ERROR_FS) {
-+      if (EXTN_SB(sb)->s_mount_state & EXTN_ERROR_FS) {
-               if (es->s_last_orphan)
-                       jbd_debug(1, "Errors on filesystem, "
-                                 "clearing orphan list.\n");
-@@ -1334,10 +1346,10 @@
-               printk(KERN_ERR "EXTN-fs: I/O error on journal device\n");
-               goto out_journal;
-       }
--      if (ntohl(journal->j_superblock->s_nr_users) != 1) {
-+      if (be32_to_cpu(journal->j_superblock->s_nr_users) != 1) {
-               printk(KERN_ERR "EXTN-fs: External journal has more than one "
-                                       "user (unsupported) - %d\n",
--                      ntohl(journal->j_superblock->s_nr_users));
-+                      be32_to_cpu(journal->j_superblock->s_nr_users));
-               goto out_journal;
-       }
-       EXTN_SB(sb)->journal_bdev = bdev;
-@@ -1463,12 +1475,14 @@
-                              struct extN_super_block * es,
-                              int sync)
- {
-+      struct buffer_head *sbh = EXTN_SB(sb)->s_sbh;
-+
-       es->s_wtime = cpu_to_le32(CURRENT_TIME);
--      BUFFER_TRACE(sb->u.extN_sb.s_sbh, "marking dirty");
--      mark_buffer_dirty(sb->u.extN_sb.s_sbh);
-+      BUFFER_TRACE(sbh, "marking dirty");
-+      mark_buffer_dirty(sbh);
-       if (sync) {
--              ll_rw_block(WRITE, 1, &sb->u.extN_sb.s_sbh);
--              wait_on_buffer(sb->u.extN_sb.s_sbh);
-+              ll_rw_block(WRITE, 1, &sbh);
-+              wait_on_buffer(sbh);
-       }
- }
-@@ -1519,7 +1533,7 @@
-               extN_warning(sb, __FUNCTION__, "Marking fs in need of "
-                            "filesystem check.");
-               
--              sb->u.extN_sb.s_mount_state |= EXTN_ERROR_FS;
-+              EXTN_SB(sb)->s_mount_state |= EXTN_ERROR_FS;
-               es->s_state |= cpu_to_le16(EXTN_ERROR_FS);
-               extN_commit_super (sb, es, 1);
-@@ -1734,14 +1748,25 @@
- static DECLARE_FSTYPE_DEV(extN_fs_type, "extN", extN_read_super);
--static int __init init_extN_fs(void)
-+static void exit_extN_fs(void)
- {
--        return register_filesystem(&extN_fs_type);
-+      unregister_filesystem(&extN_fs_type);
-+      exit_extN_xattr_user();
-+      exit_extN_xattr();
- }
--static void __exit exit_extN_fs(void)
-+static int __init init_extN_fs(void)
- {
--      unregister_filesystem(&extN_fs_type);
-+      int error = init_extN_xattr();
-+      if (!error)
-+              error = init_extN_xattr_user();
-+      if (!error)
-+              error = register_filesystem(&extN_fs_type);
-+      if (!error)
-+              return 0;
-+
-+      exit_extN_fs();
-+      return error;
- }
- EXPORT_SYMBOL(extN_bread);
---- extN/extN.orig/symlink.c   Wed Sep 25 11:51:34 2002
-+++ extN/symlink.c     Wed Sep 25 11:53:07 2002
-@@ -23,14 +23,14 @@
- static int extN_readlink(struct dentry *dentry, char *buffer, int buflen)
- {
--      char *s = (char *)dentry->d_inode->u.extN_i.i_data;
--      return vfs_readlink(dentry, buffer, buflen, s);
-+      struct extN_inode_info *ei = EXTN_I(dentry->d_inode);
-+      return vfs_readlink(dentry, buffer, buflen, (char *)ei->i_data);
- }
- static int extN_follow_link(struct dentry *dentry, struct nameidata *nd)
- {
--      char *s = (char *)dentry->d_inode->u.extN_i.i_data;
--      return vfs_follow_link(nd, s);
-+      struct extN_inode_info *ei = EXTN_I(dentry->d_inode);
-+      return vfs_follow_link(nd, (char*)ei->i_data);
- }
- struct inode_operations extN_fast_symlink_inode_operations = {
---- extN/extN-include.orig/extN_fs.h   Wed Sep 25 11:51:34 2002
-+++ include/linux/extN_fs.h    Wed Sep 25 14:13:38 2002
-@@ -17,6 +17,8 @@
- #define _LINUX_EXTN_FS_H
- #include <linux/types.h>
-+#include <linux/extN_fs_sb.h>
-+#include <linux/extN_fs_i.h>
- /*
-  * The second extended filesystem constants/structures
-@@ -58,8 +60,6 @@
-  */
- #define       EXTN_BAD_INO             1      /* Bad blocks inode */
- #define EXTN_ROOT_INO          2      /* Root inode */
--#define EXTN_ACL_IDX_INO       3      /* ACL inode */
--#define EXTN_ACL_DATA_INO      4      /* ACL inode */
- #define EXTN_BOOT_LOADER_INO   5      /* Boot loader inode */
- #define EXTN_UNDEL_DIR_INO     6      /* Undelete directory inode */
- #define EXTN_RESIZE_INO                7      /* Reserved group descriptors inode */
-@@ -84,23 +84,25 @@
- #define EXTN_MIN_BLOCK_SIZE           1024
- #define       EXTN_MAX_BLOCK_SIZE             4096
- #define EXTN_MIN_BLOCK_LOG_SIZE                 10
-+
- #ifdef __KERNEL__
--# define EXTN_BLOCK_SIZE(s)           ((s)->s_blocksize)
--#else
--# define EXTN_BLOCK_SIZE(s)           (EXTN_MIN_BLOCK_SIZE << (s)->s_log_block_size)
--#endif
--#define EXTN_ACLE_PER_BLOCK(s)                (EXTN_BLOCK_SIZE(s) / sizeof (struct extN_acl_entry))
--#define       EXTN_ADDR_PER_BLOCK(s)          (EXTN_BLOCK_SIZE(s) / sizeof (__u32))
--#ifdef __KERNEL__
--# define EXTN_BLOCK_SIZE_BITS(s)      ((s)->s_blocksize_bits)
--#else
--# define EXTN_BLOCK_SIZE_BITS(s)      ((s)->s_log_block_size + 10)
--#endif
--#ifdef __KERNEL__
--#define       EXTN_ADDR_PER_BLOCK_BITS(s)     ((s)->u.extN_sb.s_addr_per_block_bits)
--#define EXTN_INODE_SIZE(s)            ((s)->u.extN_sb.s_inode_size)
--#define EXTN_FIRST_INO(s)             ((s)->u.extN_sb.s_first_ino)
-+#define EXTN_SB(sb)   ((struct extN_sb_info *)&((sb)->u.generic_sbp))
-+#define EXTN_I(inode) ((struct extN_inode_info *)&((inode)->u.generic_ip))
-+
-+#define EXTN_BLOCK_SIZE(s)            ((s)->s_blocksize)
-+#define EXTN_BLOCK_SIZE_BITS(s)               ((s)->s_blocksize_bits)
-+#define       EXTN_ADDR_PER_BLOCK_BITS(s)     (EXTN_SB(s)->s_addr_per_block_bits)
-+#define EXTN_INODE_SIZE(s)            (EXTN_SB(s)->s_inode_size)
-+#define EXTN_FIRST_INO(s)             (EXTN_SB(s)->s_first_ino)
- #else
-+
-+/* Assume that user mode programs are passing in an extNfs superblock, not
-+ * a kernel struct super_block.  This will allow us to call the feature-test
-+ * macros from user land. */
-+#define EXTN_SB(sb)   (sb)
-+
-+#define EXTN_BLOCK_SIZE(s)    (EXTN_MIN_BLOCK_SIZE << (s)->s_log_block_size)
-+#define EXTN_BLOCK_SIZE_BITS(s)       ((s)->s_log_block_size + 10)
- #define EXTN_INODE_SIZE(s)    (((s)->s_rev_level == EXTN_GOOD_OLD_REV) ? \
-                                EXTN_GOOD_OLD_INODE_SIZE : \
-                                (s)->s_inode_size)
-@@ -108,6 +110,7 @@
-                                EXTN_GOOD_OLD_FIRST_INO : \
-                                (s)->s_first_ino)
- #endif
-+#define EXTN_ADDR_PER_BLOCK(s)        (EXTN_BLOCK_SIZE(s) / sizeof (__u32))
- /*
-  * Macro-instructions used to manage fragments
-@@ -116,36 +119,14 @@
- #define       EXTN_MAX_FRAG_SIZE              4096
- #define EXTN_MIN_FRAG_LOG_SIZE                  10
- #ifdef __KERNEL__
--# define EXTN_FRAG_SIZE(s)            ((s)->u.extN_sb.s_frag_size)
--# define EXTN_FRAGS_PER_BLOCK(s)      ((s)->u.extN_sb.s_frags_per_block)
-+# define EXTN_FRAG_SIZE(s)            (EXTN_SB(s)->s_frag_size)
-+# define EXTN_FRAGS_PER_BLOCK(s)      (EXTN_SB(s)->s_frags_per_block)
- #else
- # define EXTN_FRAG_SIZE(s)            (EXTN_MIN_FRAG_SIZE << (s)->s_log_frag_size)
- # define EXTN_FRAGS_PER_BLOCK(s)      (EXTN_BLOCK_SIZE(s) / EXTN_FRAG_SIZE(s))
- #endif
- /*
-- * ACL structures
-- */
--struct extN_acl_header        /* Header of Access Control Lists */
--{
--      __u32   aclh_size;
--      __u32   aclh_file_count;
--      __u32   aclh_acle_count;
--      __u32   aclh_first_acle;
--};
--
--struct extN_acl_entry /* Access Control List Entry */
--{
--      __u32   acle_size;
--      __u16   acle_perms;     /* Access permissions */
--      __u16   acle_type;      /* Type of entry */
--      __u16   acle_tag;       /* User or group identity */
--      __u16   acle_pad1;
--      __u32   acle_next;      /* Pointer on next entry for the */
--                                      /* same inode or on next free entry */
--};
--
--/*
-  * Structure of a blocks group descriptor
-  */
- struct extN_group_desc
-@@ -163,15 +144,13 @@
- /*
-  * Macro-instructions used to manage group descriptors
-  */
-+# define EXTN_BLOCKS_PER_GROUP(s)     (EXTN_SB(s)->s_blocks_per_group)
-+# define EXTN_INODES_PER_GROUP(s)     (EXTN_SB(s)->s_inodes_per_group)
- #ifdef __KERNEL__
--# define EXTN_BLOCKS_PER_GROUP(s)     ((s)->u.extN_sb.s_blocks_per_group)
--# define EXTN_DESC_PER_BLOCK(s)               ((s)->u.extN_sb.s_desc_per_block)
--# define EXTN_INODES_PER_GROUP(s)     ((s)->u.extN_sb.s_inodes_per_group)
--# define EXTN_DESC_PER_BLOCK_BITS(s)  ((s)->u.extN_sb.s_desc_per_block_bits)
-+# define EXTN_DESC_PER_BLOCK(s)               (EXTN_SB(s)->s_desc_per_block)
-+# define EXTN_DESC_PER_BLOCK_BITS(s)  (EXTN_SB(s)->s_desc_per_block_bits)
- #else
--# define EXTN_BLOCKS_PER_GROUP(s)     ((s)->s_blocks_per_group)
- # define EXTN_DESC_PER_BLOCK(s)               (EXTN_BLOCK_SIZE(s) / sizeof (struct extN_group_desc))
--# define EXTN_INODES_PER_GROUP(s)     ((s)->s_inodes_per_group)
- #endif
- /*
-@@ -339,12 +318,13 @@
-   #define EXTN_MOUNT_WRITEBACK_DATA   0x0C00  /* No data ordering */
- #define EXTN_MOUNT_UPDATE_JOURNAL     0x1000  /* Update the journal format */
- #define EXTN_MOUNT_NO_UID32           0x2000  /* Disable 32-bit UIDs */
-+#define EXTN_MOUNT_INDEX              0x4000  /* Enable directory index */
- /* Compatibility, for having both ext2_fs.h and extN_fs.h included at once */
- #ifndef _LINUX_EXT2_FS_H
- #define clear_opt(o, opt)             o &= ~EXTN_MOUNT_##opt
- #define set_opt(o, opt)                       o |= EXTN_MOUNT_##opt
--#define test_opt(sb, opt)             ((sb)->u.extN_sb.s_mount_opt & \
-+#define test_opt(sb, opt)             (EXTN_SB(sb)->s_mount_opt & \
-                                        EXTN_MOUNT_##opt)
- #else
- #define EXT2_MOUNT_NOLOAD             EXTN_MOUNT_NOLOAD
-@@ -441,17 +421,13 @@
- /*EC*/        __u32   s_reserved[197];        /* Padding to the end of the block */
- };
--#ifdef __KERNEL__
--#define EXTN_SB(sb)   (&((sb)->u.extN_sb))
--#define EXTN_I(inode) (&((inode)->u.extN_i))
--#else
--/* Assume that user mode programs are passing in an extNfs superblock, not
-- * a kernel struct super_block.  This will allow us to call the feature-test
-- * macros from user land. */
--#define EXTN_SB(sb)   (sb)
--#endif
--
--#define NEXT_ORPHAN(inode) (inode)->u.extN_i.i_dtime
-+#define NEXT_ORPHAN(inode) EXTN_I(inode)->i_dtime
-+static inline struct inode *orphan_list_entry(struct list_head *l)
-+{
-+      return ((struct inode *)((char *)l -
-+              (unsigned long)(offsetof(struct inode, u.generic_ip) +
-+                              offsetof(struct extN_inode_info, i_orphan))));
-+}
- /*
-  * Codes for operating systems
-@@ -512,7 +488,7 @@
- #define EXTN_FEATURE_INCOMPAT_RECOVER         0x0004 /* Needs recovery */
- #define EXTN_FEATURE_INCOMPAT_JOURNAL_DEV     0x0008 /* Journal device */
--#define EXTN_FEATURE_COMPAT_SUPP      0
-+#define EXTN_FEATURE_COMPAT_SUPP      EXTN_FEATURE_COMPAT_EXT_ATTR
- #define EXTN_FEATURE_INCOMPAT_SUPP    (EXTN_FEATURE_INCOMPAT_FILETYPE| \
-                                        EXTN_FEATURE_INCOMPAT_RECOVER)
- #define EXTN_FEATURE_RO_COMPAT_SUPP   (EXTN_FEATURE_RO_COMPAT_SPARSE_SUPER| \
-@@ -575,6 +551,24 @@
- #define EXTN_DIR_ROUND                        (EXTN_DIR_PAD - 1)
- #define EXTN_DIR_REC_LEN(name_len)    (((name_len) + 8 + EXTN_DIR_ROUND) & \
-                                        ~EXTN_DIR_ROUND)
-+/*
-+ * Hash Tree Directory indexing
-+ * (c) Daniel Phillips, 2001
-+ */
-+
-+#define CONFIG_EXTN_INDEX
-+
-+#ifdef CONFIG_EXTN_INDEX
-+  enum {extN_dx = 1};
-+  #define is_dx(dir) (EXTN_I(dir)->i_flags & EXTN_INDEX_FL)
-+#define EXTN_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXTN_LINK_MAX)
-+#define EXTN_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1)
-+#else
-+  enum {extN_dx = 0};
-+  #define is_dx(dir) 0
-+#define EXTN_DIR_LINK_MAX(dir) ((dir)->i_nlink >= EXTN_LINK_MAX)
-+#define EXTN_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2)
-+#endif
- #ifdef __KERNEL__
- /*
-@@ -587,6 +581,24 @@
-       unsigned long block_group;
- };
-+/* Defined for extended attributes */
-+#define CONFIG_EXTN_FS_XATTR y
-+#ifndef ENOATTR
-+#define ENOATTR ENODATA               /* No such attribute */
-+#endif
-+#ifndef ENOTSUP
-+#define ENOTSUP EOPNOTSUPP    /* Operation not supported */
-+#endif
-+#ifndef XATTR_NAME_MAX
-+#define XATTR_NAME_MAX   255  /* # chars in an extended attribute name */
-+#define XATTR_SIZE_MAX 65536  /* size of an extended attribute value (64k) */
-+#define XATTR_LIST_MAX 65536  /* size of extended attribute namelist (64k) */
-+#endif
-+#ifndef XATTR_CREATE
-+#define XATTR_CREATE  1       /* set value, fail if attr already exists */
-+#define XATTR_REPLACE 2       /* set value, fail if attr does not exist */
-+#endif
-+
- /*
-  * Function prototypes
-  */
-@@ -628,6 +640,7 @@
- extern unsigned long extN_count_free (struct buffer_head *, unsigned);
- /* inode.c */
-+extern int extN_forget(handle_t *, int, struct inode *, struct buffer_head *, int);
- extern struct buffer_head * extN_getblk (handle_t *, struct inode *, long, int, int *);
- extern struct buffer_head * extN_bread (handle_t *, struct inode *, int, int, int *);
---- extN/extN-include.orig/extN_jbd.h  Wed Sep 25 11:51:34 2002
-+++ include/linux/extN_jbd.h   Wed Sep 25 11:53:07 2002
-@@ -30,13 +30,19 @@
- #define EXTN_SINGLEDATA_TRANS_BLOCKS  8
-+/* Extended attributes may touch two data buffers, two bitmap buffers,
-+ * and two group and summaries. */
-+
-+#define EXTN_XATTR_TRANS_BLOCKS               8
-+
- /* Define the minimum size for a transaction which modifies data.  This
-  * needs to take into account the fact that we may end up modifying two
-  * quota files too (one for the group, one for the user quota).  The
-  * superblock only gets updated once, of course, so don't bother
-  * counting that again for the quota updates. */
--#define EXTN_DATA_TRANS_BLOCKS                (3 * EXTN_SINGLEDATA_TRANS_BLOCKS - 2)
-+#define EXTN_DATA_TRANS_BLOCKS                (3 * EXTN_SINGLEDATA_TRANS_BLOCKS + \
-+                                       EXTN_XATTR_TRANS_BLOCKS - 2)
- extern int extN_writepage_trans_blocks(struct inode *inode);
-@@ -63,6 +69,8 @@
- #define EXTN_RESERVE_TRANS_BLOCKS     12
-+#define EXTN_INDEX_EXTRA_TRANS_BLOCKS 8
-+
- int
- extN_mark_iloc_dirty(handle_t *handle, 
-                    struct inode *inode,
-@@ -289,7 +297,7 @@
-               return 1;
-       if (test_opt(inode->i_sb, DATA_FLAGS) == EXTN_MOUNT_JOURNAL_DATA)
-               return 1;
--      if (inode->u.extN_i.i_flags & EXTN_JOURNAL_DATA_FL)
-+      if (EXTN_I(inode)->i_flags & EXTN_JOURNAL_DATA_FL)
-               return 1;
-       return 0;
- }
---- /dev/null  Tue Aug  6 08:46:29 2002
-+++ include/linux/xattr.h      Wed Sep 25 11:51:34 2002
-@@ -0,0 +1,15 @@
-+/*
-+  File: linux/xattr.h
-+
-+  Extended attributes handling.
-+
-+  Copyright (C) 2001 by Andreas Gruenbacher <a.gruenbacher@computer.org>
-+  Copyright (C) 2001 SGI - Silicon Graphics, Inc <linux-xfs@oss.sgi.com>
-+*/
-+#ifndef _LINUX_XATTR_H
-+#define _LINUX_XATTR_H
-+
-+#define XATTR_CREATE  1       /* set value, fail if attr already exists */
-+#define XATTR_REPLACE 2       /* set value, fail if attr does not exist */
-+
-+#endif        /* _LINUX_XATTR_H */
---- /dev/null  Tue Aug  6 08:46:29 2002
-+++ include/linux/extN_xattr.h Wed Sep 25 11:51:34 2002
-@@ -0,0 +1,155 @@
-+/*
-+  File: linux/extN_xattr.h
-+
-+  On-disk format of extended attributes for the extN filesystem.
-+
-+  (C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
-+*/
-+
-+#include <linux/config.h>
-+#include <linux/init.h>
-+#include <linux/xattr.h>
-+
-+/* Magic value in attribute blocks */
-+#define EXTN_XATTR_MAGIC              0xEA020000
-+
-+/* Maximum number of references to one attribute block */
-+#define EXTN_XATTR_REFCOUNT_MAX               1024
-+
-+/* Name indexes */
-+#define EXTN_XATTR_INDEX_MAX                  10
-+#define EXTN_XATTR_INDEX_USER                 1
-+
-+struct extN_xattr_header {
-+      __u32   h_magic;        /* magic number for identification */
-+      __u32   h_refcount;     /* reference count */
-+      __u32   h_blocks;       /* number of disk blocks used */
-+      __u32   h_hash;         /* hash value of all attributes */
-+      __u32   h_reserved[4];  /* zero right now */
-+};
-+
-+struct extN_xattr_entry {
-+      __u8    e_name_len;     /* length of name */
-+      __u8    e_name_index;   /* attribute name index */
-+      __u16   e_value_offs;   /* offset in disk block of value */
-+      __u32   e_value_block;  /* disk block attribute is stored on (n/i) */
-+      __u32   e_value_size;   /* size of attribute value */
-+      __u32   e_hash;         /* hash value of name and value */
-+      char    e_name[0];      /* attribute name */
-+};
-+
-+#define EXTN_XATTR_PAD_BITS           2
-+#define EXTN_XATTR_PAD                (1<<EXTN_XATTR_PAD_BITS)
-+#define EXTN_XATTR_ROUND              (EXTN_XATTR_PAD-1)
-+#define EXTN_XATTR_LEN(name_len) \
-+      (((name_len) + EXTN_XATTR_ROUND + \
-+      sizeof(struct extN_xattr_entry)) & ~EXTN_XATTR_ROUND)
-+#define EXTN_XATTR_NEXT(entry) \
-+      ( (struct extN_xattr_entry *)( \
-+        (char *)(entry) + EXTN_XATTR_LEN((entry)->e_name_len)) )
-+#define EXTN_XATTR_SIZE(size) \
-+      (((size) + EXTN_XATTR_ROUND) & ~EXTN_XATTR_ROUND)
-+
-+#ifdef __KERNEL__
-+
-+# ifdef CONFIG_EXTN_FS_XATTR
-+
-+struct extN_xattr_handler {
-+      char *prefix;
-+      size_t (*list)(char *list, struct inode *inode, const char *name,
-+                     int name_len);
-+      int (*get)(struct inode *inode, const char *name, void *buffer,
-+                 size_t size);
-+      int (*set)(struct inode *inode, const char *name, void *buffer,
-+                 size_t size, int flags);
-+};
-+
-+extern int extN_xattr_register(int, struct extN_xattr_handler *);
-+extern void extN_xattr_unregister(int, struct extN_xattr_handler *);
-+
-+extern int extN_setxattr(struct dentry *, const char *, void *, size_t, int);
-+extern ssize_t extN_getxattr(struct dentry *, const char *, void *, size_t);
-+extern ssize_t extN_listxattr(struct dentry *, char *, size_t);
-+extern int extN_removexattr(struct dentry *, const char *);
-+
-+extern int extN_xattr_get(struct inode *, int, const char *, void *, size_t);
-+extern int extN_xattr_list(struct inode *, char *, size_t);
-+extern int extN_xattr_set(handle_t *handle, struct inode *, int, const char *, void *, size_t, int);
-+
-+extern void extN_xattr_drop_inode(handle_t *, struct inode *);
-+extern void extN_xattr_put_super(struct super_block *);
-+
-+extern int init_extN_xattr(void) __init;
-+extern void exit_extN_xattr(void);
-+
-+# else  /* CONFIG_EXTN_FS_XATTR */
-+#  define extN_setxattr               NULL
-+#  define extN_getxattr               NULL
-+#  define extN_listxattr      NULL
-+#  define extN_removexattr    NULL
-+
-+static inline int
-+extN_xattr_get(struct inode *inode, int name_index, const char *name,
-+             void *buffer, size_t size, int flags)
-+{
-+      return -ENOTSUP;
-+}
-+
-+static inline int
-+extN_xattr_list(struct inode *inode, void *buffer, size_t size, int flags)
-+{
-+      return -ENOTSUP;
-+}
-+
-+static inline int
-+extN_xattr_set(handle_t *handle, struct inode *inode, int name_index,
-+             const char *name, void *value, size_t size, int flags)
-+{
-+      return -ENOTSUP;
-+}
-+
-+static inline void
-+extN_xattr_drop_inode(handle_t *handle, struct inode *inode)
-+{
-+}
-+
-+static inline void
-+extN_xattr_put_super(struct super_block *sb)
-+{
-+}
-+
-+static inline int
-+init_extN_xattr(void)
-+{
-+      return 0;
-+}
-+
-+static inline void
-+exit_extN_xattr(void)
-+{
-+}
-+
-+# endif  /* CONFIG_EXTN_FS_XATTR */
-+
-+# ifdef CONFIG_EXTN_FS_XATTR_USER
-+
-+extern int init_extN_xattr_user(void) __init;
-+extern void exit_extN_xattr_user(void);
-+
-+# else  /* CONFIG_EXTN_FS_XATTR_USER */
-+
-+static inline int
-+init_extN_xattr_user(void)
-+{
-+      return 0;
-+}
-+
-+static inline void
-+exit_extN_xattr_user(void)
-+{
-+}
-+
-+#endif  /* CONFIG_EXTN_FS_XATTR_USER */
-+
-+#endif  /* __KERNEL__ */
-+
---- /dev/null  Tue Aug  6 08:46:29 2002
-+++ extN/xattr.c       Wed Sep 25 11:51:34 2002
-@@ -0,0 +1,1247 @@
-+/*
-+ * linux/fs/extN/xattr.c
-+ *
-+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
-+ *
-+ * Fix by Harrison Xing <harrison@mountainviewdata.com>.
-+ * Ext3 code with a lot of help from Eric Jarman <ejarman@acm.org>.
-+ * Extended attributes for symlinks and special files added per
-+ *  suggestion of Luka Renko <luka.renko@hermes.si>.
-+ */
-+
-+/*
-+ * Extended attributes are stored on disk blocks allocated outside of
-+ * any inode. The i_file_acl field is then made to point to this allocated
-+ * block. If all extended attributes of an inode are identical, these
-+ * inodes may share the same extended attribute block. Such situations
-+ * are automatically detected by keeping a cache of recent attribute block
-+ * numbers and hashes over the block's contents in memory.
-+ *
-+ *
-+ * Extended attribute block layout:
-+ *
-+ *   +------------------+
-+ *   | header           |
-+ *   Â¦ entry 1          | |
-+ *   | entry 2          | | growing downwards
-+ *   | entry 3          | v
-+ *   | four null bytes  |
-+ *   | . . .            |
-+ *   | value 1          | ^
-+ *   | value 3          | | growing upwards
-+ *   | value 2          | |
-+ *   +------------------+
-+ *
-+ * The block header is followed by multiple entry descriptors. These entry
-+ * descriptors are variable in size, and alligned to EXTN_XATTR_PAD
-+ * byte boundaries. The entry descriptors are sorted by attribute name,
-+ * so that two extended attribute blocks can be compared efficiently.
-+ *
-+ * Attribute values are aligned to the end of the block, stored in
-+ * no specific order. They are also padded to EXTN_XATTR_PAD byte
-+ * boundaries. No additional gaps are left between them.
-+ *
-+ * Locking strategy
-+ * ----------------
-+ * The VFS already holds the BKL and the inode->i_sem semaphore when any of
-+ * the xattr inode operations are called, so we are guaranteed that only one
-+ * processes accesses extended attributes of an inode at any time.
-+ *
-+ * For writing we also grab the extN_xattr_sem semaphore. This ensures that
-+ * only a single process is modifying an extended attribute block, even
-+ * if the block is shared among inodes.
-+ *
-+ * Note for porting to 2.5
-+ * -----------------------
-+ * The BKL will no longer be held in the xattr inode operations.
-+ */
-+
-+#include <linux/fs.h>
-+#include <linux/locks.h>
-+#include <linux/slab.h>
-+#include <linux/extN_jbd.h>
-+#include <linux/extN_fs.h>
-+#include <linux/extN_xattr.h>
-+#ifdef CONFIG_EXTN_FS_XATTR_SHARING
-+#include <linux/mbcache.h>
-+#endif
-+#include <linux/quotaops.h>
-+#include <asm/semaphore.h>
-+#include <linux/compatmac.h>
-+#include <linux/module.h>
-+
-+/* These symbols may be needed by a module. */
-+EXPORT_SYMBOL(extN_xattr_register);
-+EXPORT_SYMBOL(extN_xattr_unregister);
-+EXPORT_SYMBOL(extN_xattr_get);
-+EXPORT_SYMBOL(extN_xattr_list);
-+EXPORT_SYMBOL(extN_xattr_set);
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-+# define mark_buffer_dirty(bh) mark_buffer_dirty(bh, 1)
-+#endif
-+
-+#define HDR(bh) ((struct extN_xattr_header *)((bh)->b_data))
-+#define ENTRY(ptr) ((struct extN_xattr_entry *)(ptr))
-+#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
-+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
-+
-+#ifdef EXTN_XATTR_DEBUG
-+# define ea_idebug(inode, f...) do { \
-+              printk(KERN_DEBUG "inode %s:%ld: ", \
-+                      kdevname(inode->i_dev), inode->i_ino); \
-+              printk(f); \
-+              printk("\n"); \
-+      } while (0)
-+# define ea_bdebug(bh, f...) do { \
-+              printk(KERN_DEBUG "block %s:%ld: ", \
-+                      kdevname(bh->b_dev), bh->b_blocknr); \
-+              printk(f); \
-+              printk("\n"); \
-+      } while (0)
-+#else
-+# define ea_idebug(f...)
-+# define ea_bdebug(f...)
-+#endif
-+
-+static int extN_xattr_set2(handle_t *, struct inode *, struct buffer_head *,
-+                         struct extN_xattr_header *);
-+
-+#ifdef CONFIG_EXTN_FS_XATTR_SHARING
-+
-+static int extN_xattr_cache_insert(struct buffer_head *);
-+static struct buffer_head *extN_xattr_cache_find(struct inode *,
-+                                               struct extN_xattr_header *);
-+static void extN_xattr_cache_remove(struct buffer_head *);
-+static void extN_xattr_rehash(struct extN_xattr_header *,
-+                            struct extN_xattr_entry *);
-+
-+static struct mb_cache *extN_xattr_cache;
-+
-+#else
-+# define extN_xattr_cache_insert(bh) 0
-+# define extN_xattr_cache_find(inode, header) NULL
-+# define extN_xattr_cache_remove(bh) do {} while(0)
-+# define extN_xattr_rehash(header, entry) do {} while(0)
-+#endif
-+
-+/*
-+ * If a file system does not share extended attributes among inodes,
-+ * we should not need the extN_xattr_sem semaphore. However, the
-+ * filesystem may still contain shared blocks, so we always take
-+ * the lock.
-+ */
-+
-+DECLARE_MUTEX(extN_xattr_sem);
-+
-+static inline void
-+extN_xattr_lock(void)
-+{
-+      down(&extN_xattr_sem);
-+}
-+
-+static inline void
-+extN_xattr_unlock(void)
-+{
-+      up(&extN_xattr_sem);
-+}
-+
-+static inline int
-+extN_xattr_new_block(handle_t *handle, struct inode *inode,
-+                   int * errp, int force)
-+{
-+      struct super_block *sb = inode->i_sb;
-+      int goal = le32_to_cpu(EXTN_SB(sb)->s_es->s_first_data_block) +
-+              EXTN_I(inode)->i_block_group * EXTN_BLOCKS_PER_GROUP(sb);
-+
-+      /* How can we enforce the allocation? */
-+      int block = extN_new_block(handle, inode, goal, 0, 0, errp);
-+#ifdef OLD_QUOTAS
-+      if (!*errp)
-+              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-+#endif
-+      return block;
-+}
-+
-+static inline int
-+extN_xattr_quota_alloc(struct inode *inode, int force)
-+{
-+      /* How can we enforce the allocation? */
-+#ifdef OLD_QUOTAS
-+      int error = DQUOT_ALLOC_BLOCK(inode->i_sb, inode, 1);
-+      if (!error)
-+              inode->i_blocks += inode->i_sb->s_blocksize >> 9;
-+#else
-+      int error = DQUOT_ALLOC_BLOCK(inode, 1);
-+#endif
-+      return error;
-+}
-+
-+#ifdef OLD_QUOTAS
-+
-+static inline void
-+extN_xattr_quota_free(struct inode *inode)
-+{
-+      DQUOT_FREE_BLOCK(inode->i_sb, inode, 1);
-+      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-+}
-+
-+static inline void
-+extN_xattr_free_block(handle_t *handle, struct inode * inode,
-+                    unsigned long block)
-+{
-+      extN_free_blocks(handle, inode, block, 1);
-+      inode->i_blocks -= inode->i_sb->s_blocksize >> 9;
-+}
-+
-+#else
-+# define extN_xattr_quota_free(inode) \
-+      DQUOT_FREE_BLOCK(inode, 1)
-+# define extN_xattr_free_block(handle, inode, block) \
-+      extN_free_blocks(handle, inode, block, 1)
-+#endif
-+
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,18)
-+
-+static inline struct buffer_head *
-+sb_bread(struct super_block *sb, int block)
-+{
-+      return bread(sb->s_dev, block, sb->s_blocksize);
-+}
-+
-+static inline struct buffer_head *
-+sb_getblk(struct super_block *sb, int block)
-+{
-+      return getblk(sb->s_dev, block, sb->s_blocksize);
-+}
-+
-+#endif
-+
-+struct extN_xattr_handler *extN_xattr_handlers[EXTN_XATTR_INDEX_MAX];
-+rwlock_t extN_handler_lock = RW_LOCK_UNLOCKED;
-+
-+int
-+extN_xattr_register(int name_index, struct extN_xattr_handler *handler)
-+{
-+      int error = -EINVAL;
-+
-+      if (name_index > 0 && name_index <= EXTN_XATTR_INDEX_MAX) {
-+              write_lock(&extN_handler_lock);
-+              if (!extN_xattr_handlers[name_index-1]) {
-+                      extN_xattr_handlers[name_index-1] = handler;
-+                      error = 0;
-+              }
-+              write_unlock(&extN_handler_lock);
-+      }
-+      return error;
-+}
-+
-+void
-+extN_xattr_unregister(int name_index, struct extN_xattr_handler *handler)
-+{
-+      if (name_index > 0 || name_index <= EXTN_XATTR_INDEX_MAX) {
-+              write_lock(&extN_handler_lock);
-+              extN_xattr_handlers[name_index-1] = NULL;
-+              write_unlock(&extN_handler_lock);
-+      }
-+}
-+
-+static inline const char *
-+strcmp_prefix(const char *a, const char *a_prefix)
-+{
-+      while (*a_prefix && *a == *a_prefix) {
-+              a++;
-+              a_prefix++;
-+      }
-+      return *a_prefix ? NULL : a;
-+}
-+
-+/*
-+ * Decode the extended attribute name, and translate it into
-+ * the name_index and name suffix.
-+ */
-+static inline struct extN_xattr_handler *
-+extN_xattr_resolve_name(const char **name)
-+{
-+      struct extN_xattr_handler *handler = NULL;
-+      int i;
-+
-+      if (!*name)
-+              return NULL;
-+      read_lock(&extN_handler_lock);
-+      for (i=0; i<EXTN_XATTR_INDEX_MAX; i++) {
-+              if (extN_xattr_handlers[i]) {
-+                      const char *n = strcmp_prefix(*name,
-+                              extN_xattr_handlers[i]->prefix);
-+                      if (n) {
-+                              handler = extN_xattr_handlers[i];
-+                              *name = n;
-+                              break;
-+                      }
-+              }
-+      }
-+      read_unlock(&extN_handler_lock);
-+      return handler;
-+}
-+
-+static inline struct extN_xattr_handler *
-+extN_xattr_handler(int name_index)
-+{
-+      struct extN_xattr_handler *handler = NULL;
-+      if (name_index > 0 && name_index <= EXTN_XATTR_INDEX_MAX) {
-+              read_lock(&extN_handler_lock);
-+              handler = extN_xattr_handlers[name_index-1];
-+              read_unlock(&extN_handler_lock);
-+      }
-+      return handler;
-+}
-+
-+/*
-+ * Inode operation getxattr()
-+ *
-+ * dentry->d_inode->i_sem down
-+ * BKL held [before 2.5.x]
-+ */
-+ssize_t
-+extN_getxattr(struct dentry *dentry, const char *name,
-+            void *buffer, size_t size)
-+{
-+      struct extN_xattr_handler *handler;
-+      struct inode *inode = dentry->d_inode;
-+
-+      handler = extN_xattr_resolve_name(&name);
-+      if (!handler)
-+              return -ENOTSUP;
-+      return handler->get(inode, name, buffer, size);
-+}
-+
-+/*
-+ * Inode operation listxattr()
-+ *
-+ * dentry->d_inode->i_sem down
-+ * BKL held [before 2.5.x]
-+ */
-+ssize_t
-+extN_listxattr(struct dentry *dentry, char *buffer, size_t size)
-+{
-+      return extN_xattr_list(dentry->d_inode, buffer, size);
-+}
-+
-+/*
-+ * Inode operation setxattr()
-+ *
-+ * dentry->d_inode->i_sem down
-+ * BKL held [before 2.5.x]
-+ */
-+int
-+extN_setxattr(struct dentry *dentry, const char *name,
-+            void *value, size_t size, int flags)
-+{
-+      struct extN_xattr_handler *handler;
-+      struct inode *inode = dentry->d_inode;
-+
-+      if (size == 0)
-+              value = "";  /* empty EA, do not remove */
-+      handler = extN_xattr_resolve_name(&name);
-+      if (!handler)
-+              return -ENOTSUP;
-+      return handler->set(inode, name, value, size, flags);
-+}
-+
-+/*
-+ * Inode operation removexattr()
-+ *
-+ * dentry->d_inode->i_sem down
-+ * BKL held [before 2.5.x]
-+ */
-+int
-+extN_removexattr(struct dentry *dentry, const char *name)
-+{
-+      struct extN_xattr_handler *handler;
-+      struct inode *inode = dentry->d_inode;
-+
-+      handler = extN_xattr_resolve_name(&name);
-+      if (!handler)
-+              return -ENOTSUP;
-+      return handler->set(inode, name, NULL, 0, XATTR_REPLACE);
-+}
-+
-+/*
-+ * extN_xattr_get()
-+ *
-+ * Copy an extended attribute into the buffer
-+ * provided, or compute the buffer size required.
-+ * Buffer is NULL to compute the size of the buffer required.
-+ *
-+ * Returns a negative error number on failure, or the number of bytes
-+ * used / required on success.
-+ */
-+int
-+extN_xattr_get(struct inode *inode, int name_index, const char *name,
-+             void *buffer, size_t buffer_size)
-+{
-+      struct buffer_head *bh = NULL;
-+      struct extN_xattr_entry *entry;
-+      unsigned int block, size;
-+      char *end;
-+      int name_len, error;
-+
-+      ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
-+                name_index, name, buffer, (long)buffer_size);
-+
-+      if (name == NULL)
-+              return -EINVAL;
-+      if (!EXTN_I(inode)->i_file_acl)
-+              return -ENOATTR;
-+      block = EXTN_I(inode)->i_file_acl;
-+      ea_idebug(inode, "reading block %d", block);
-+      bh = sb_bread(inode->i_sb, block);
-+      if (!bh)
-+              return -EIO;
-+      ea_bdebug(bh, "b_count=%d, refcount=%d",
-+              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
-+      end = bh->b_data + bh->b_size;
-+      if (HDR(bh)->h_magic != cpu_to_le32(EXTN_XATTR_MAGIC) ||
-+          HDR(bh)->h_blocks != cpu_to_le32(1)) {
-+bad_block:    extN_error(inode->i_sb, "extN_xattr_get",
-+                      "inode %ld: bad block %d", inode->i_ino, block);
-+              error = -EIO;
-+              goto cleanup;
-+      }
-+      /* find named attribute */
-+      name_len = strlen(name);
-+
-+      error = -ERANGE;
-+      if (name_len > 255)
-+              goto cleanup;
-+      entry = FIRST_ENTRY(bh);
-+      while (!IS_LAST_ENTRY(entry)) {
-+              struct extN_xattr_entry *next =
-+                      EXTN_XATTR_NEXT(entry);
-+              if ((char *)next >= end)
-+                      goto bad_block;
-+              if (name_index == entry->e_name_index &&
-+                  name_len == entry->e_name_len &&
-+                  memcmp(name, entry->e_name, name_len) == 0)
-+                      goto found;
-+              entry = next;
-+      }
-+      /* Check the remaining name entries */
-+      while (!IS_LAST_ENTRY(entry)) {
-+              struct extN_xattr_entry *next =
-+                      EXTN_XATTR_NEXT(entry);
-+              if ((char *)next >= end)
-+                      goto bad_block;
-+              entry = next;
-+      }
-+      if (extN_xattr_cache_insert(bh))
-+              ea_idebug(inode, "cache insert failed");
-+      error = -ENOATTR;
-+      goto cleanup;
-+found:
-+      /* check the buffer size */
-+      if (entry->e_value_block != 0)
-+              goto bad_block;
-+      size = le32_to_cpu(entry->e_value_size);
-+      if (size > inode->i_sb->s_blocksize ||
-+          le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
-+              goto bad_block;
-+
-+      if (extN_xattr_cache_insert(bh))
-+              ea_idebug(inode, "cache insert failed");
-+      if (buffer) {
-+              error = -ERANGE;
-+              if (size > buffer_size)
-+                      goto cleanup;
-+              /* return value of attribute */
-+              memcpy(buffer, bh->b_data + le16_to_cpu(entry->e_value_offs),
-+                      size);
-+      }
-+      error = size;
-+
-+cleanup:
-+      brelse(bh);
-+
-+      return error;
-+}
-+
-+/*
-+ * extN_xattr_list()
-+ *
-+ * Copy a list of attribute names into the buffer
-+ * provided, or compute the buffer size required.
-+ * Buffer is NULL to compute the size of the buffer required.
-+ *
-+ * Returns a negative error number on failure, or the number of bytes
-+ * used / required on success.
-+ */
-+int
-+extN_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
-+{
-+      struct buffer_head *bh = NULL;
-+      struct extN_xattr_entry *entry;
-+      unsigned int block, size = 0;
-+      char *buf, *end;
-+      int error;
-+
-+      ea_idebug(inode, "buffer=%p, buffer_size=%ld",
-+                buffer, (long)buffer_size);
-+
-+      if (!EXTN_I(inode)->i_file_acl)
-+              return 0;
-+      block = EXTN_I(inode)->i_file_acl;
-+      ea_idebug(inode, "reading block %d", block);
-+      bh = sb_bread(inode->i_sb, block);
-+      if (!bh)
-+              return -EIO;
-+      ea_bdebug(bh, "b_count=%d, refcount=%d",
-+              atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
-+      end = bh->b_data + bh->b_size;
-+      if (HDR(bh)->h_magic != cpu_to_le32(EXTN_XATTR_MAGIC) ||
-+          HDR(bh)->h_blocks != cpu_to_le32(1)) {
-+bad_block:    extN_error(inode->i_sb, "extN_xattr_list",
-+                      "inode %ld: bad block %d", inode->i_ino, block);
-+              error = -EIO;
-+              goto cleanup;
-+      }
-+      /* compute the size required for the list of attribute names */
-+      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
-+           entry = EXTN_XATTR_NEXT(entry)) {
-+              struct extN_xattr_handler *handler;
-+              struct extN_xattr_entry *next =
-+                      EXTN_XATTR_NEXT(entry);
-+              if ((char *)next >= end)
-+                      goto bad_block;
-+
-+              handler = extN_xattr_handler(entry->e_name_index);
-+              if (handler) {
-+                      size += handler->list(NULL, inode, entry->e_name,
-+                                            entry->e_name_len) + 1;
-+              }
-+      }
-+
-+      if (extN_xattr_cache_insert(bh))
-+              ea_idebug(inode, "cache insert failed");
-+      if (!buffer) {
-+              error = size;
-+              goto cleanup;
-+      } else {
-+              error = -ERANGE;
-+              if (size > buffer_size)
-+                      goto cleanup;
-+      }
-+
-+      /* list the attribute names */
-+      buf = buffer;
-+      for (entry = FIRST_ENTRY(bh); !IS_LAST_ENTRY(entry);
-+           entry = EXTN_XATTR_NEXT(entry)) {
-+              struct extN_xattr_handler *handler;
-+
-+              handler = extN_xattr_handler(entry->e_name_index);
-+              if (handler) {
-+                      buf += handler->list(buf, inode, entry->e_name,
-+                                           entry->e_name_len);
-+                      *buf++ = '\0';
-+              }
-+      }
-+      error = size;
-+
-+cleanup:
-+      brelse(bh);
-+
-+      return error;
-+}
-+
-+/*
-+ * If the EXTN_FEATURE_COMPAT_EXT_ATTR feature of this file system is
-+ * not set, set it.
-+ */
-+static void extN_xattr_update_super_block(handle_t *handle,
-+                                        struct super_block *sb)
-+{
-+      if (EXTN_HAS_COMPAT_FEATURE(sb, EXTN_FEATURE_COMPAT_EXT_ATTR))
-+              return;
-+
-+      lock_super(sb);
-+      extN_journal_get_write_access(handle, EXTN_SB(sb)->s_sbh);
-+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-+      EXTN_SB(sb)->s_feature_compat |= EXTN_FEATURE_COMPAT_EXT_ATTR;
-+#endif
-+      EXTN_SB(sb)->s_es->s_feature_compat |=
-+              cpu_to_le32(EXTN_FEATURE_COMPAT_EXT_ATTR);
-+      sb->s_dirt = 1;
-+      extN_journal_dirty_metadata(handle, EXTN_SB(sb)->s_sbh);
-+      unlock_super(sb);
-+}
-+
-+/*
-+ * extN_xattr_set()
-+ *
-+ * Create, replace or remove an extended attribute for this inode. Buffer
-+ * is NULL to remove an existing extended attribute, and non-NULL to
-+ * either replace an existing extended attribute, or create a new extended
-+ * attribute. The flags XATTR_REPLACE and XATTR_CREATE
-+ * specify that an extended attribute must exist and must not exist
-+ * previous to the call, respectively.
-+ *
-+ * Returns 0, or a negative error number on failure.
-+ */
-+int
-+extN_xattr_set(handle_t *handle, struct inode *inode, int name_index,
-+             const char *name, void *value, size_t value_len, int flags)
-+{
-+      struct super_block *sb = inode->i_sb;
-+      struct buffer_head *bh = NULL;
-+      struct extN_xattr_header *header = NULL;
-+      struct extN_xattr_entry *here, *last;
-+      unsigned int name_len;
-+      int min_offs = sb->s_blocksize, not_found = 1, free, error;
-+      char *end;
-+      
-+      /*
-+       * header -- Points either into bh, or to a temporarily
-+       *           allocated buffer.
-+       * here -- The named entry found, or the place for inserting, within
-+       *         the block pointed to by header.
-+       * last -- Points right after the last named entry within the block
-+       *         pointed to by header.
-+       * min_offs -- The offset of the first value (values are aligned
-+       *             towards the end of the block).
-+       * end -- Points right after the block pointed to by header.
-+       */
-+      
-+      ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
-+                name_index, name, value, (long)value_len);
-+
-+      if (IS_RDONLY(inode))
-+              return -EROFS;
-+      if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
-+              return -EPERM;
-+      if (value == NULL)
-+              value_len = 0;
-+      if (name == NULL)
-+              return -EINVAL;
-+      name_len = strlen(name);
-+      if (name_len > 255 || value_len > sb->s_blocksize)
-+              return -ERANGE;
-+      extN_xattr_lock();
-+
-+      if (EXTN_I(inode)->i_file_acl) {
-+              /* The inode already has an extended attribute block. */
-+              int block = EXTN_I(inode)->i_file_acl;
-+
-+              bh = sb_bread(sb, block);
-+              error = -EIO;
-+              if (!bh)
-+                      goto cleanup;
-+              ea_bdebug(bh, "b_count=%d, refcount=%d",
-+                      atomic_read(&(bh->b_count)),
-+                      le32_to_cpu(HDR(bh)->h_refcount));
-+              header = HDR(bh);
-+              end = bh->b_data + bh->b_size;
-+              if (header->h_magic != cpu_to_le32(EXTN_XATTR_MAGIC) ||
-+                  header->h_blocks != cpu_to_le32(1)) {
-+bad_block:            extN_error(sb, "extN_xattr_set",
-+                              "inode %ld: bad block %d", inode->i_ino, block);
-+                      error = -EIO;
-+                      goto cleanup;
-+              }
-+              /* Find the named attribute. */
-+              here = FIRST_ENTRY(bh);
-+              while (!IS_LAST_ENTRY(here)) {
-+                      struct extN_xattr_entry *next = EXTN_XATTR_NEXT(here);
-+                      if ((char *)next >= end)
-+                              goto bad_block;
-+                      if (!here->e_value_block && here->e_value_size) {
-+                              int offs = le16_to_cpu(here->e_value_offs);
-+                              if (offs < min_offs)
-+                                      min_offs = offs;
-+                      }
-+                      not_found = name_index - here->e_name_index;
-+                      if (!not_found)
-+                              not_found = name_len - here->e_name_len;
-+                      if (!not_found)
-+                              not_found = memcmp(name, here->e_name,name_len);
-+                      if (not_found <= 0)
-+                              break;
-+                      here = next;
-+              }
-+              last = here;
-+              /* We still need to compute min_offs and last. */
-+              while (!IS_LAST_ENTRY(last)) {
-+                      struct extN_xattr_entry *next = EXTN_XATTR_NEXT(last);
-+                      if ((char *)next >= end)
-+                              goto bad_block;
-+                      if (!last->e_value_block && last->e_value_size) {
-+                              int offs = le16_to_cpu(last->e_value_offs);
-+                              if (offs < min_offs)
-+                                      min_offs = offs;
-+                      }
-+                      last = next;
-+              }
-+
-+              /* Check whether we have enough space left. */
-+              free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
-+      } else {
-+              /* We will use a new extended attribute block. */
-+              free = sb->s_blocksize -
-+                      sizeof(struct extN_xattr_header) - sizeof(__u32);
-+              here = last = NULL;  /* avoid gcc uninitialized warning. */
-+      }
-+
-+      if (not_found) {
-+              /* Request to remove a nonexistent attribute? */
-+              error = -ENOATTR;
-+              if (flags & XATTR_REPLACE)
-+                      goto cleanup;
-+              error = 0;
-+              if (value == NULL)
-+                      goto cleanup;
-+              else
-+                      free -= EXTN_XATTR_LEN(name_len);
-+      } else {
-+              /* Request to create an existing attribute? */
-+              error = -EEXIST;
-+              if (flags & XATTR_CREATE)
-+                      goto cleanup;
-+              if (!here->e_value_block && here->e_value_size) {
-+                      unsigned int size = le32_to_cpu(here->e_value_size);
-+
-+                      if (le16_to_cpu(here->e_value_offs) + size > 
-+                          sb->s_blocksize || size > sb->s_blocksize)
-+                              goto bad_block;
-+                      free += EXTN_XATTR_SIZE(size);
-+              }
-+      }
-+      free -= EXTN_XATTR_SIZE(value_len);
-+      error = -ENOSPC;
-+      if (free < 0)
-+              goto cleanup;
-+
-+      /* Here we know that we can set the new attribute. */
-+
-+      if (header) {
-+              if (header->h_refcount == cpu_to_le32(1)) {
-+                      ea_bdebug(bh, "modifying in-place");
-+                      extN_xattr_cache_remove(bh);
-+                      error = extN_journal_get_write_access(handle, bh);
-+                      if (error)
-+                              goto cleanup;
-+              } else {
-+                      int offset;
-+
-+                      ea_bdebug(bh, "cloning");
-+                      header = kmalloc(bh->b_size, GFP_KERNEL);
-+                      error = -ENOMEM;
-+                      if (header == NULL)
-+                              goto cleanup;
-+                      memcpy(header, HDR(bh), bh->b_size);
-+                      header->h_refcount = cpu_to_le32(1);
-+                      offset = (char *)header - bh->b_data;
-+                      here = ENTRY((char *)here + offset);
-+                      last = ENTRY((char *)last + offset);
-+              }
-+      } else {
-+              /* Allocate a buffer where we construct the new block. */
-+              header = kmalloc(sb->s_blocksize, GFP_KERNEL);
-+              error = -ENOMEM;
-+              if (header == NULL)
-+                      goto cleanup;
-+              memset(header, 0, sb->s_blocksize);
-+              end = (char *)header + sb->s_blocksize;
-+              header->h_magic = cpu_to_le32(EXTN_XATTR_MAGIC);
-+              header->h_blocks = header->h_refcount = cpu_to_le32(1);
-+              last = here = ENTRY(header+1);
-+      }
-+
-+      if (not_found) {
-+              /* Insert the new name. */
-+              int size = EXTN_XATTR_LEN(name_len);
-+              int rest = (char *)last - (char *)here;
-+              memmove((char *)here + size, here, rest);
-+              memset(here, 0, size);
-+              here->e_name_index = name_index;
-+              here->e_name_len = name_len;
-+              memcpy(here->e_name, name, name_len);
-+      } else {
-+              /* Remove the old value. */
-+              if (!here->e_value_block && here->e_value_size) {
-+                      char *first_val = (char *)header + min_offs;
-+                      int offs = le16_to_cpu(here->e_value_offs);
-+                      char *val = (char *)header + offs;
-+                      size_t size = EXTN_XATTR_SIZE(
-+                              le32_to_cpu(here->e_value_size));
-+                      memmove(first_val + size, first_val, val - first_val);
-+                      memset(first_val, 0, size);
-+                      here->e_value_offs = 0;
-+                      min_offs += size;
-+
-+                      /* Adjust all value offsets. */
-+                      last = ENTRY(header+1);
-+                      while (!IS_LAST_ENTRY(last)) {
-+                              int o = le16_to_cpu(last->e_value_offs);
-+                              if (!last->e_value_block && o < offs)
-+                                      last->e_value_offs =
-+                                              cpu_to_le16(o + size);
-+                              last = EXTN_XATTR_NEXT(last);
-+                      }
-+              }
-+              if (value == NULL) {
-+                      /* Remove this attribute. */
-+                      if (EXTN_XATTR_NEXT(ENTRY(header+1)) == last) {
-+                              /* This block is now empty. */
-+                              error = extN_xattr_set2(handle, inode, bh,NULL);
-+                              goto cleanup;
-+                      } else {
-+                              /* Remove the old name. */
-+                              int size = EXTN_XATTR_LEN(name_len);
-+                              last = ENTRY((char *)last - size);
-+                              memmove(here, (char*)here + size,
-+                                      (char*)last - (char*)here);
-+                              memset(last, 0, size);
-+                      }
-+              }
-+      }
-+
-+      if (value != NULL) {
-+              /* Insert the new value. */
-+              here->e_value_size = cpu_to_le32(value_len);
-+              if (value_len) {
-+                      size_t size = EXTN_XATTR_SIZE(value_len);
-+                      char *val = (char *)header + min_offs - size;
-+                      here->e_value_offs =
-+                              cpu_to_le16((char *)val - (char *)header);
-+                      memset(val + size - EXTN_XATTR_PAD, 0,
-+                             EXTN_XATTR_PAD); /* Clear the pad bytes. */
-+                      memcpy(val, value, value_len);
-+              }
-+      }
-+      extN_xattr_rehash(header, here);
-+
-+      error = extN_xattr_set2(handle, inode, bh, header);
-+
-+cleanup:
-+      brelse(bh);
-+      if (!(bh && header == HDR(bh)))
-+              kfree(header);
-+      extN_xattr_unlock();
-+
-+      return error;
-+}
-+
-+/*
-+ * Second half of extN_xattr_set(): Update the file system.
-+ */
-+static int
-+extN_xattr_set2(handle_t *handle, struct inode *inode,
-+              struct buffer_head *old_bh, struct extN_xattr_header *header)
-+{
-+      struct super_block *sb = inode->i_sb;
-+      struct buffer_head *new_bh = NULL;
-+      int error;
-+
-+      if (header) {
-+              new_bh = extN_xattr_cache_find(inode, header);
-+              if (new_bh) {
-+                      /*
-+                       * We found an identical block in the cache.
-+                       * The old block will be released after updating
-+                       * the inode.
-+                       */
-+                      ea_bdebug(old_bh, "reusing block %ld",
-+                              new_bh->b_blocknr);
-+                      
-+                      error = -EDQUOT;
-+                      if (extN_xattr_quota_alloc(inode, 1))
-+                              goto cleanup;
-+                      
-+                      error = extN_journal_get_write_access(handle, new_bh);
-+                      if (error)
-+                              goto cleanup;
-+                      HDR(new_bh)->h_refcount = cpu_to_le32(
-+                              le32_to_cpu(HDR(new_bh)->h_refcount) + 1);
-+                      ea_bdebug(new_bh, "refcount now=%d",
-+                              le32_to_cpu(HDR(new_bh)->h_refcount));
-+              } else if (old_bh && header == HDR(old_bh)) {
-+                      /* Keep this block. */
-+                      new_bh = old_bh;
-+                      (void)extN_xattr_cache_insert(new_bh);
-+              } else {
-+                      /* We need to allocate a new block */
-+                      int force = EXTN_I(inode)->i_file_acl != 0;
-+                      int block = extN_xattr_new_block(handle, inode,
-+                                                       &error, force);
-+                      if (error)
-+                              goto cleanup;
-+                      ea_idebug(inode, "creating block %d", block);
-+
-+                      new_bh = sb_getblk(sb, block);
-+                      if (!new_bh) {
-+getblk_failed:                        extN_xattr_free_block(handle, inode, block);
-+                              error = -EIO;
-+                              goto cleanup;
-+                      }
-+                      lock_buffer(new_bh);
-+                      error = extN_journal_get_create_access(handle, new_bh);
-+                      if (error) {
-+                              unlock_buffer(new_bh);
-+                              goto getblk_failed;
-+                      }
-+                      memcpy(new_bh->b_data, header, new_bh->b_size);
-+                      mark_buffer_uptodate(new_bh, 1);
-+                      unlock_buffer(new_bh);
-+                      (void)extN_xattr_cache_insert(new_bh);
-+                      extN_xattr_update_super_block(handle, sb);
-+              }
-+              error = extN_journal_dirty_metadata(handle, new_bh);
-+              if (error)
-+                      goto cleanup;
-+      }
-+
-+      /* Update the inode. */
-+      EXTN_I(inode)->i_file_acl = new_bh ? new_bh->b_blocknr : 0;
-+      inode->i_ctime = CURRENT_TIME;
-+      extN_mark_inode_dirty(handle, inode);
-+      if (IS_SYNC(inode))
-+              handle->h_sync = 1;
-+
-+      error = 0;
-+      if (old_bh && old_bh != new_bh) {
-+              /*
-+               * If there was an old block, and we are not still using it,
-+               * we now release the old block.
-+              */
-+              unsigned int refcount = le32_to_cpu(HDR(old_bh)->h_refcount);
-+
-+              error = extN_journal_get_write_access(handle, old_bh);
-+              if (error)
-+                      goto cleanup;
-+              if (refcount == 1) {
-+                      /* Free the old block. */
-+                      ea_bdebug(old_bh, "freeing");
-+                      extN_xattr_free_block(handle, inode, old_bh->b_blocknr);
-+
-+                      /* extN_forget() calls bforget() for us, but we
-+                         let our caller release old_bh, so we need to
-+                         duplicate the handle before. */
-+                      get_bh(old_bh);
-+                      extN_forget(handle, 1, inode, old_bh,old_bh->b_blocknr);
-+              } else {
-+                      /* Decrement the refcount only. */
-+                      refcount--;
-+                      HDR(old_bh)->h_refcount = cpu_to_le32(refcount);
-+                      extN_xattr_quota_free(inode);
-+                      extN_journal_dirty_metadata(handle, old_bh);
-+                      ea_bdebug(old_bh, "refcount now=%d", refcount);
-+              }
-+      }
-+
-+cleanup:
-+      if (old_bh != new_bh)
-+              brelse(new_bh);
-+
-+      return error;
-+}
-+
-+/*
-+ * extN_xattr_drop_inode()
-+ *
-+ * Free extended attribute resources associated with this inode. This
-+ * is called immediately before an inode is freed.
-+ */
-+void
-+extN_xattr_drop_inode(handle_t *handle, struct inode *inode)
-+{
-+      struct buffer_head *bh;
-+      unsigned int block = EXTN_I(inode)->i_file_acl;
-+
-+      if (!block)
-+              return;
-+      extN_xattr_lock();
-+
-+      bh = sb_bread(inode->i_sb, block);
-+      if (!bh) {
-+              extN_error(inode->i_sb, "extN_xattr_drop_inode",
-+                      "inode %ld: block %d read error", inode->i_ino, block);
-+              goto cleanup;
-+      }
-+      ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
-+      if (HDR(bh)->h_magic != cpu_to_le32(EXTN_XATTR_MAGIC) ||
-+          HDR(bh)->h_blocks != cpu_to_le32(1)) {
-+              extN_error(inode->i_sb, "extN_xattr_drop_inode",
-+                      "inode %ld: bad block %d", inode->i_ino, block);
-+              goto cleanup;
-+      }
-+      extN_journal_get_write_access(handle, bh);
-+      ea_bdebug(bh, "refcount now=%d", le32_to_cpu(HDR(bh)->h_refcount) - 1);
-+      if (HDR(bh)->h_refcount == cpu_to_le32(1)) {
-+              extN_xattr_cache_remove(bh);
-+              extN_xattr_free_block(handle, inode, block);
-+              extN_forget(handle, 1, inode, bh, block);
-+              bh = NULL;
-+      } else {
-+              HDR(bh)->h_refcount = cpu_to_le32(
-+                      le32_to_cpu(HDR(bh)->h_refcount) - 1);
-+              extN_journal_dirty_metadata(handle, bh);
-+              if (IS_SYNC(inode))
-+                      handle->h_sync = 1;
-+              extN_xattr_quota_free(inode);
-+      }
-+      EXTN_I(inode)->i_file_acl = 0;
-+
-+cleanup:
-+      brelse(bh);
-+      extN_xattr_unlock();
-+}
-+
-+/*
-+ * extN_xattr_put_super()
-+ *
-+ * This is called when a file system is unmounted.
-+ */
-+void
-+extN_xattr_put_super(struct super_block *sb)
-+{
-+#ifdef CONFIG_EXTN_FS_XATTR_SHARING
-+      mb_cache_shrink(extN_xattr_cache, sb->s_dev);
-+#endif
-+}
-+
-+#ifdef CONFIG_EXTN_FS_XATTR_SHARING
-+
-+/*
-+ * extN_xattr_cache_insert()
-+ *
-+ * Create a new entry in the extended attribute cache, and insert
-+ * it unless such an entry is already in the cache.
-+ *
-+ * Returns 0, or a negative error number on failure.
-+ */
-+static int
-+extN_xattr_cache_insert(struct buffer_head *bh)
-+{
-+      __u32 hash = le32_to_cpu(HDR(bh)->h_hash);
-+      struct mb_cache_entry *ce;
-+      int error;
-+
-+      ce = mb_cache_entry_alloc(extN_xattr_cache);
-+      if (!ce)
-+              return -ENOMEM;
-+      error = mb_cache_entry_insert(ce, bh->b_dev, bh->b_blocknr, &hash);
-+      if (error) {
-+              mb_cache_entry_free(ce);
-+              if (error == -EBUSY) {
-+                      ea_bdebug(bh, "already in cache (%d cache entries)",
-+                              atomic_read(&extN_xattr_cache->c_entry_count));
-+                      error = 0;
-+              }
-+      } else {
-+              ea_bdebug(bh, "inserting [%x] (%d cache entries)", (int)hash,
-+                        atomic_read(&extN_xattr_cache->c_entry_count));
-+              mb_cache_entry_release(ce);
-+      }
-+      return error;
-+}
-+
-+/*
-+ * extN_xattr_cmp()
-+ *
-+ * Compare two extended attribute blocks for equality.
-+ *
-+ * Returns 0 if the blocks are equal, 1 if they differ, and
-+ * a negative error number on errors.
-+ */
-+static int
-+extN_xattr_cmp(struct extN_xattr_header *header1,
-+             struct extN_xattr_header *header2)
-+{
-+      struct extN_xattr_entry *entry1, *entry2;
-+
-+      entry1 = ENTRY(header1+1);
-+      entry2 = ENTRY(header2+1);
-+      while (!IS_LAST_ENTRY(entry1)) {
-+              if (IS_LAST_ENTRY(entry2))
-+                      return 1;
-+              if (entry1->e_hash != entry2->e_hash ||
-+                  entry1->e_name_len != entry2->e_name_len ||
-+                  entry1->e_value_size != entry2->e_value_size ||
-+                  memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))
-+                      return 1;
-+              if (entry1->e_value_block != 0 || entry2->e_value_block != 0)
-+                      return -EIO;
-+              if (memcmp((char *)header1 + le16_to_cpu(entry1->e_value_offs),
-+                         (char *)header2 + le16_to_cpu(entry2->e_value_offs),
-+                         le32_to_cpu(entry1->e_value_size)))
-+                      return 1;
-+
-+              entry1 = EXTN_XATTR_NEXT(entry1);
-+              entry2 = EXTN_XATTR_NEXT(entry2);
-+      }
-+      if (!IS_LAST_ENTRY(entry2))
-+              return 1;
-+      return 0;
-+}
-+
-+/*
-+ * extN_xattr_cache_find()
-+ *
-+ * Find an identical extended attribute block.
-+ *
-+ * Returns a pointer to the block found, or NULL if such a block was
-+ * not found or an error occurred.
-+ */
-+static struct buffer_head *
-+extN_xattr_cache_find(struct inode *inode, struct extN_xattr_header *header)
-+{
-+      __u32 hash = le32_to_cpu(header->h_hash);
-+      struct mb_cache_entry *ce;
-+
-+      if (!header->h_hash)
-+              return NULL;  /* never share */
-+      ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
-+      ce = mb_cache_entry_find_first(extN_xattr_cache, 0, inode->i_dev, hash);
-+      while (ce) {
-+              struct buffer_head *bh = sb_bread(inode->i_sb, ce->e_block);
-+
-+              if (!bh) {
-+                      extN_error(inode->i_sb, "extN_xattr_cache_find",
-+                              "inode %ld: block %ld read error",
-+                              inode->i_ino, ce->e_block);
-+              } else if (le32_to_cpu(HDR(bh)->h_refcount) >
-+                         EXTN_XATTR_REFCOUNT_MAX) {
-+                      ea_idebug(inode, "block %ld refcount %d>%d",ce->e_block,
-+                              le32_to_cpu(HDR(bh)->h_refcount),
-+                              EXTN_XATTR_REFCOUNT_MAX);
-+              } else if (!extN_xattr_cmp(header, HDR(bh))) {
-+                      ea_bdebug(bh, "b_count=%d",atomic_read(&(bh->b_count)));
-+                      mb_cache_entry_release(ce);
-+                      return bh;
-+              }
-+              brelse(bh);
-+              ce = mb_cache_entry_find_next(ce, 0, inode->i_dev, hash);
-+      }
-+      return NULL;
-+}
-+
-+/*
-+ * extN_xattr_cache_remove()
-+ *
-+ * Remove the cache entry of a block from the cache. Called when a
-+ * block becomes invalid.
-+ */
-+static void
-+extN_xattr_cache_remove(struct buffer_head *bh)
-+{
-+      struct mb_cache_entry *ce;
-+
-+      ce = mb_cache_entry_get(extN_xattr_cache, bh->b_dev, bh->b_blocknr);
-+      if (ce) {
-+              ea_bdebug(bh, "removing (%d cache entries remaining)",
-+                        atomic_read(&extN_xattr_cache->c_entry_count)-1);
-+              mb_cache_entry_free(ce);
-+      } else 
-+              ea_bdebug(bh, "no cache entry");
-+}
-+
-+#define NAME_HASH_SHIFT 5
-+#define VALUE_HASH_SHIFT 16
-+
-+/*
-+ * extN_xattr_hash_entry()
-+ *
-+ * Compute the hash of an extended attribute.
-+ */
-+static inline void extN_xattr_hash_entry(struct extN_xattr_header *header,
-+                                       struct extN_xattr_entry *entry)
-+{
-+      __u32 hash = 0;
-+      char *name = entry->e_name;
-+      int n;
-+
-+      for (n=0; n < entry->e_name_len; n++) {
-+              hash = (hash << NAME_HASH_SHIFT) ^
-+                     (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
-+                     *name++;
-+      }
-+
-+      if (entry->e_value_block == 0 && entry->e_value_size != 0) {
-+              __u32 *value = (__u32 *)((char *)header +
-+                      le16_to_cpu(entry->e_value_offs));
-+              for (n = (le32_to_cpu(entry->e_value_size) +
-+                   EXTN_XATTR_ROUND) >> EXTN_XATTR_PAD_BITS; n; n--) {
-+                      hash = (hash << VALUE_HASH_SHIFT) ^
-+                             (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
-+                             le32_to_cpu(*value++);
-+              }
-+      }
-+      entry->e_hash = cpu_to_le32(hash);
-+}
-+
-+#undef NAME_HASH_SHIFT
-+#undef VALUE_HASH_SHIFT
-+
-+#define BLOCK_HASH_SHIFT 16
-+
-+/*
-+ * extN_xattr_rehash()
-+ *
-+ * Re-compute the extended attribute hash value after an entry has changed.
-+ */
-+static void extN_xattr_rehash(struct extN_xattr_header *header,
-+                            struct extN_xattr_entry *entry)
-+{
-+      struct extN_xattr_entry *here;
-+      __u32 hash = 0;
-+      
-+      extN_xattr_hash_entry(header, entry);
-+      here = ENTRY(header+1);
-+      while (!IS_LAST_ENTRY(here)) {
-+              if (!here->e_hash) {
-+                      /* Block is not shared if an entry's hash value == 0 */
-+                      hash = 0;
-+                      break;
-+              }
-+              hash = (hash << BLOCK_HASH_SHIFT) ^
-+                     (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
-+                     le32_to_cpu(here->e_hash);
-+              here = EXTN_XATTR_NEXT(here);
-+      }
-+      header->h_hash = cpu_to_le32(hash);
-+}
-+
-+#undef BLOCK_HASH_SHIFT
-+
-+int __init
-+init_extN_xattr(void)
-+{
-+      extN_xattr_cache = mb_cache_create("extN_xattr", NULL,
-+              sizeof(struct mb_cache_entry) +
-+              sizeof(struct mb_cache_entry_index), 1, 61);
-+      if (!extN_xattr_cache)
-+              return -ENOMEM;
-+
-+      return 0;
-+}
-+
-+void
-+exit_extN_xattr(void)
-+{
-+      if (extN_xattr_cache)
-+              mb_cache_destroy(extN_xattr_cache);
-+      extN_xattr_cache = NULL;
-+}
-+
-+#else  /* CONFIG_EXTN_FS_XATTR_SHARING */
-+
-+int __init
-+init_extN_xattr(void)
-+{
-+      return 0;
-+}
-+
-+void
-+exit_extN_xattr(void)
-+{
-+}
-+
-+#endif  /* CONFIG_EXTN_FS_XATTR_SHARING */