xfs
[Top] [All Lists]

Review: xfs_repair fixes for dir2 corruption

To: <xfs@xxxxxxxxxxx>
Subject: Review: xfs_repair fixes for dir2 corruption
From: "Barry Naujok" <bnaujok@xxxxxxxxxxxxxxxxx>
Date: Fri, 28 Jul 2006 11:58:52 +1000
In-reply-to: <20060728091735.B2196410@xxxxxxxxxxxxxxxxxxxxxxxx>
Sender: xfs-bounce@xxxxxxxxxxx
Thread-index: Acax00DM5+WfZtIjRIOJK2rx/dCVVAAFQ6xw
This patch addresses the following xfs_repair issues:
  - Can rebuild most corrupted directories. Some will be 
    beyond repair and the contents of those will end up 
    in lost+found.
  - Fixed potential problems with the duplicate name 
    checking by properly referencing the buffers where
    the names are stored.
  - Unified the two hash lists used in directory checks.
  - Fixed a case where incorrectly reference counted and
    dirty buffers were never written to disk (most 
    common observation of this is an inaccessible 
    lost+found directory after a repair).

For those that are keen to fix the 16777216 problem, give this patch a go.




===========================================================================
xfsprogs/include/cache.h
===========================================================================

--- a/xfsprogs/include/cache.h  2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/include/cache.h  2006-07-27 16:17:47.804986322 +1000
@@ -30,6 +30,7 @@ struct cache_node;
 typedef void *cache_key_t;
 typedef void (*cache_walk_t)(struct cache_node *);
 typedef struct cache_node * (*cache_node_alloc_t)(void);
+typedef void (*cache_node_flush_t)(struct cache_node *);
 typedef void (*cache_node_relse_t)(struct cache_node *);
 typedef unsigned int (*cache_node_hash_t)(cache_key_t, unsigned int);
 typedef int (*cache_node_compare_t)(struct cache_node *, cache_key_t);
@@ -38,6 +39,7 @@ typedef unsigned int (*cache_bulk_relse_
 struct cache_operations {
        cache_node_hash_t       hash;
        cache_node_alloc_t      alloc;
+       cache_node_flush_t      flush;
        cache_node_relse_t      relse;
        cache_node_compare_t    compare;
        cache_bulk_relse_t      bulkrelse;      /* optional */
@@ -49,6 +51,7 @@ struct cache {
        pthread_mutex_t         c_mutex;        /* node count mutex */
        cache_node_hash_t       hash;           /* node hash function */
        cache_node_alloc_t      alloc;          /* allocation function */
+       cache_node_flush_t      flush;          /* flush dirty data function */
        cache_node_relse_t      relse;          /* memory free function */
        cache_node_compare_t    compare;        /* comparison routine */
        cache_bulk_relse_t      bulkrelse;      /* bulk release routine */
@@ -75,6 +78,7 @@ struct cache *cache_init(unsigned int, s
 void cache_destroy(struct cache *);
 void cache_walk(struct cache *, cache_walk_t);
 void cache_purge(struct cache *);
+void cache_flush(struct cache *);
 
 int cache_node_get(struct cache *, cache_key_t, struct cache_node **);
 void cache_node_put(struct cache_node *);

===========================================================================
xfsprogs/include/libxfs.h
===========================================================================

--- a/xfsprogs/include/libxfs.h 2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/include/libxfs.h 2006-07-27 16:34:45.308344014 +1000
@@ -257,6 +257,7 @@ extern int  libxfs_writebuf_int (xfs_buf_
 extern struct cache    *libxfs_bcache;
 extern struct cache_operations libxfs_bcache_operations;
 extern void    libxfs_bcache_purge (void);
+extern void    libxfs_bcache_flush (void);
 extern xfs_buf_t       *libxfs_getbuf (dev_t, xfs_daddr_t, int);
 extern void    libxfs_putbuf (xfs_buf_t *);
 extern void    libxfs_purgebuf (xfs_buf_t *);
@@ -467,6 +468,8 @@ extern int  libxfs_bmap_finish (xfs_trans
                                xfs_fsblock_t, int *);
 extern int     libxfs_bmap_next_offset (xfs_trans_t *, xfs_inode_t *,
                                xfs_fileoff_t *, int);
+extern int     libxfs_bmap_last_offset(xfs_trans_t *, xfs_inode_t *, 
+                               xfs_fileoff_t *, int);
 extern int     libxfs_bunmapi (xfs_trans_t *, xfs_inode_t *, xfs_fileoff_t,
                                xfs_filblks_t, int, xfs_extnum_t,
                                xfs_fsblock_t *, xfs_bmap_free_t *, int *);

===========================================================================
xfsprogs/libxfs/cache.c
===========================================================================

--- a/xfsprogs/libxfs/cache.c   2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/cache.c   2006-07-27 17:42:43.812685388 +1000
@@ -60,6 +60,7 @@ cache_init(
        cache->c_hashsize = hashsize;
        cache->hash = cache_operations->hash;
        cache->alloc = cache_operations->alloc;
+       cache->flush = cache_operations->flush;
        cache->relse = cache_operations->relse;
        cache->compare = cache_operations->compare;
        cache->bulkrelse = cache_operations->bulkrelse ?
@@ -422,6 +423,39 @@ cache_purge(
                cache_abort();
        }
 #endif
+       /* flush any remaining nodes to disk */
+       cache_flush(cache);
+}
+
+/*
+ * Flush all nodes in the cache to disk. 
+ */
+void
+cache_flush(
+       struct cache *          cache)
+{
+       struct cache_hash *     hash;
+       struct list_head *      head;
+       struct list_head *      pos;
+       struct cache_node *     node;
+       int                     i;
+       
+       if (!cache->flush)
+               return;
+       
+       for (i = 0; i < cache->c_hashsize; i++) {
+               hash = &cache->c_hash[i];
+               
+               pthread_mutex_lock(&hash->ch_mutex);
+               head = &hash->ch_list;
+               for (pos = head->next; pos != head; pos = pos->next) {
+                       node = (struct cache_node *)pos;
+                       pthread_mutex_lock(&node->cn_mutex);
+                       cache->flush(node);
+                       pthread_mutex_unlock(&node->cn_mutex);
+               }
+               pthread_mutex_unlock(&hash->ch_mutex);
+       }
 }
 
 #define        HASH_REPORT     (3*HASH_CACHE_RATIO)

===========================================================================
xfsprogs/libxfs/rdwr.c
===========================================================================

--- a/xfsprogs/libxfs/rdwr.c    2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/rdwr.c    2006-07-27 16:40:56.612373938 +1000
@@ -416,6 +416,15 @@ libxfs_iomove(xfs_buf_t *bp, uint boff, 
 }
 
 static void
+libxfs_bflush(struct cache_node *node)
+{
+       xfs_buf_t               *bp = (xfs_buf_t *)node;
+
+       if ((bp != NULL) && (bp->b_flags & LIBXFS_B_DIRTY))
+               libxfs_writebufr(bp);
+}
+
+static void
 libxfs_brelse(struct cache_node *node)
 {
        xfs_buf_t               *bp = (xfs_buf_t *)node;
@@ -442,9 +451,16 @@ libxfs_bcache_purge(void)
        cache_purge(libxfs_bcache);
 }
 
+void 
+libxfs_bcache_flush(void)
+{
+       cache_flush(libxfs_bcache);
+}
+
 struct cache_operations libxfs_bcache_operations = {
        /* .hash */     libxfs_bhash,
        /* .alloc */    libxfs_balloc,
+       /* .flush */    libxfs_bflush,
        /* .relse */    libxfs_brelse,
        /* .compare */  libxfs_bcompare,
        /* .bulkrelse */ NULL   /* TODO: lio_listio64 interface? */
@@ -649,6 +665,7 @@ libxfs_icache_purge(void)
 struct cache_operations libxfs_icache_operations = {
        /* .hash */     libxfs_ihash,
        /* .alloc */    libxfs_ialloc,
+       /* .flush */    NULL,
        /* .relse */    libxfs_irelse,
        /* .compare */  libxfs_icompare,
        /* .bulkrelse */ NULL

===========================================================================
xfsprogs/libxfs/xfs.h
===========================================================================

--- a/xfsprogs/libxfs/xfs.h     2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/libxfs/xfs.h     2006-07-25 12:05:31.917896892 +1000
@@ -98,6 +98,7 @@
 #define xfs_bmapi_single               libxfs_bmapi_single
 #define xfs_bmap_finish                        libxfs_bmap_finish
 #define xfs_bmap_del_free              libxfs_bmap_del_free
+#define xfs_bmap_last_offset           libxfs_bmap_last_offset
 #define xfs_bunmapi                    libxfs_bunmapi
 #define xfs_free_extent                        libxfs_free_extent
 #define xfs_rtfree_extent              libxfs_rtfree_extent

===========================================================================
xfsprogs/repair/phase6.c
===========================================================================

--- a/xfsprogs/repair/phase6.c  2006-07-28 11:46:09.000000000 +1000
+++ b/xfsprogs/repair/phase6.c  2006-07-28 11:30:50.033905530 +1000
@@ -36,43 +36,35 @@ static int orphanage_entered;
 
 /*
  * Data structures and routines to keep track of directory entries
- * and whether their leaf entry has been seen
+ * and whether their leaf entry has been seen. Also used for name
+ * duplicate checking and rebuilding step if required.
  */
 typedef struct dir_hash_ent {
-       struct dir_hash_ent     *next;  /* pointer to next entry */
+       struct dir_hash_ent     *nextbyaddr;/* pointer to next entry */
+       struct dir_hash_ent     *nextbyhash;
+       struct dir_hash_ent     *nextbyorder;
        xfs_dir2_leaf_entry_t   ent;    /* address and hash value */
+       xfs_ino_t               inum;   /* inode of name */
        short                   junkit; /* name starts with / */
        short                   seen;   /* have seen leaf entry */
+       int                     namelen;/* length of name */
+       uchar_t                 *name;  /* pointer to name (no NULL) */
 } dir_hash_ent_t;
 
 typedef struct dir_hash_tab {
        int                     size;   /* size of hash table */
-       dir_hash_ent_t          *tab[1];/* actual hash table, variable size */
+       int                     names_duped;
+       dir_hash_ent_t          *first;
+       dir_hash_ent_t          *last;
+       dir_hash_ent_t          **byhash;/* actual hash table, variable size */
+       dir_hash_ent_t          **byaddr;/* actual hash table, variable size */
 } dir_hash_tab_t;
+
 #define        DIR_HASH_TAB_SIZE(n)    \
-       (offsetof(dir_hash_tab_t, tab) + (sizeof(dir_hash_ent_t *) * (n)))
+       (sizeof(dir_hash_tab_t) + (sizeof(dir_hash_ent_t *) * (n) * 2))
 #define        DIR_HASH_FUNC(t,a)      ((a) % (t)->size)
 
 /*
- * Track names to check for duplicates in a directory.
- */
-
-typedef struct name_hash_ent {
-       struct name_hash_ent    *next;  /* pointer to next entry */
-       xfs_dahash_t            hashval;/* hash value of name */
-       int                     namelen;/* length of name */
-       uchar_t                 *name;  /* pointer to name (no NULL) */
-} name_hash_ent_t;             
-
-typedef struct name_hash_tab {
-       int                     size;   /* size of hash table */
-       name_hash_ent_t         *tab[1];/* actual hash table, variable size */
-} name_hash_tab_t;
-#define        NAME_HASH_TAB_SIZE(n)   \
-       (offsetof(name_hash_tab_t, tab) + (sizeof(name_hash_ent_t *) * (n)))
-#define        NAME_HASH_FUNC(t,a)     ((a) % (t)->size)
-
-/*
  * Track the contents of the freespace table in a directory.
  */
 typedef struct freetab {
@@ -94,28 +86,75 @@ typedef struct freetab {
 #define        DIR_HASH_CK_BADSTALE    5
 #define        DIR_HASH_CK_TOTAL       6
 
-static void
+/*
+ * Returns 0 if the name already exists (ie. a duplicate)
+ */
+static int
 dir_hash_add(
        dir_hash_tab_t          *hashtab,
-       xfs_dahash_t            hash,
-       xfs_dir2_dataptr_t      addr,
-       int                     junk)
-{
-       int                     i;
+       xfs_dir2_dataptr_t      addr,   
+       xfs_ino_t               inum,
+       int                     namelen,
+       uchar_t                 *name)
+{
+       xfs_dahash_t            hash = 0;
+       int                     byaddr;
+       int                     byhash = 0;
        dir_hash_ent_t          *p;
-
-       i = DIR_HASH_FUNC(hashtab, addr);
+       int                     dup;
+       short                   junk;
+       
+       junk = name[0] == '/';
+       byaddr = DIR_HASH_FUNC(hashtab, addr);
+       dup = 0;
+
+       if (!junk) {
+               hash = libxfs_da_hashname(name, namelen);
+               byhash = DIR_HASH_FUNC(hashtab, hash);
+
+               /* 
+                * search hash bucket for existing name.
+                */
+               for (p = hashtab->byhash[byhash]; p; p = p->nextbyhash) {
+                       if (p->ent.hashval == hash && p->namelen == namelen) {
+                               if (memcmp(p->name, name, namelen) == 0) {
+                                       dup = 1;
+                                       break;
+                               }
+                       }
+               }
+       }
+       
        if ((p = malloc(sizeof(*p))) == NULL)
                do_error(_("malloc failed in dir_hash_add (%u bytes)\n"),
                        sizeof(*p));
-       p->next = hashtab->tab[i];
-       hashtab->tab[i] = p;
-       if (!(p->junkit = junk))
+       
+       p->nextbyaddr = hashtab->byaddr[byaddr];
+       hashtab->byaddr[byaddr] = p;
+       if (hashtab->last) 
+               hashtab->last->nextbyorder = p;
+       else
+               hashtab->first = p;
+       p->nextbyorder = NULL;
+       hashtab->last = p;
+       
+       if (!(p->junkit = junk)) {
                p->ent.hashval = hash;
+               p->nextbyhash = hashtab->byhash[byhash];
+               hashtab->byhash[byhash] = p;
+       }
        p->ent.address = addr;
+       p->inum = inum;
        p->seen = 0;
+       p->namelen = namelen;
+       p->name = name;
+       
+       return !dup;
 }
 
+/*
+ * checks to see if any data entries are not in the leaf blocks 
+ */
 static int
 dir_hash_unseen(
        dir_hash_tab_t  *hashtab)
@@ -124,7 +163,7 @@ dir_hash_unseen(
        dir_hash_ent_t  *p;
 
        for (i = 0; i < hashtab->size; i++) {
-               for (p = hashtab->tab[i]; p; p = p->next) {
+               for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
                        if (p->seen == 0)
                                return 1;
                }
@@ -173,8 +212,10 @@ dir_hash_done(
        dir_hash_ent_t  *p;
 
        for (i = 0; i < hashtab->size; i++) {
-               for (p = hashtab->tab[i]; p; p = n) {
-                       n = p->next;
+               for (p = hashtab->byaddr[i]; p; p = n) {
+                       n = p->nextbyaddr;
+                       if (hashtab->names_duped)
+                               free(p->name);
                        free(p);
                }
        }
@@ -196,6 +237,10 @@ dir_hash_init(
        if ((hashtab = calloc(DIR_HASH_TAB_SIZE(hsize), 1)) == NULL)
                do_error(_("calloc failed in dir_hash_init\n"));
        hashtab->size = hsize;
+       hashtab->byhash = (dir_hash_ent_t**)((char *)hashtab + 
+               sizeof(dir_hash_tab_t));
+       hashtab->byaddr = (dir_hash_ent_t**)((char *)hashtab + 
+               sizeof(dir_hash_tab_t) + sizeof(dir_hash_ent_t*) * hsize);
        return hashtab;
 }
 
@@ -209,7 +254,7 @@ dir_hash_see(
        dir_hash_ent_t          *p;
 
        i = DIR_HASH_FUNC(hashtab, addr);
-       for (p = hashtab->tab[i]; p; p = p->next) {
+       for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
                if (p->ent.address != addr)
                        continue;
                if (p->seen)
@@ -222,6 +267,10 @@ dir_hash_see(
        return DIR_HASH_CK_NODATA;
 }
 
+/*
+ * checks to make sure leafs match a data entry, and that the stale
+ * count is valid.
+ */
 static int
 dir_hash_see_all(
        dir_hash_tab_t          *hashtab,
@@ -246,81 +295,25 @@ dir_hash_see_all(
 }
 
 /*
- * Returns 0 if the name already exists (ie. a duplicate)
+ * Convert name pointers into locally allocated memory
  */
-static int
-name_hash_add(
-       name_hash_tab_t         *nametab,
-       uchar_t                 *name,
-       int                     namelen)
+static void
+dir_hash_dup_names(dir_hash_tab_t *hashtab)
 {
-       xfs_dahash_t            hash;
-       int                     i;
-       name_hash_ent_t         *p;
-
-       hash = libxfs_da_hashname(name, namelen);
-                       
-       i = NAME_HASH_FUNC(nametab, hash);
-       
-       /* 
-        * search hash bucket for existing name.
-        */
-       for (p = nametab->tab[i]; p; p = p->next) {
-               if (p->hashval == hash && p->namelen == namelen) {
-                       if (memcmp(p->name, name, namelen) == 0) 
-                               return 0; /* exists */
-               }
-       }
-       
-       if ((p = malloc(sizeof(*p))) == NULL)
-               do_error(_("malloc failed in name_hash_add (%u bytes)\n"),
-                       sizeof(*p));
+       uchar_t                 *name;
+       dir_hash_ent_t          *p;
        
-       p->next = nametab->tab[i];
-       p->hashval = hash;
-       p->name = name;
-       p->namelen = namelen;
-       nametab->tab[i] = p;
+       if (hashtab->names_duped)
+               return;
        
-       return 1;       /* success, no duplicate */
-}
-
-static name_hash_tab_t *
-name_hash_init(
-       xfs_fsize_t     size)
-{
-       name_hash_tab_t *nametab;
-       int             hsize;
-
-       hsize = size / (16 * 4);
-       if (hsize > 1024)
-               hsize = 1024;
-       else if (hsize < 16)
-               hsize = 16;
-       if ((nametab = calloc(NAME_HASH_TAB_SIZE(hsize), 1)) == NULL)
-               do_error(_("calloc failed in name_hash_init\n"));
-       nametab->size = hsize;
-       return nametab;
-}
-
-static void
-name_hash_done(
-       name_hash_tab_t *nametab)
-{
-       int             i;
-       name_hash_ent_t *n;
-       name_hash_ent_t *p;
-
-       for (i = 0; i < nametab->size; i++) {
-               for (p = nametab->tab[i]; p; p = n) {
-                       n = p->next;
-                       free(p);
-               }
+       for (p = hashtab->first; p; p = p->nextbyorder) {
+               name = malloc(p->namelen);
+               memcpy(name, p->name, p->namelen);
+               p->name = name;
        }
-       free(nametab);
+       hashtab->names_duped = 1;
 }
 
-
 /*
  * Version 1 or 2 directory routine wrappers
 */
@@ -1385,7 +1378,8 @@ lf_block_dir_entry_check(xfs_mount_t              *m
                        dir_stack_t             *stack,
                        ino_tree_node_t         *current_irec,
                        int                     current_ino_offset,
-                       name_hash_tab_t         *nametab)
+                       dir_hash_tab_t          *hashtab,
+                       xfs_dablk_t             da_bno)
 {
        xfs_dir_leaf_entry_t    *entry;
        ino_tree_node_t         *irec;
@@ -1545,7 +1539,9 @@ lf_block_dir_entry_check(xfs_mount_t              *m
                /*
                 * check for duplicate names in directory.
                 */ 
-               if (!name_hash_add(nametab, namest->name, entry->namelen)) {
+               if (!dir_hash_add(hashtab, (da_bno << mp->m_sb.sb_blocklog) + 
+                                               entry->nameidx, 
+                               lino, entry->namelen, namest->name)) {
                        do_warn(
                _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
                                fname, lino, ino);
@@ -1635,7 +1631,7 @@ longform_dir_entry_check(xfs_mount_t      *mp
                        dir_stack_t     *stack,
                        ino_tree_node_t *irec,
                        int             ino_offset,
-                       name_hash_tab_t *nametab)
+                       dir_hash_tab_t  *hashtab)
 {
        xfs_dir_leafblock_t     *leaf;
        xfs_buf_t               *bp;
@@ -1677,8 +1673,6 @@ longform_dir_entry_check(xfs_mount_t      *mp
 
                leaf = (xfs_dir_leafblock_t *)XFS_BUF_PTR(bp);
 
-               da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
-
                if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) !=
                    XFS_DIR_LEAF_MAGIC)  {
                        if (!no_modify)  {
@@ -1699,9 +1693,11 @@ _("bad magic # (0x%x) for dir ino %llu l
                }
 
                if (!skipit)
-                       lf_block_dir_entry_check(mp, ino, leaf, &dirty,
-                                               num_illegal, need_dot, stack,
-                                               irec, ino_offset, nametab);
+                       lf_block_dir_entry_check(mp, ino, leaf, &dirty, 
+                                       num_illegal, need_dot, stack, irec, 
+                                       ino_offset, hashtab, da_bno);
+
+               da_bno = INT_GET(leaf->hdr.info.forw, ARCH_CONVERT);
 
                ASSERT(dirty == 0 || (dirty && !no_modify));
 
@@ -1745,6 +1741,127 @@ _("can't map leaf block %d in dir %llu, 
 }
 
 /*
+ * Unexpected failure during the rebuild will leave the entries in
+ * lost+found on the next run
+ */
+
+static void 
+longform_dir2_rebuild(
+       xfs_mount_t     *mp,
+       xfs_ino_t       ino,
+       xfs_inode_t     *ip,
+       dir_hash_tab_t  *hashtab)
+{
+       int                     error;
+       int                     nres;
+       xfs_trans_t             *tp;
+       xfs_fileoff_t           lastblock;
+       xfs_fsblock_t           firstblock;
+       xfs_bmap_free_t         flist;
+       xfs_ino_t               parentino;
+       xfs_inode_t             *pip;
+       int                     byhash;
+       dir_hash_ent_t          *p;
+       int                     committed;
+       int                     done;
+       
+       /* 
+        * trash directory completely and rebuild from scratch using the
+        * name/inode pairs in the hash table
+        */
+        
+       do_warn(_("rebuilding directory inode %llu\n"), ino);
+       
+       /* 
+        * first attempt to locate the parent inode, if it can't be found,
+        * we'll use the lost+found inode 
+        */
+       byhash = DIR_HASH_FUNC(hashtab, libxfs_da_hashname((uchar_t*)"..", 2));
+       parentino = orphanage_ino;
+       for (p = hashtab->byhash[byhash]; p; p = p->nextbyhash) {
+               if (p->namelen == 2 && p->name[0] == '.' && p->name[1] == '.') {
+                       parentino = p->inum;
+                       break;
+               }
+       }
+
+       XFS_BMAP_INIT(&flist, &firstblock);
+               
+       tp = libxfs_trans_alloc(mp, 0);
+       nres = XFS_REMOVE_SPACE_RES(mp);
+       error = libxfs_trans_reserve(tp, nres, XFS_REMOVE_LOG_RES(mp), 0,
+                       XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
+       if (error)
+               res_failed(error);
+       libxfs_trans_ijoin(tp, ip, 0);
+       libxfs_trans_ihold(tp, ip);
+       
+       if ((error = libxfs_bmap_last_offset(tp, ip, &lastblock, 
+                                               XFS_DATA_FORK)))
+               do_error(_("xfs_bmap_last_offset failed -- error - %d\n"), 
+                       error);
+       
+       /* re-init the directory to shortform */
+       if ((error = libxfs_trans_iget(mp, tp, parentino, 0, 0, &pip)))
+               do_error(
+               _("couldn't iget parent inode %llu -- error - %d\n"),
+                       parentino, error);
+
+       /* free all data, leaf, node and freespace blocks */
+       
+       if ((error = libxfs_bunmapi(tp, ip, 0, lastblock, 
+                       XFS_BMAPI_METADATA, 0, &firstblock, &flist,
+                       &done))) 
+               do_error(_("xfs_bunmapi failed -- error - %d\n"), error);
+       ASSERT(done);
+
+       libxfs_dir2_init(tp, ip, pip);
+       
+       error = libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
+                               
+       libxfs_trans_commit(tp, 
+                       XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC, 0);
+               
+       /* go through the hash list and re-add the inodes */
+
+       for (p = hashtab->first; p; p = p->nextbyorder) {
+               
+               if (p->name[0] == '/' || (p->name[0] == '.' && (p->namelen == 1 
+                               || (p->namelen == 2 && p->name[1] == '.'))))
+                       continue;
+               
+               tp = libxfs_trans_alloc(mp, 0);
+               nres = XFS_CREATE_SPACE_RES(mp, p->namelen);
+               if ((error = libxfs_trans_reserve(tp, nres, 
+                               XFS_CREATE_LOG_RES(mp), 0,
+                               XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT)))
+                       do_error(
+       _("space reservation failed (%d), filesystem may be out of space\n"),
+                               error);
+
+               libxfs_trans_ijoin(tp, ip, 0);
+               libxfs_trans_ihold(tp, ip);
+
+               XFS_BMAP_INIT(&flist, &firstblock);
+               if ((error = libxfs_dir2_createname(tp, ip, (char*)p->name, 
+                               p->namelen, p->inum, &firstblock, &flist, 
nres)))
+                       do_error(
+_("name create failed in ino %llu (%d), filesystem may be out of space\n"),
+                               ino, error);
+
+               if ((error = libxfs_bmap_finish(&tp, &flist, firstblock, 
+                               &committed)))
+                       do_error(
+       _("bmap finish failed (%d), filesystem may be out of space\n"),
+                               error);
+
+               libxfs_trans_commit(tp, 
+                               XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC, 0);
+       }
+}
+
+
+/*
  * Kill a block in a version 2 inode.
  * Makes its own transaction.
  */
@@ -1807,7 +1924,6 @@ longform_dir2_entry_check_data(
        xfs_dabuf_t             **bpp,
        dir_hash_tab_t          *hashtab,
        freetab_t               **freetabp,
-       name_hash_tab_t         *nametab,
        xfs_dablk_t             da_bno,
        int                     isblock)
 {
@@ -1828,6 +1944,7 @@ longform_dir2_entry_check_data(
        freetab_t               *freetab;
        int                     i;
        int                     ino_offset;
+       xfs_ino_t               inum;
        ino_tree_node_t         *irec;
        int                     junkit;
        int                     lastfree;
@@ -1956,8 +2073,7 @@ longform_dir2_entry_check_data(
        libxfs_trans_ijoin(tp, ip, 0);
        libxfs_trans_ihold(tp, ip);
        libxfs_da_bjoin(tp, bp);
-       if (isblock)
-               libxfs_da_bhold(tp, bp);
+       libxfs_da_bhold(tp, bp);
        XFS_BMAP_INIT(&flist, &firstblock);
        if (INT_GET(d->hdr.magic, ARCH_CONVERT) != wantmagic) {
                do_warn(_("bad directory block magic # %#x for directory inode "
@@ -1987,7 +2103,7 @@ longform_dir2_entry_check_data(
        while (ptr < endptr) {
                dup = (xfs_dir2_data_unused_t *)ptr;
                if (INT_GET(dup->freetag, ARCH_CONVERT) ==
-                   XFS_DIR2_DATA_FREE_TAG) {
+                                               XFS_DIR2_DATA_FREE_TAG) {
                        if (lastfree) {
                                do_warn(_("directory inode %llu block %u has "
                                          "consecutive free entries: "),
@@ -2011,10 +2127,24 @@ longform_dir2_entry_check_data(
                addr = XFS_DIR2_DB_OFF_TO_DATAPTR(mp, db, ptr - (char *)d);
                dep = (xfs_dir2_data_entry_t *)ptr;
                ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
+               inum = INT_GET(dep->inumber, ARCH_CONVERT);
                lastfree = 0;
-               dir_hash_add(hashtab,
-                       libxfs_da_hashname((uchar_t *)dep->name, dep->namelen),
-                       addr, dep->name[0] == '/');
+               if (!dir_hash_add(hashtab, addr, inum, dep->namelen, 
+                               dep->name)) {
+                       do_warn(
+               _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
+                               fname, inum, ip->i_ino);
+                       if (!no_modify) {
+                               if (verbose)
+                                       do_warn(
+                                       _(", marking entry to be junked\n"));
+                               else
+                                       do_warn("\n");
+                       } else {
+                               do_warn(_(", would junk entry\n"));
+                       }
+                       dep->name[0] = '/';
+               }
                /*
                 * skip bogus entries (leading '/').  they'll be deleted
                 * later.  must still log it, else we leak references to
@@ -2029,7 +2159,7 @@ longform_dir2_entry_check_data(
                junkit = 0;
                bcopy(dep->name, fname, dep->namelen);
                fname[dep->namelen] = '\0';
-               ASSERT(INT_GET(dep->inumber, ARCH_CONVERT) != NULLFSINO);
+               ASSERT(inum != NULLFSINO);
                /*
                 * skip the '..' entry since it's checked when the
                 * directory is reached by something else.  if it never
@@ -2039,7 +2169,7 @@ longform_dir2_entry_check_data(
                if (dep->namelen == 2 && dep->name[0] == '.' &&
                    dep->name[1] == '.')
                        continue;
-               ASSERT(no_modify || !verify_inum(mp, INT_GET(dep->inumber, 
ARCH_CONVERT)));
+               ASSERT(no_modify || !verify_inum(mp, inum));
                /*
                 * special case the . entry.  we know there's only one
                 * '.' and only '.' points to itself because bogus entries
@@ -2049,7 +2179,7 @@ longform_dir2_entry_check_data(
                 * '..' is already accounted for or will be taken care
                 * of when directory is moved to orphanage.
                 */
-               if (ip->i_ino == INT_GET(dep->inumber, ARCH_CONVERT))  {
+               if (ip->i_ino == inum)  {
                        ASSERT(dep->name[0] == '.' && dep->namelen == 1);
                        add_inode_ref(current_irec, current_ino_offset);
                        *need_dot = 0;
@@ -2062,23 +2192,18 @@ longform_dir2_entry_check_data(
                 * just skip it.  no need to process it and it's ..
                 * link is already accounted for.
                 */
-               if (INT_GET(dep->inumber, ARCH_CONVERT) == orphanage_ino &&
-                   strcmp(fname, ORPHANAGE) == 0)
+               if (inum == orphanage_ino && strcmp(fname, ORPHANAGE) == 0)
                        continue;
                /*
                 * skip entries with bogus inumbers if we're in no modify mode
                 */
-               if (no_modify &&
-                   verify_inum(mp, INT_GET(dep->inumber, ARCH_CONVERT)))
+               if (no_modify && verify_inum(mp, inum))
                        continue;
                /*
                 * ok, now handle the rest of the cases besides '.' and '..'
                 */
-               irec = find_inode_rec(
-                       XFS_INO_TO_AGNO(mp,
-                               INT_GET(dep->inumber, ARCH_CONVERT)),
-                       XFS_INO_TO_AGINO(mp,
-                               INT_GET(dep->inumber, ARCH_CONVERT)));
+               irec = find_inode_rec(XFS_INO_TO_AGNO(mp, inum),
+                                       XFS_INO_TO_AGINO(mp, inum));
                if (irec == NULL)  {
                        nbad++;
                        do_warn(_("entry \"%s\" in directory inode %llu points "
@@ -2093,9 +2218,7 @@ longform_dir2_entry_check_data(
                        }
                        continue;
                }
-               ino_offset = XFS_INO_TO_AGINO(mp,
-                               INT_GET(dep->inumber, ARCH_CONVERT)) -
-                                       irec->ino_startnum;
+               ino_offset = XFS_INO_TO_AGINO(mp, inum) - irec->ino_startnum;
                /*
                 * if it's a free inode, blow out the entry.
                 * by now, any inode that we think is free
@@ -2106,18 +2229,13 @@ longform_dir2_entry_check_data(
                         * don't complain if this entry points to the old
                         * and now-free lost+found inode
                         */
-                       if (verbose || no_modify ||
-                           INT_GET(dep->inumber, ARCH_CONVERT) !=
-                           old_orphanage_ino)
+                       if (verbose || no_modify || inum != old_orphanage_ino)
                                do_warn(
        _("entry \"%s\" in directory inode %llu points to free inode %llu"),
-                                       fname, ip->i_ino,
-                                       INT_GET(dep->inumber, ARCH_CONVERT));
+                                       fname, ip->i_ino, inum);
                        nbad++;
                        if (!no_modify)  {
-                               if (verbose ||
-                                   INT_GET(dep->inumber, ARCH_CONVERT) !=
-                                   old_orphanage_ino)
+                               if (verbose || inum != old_orphanage_ino)
                                        do_warn(
                                        _(", marking entry to be junked\n"));
                                else
@@ -2130,28 +2248,6 @@ longform_dir2_entry_check_data(
                        continue;
                }
                /*
-                * check for duplicate names in directory.
-                */ 
-               if (!name_hash_add(nametab, dep->name, dep->namelen)) {
-                       do_warn(
-               _("entry \"%s\" (ino %llu) in dir %llu is a duplicate name"),
-                               fname, INT_GET(dep->inumber, ARCH_CONVERT),
-                               ip->i_ino);
-                       nbad++;
-                       if (!no_modify) {
-                               if (verbose)
-                                       do_warn(
-                                       _(", marking entry to be junked\n"));
-                               else
-                                       do_warn("\n");
-                               dep->name[0] = '/';
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
-                       } else {
-                               do_warn(_(", would junk entry\n"));
-                       }
-                       continue;
-               }
-               /*
                 * check easy case first, regular inode, just bump
                 * the link count and continue
                 */
@@ -2172,22 +2268,17 @@ longform_dir2_entry_check_data(
                        junkit = 1;
                        do_warn(
 _("entry \"%s\" in dir %llu points to an already connected directory inode 
%llu,\n"),
-                               fname, ip->i_ino,
-                               INT_GET(dep->inumber, ARCH_CONVERT));
+                               fname, ip->i_ino, inum);
                } else if (parent == ip->i_ino)  {
                        add_inode_reached(irec, ino_offset);
                        add_inode_ref(current_irec, current_ino_offset);
-                       if (!is_inode_refchecked(
-                               INT_GET(dep->inumber, ARCH_CONVERT), irec,
-                                       ino_offset))
-                               push_dir(stack,
-                                       INT_GET(dep->inumber, ARCH_CONVERT));
+                       if (!is_inode_refchecked(inum, irec, ino_offset))
+                               push_dir(stack, inum);
                } else  {
                        junkit = 1;
                        do_warn(
 _("entry \"%s\" in dir inode %llu inconsistent with .. value (%llu) in ino 
%llu,\n"),
-                               fname, ip->i_ino, parent,
-                               INT_GET(dep->inumber, ARCH_CONVERT));
+                               fname, ip->i_ino, parent, inum);
                }
                if (junkit)  {
                        junkit = 0;
@@ -2195,9 +2286,7 @@ _("entry \"%s\" in dir inode %llu incons
                        if (!no_modify)  {
                                dep->name[0] = '/';
                                libxfs_dir2_data_log_entry(tp, bp, dep);
-                               if (verbose ||
-                                   INT_GET(dep->inumber, ARCH_CONVERT) !=
-                                   old_orphanage_ino)
+                               if (verbose || inum != old_orphanage_ino)
                                        do_warn(
                                        _("\twill clear entry \"%s\"\n"),
                                                fname);
@@ -2212,8 +2301,6 @@ _("entry \"%s\" in dir inode %llu incons
                libxfs_dir2_data_freescan(mp, d, &needlog, NULL);
        if (needlog)
                libxfs_dir2_data_log_header(tp, bp);
-       else if (!isblock && !nbad)
-               libxfs_da_brelse(tp, bp);
        libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
        libxfs_trans_commit(tp, 0, 0);
        freetab->ents[db].v = INT_GET(d->hdr.bestfree[0].length, ARCH_CONVERT);
@@ -2306,19 +2393,19 @@ longform_dir2_check_node(
        xfs_fileoff_t           next_da_bno;
        int                     seeval = 0;
        int                     used;
-
+       
        for (da_bno = mp->m_dirleafblk, next_da_bno = 0;
             next_da_bno != NULLFILEOFF && da_bno < mp->m_dirfreeblk;
             da_bno = (xfs_dablk_t)next_da_bno) {
                next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
-               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK))
+               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK)) 
                        break;
                if (libxfs_da_read_bufr(NULL, ip, da_bno, -1, &bp,
                                XFS_DATA_FORK)) {
-                       do_error(
-                       _("can't read block %u for directory inode %llu\n"),
+                       do_warn(
+                       _("can't read leaf block %u for directory inode 
%llu\n"),
                                da_bno, ip->i_ino);
-                       /* NOTREACHED */
+                       return 1;
                }
                leaf = bp->data;
                if (INT_GET(leaf->hdr.info.magic, ARCH_CONVERT) !=
@@ -2348,23 +2435,24 @@ longform_dir2_check_node(
                seeval = dir_hash_see_all(hashtab, leaf->ents, 
INT_GET(leaf->hdr.count, ARCH_CONVERT),
                        INT_GET(leaf->hdr.stale, ARCH_CONVERT));
                libxfs_da_brelse(NULL, bp);
-               if (seeval != DIR_HASH_CK_OK)
+               if (seeval != DIR_HASH_CK_OK) 
                        return 1;
        }
-       if (dir_hash_check(hashtab, ip, seeval))
+       if (dir_hash_check(hashtab, ip, seeval)) 
                return 1;
+       
        for (da_bno = mp->m_dirfreeblk, next_da_bno = 0;
             next_da_bno != NULLFILEOFF;
             da_bno = (xfs_dablk_t)next_da_bno) {
                next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
-               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK))
+               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK)) 
                        break;
                if (libxfs_da_read_bufr(NULL, ip, da_bno, -1, &bp,
                                XFS_DATA_FORK)) {
-                       do_error(_("can't read block %u for directory inode "
-                                  "%llu\n"),
+                       do_warn(
+               _("can't read freespace block %u for directory inode %llu\n"),
                                da_bno, ip->i_ino);
-                       /* NOTREACHED */
+                       return 1;
                }
                free = bp->data;
                fdb = XFS_DIR2_DA_TO_DB(mp, da_bno);
@@ -2418,388 +2506,9 @@ longform_dir2_check_node(
 }
 
 /*
- * Rebuild a directory: set up.
- * Turn it into a node-format directory with no contents in the
- * upper area.  Also has correct freespace blocks.
- */
-void
-longform_dir2_rebuild_setup(
-       xfs_mount_t             *mp,
-       xfs_ino_t               ino,
-       xfs_inode_t             *ip,
-       freetab_t               *freetab)
-{
-       xfs_da_args_t           args;
-       int                     committed;
-       xfs_dir2_data_t         *data = NULL;
-       xfs_dabuf_t             *dbp;
-       int                     error;
-       xfs_dir2_db_t           fbno;
-       xfs_dabuf_t             *fbp;
-       xfs_fsblock_t           firstblock;
-       xfs_bmap_free_t         flist;
-       xfs_dir2_free_t         *free;
-       int                     i;
-       int                     j;
-       xfs_dablk_t             lblkno;
-       xfs_dabuf_t             *lbp;
-       xfs_dir2_leaf_t         *leaf;
-       int                     nres;
-       xfs_trans_t             *tp;
-
-       /* read first directory block */
-       tp = libxfs_trans_alloc(mp, 0);
-       nres = XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK);
-       error = libxfs_trans_reserve(tp,
-               nres, XFS_CREATE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES,
-               XFS_CREATE_LOG_COUNT);
-       if (error)
-               res_failed(error);
-       libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
-       XFS_BMAP_INIT(&flist, &firstblock);
-       if (libxfs_da_read_buf(tp, ip, mp->m_dirdatablk, -2, &dbp,
-                       XFS_DATA_FORK)) {
-               do_error(_("can't read block %u for directory inode %llu\n"),
-                       mp->m_dirdatablk, ino);
-               /* NOTREACHED */
-       }
-
-       if (dbp)
-               data = dbp->data;
-
-       /* check for block format directory */
-       if (data &&
-           INT_GET((data)->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
-               xfs_dir2_block_t        *block;
-               xfs_dir2_leaf_entry_t   *blp;
-               xfs_dir2_block_tail_t   *btp;
-               int                     needlog;
-               int                     needscan;
-
-               /* convert directory block from block format to data format */
-               INT_SET(data->hdr.magic, ARCH_CONVERT, XFS_DIR2_DATA_MAGIC);
-
-               /* construct freelist */
-               block = (xfs_dir2_block_t *)data;
-               btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
-               blp = XFS_DIR2_BLOCK_LEAF_P(btp);
-               needlog = needscan = 0;
-               libxfs_dir2_data_make_free(tp, dbp, (char *)blp - (char *)block,
-                       (char *)block + mp->m_dirblksize - (char *)blp,
-                       &needlog, &needscan);
-               if (needscan)
-                       libxfs_dir2_data_freescan(mp, data, &needlog, NULL);
-               libxfs_da_log_buf(tp, dbp, 0, mp->m_dirblksize - 1);
-       } else if (dbp) {
-               libxfs_da_brelse(tp, dbp);
-       }
-
-       /* allocate blocks for btree */
-       bzero(&args, sizeof(args));
-       args.trans = tp;
-       args.dp = ip;
-       args.whichfork = XFS_DATA_FORK;
-       args.firstblock = &firstblock;
-       args.flist = &flist;
-       args.total = nres;
-       if ((error = libxfs_da_grow_inode(&args, &lblkno)) ||
-           (error = libxfs_da_get_buf(tp, ip, lblkno, -1, &lbp, 
XFS_DATA_FORK))) {
-               do_error(_("can't add btree block to directory inode %llu\n"),
-                       ino);
-               /* NOTREACHED */
-       }
-       leaf = lbp->data;
-       bzero(leaf, mp->m_dirblksize);
-       INT_SET(leaf->hdr.info.magic, ARCH_CONVERT, XFS_DIR2_LEAFN_MAGIC);
-       libxfs_da_log_buf(tp, lbp, 0, mp->m_dirblksize - 1);
-       libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-       libxfs_trans_commit(tp, 0, 0);
-
-       for (i = 0; i < freetab->nents; i += XFS_DIR2_MAX_FREE_BESTS(mp)) {
-               tp = libxfs_trans_alloc(mp, 0);
-               nres = XFS_DAENTER_SPACE_RES(mp, XFS_DATA_FORK);
-               error = libxfs_trans_reserve(tp,
-                       nres, XFS_CREATE_LOG_RES(mp), 0, XFS_TRANS_PERM_LOG_RES,
-                       XFS_CREATE_LOG_COUNT);
-               if (error)
-                       res_failed(error);
-               libxfs_trans_ijoin(tp, ip, 0);
-               libxfs_trans_ihold(tp, ip);
-               XFS_BMAP_INIT(&flist, &firstblock);
-               bzero(&args, sizeof(args));
-               args.trans = tp;
-               args.dp = ip;
-               args.whichfork = XFS_DATA_FORK;
-               args.firstblock = &firstblock;
-               args.flist = &flist;
-               args.total = nres;
-               if ((error = libxfs_dir2_grow_inode(&args, XFS_DIR2_FREE_SPACE,
-                                                &fbno)) ||
-                   (error = libxfs_da_get_buf(tp, ip, XFS_DIR2_DB_TO_DA(mp, 
fbno),
-                                           -1, &fbp, XFS_DATA_FORK))) {
-                       do_error(_("can't add free block to directory inode "
-                                  "%llu\n"),
-                               ino);
-                       /* NOTREACHED */
-               }
-               free = fbp->data;
-               bzero(free, mp->m_dirblksize);
-               INT_SET(free->hdr.magic, ARCH_CONVERT, XFS_DIR2_FREE_MAGIC);
-               INT_SET(free->hdr.firstdb, ARCH_CONVERT, i);
-               INT_SET(free->hdr.nvalid, ARCH_CONVERT, 
XFS_DIR2_MAX_FREE_BESTS(mp));
-               if (i + INT_GET(free->hdr.nvalid, ARCH_CONVERT) > 
freetab->nents)
-                       INT_SET(free->hdr.nvalid, ARCH_CONVERT, freetab->nents 
- i);
-               for (j = 0; j < INT_GET(free->hdr.nvalid, ARCH_CONVERT); j++) {
-                       INT_SET(free->bests[j], ARCH_CONVERT, freetab->ents[i + 
j].v);
-                       if (INT_GET(free->bests[j], ARCH_CONVERT) != 
NULLDATAOFF)
-                               INT_MOD(free->hdr.nused, ARCH_CONVERT, +1);
-               }
-               libxfs_da_log_buf(tp, fbp, 0, mp->m_dirblksize - 1);
-               libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-               libxfs_trans_commit(tp, 0, 0);
-       }
-}
-
-/*
- * Rebuild the entries from a single data block.
- */
-void
-longform_dir2_rebuild_data(
-       xfs_mount_t             *mp,
-       xfs_ino_t               ino,
-       xfs_inode_t             *ip,
-       xfs_dablk_t             da_bno)
-{
-       xfs_dabuf_t             *bp;
-       xfs_dir2_block_tail_t   *btp;
-       int                     committed;
-       xfs_dir2_data_t         *data;
-       xfs_dir2_db_t           dbno;
-       xfs_dir2_data_entry_t   *dep;
-       xfs_dir2_data_unused_t  *dup;
-       char                    *endptr;
-       int                     error;
-       xfs_dir2_free_t         *fblock;
-       xfs_dabuf_t             *fbp;
-       xfs_dir2_db_t           fdb;
-       int                     fi;
-       xfs_fsblock_t           firstblock;
-       xfs_bmap_free_t         flist;
-       int                     needlog;
-       int                     needscan;
-       int                     nres;
-       char                    *ptr;
-       xfs_trans_t             *tp;
-
-       if (libxfs_da_read_buf(NULL, ip, da_bno, da_bno == 0 ? -2 : -1, &bp,
-                       XFS_DATA_FORK)) {
-               do_error(_("can't read block %u for directory inode %llu\n"),
-                       da_bno, ino);
-               /* NOTREACHED */
-       }
-       if (da_bno == 0 && bp == NULL)
-               /*
-                * The block was punched out.
-                */
-               return;
-       ASSERT(bp);
-       dbno = XFS_DIR2_DA_TO_DB(mp, da_bno);
-       fdb = XFS_DIR2_DB_TO_FDB(mp, dbno);
-       if (libxfs_da_read_buf(NULL, ip, XFS_DIR2_DB_TO_DA(mp, fdb), -1, &fbp,
-                       XFS_DATA_FORK)) {
-               do_error(_("can't read block %u for directory inode %llu\n"),
-                       XFS_DIR2_DB_TO_DA(mp, fdb), ino);
-               /* NOTREACHED */
-       }
-       data = malloc(mp->m_dirblksize);
-       if (!data) {
-               do_error(
-               _("malloc failed in longform_dir2_rebuild_data (%u bytes)\n"),
-                       mp->m_dirblksize);
-               exit(1);
-       }
-       bcopy(bp->data, data, mp->m_dirblksize);
-       ptr = (char *)data->u;
-       if (INT_GET(data->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
-               btp = XFS_DIR2_BLOCK_TAIL_P(mp, (xfs_dir2_block_t *)data);
-               endptr = (char *)XFS_DIR2_BLOCK_LEAF_P(btp);
-       } else
-               endptr = (char *)data + mp->m_dirblksize;
-       fblock = fbp->data;
-       fi = XFS_DIR2_DB_TO_FDINDEX(mp, dbno);
-       tp = libxfs_trans_alloc(mp, 0);
-       error = libxfs_trans_reserve(tp, 0, XFS_CREATE_LOG_RES(mp), 0,
-               XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
-       if (error)
-               res_failed(error);
-       libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
-       libxfs_da_bjoin(tp, bp);
-       libxfs_da_bhold(tp, bp);
-       libxfs_da_bjoin(tp, fbp);
-       libxfs_da_bhold(tp, fbp);
-       XFS_BMAP_INIT(&flist, &firstblock);
-       needlog = needscan = 0;
-       bzero(((xfs_dir2_data_t *)(bp->data))->hdr.bestfree,
-               sizeof(data->hdr.bestfree));
-       libxfs_dir2_data_make_free(tp, bp, 
(xfs_dir2_data_aoff_t)sizeof(data->hdr),
-               mp->m_dirblksize - sizeof(data->hdr), &needlog, &needscan);
-       ASSERT(needscan == 0);
-       libxfs_dir2_data_log_header(tp, bp);
-       INT_SET(fblock->bests[fi], ARCH_CONVERT,
-               INT_GET(((xfs_dir2_data_t 
*)(bp->data))->hdr.bestfree[0].length, ARCH_CONVERT));
-       libxfs_dir2_free_log_bests(tp, fbp, fi, fi);
-       libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-       libxfs_trans_commit(tp, 0, 0);
-
-       while (ptr < endptr) {
-               dup = (xfs_dir2_data_unused_t *)ptr;
-               if (INT_GET(dup->freetag, ARCH_CONVERT) == 
XFS_DIR2_DATA_FREE_TAG) {
-                       ptr += INT_GET(dup->length, ARCH_CONVERT);
-                       continue;
-               }
-               dep = (xfs_dir2_data_entry_t *)ptr;
-               ptr += XFS_DIR2_DATA_ENTSIZE(dep->namelen);
-               if (dep->name[0] == '/')
-                       continue;
-               tp = libxfs_trans_alloc(mp, 0);
-               nres = XFS_CREATE_SPACE_RES(mp, dep->namelen);
-               error = libxfs_trans_reserve(tp, nres, XFS_CREATE_LOG_RES(mp), 
0,
-                       XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
-               if (error)
-                       res_failed(error);
-               libxfs_trans_ijoin(tp, ip, 0);
-               libxfs_trans_ihold(tp, ip);
-               libxfs_da_bjoin(tp, bp);
-               libxfs_da_bhold(tp, bp);
-               libxfs_da_bjoin(tp, fbp);
-               libxfs_da_bhold(tp, fbp);
-               XFS_BMAP_INIT(&flist, &firstblock);
-               error = dir_createname(mp, tp, ip, (char *)dep->name,
-                       dep->namelen, INT_GET(dep->inumber, ARCH_CONVERT),
-                       &firstblock, &flist, nres);
-               ASSERT(error == 0);
-               libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-               libxfs_trans_commit(tp, 0, 0);
-       }
-       libxfs_da_brelse(NULL, bp);
-       libxfs_da_brelse(NULL, fbp);
-       free(data);
-}
-
-/*
- * Finish the rebuild of a directory.
- * Stuff / in and then remove it, this forces the directory to end
- * up in the right format.
- */
-void
-longform_dir2_rebuild_finish(
-       xfs_mount_t             *mp,
-       xfs_ino_t               ino,
-       xfs_inode_t             *ip)
-{
-       int                     committed;
-       int                     error;
-       xfs_fsblock_t           firstblock;
-       xfs_bmap_free_t         flist;
-       int                     nres;
-       xfs_trans_t             *tp;
-
-       tp = libxfs_trans_alloc(mp, 0);
-       nres = XFS_CREATE_SPACE_RES(mp, 1);
-       error = libxfs_trans_reserve(tp, nres, XFS_CREATE_LOG_RES(mp), 0,
-               XFS_TRANS_PERM_LOG_RES, XFS_CREATE_LOG_COUNT);
-       if (error)
-               res_failed(error);
-       libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
-       XFS_BMAP_INIT(&flist, &firstblock);
-       error = dir_createname(mp, tp, ip, "/", 1, ino,
-                       &firstblock, &flist, nres);
-       ASSERT(error == 0);
-       libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-       libxfs_trans_commit(tp, 0, 0);
-
-       /* could kill trailing empty data blocks here */
-
-       tp = libxfs_trans_alloc(mp, 0);
-       nres = XFS_REMOVE_SPACE_RES(mp);
-       error = libxfs_trans_reserve(tp, nres, XFS_REMOVE_LOG_RES(mp), 0,
-               XFS_TRANS_PERM_LOG_RES, XFS_REMOVE_LOG_COUNT);
-       if (error)
-               res_failed(error);
-       libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
-       XFS_BMAP_INIT(&flist, &firstblock);
-       error = dir_removename(mp, tp, ip, "/", 1, ino,
-                       &firstblock, &flist, nres);
-       ASSERT(error == 0);
-       libxfs_bmap_finish(&tp, &flist, firstblock, &committed);
-       libxfs_trans_commit(tp, 0, 0);
-}
-
-/*
- * Rebuild a directory.
- * Remove all the non-data blocks.
- * Re-initialize to (empty) node form.
- * Loop over the data blocks reinserting each entry.
- * Force the directory into the right format.
- */
-void
-longform_dir2_rebuild(
-       xfs_mount_t     *mp,
-       xfs_ino_t       ino,
-       xfs_inode_t     *ip,
-       int             *num_illegal,
-       freetab_t       *freetab,
-       int             isblock)
-{
-       xfs_dabuf_t     *bp;
-       xfs_dablk_t     da_bno;
-       xfs_fileoff_t   next_da_bno;
-
-       do_warn(_("rebuilding directory inode %llu\n"), ino);
-
-       /* kill leaf blocks */
-       for (da_bno = mp->m_dirleafblk, next_da_bno = isblock ? NULLFILEOFF : 0;
-            next_da_bno != NULLFILEOFF;
-            da_bno = (xfs_dablk_t)next_da_bno) {
-               next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
-               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK))
-                       break;
-               if (libxfs_da_get_buf(NULL, ip, da_bno, -1, &bp, 
XFS_DATA_FORK)) {
-                       do_error(_("can't get block %u for directory inode "
-                                  "%llu\n"),
-                               da_bno, ino);
-                       /* NOTREACHED */
-               }
-               dir2_kill_block(mp, ip, da_bno, bp);
-       }
-
-       /* rebuild empty btree and freelist */
-       longform_dir2_rebuild_setup(mp, ino, ip, freetab);
-
-       /* rebuild directory */
-       for (da_bno = mp->m_dirdatablk, next_da_bno = 0;
-            da_bno < mp->m_dirleafblk && next_da_bno != NULLFILEOFF;
-            da_bno = (xfs_dablk_t)next_da_bno) {
-               next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
-               if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK))
-                       break;
-               longform_dir2_rebuild_data(mp, ino, ip, da_bno);
-       }
-
-       /* put the directory in the appropriate on-disk format */
-       longform_dir2_rebuild_finish(mp, ino, ip);
-       *num_illegal = 0;
-}
-
-/*
- * succeeds or dies, inode never gets dirtied since all changes
- * happen in file blocks.  the inode size and other core info
- * is already correct, it's just the leaf entries that get altered.
- * XXX above comment is wrong for v2 - need to see why it matters
+ * If a directory is corrupt, we need to read in as many entries as possible,
+ * destroy the entry and create a new one with recovered name/inode pairs.
+ * (ie. get libxfs to do all the grunt work)
  */
 void
 longform_dir2_entry_check(xfs_mount_t  *mp,
@@ -2810,15 +2519,14 @@ longform_dir2_entry_check(xfs_mount_t   *m
                        dir_stack_t     *stack,
                        ino_tree_node_t *irec,
                        int             ino_offset,
-                       name_hash_tab_t *nametab)
+                       dir_hash_tab_t  *hashtab)
 {
        xfs_dir2_block_t        *block;
        xfs_dir2_leaf_entry_t   *blp;
-       xfs_dabuf_t             *bp;
+       xfs_dabuf_t             **bplist;
        xfs_dir2_block_tail_t   *btp;
        xfs_dablk_t             da_bno;
        freetab_t               *freetab;
-       dir_hash_tab_t          *hashtab;
        int                     i;
        int                     isblock;
        int                     isleaf;
@@ -2840,6 +2548,7 @@ longform_dir2_entry_check(xfs_mount_t     *m
                freetab->ents[i].v = NULLDATAOFF;
                freetab->ents[i].s = 0;
        }
+       bplist = calloc(freetab->naents, sizeof(xfs_dabuf_t*));
        /* is this a block, leaf, or node directory? */
        libxfs_dir2_isblock(NULL, ip, &isblock);
        libxfs_dir2_isleaf(NULL, ip, &isleaf);
@@ -2847,50 +2556,58 @@ longform_dir2_entry_check(xfs_mount_t   *m
        if (do_prefetch && !isblock)
                prefetch_p6_dir2(mp, ip);
 
-       /* check directory data */
-       hashtab = dir_hash_init(ip->i_d.di_size);
+       /* check directory "data" blocks (ie. name/inode pairs) */
        for (da_bno = 0, next_da_bno = 0;
             next_da_bno != NULLFILEOFF && da_bno < mp->m_dirleafblk;
             da_bno = (xfs_dablk_t)next_da_bno) {
                next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
+               ASSERT(XFS_DIR2_DA_TO_DB(mp, da_bno) < freetab->naents);
                if (libxfs_bmap_next_offset(NULL, ip, &next_da_bno, 
XFS_DATA_FORK))
                        break;
-               if (libxfs_da_read_bufr(NULL, ip, da_bno,
-                               da_bno == 0 ? -2 : -1, &bp, XFS_DATA_FORK)) {
-                       do_error(_("can't read block %u for directory inode "
-                                  "%llu\n"),
+               if (libxfs_da_read_bufr(NULL, ip, da_bno, -1, 
+                               &bplist[XFS_DIR2_DA_TO_DB(mp, da_bno)], 
+                               XFS_DATA_FORK)) {
+                       do_warn(_(
+                       "can't read data block %u for directory inode %llu\n"),
                                da_bno, ino);
-                       /* NOTREACHED */
+                       *num_illegal++;
+                       continue;       /* try and read all "data" blocks */
                }
-               /* is there a hole at the start? */
-               if (da_bno == 0 && bp == NULL)
-                       continue;
                longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
-                       stack, irec, ino_offset, &bp, hashtab, &freetab, 
-                       nametab, da_bno, isblock);
-               /* it releases the buffer unless isblock is set */
+                               stack, irec, ino_offset, 
+                               &bplist[XFS_DIR2_DA_TO_DB(mp, da_bno)], 
hashtab,  
+                               &freetab, da_bno, isblock);
        }
        fixit = (*num_illegal != 0) || dir2_is_badino(ino);
 
        /* check btree and freespace */
        if (isblock) {
-               ASSERT(bp);
-               block = bp->data;
+               block = bplist[0]->data;
                btp = XFS_DIR2_BLOCK_TAIL_P(mp, block);
                blp = XFS_DIR2_BLOCK_LEAF_P(btp);
-               seeval = dir_hash_see_all(hashtab, blp, INT_GET(btp->count, 
ARCH_CONVERT), INT_GET(btp->stale,
ARCH_CONVERT));
+               seeval = dir_hash_see_all(hashtab, blp, 
+                               INT_GET(btp->count, ARCH_CONVERT), 
+                               INT_GET(btp->stale, ARCH_CONVERT));
                if (dir_hash_check(hashtab, ip, seeval))
                        fixit |= 1;
-               libxfs_da_brelse(NULL, bp);
        } else if (isleaf) {
                fixit |= longform_dir2_check_leaf(mp, ip, hashtab, freetab);
        } else {
                fixit |= longform_dir2_check_node(mp, ip, hashtab, freetab);
        }
-       dir_hash_done(hashtab);
-       if (!no_modify && fixit)
-               longform_dir2_rebuild(mp, ino, ip, num_illegal, freetab,
-                       isblock);
+       if (!no_modify && fixit) {
+               dir_hash_dup_names(hashtab);
+               for (i = 0; i < freetab->naents; i++) 
+                       if (bplist[i])
+                               libxfs_da_brelse(NULL, bplist[i]);
+               longform_dir2_rebuild(mp, ino, ip, hashtab);
+               *num_illegal = 0;
+       } else {
+               for (i = 0; i < freetab->naents; i++) 
+                       if (bplist[i])
+                               libxfs_da_brelse(NULL, bplist[i]);
+       }
+       
        free(freetab);
 }
 
@@ -2906,7 +2623,7 @@ shortform_dir_entry_check(xfs_mount_t     *m
                        dir_stack_t     *stack,
                        ino_tree_node_t *current_irec,
                        int             current_ino_offset,
-                       name_hash_tab_t *nametab)
+                       dir_hash_tab_t  *hashtab)
 {
        xfs_ino_t               lino;
        xfs_ino_t               parent;
@@ -3044,7 +2761,7 @@ _("entry \"%s\" in shortform dir %llu re
                ASSERT(irec != NULL);
 
                ino_offset = XFS_INO_TO_AGINO(mp, lino) - irec->ino_startnum;
-
+               
                /*
                 * if it's a free inode, blow out the entry.
                 * by now, any inode that we think is free
@@ -3066,8 +2783,9 @@ _("entry \"%s\" in shortform dir inode %
                                do_warn(_("would junk entry \"%s\"\n"),
                                        fname);
                        }
-               } else if (!name_hash_add(nametab, sf_entry->name, 
-                                       sf_entry->namelen)) {
+               } else if (!dir_hash_add(hashtab, 
+                               (xfs_dir2_dataptr_t)(sf_entry - &sf->list[0]),
+                               lino, sf_entry->namelen, sf_entry->name)) {
                        /*
                         * check for duplicate names in directory.
                         */ 
@@ -3311,7 +3029,7 @@ shortform_dir2_entry_check(xfs_mount_t    *
                        dir_stack_t     *stack,
                        ino_tree_node_t *current_irec,
                        int             current_ino_offset,
-                       name_hash_tab_t *nametab)
+                       dir_hash_tab_t  *hashtab)
 {
        xfs_ino_t               lino;
        xfs_ino_t               parent;
@@ -3484,7 +3202,9 @@ shortform_dir2_entry_check(xfs_mount_t    *
                                do_warn(_("would junk entry \"%s\"\n"),
                                        fname);
                        }
-               } else if (!name_hash_add(nametab, sfep->name, sfep->namelen)) {
+               } else if (!dir_hash_add(hashtab, (xfs_dir2_dataptr_t)
+                                       (sfep - XFS_DIR2_SF_FIRSTENTRY(sfp)),
+                               lino, sfep->namelen, sfep->name)) {
                        /*
                         * check for duplicate names in directory.
                         */ 
@@ -3650,7 +3370,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
        xfs_trans_t             *tp;
        xfs_dahash_t            hashval;
        ino_tree_node_t         *irec;
-       name_hash_tab_t         *nametab;
+       dir_hash_tab_t          *hashtab;
        int                     ino_offset, need_dot, committed;
        int                     dirty, num_illegal, error, nres;
 
@@ -3731,7 +3451,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
 
                add_inode_refchecked(ino, irec, ino_offset);
 
-               nametab = name_hash_init(ip->i_d.di_size);
+               hashtab = dir_hash_init(ip->i_d.di_size);
 
                /*
                 * look for bogus entries
@@ -3750,13 +3470,13 @@ process_dirstack(xfs_mount_t *mp, dir_st
                                                        &num_illegal, &need_dot,
                                                        stack, irec,
                                                        ino_offset,
-                                                       nametab);
+                                                       hashtab);
                        else
                                longform_dir_entry_check(mp, ino, ip,
                                                        &num_illegal, &need_dot,
                                                        stack, irec,
                                                        ino_offset,
-                                                       nametab);
+                                                       hashtab);
                        break;
                case XFS_DINODE_FMT_LOCAL:
                        tp = libxfs_trans_alloc(mp, 0);
@@ -3781,12 +3501,12 @@ process_dirstack(xfs_mount_t *mp, dir_st
                                shortform_dir2_entry_check(mp, ino, ip, &dirty,
                                                        stack, irec,
                                                        ino_offset,
-                                                       nametab);
+                                                       hashtab);
                        else
                                shortform_dir_entry_check(mp, ino, ip, &dirty,
                                                        stack, irec,
                                                        ino_offset,
-                                                       nametab);
+                                                       hashtab);
 
                        ASSERT(dirty == 0 || (dirty && !no_modify));
                        if (dirty)  {
@@ -3801,7 +3521,7 @@ process_dirstack(xfs_mount_t *mp, dir_st
                default:
                        break;
                }
-               name_hash_done(nametab);
+               dir_hash_done(hashtab);
 
                hashval = 0;
 



<Prev in Thread] Current Thread [Next in Thread>