]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/dir2.c
xfs_repair: rebuild directory when non-root leafn blocks claim block 0
[thirdparty/xfsprogs-dev.git] / repair / dir2.c
index fbb948456b1f25585eae33c7ccd2bd1f67614bfb..e67ec590c0917698fcab4df412b52cba13a6505f 100644 (file)
@@ -1,40 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
  * All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-#include <libxfs.h>
+#include "libxfs.h"
 #include "avl.h"
 #include "globals.h"
 #include "incore.h"
 #include "err_protos.h"
 #include "dinode.h"
-#include "dir.h"
 #include "dir2.h"
 #include "bmap.h"
+#include "da_util.h"
 #include "prefetch.h"
 #include "progress.h"
 
-/*
- * Tag bad directory entries with this.
- * We can't tag them with -1 since that will look like a
- * data_unused_t instead of a data_entry_t.
- */
-#define        BADFSINO        ((xfs_ino_t)0xfeffffffffffffffULL)
-
 /*
  * Known bad inode list.  These are seen when the leaf and node
  * block linkages are incorrect.
@@ -43,9 +24,10 @@ typedef struct dir2_bad {
        xfs_ino_t       ino;
        struct dir2_bad *next;
 } dir2_bad_t;
-dir2_bad_t *dir2_bad_list;
 
-void
+static dir2_bad_t *dir2_bad_list;
+
+static void
 dir2_add_badlist(
        xfs_ino_t       ino)
 {
@@ -53,7 +35,7 @@ dir2_add_badlist(
 
        if ((l = malloc(sizeof(dir2_bad_t))) == NULL) {
                do_error(
-               _("malloc failed (%u bytes) dir2_add_badlist:ino %llu\n"),
+_("malloc failed (%zu bytes) dir2_add_badlist:ino %" PRIu64 "\n"),
                        sizeof(dir2_bad_t), ino);
                exit(1);
        }
@@ -74,685 +56,6 @@ dir2_is_badino(
        return 0;
 }
 
-/*
- * Multibuffer handling.
- * V2 directory blocks can be noncontiguous, needing multiple buffers.
- */
-xfs_dabuf_t *
-da_read_buf(
-       xfs_mount_t     *mp,
-       int             nex,
-       bmap_ext_t      *bmp)
-{
-       xfs_buf_t       *bp;
-       xfs_buf_t       *bparray[4];
-       xfs_buf_t       **bplist;
-       xfs_dabuf_t     *dabuf;
-       int             i;
-       int             off;
-
-       if (nex > (sizeof(bparray)/sizeof(xfs_buf_t *))) {
-               bplist = calloc(nex, sizeof(*bplist));
-               if (bplist == NULL) {
-                       do_error(_("couldn't malloc dir2 buffer list\n"));
-                       exit(1);
-               }
-       }
-       else {
-               /* common case avoids calloc/free */
-               bplist = bparray;
-       }
-       for (i = 0; i < nex; i++) {
-               pftrace("about to read off %llu (len = %d)",
-                       (long long)XFS_FSB_TO_DADDR(mp, bmp[i].startblock),
-                       XFS_FSB_TO_BB(mp, bmp[i].blockcount));
-
-               bplist[i] = libxfs_readbuf(mp->m_dev,
-                               XFS_FSB_TO_DADDR(mp, bmp[i].startblock),
-                               XFS_FSB_TO_BB(mp, bmp[i].blockcount), 0);
-               if (!bplist[i]) {
-                       nex = i;
-                       goto failed;
-               }
-
-               pftrace("readbuf %p (%llu, %d)", bplist[i],
-                       (long long)XFS_BUF_ADDR(bplist[i]),
-                       XFS_BUF_COUNT(bplist[i]));
-       }
-       dabuf = malloc(XFS_DA_BUF_SIZE(nex));
-       if (dabuf == NULL) {
-               do_error(_("couldn't malloc dir2 buffer header\n"));
-               exit(1);
-       }
-       dabuf->dirty = 0;
-       dabuf->nbuf = nex;
-       if (nex == 1) {
-               bp = bplist[0];
-               dabuf->bbcount = (short)BTOBB(XFS_BUF_COUNT(bp));
-               dabuf->data = XFS_BUF_PTR(bp);
-               dabuf->bps[0] = bp;
-       } else {
-               for (i = 0, dabuf->bbcount = 0; i < nex; i++) {
-                       dabuf->bps[i] = bp = bplist[i];
-                       dabuf->bbcount += BTOBB(XFS_BUF_COUNT(bp));
-               }
-               dabuf->data = malloc(BBTOB(dabuf->bbcount));
-               if (dabuf->data == NULL) {
-                       do_error(_("couldn't malloc dir2 buffer data\n"));
-                       exit(1);
-               }
-               for (i = off = 0; i < nex; i++, off += XFS_BUF_COUNT(bp)) {
-                       bp = bplist[i];
-                       memmove((char *)dabuf->data + off, XFS_BUF_PTR(bp),
-                               XFS_BUF_COUNT(bp));
-               }
-       }
-       if (bplist != bparray)
-               free(bplist);
-       return dabuf;
-failed:
-       for (i = 0; i < nex; i++)
-               libxfs_putbuf(bplist[i]);
-       if (bplist != bparray)
-               free(bplist);
-       return NULL;
-}
-
-static void
-da_buf_clean(
-       xfs_dabuf_t     *dabuf)
-{
-       xfs_buf_t       *bp;
-       int             i;
-       int             off;
-
-       if (dabuf->dirty) {
-               dabuf->dirty = 0;
-               for (i=off=0; i < dabuf->nbuf; i++, off += XFS_BUF_COUNT(bp)) {
-                       bp = dabuf->bps[i];
-                       memmove(XFS_BUF_PTR(bp), (char *)dabuf->data + off,
-                               XFS_BUF_COUNT(bp));
-               }
-       }
-}
-
-static void
-da_buf_done(
-       xfs_dabuf_t     *dabuf)
-{
-       da_buf_clean(dabuf);
-       if (dabuf->nbuf > 1)
-               free(dabuf->data);
-       free(dabuf);
-}
-
-int
-da_bwrite(
-       xfs_mount_t     *mp,
-       xfs_dabuf_t     *dabuf)
-{
-       xfs_buf_t       *bp;
-       xfs_buf_t       **bplist;
-       int             e;
-       int             error;
-       int             i;
-       int             nbuf;
-       int             off;
-
-       if ((nbuf = dabuf->nbuf) == 1) {
-               bplist = &bp;
-               bp = dabuf->bps[0];
-       } else {
-               bplist = malloc(nbuf * sizeof(*bplist));
-               if (bplist == NULL) {
-                       do_error(_("couldn't malloc dir2 buffer list\n"));
-                       exit(1);
-               }
-               memmove(bplist, dabuf->bps, nbuf * sizeof(*bplist));
-               for (i = off = 0; i < nbuf; i++, off += XFS_BUF_COUNT(bp)) {
-                       bp = bplist[i];
-                       memmove(XFS_BUF_PTR(bp), (char *)dabuf->data + off,
-                               XFS_BUF_COUNT(bp));
-               }
-       }
-       da_buf_done(dabuf);
-       for (i = error = 0; i < nbuf; i++) {
-               e = libxfs_writebuf(bplist[i], 0);
-               if (e)
-                       error = e;
-       }
-       if (bplist != &bp)
-               free(bplist);
-       return error;
-}
-
-void
-da_brelse(
-       xfs_dabuf_t     *dabuf)
-{
-       xfs_buf_t       *bp;
-       xfs_buf_t       **bplist;
-       int             i;
-       int             nbuf;
-
-       if ((nbuf = dabuf->nbuf) == 1) {
-               bplist = &bp;
-               bp = dabuf->bps[0];
-       } else {
-               bplist = malloc(nbuf * sizeof(*bplist));
-               if (bplist == NULL) {
-                       do_error(_("couldn't malloc dir2 buffer list\n"));
-                       exit(1);
-               }
-               memmove(bplist, dabuf->bps, nbuf * sizeof(*bplist));
-       }
-       da_buf_done(dabuf);
-       for (i = 0; i < nbuf; i++) {
-               pftrace("putbuf %p (%llu)", bplist[i],
-                                       (long long)XFS_BUF_ADDR(bplist[i]));
-               libxfs_putbuf(bplist[i]);
-       }
-       if (bplist != &bp)
-               free(bplist);
-}
-
-/*
- * walk tree from root to the left-most leaf block reading in
- * blocks and setting up cursor.  passes back file block number of the
- * left-most leaf block if successful (bno).  returns 1 if successful,
- * 0 if unsuccessful.
- */
-int
-traverse_int_dir2block(xfs_mount_t     *mp,
-               dir2_bt_cursor_t        *da_cursor,
-               xfs_dablk_t             *rbno)
-{
-       bmap_ext_t              *bmp;
-       xfs_dablk_t             bno;
-       xfs_dabuf_t             *bp;
-       int                     i;
-       int                     nex;
-       xfs_da_blkinfo_t        *info;
-       xfs_da_intnode_t        *node;
-       bmap_ext_t              lbmp;
-
-       /*
-        * traverse down left-side of tree until we hit the
-        * left-most leaf block setting up the btree cursor along
-        * the way.
-        */
-       bno = mp->m_dirleafblk;
-       i = -1;
-       info = NULL;
-       da_cursor->active = 0;
-
-       do {
-               /*
-                * read in each block along the way and set up cursor
-                */
-               nex = blkmap_getn(da_cursor->blkmap, bno, mp->m_dirblkfsbs,
-                               &bmp, &lbmp);
-
-               if (nex == 0)
-                       goto error_out;
-
-               bp = da_read_buf(mp, nex, bmp);
-               if (bmp != &lbmp)
-                       free(bmp);
-               if (bp == NULL) {
-                       do_warn(_("can't read block %u for directory inode "
-                                 "%llu\n"),
-                               bno, da_cursor->ino);
-                       goto error_out;
-               }
-
-               info = bp->data;
-
-               if (be16_to_cpu(info->magic) == XFS_DIR2_LEAFN_MAGIC)  {
-                       if ( i != -1 ) {
-                               do_warn(_("found non-root LEAFN node in inode "
-                                         "%llu bno = %u\n"),
-                                       da_cursor->ino, bno);
-                       }
-                       *rbno = 0;
-                       da_brelse(bp);
-                       return(1);
-               } else if (be16_to_cpu(info->magic) != XFS_DA_NODE_MAGIC)  {
-                       da_brelse(bp);
-                       do_warn(_("bad dir magic number 0x%x in inode %llu "
-                                 "bno = %u\n"),
-                               be16_to_cpu(info->magic),
-                                       da_cursor->ino, bno);
-                       goto error_out;
-               }
-               node = (xfs_da_intnode_t*)info;
-               if (be16_to_cpu(node->hdr.count) > mp->m_dir_node_ents)  {
-                       da_brelse(bp);
-                       do_warn(_("bad record count in inode %llu, count = %d, "
-                                 "max = %d\n"), da_cursor->ino,
-                               be16_to_cpu(node->hdr.count),
-                               mp->m_dir_node_ents);
-                       goto error_out;
-               }
-               /*
-                * maintain level counter
-                */
-               if (i == -1) {
-                       i = da_cursor->active = be16_to_cpu(node->hdr.level);
-                       if (i >= XFS_DA_NODE_MAXDEPTH) {
-                               do_warn(_("bad header depth for directory "
-                                         "inode %llu\n"),
-                                       da_cursor->ino);
-                               da_brelse(bp);
-                               i = -1;
-                               goto error_out;
-                       }
-               } else {
-                       if (be16_to_cpu(node->hdr.level) == i - 1)  {
-                               i--;
-                       } else  {
-                               do_warn(_("bad directory btree for directory "
-                                         "inode %llu\n"),
-                                       da_cursor->ino);
-                               da_brelse(bp);
-                               goto error_out;
-                       }
-               }
-
-               da_cursor->level[i].hashval =
-                                       be32_to_cpu(node->btree[0].hashval);
-               da_cursor->level[i].bp = bp;
-               da_cursor->level[i].bno = bno;
-               da_cursor->level[i].index = 0;
-
-               /*
-                * set up new bno for next level down
-                */
-               bno = be32_to_cpu(node->btree[0].before);
-       } while (info != NULL && i > 1);
-
-       /*
-        * now return block number and get out
-        */
-       *rbno = da_cursor->level[0].bno = bno;
-       return(1);
-
-error_out:
-       while (i > 1 && i <= da_cursor->active)  {
-               da_brelse(da_cursor->level[i].bp);
-               i++;
-       }
-
-       return(0);
-}
-
-/*
- * blow out buffer for this level and all the rest above as well
- * if error == 0, we are not expecting to encounter any unreleased
- * buffers (e.g. if we do, it's a mistake).  if error == 1, we're
- * in an error-handling case so unreleased buffers may exist.
- */
-void
-release_dir2_cursor_int(xfs_mount_t            *mp,
-                       dir2_bt_cursor_t        *cursor,
-                       int                     prev_level,
-                       int                     error)
-{
-       int     level = prev_level + 1;
-
-       if (cursor->level[level].bp != NULL)  {
-               if (!error)  {
-                       do_warn(_("release_dir2_cursor_int got unexpected "
-                                 "non-null bp, dabno = %u\n"),
-                               cursor->level[level].bno);
-               }
-               ASSERT(error != 0);
-
-               da_brelse(cursor->level[level].bp);
-               cursor->level[level].bp = NULL;
-       }
-
-       if (level < cursor->active)
-               release_dir2_cursor_int(mp, cursor, level, error);
-
-       return;
-}
-
-void
-release_dir2_cursor(xfs_mount_t                *mp,
-               dir2_bt_cursor_t        *cursor,
-               int                     prev_level)
-{
-       release_dir2_cursor_int(mp, cursor, prev_level, 0);
-}
-
-void
-err_release_dir2_cursor(xfs_mount_t            *mp,
-                       dir2_bt_cursor_t        *cursor,
-                       int                     prev_level)
-{
-       release_dir2_cursor_int(mp, cursor, prev_level, 1);
-}
-
-/*
- * make sure that all entries in all blocks along the right side of
- * of the tree are used and hashval's are consistent.  level is the
- * level of the descendent block.  returns 0 if good (even if it had
- * to be fixed up), and 1 if bad.  The right edge of the tree is
- * technically a block boundary.  This routine should be used then
- * instead of verify_dir2_path().
- */
-int
-verify_final_dir2_path(xfs_mount_t     *mp,
-               dir2_bt_cursor_t        *cursor,
-               const int               p_level)
-{
-       xfs_da_intnode_t        *node;
-       int                     bad = 0;
-       int                     entry;
-       int                     this_level = p_level + 1;
-
-       /*
-        * the index should point to the next "unprocessed" entry
-        * in the block which should be the final (rightmost) entry
-        */
-       entry = cursor->level[this_level].index;
-       node = (xfs_da_intnode_t *)(cursor->level[this_level].bp->data);
-       /*
-        * check internal block consistency on this level -- ensure
-        * that all entries are used, encountered and expected hashvals
-        * match, etc.
-        */
-       if (entry != be16_to_cpu(node->hdr.count) - 1)  {
-               do_warn(
-               _("directory block used/count inconsistency - %d / %hu\n"),
-                       entry, be16_to_cpu(node->hdr.count));
-               bad++;
-       }
-       /*
-        * hash values monotonically increasing ???
-        */
-       if (cursor->level[this_level].hashval >=
-                               be32_to_cpu(node->btree[entry].hashval))  {
-               do_warn(_("directory/attribute block hashvalue inconsistency, "
-                         "expected > %u / saw %u\n"),
-                       cursor->level[this_level].hashval,
-                       be32_to_cpu(node->btree[entry].hashval));
-               bad++;
-       }
-       if (be32_to_cpu(node->hdr.info.forw) != 0)  {
-               do_warn(_("bad directory/attribute forward block pointer, "
-                         "expected 0, saw %u\n"),
-                       be32_to_cpu(node->hdr.info.forw));
-               bad++;
-       }
-       if (bad)  {
-               do_warn(_("bad directory block in inode %llu\n"), cursor->ino);
-               return(1);
-       }
-       /*
-        * keep track of greatest block # -- that gets
-        * us the length of the directory
-        */
-       if (cursor->level[this_level].bno > cursor->greatest_bno)
-               cursor->greatest_bno = cursor->level[this_level].bno;
-
-       /*
-        * ok, now check descendant block number against this level
-        */
-       if (cursor->level[p_level].bno !=
-                               be32_to_cpu(node->btree[entry].before))
-               return(1);
-
-       if (cursor->level[p_level].hashval !=
-                               be32_to_cpu(node->btree[entry].hashval))  {
-               if (!no_modify)  {
-                       do_warn(_("correcting bad hashval in non-leaf dir "
-                                 "block\n\tin (level %d) in inode %llu.\n"),
-                               this_level, cursor->ino);
-                       node->btree[entry].hashval = cpu_to_be32(
-                                               cursor->level[p_level].hashval);
-                       cursor->level[this_level].dirty++;
-               } else  {
-                       do_warn(_("would correct bad hashval in non-leaf dir "
-                                 "block\n\tin (level %d) in inode %llu.\n"),
-                               this_level, cursor->ino);
-               }
-       }
-
-       /*
-        * release/write buffer
-        */
-       ASSERT(cursor->level[this_level].dirty == 0 ||
-               (cursor->level[this_level].dirty && !no_modify));
-
-       if (cursor->level[this_level].dirty && !no_modify)
-               da_bwrite(mp, cursor->level[this_level].bp);
-       else
-               da_brelse(cursor->level[this_level].bp);
-
-       cursor->level[this_level].bp = NULL;
-
-       /*
-        * bail out if this is the root block (top of tree)
-        */
-       if (this_level >= cursor->active)
-               return(0);
-       /*
-        * set hashvalue to correctl reflect the now-validated
-        * last entry in this block and continue upwards validation
-        */
-       cursor->level[this_level].hashval =
-               be32_to_cpu(node->btree[entry].hashval);
-
-       return(verify_final_dir2_path(mp, cursor, this_level));
-}
-
-/*
- * Verifies the path from a descendant block up to the root.
- * Should be called when the descendant level traversal hits
- * a block boundary before crossing the boundary (reading in a new
- * block).
- *
- * the directory/attr btrees work differently to the other fs btrees.
- * each interior block contains records that are <hashval, bno>
- * pairs.  The bno is a file bno, not a filesystem bno.  The last
- * hashvalue in the block <bno> will be <hashval>.  BUT unlike
- * the freespace btrees, the *last* value in each block gets
- * propagated up the tree instead of the first value in each block.
- * that is, the interior records point to child blocks and the *greatest*
- * hash value contained by the child block is the one the block above
- * uses as the key for the child block.
- *
- * level is the level of the descendent block.  returns 0 if good,
- * and 1 if bad.  The descendant block may be a leaf block.
- *
- * the invariant here is that the values in the cursor for the
- * levels beneath this level (this_level) and the cursor index
- * for this level *must* be valid.
- *
- * that is, the hashval/bno info is accurate for all
- * DESCENDANTS and match what the node[index] information
- * for the current index in the cursor for this level.
- *
- * the index values in the cursor for the descendant level
- * are allowed to be off by one as they will reflect the
- * next entry at those levels to be processed.
- *
- * the hashvalue for the current level can't be set until
- * we hit the last entry in the block so, it's garbage
- * until set by this routine.
- *
- * bno and bp for the current block/level are always valid
- * since they have to be set so we can get a buffer for the
- * block.
- */
-int
-verify_dir2_path(xfs_mount_t   *mp,
-       dir2_bt_cursor_t        *cursor,
-       const int               p_level)
-{
-       xfs_da_intnode_t        *node;
-       xfs_da_intnode_t        *newnode;
-       xfs_dablk_t             dabno;
-       xfs_dabuf_t             *bp;
-       int                     bad;
-       int                     entry;
-       int                     this_level = p_level + 1;
-       bmap_ext_t              *bmp;
-       int                     nex;
-       bmap_ext_t              lbmp;
-
-       /*
-        * index is currently set to point to the entry that
-        * should be processed now in this level.
-        */
-       entry = cursor->level[this_level].index;
-       node = cursor->level[this_level].bp->data;
-
-       /*
-        * if this block is out of entries, validate this
-        * block and move on to the next block.
-        * and update cursor value for said level
-        */
-       if (entry >= be16_to_cpu(node->hdr.count))  {
-               /*
-                * update the hash value for this level before
-                * validating it.  bno value should be ok since
-                * it was set when the block was first read in.
-                */
-               cursor->level[this_level].hashval =
-                       be32_to_cpu(node->btree[entry - 1].hashval);
-
-               /*
-                * keep track of greatest block # -- that gets
-                * us the length of the directory
-                */
-               if (cursor->level[this_level].bno > cursor->greatest_bno)
-                       cursor->greatest_bno = cursor->level[this_level].bno;
-
-               /*
-                * validate the path for the current used-up block
-                * before we trash it
-                */
-               if (verify_dir2_path(mp, cursor, this_level))
-                       return(1);
-               /*
-                * ok, now get the next buffer and check sibling pointers
-                */
-               dabno = be32_to_cpu(node->hdr.info.forw);
-               ASSERT(dabno != 0);
-               nex = blkmap_getn(cursor->blkmap, dabno, mp->m_dirblkfsbs,
-                       &bmp, &lbmp);
-               if (nex == 0) {
-                       do_warn(_("can't get map info for block %u of "
-                                 "directory inode %llu\n"),
-                               dabno, cursor->ino);
-                       return(1);
-               }
-
-               bp = da_read_buf(mp, nex, bmp);
-               if (bmp != &lbmp)
-                       free(bmp);
-
-               if (bp == NULL) {
-                       do_warn(_("can't read block %u for directory inode "
-                                 "%llu\n"),
-                               dabno, cursor->ino);
-                       return(1);
-               }
-
-               newnode = bp->data;
-               /*
-                * verify magic number and back pointer, sanity-check
-                * entry count, verify level
-                */
-               bad = 0;
-               if (XFS_DA_NODE_MAGIC != be16_to_cpu(newnode->hdr.info.magic)) {
-                       do_warn(_("bad magic number %x in block %u for "
-                                 "directory inode %llu\n"),
-                               be16_to_cpu(newnode->hdr.info.magic),
-                               dabno, cursor->ino);
-                       bad++;
-               }
-               if (be32_to_cpu(newnode->hdr.info.back) !=
-                                       cursor->level[this_level].bno)  {
-                       do_warn(_("bad back pointer in block %u for directory "
-                                 "inode %llu\n"),
-                               dabno, cursor->ino);
-                       bad++;
-               }
-               if (be16_to_cpu(newnode->hdr.count) > mp->m_dir_node_ents)  {
-                       do_warn(_("entry count %d too large in block %u for "
-                                 "directory inode %llu\n"),
-                               be16_to_cpu(newnode->hdr.count),
-                               dabno, cursor->ino);
-                       bad++;
-               }
-               if (be16_to_cpu(newnode->hdr.level) != this_level)  {
-                       do_warn(_("bad level %d in block %u for directory "
-                                 "inode %llu\n"),
-                               be16_to_cpu(newnode->hdr.level),
-                               dabno, cursor->ino);
-                       bad++;
-               }
-               if (bad)  {
-                       da_brelse(bp);
-                       return(1);
-               }
-               /*
-                * update cursor, write out the *current* level if
-                * required.  don't write out the descendant level
-                */
-               ASSERT(cursor->level[this_level].dirty == 0 ||
-                       (cursor->level[this_level].dirty && !no_modify));
-
-               if (cursor->level[this_level].dirty && !no_modify)
-                       da_bwrite(mp, cursor->level[this_level].bp);
-               else
-                       da_brelse(cursor->level[this_level].bp);
-               cursor->level[this_level].bp = bp;
-               cursor->level[this_level].dirty = 0;
-               cursor->level[this_level].bno = dabno;
-               cursor->level[this_level].hashval =
-                       be32_to_cpu(newnode->btree[0].hashval);
-               node = newnode;
-
-               entry = cursor->level[this_level].index = 0;
-       }
-       /*
-        * ditto for block numbers
-        */
-       if (cursor->level[p_level].bno !=
-                               be32_to_cpu(node->btree[entry].before))
-               return(1);
-       /*
-        * ok, now validate last hashvalue in the descendant
-        * block against the hashval in the current entry
-        */
-       if (cursor->level[p_level].hashval !=
-                               be32_to_cpu(node->btree[entry].hashval))  {
-               if (!no_modify)  {
-                       do_warn(_("correcting bad hashval in interior dir "
-                                 "block\n\tin (level %d) in inode %llu.\n"),
-                               this_level, cursor->ino);
-                       node->btree[entry].hashval = cpu_to_be32(
-                                       cursor->level[p_level].hashval);
-                       cursor->level[this_level].dirty++;
-               } else  {
-                       do_warn(_("would correct bad hashval in interior dir "
-                                 "block\n\tin (level %d) in inode %llu.\n"),
-                               this_level, cursor->ino);
-               }
-       }
-       /*
-        * increment index for this level to point to next entry
-        * (which should point to the next descendant block)
-        */
-       cursor->level[this_level].index++;
-       return(0);
-}
-
 /*
  * Fix up a shortform directory which was in long form (i8count set)
  * and is now in short form (i8count clear).
@@ -760,28 +63,29 @@ verify_dir2_path(xfs_mount_t       *mp,
  */
 void
 process_sf_dir2_fixi8(
-       xfs_dir2_sf_t           *sfp,
+       struct xfs_mount        *mp,
+       struct xfs_dir2_sf_hdr  *sfp,
        xfs_dir2_sf_entry_t     **next_sfep)
 {
        xfs_ino_t               ino;
-       xfs_dir2_sf_t           *newsfp;
+       struct xfs_dir2_sf_hdr  *newsfp;
        xfs_dir2_sf_entry_t     *newsfep;
-       xfs_dir2_sf_t           *oldsfp;
+       struct xfs_dir2_sf_hdr  *oldsfp;
        xfs_dir2_sf_entry_t     *oldsfep;
        int                     oldsize;
 
        newsfp = sfp;
-       oldsize = (__psint_t)*next_sfep - (__psint_t)sfp;
+       oldsize = (intptr_t)*next_sfep - (intptr_t)sfp;
        oldsfp = malloc(oldsize);
        if (oldsfp == NULL) {
                do_error(_("couldn't malloc dir2 shortform copy\n"));
                exit(1);
        }
        memmove(oldsfp, newsfp, oldsize);
-       newsfp->hdr.count = oldsfp->hdr.count;
-       newsfp->hdr.i8count = 0;
-       ino = xfs_dir2_sf_get_inumber(oldsfp, &oldsfp->hdr.parent);
-       xfs_dir2_sf_put_inumber(newsfp, &ino, &newsfp->hdr.parent);
+       newsfp->count = oldsfp->count;
+       newsfp->i8count = 0;
+       ino = M_DIROPS(mp)->sf_get_parent_ino(sfp);
+       M_DIROPS(mp)->sf_put_parent_ino(newsfp, ino);
        oldsfep = xfs_dir2_sf_firstentry(oldsfp);
        newsfep = xfs_dir2_sf_firstentry(newsfp);
        while ((int)((char *)oldsfep - (char *)oldsfp) < oldsize) {
@@ -789,12 +93,10 @@ process_sf_dir2_fixi8(
                xfs_dir2_sf_put_offset(newsfep,
                        xfs_dir2_sf_get_offset(oldsfep));
                memmove(newsfep->name, oldsfep->name, newsfep->namelen);
-               ino = xfs_dir2_sf_get_inumber(oldsfp,
-                       xfs_dir2_sf_inumberp(oldsfep));
-               xfs_dir2_sf_put_inumber(newsfp, &ino,
-                       xfs_dir2_sf_inumberp(newsfep));
-               oldsfep = xfs_dir2_sf_nextentry(oldsfp, oldsfep);
-               newsfep = xfs_dir2_sf_nextentry(newsfp, newsfep);
+               ino = M_DIROPS(mp)->sf_get_ino(oldsfp, oldsfep);
+               M_DIROPS(mp)->sf_put_ino(newsfp, newsfep, ino);
+               oldsfep = M_DIROPS(mp)->sf_nextentry(oldsfp, oldsfep);
+               newsfep = M_DIROPS(mp)->sf_nextentry(newsfp, newsfep);
        }
        *next_sfep = newsfep;
        free(oldsfp);
@@ -805,21 +107,22 @@ process_sf_dir2_fixi8(
  */
 static void
 process_sf_dir2_fixoff(
+       xfs_mount_t     *mp,
        xfs_dinode_t    *dip)
 {
        int                     i;
        int                     offset;
        xfs_dir2_sf_entry_t     *sfep;
-       xfs_dir2_sf_t           *sfp;
+       struct xfs_dir2_sf_hdr  *sfp;
 
-       sfp = (xfs_dir2_sf_t *)XFS_DFORK_DPTR(dip);
+       sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip);
        sfep = xfs_dir2_sf_firstentry(sfp);
-       offset = XFS_DIR2_DATA_FIRST_OFFSET;
+       offset = M_DIROPS(mp)->data_first_offset;
 
-       for (i = 0; i < sfp->hdr.count; i++) {
+       for (i = 0; i < sfp->count; i++) {
                xfs_dir2_sf_put_offset(sfep, offset);
-               offset += xfs_dir2_data_entsize(sfep->namelen);
-               sfep = xfs_dir2_sf_nextentry(sfp, sfep);
+               offset += M_DIROPS(mp)->data_entsize(sfep->namelen);
+               sfep = M_DIROPS(mp)->sf_nextentry(sfp, sfep);
        }
 }
 
@@ -845,7 +148,7 @@ process_sf_dir2(
        int                     bad_sfnamelen;
        int                     i;
        int                     i8;
-       __int64_t               ino_dir_size;
+       int64_t                 ino_dir_size;
        int                     ino_off;
        ino_tree_node_t         *irec_p;
        int                     junkit;
@@ -857,18 +160,18 @@ process_sf_dir2(
        xfs_dir2_sf_entry_t     *next_sfep;
        int                     num_entries;
        int                     offset;
-       xfs_dir2_sf_t           *sfp;
+       struct xfs_dir2_sf_hdr  *sfp;
        xfs_dir2_sf_entry_t     *sfep;
        int                     tmp_elen;
        int                     tmp_len;
        xfs_dir2_sf_entry_t     *tmp_sfep;
        xfs_ino_t               zero = 0;
 
-       sfp = (xfs_dir2_sf_t *)XFS_DFORK_DPTR(dip);
+       sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip);
        max_size = XFS_DFORK_DSIZE(dip, mp);
-       num_entries = sfp->hdr.count;
+       num_entries = sfp->count;
        ino_dir_size = be64_to_cpu(dip->di_size);
-       offset = XFS_DIR2_DATA_FIRST_OFFSET;
+       offset = M_DIROPS(mp)->data_first_offset;
        bad_offset = *repair = 0;
 
        ASSERT(ino_dir_size <= max_size);
@@ -876,13 +179,12 @@ process_sf_dir2(
        /*
         * Initialize i8 based on size of parent inode number.
         */
-       i8 = (xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent)
-               > XFS_DIR2_MAX_SHORT_INUM);
+       i8 = (M_DIROPS(mp)->sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM);
 
        /*
         * check for bad entry count
         */
-       if (num_entries * xfs_dir2_sf_entsize_byname(sfp, 1) +
+       if (num_entries * M_DIROPS(mp)->sf_entsize(sfp, 1) +
                    xfs_dir2_sf_hdr_size(0) > max_size || num_entries == 0)
                num_entries = 0xFF;
 
@@ -890,7 +192,7 @@ process_sf_dir2(
         * run through entries, stop at first bad entry, don't need
         * to check for .. since that's encoded in its own field
         */
-       sfep = next_sfep = xfs_dir2_sf_firstentry(sfp);
+       next_sfep = xfs_dir2_sf_firstentry(sfp);
        for (i = 0;
             i < num_entries && ino_dir_size > (char *)next_sfep - (char *)sfp;
             i++) {
@@ -898,7 +200,7 @@ process_sf_dir2(
                sfep = next_sfep;
                junkit = 0;
                bad_sfnamelen = 0;
-               lino = xfs_dir2_sf_get_inumber(sfp, xfs_dir2_sf_inumberp(sfep));
+               lino = M_DIROPS(mp)->sf_get_ino(sfp, sfep);
                /*
                 * if entry points to self, junk it since only '.' or '..'
                 * should do that and shortform dirs don't contain either
@@ -928,6 +230,9 @@ process_sf_dir2(
                } else if (lino == mp->m_sb.sb_gquotino)  {
                        junkit = 1;
                        junkreason = _("group quota");
+               } else if (lino == mp->m_sb.sb_pquotino)  {
+                       junkit = 1;
+                       junkreason = _("project quota");
                } else if ((irec_p = find_inode_rec(mp,
                                        XFS_INO_TO_AGNO(mp, lino),
                                        XFS_INO_TO_AGINO(mp, lino))) != NULL) {
@@ -966,100 +271,37 @@ process_sf_dir2(
                }
                namelen = sfep->namelen;
                if (junkit)
-                       do_warn(_("entry \"%*.*s\" in shortform directory %llu "
-                                 "references %s inode %llu\n"),
+                       do_warn(
+_("entry \"%*.*s\" in shortform directory %" PRIu64 " references %s inode %" PRIu64 "\n"),
                                namelen, namelen, sfep->name, ino, junkreason,
                                lino);
-               if (namelen == 0)  {
-                       /*
-                        * if we're really lucky, this is
-                        * the last entry in which case we
-                        * can use the dir size to set the
-                        * namelen value.  otherwise, forget
-                        * it because we're not going to be
-                        * able to find the next entry.
-                        */
-                       bad_sfnamelen = 1;
 
-                       if (i == num_entries - 1)  {
-                               namelen = ino_dir_size -
-                                       ((__psint_t) &sfep->name[0] -
-                                        (__psint_t) sfp);
-                               if (!no_modify)  {
-                                       do_warn(_("zero length entry in "
-                                                 "shortform dir %llu, "
-                                                 "resetting to %d\n"),
-                                               ino, namelen);
-                                       sfep->namelen = namelen;
-                               } else  {
-                                       do_warn(_("zero length entry in "
-                                                 "shortform dir %llu, "
-                                                 "would set to %d\n"),
-                                               ino, namelen);
-                               }
-                       } else  {
-                               do_warn(_("zero length entry in shortform dir "
-                                         "%llu"),
-                                       ino);
-                               if (!no_modify)
-                                       do_warn(_(", junking %d entries\n"),
-                                               num_entries - i);
-                               else
-                                       do_warn(_(", would junk %d entries\n"),
-                                               num_entries - i);
-                               /*
-                                * don't process the rest of the directory,
-                                * break out of processing looop
-                                */
-                               break;
-                       }
-               } else if ((__psint_t) sfep - (__psint_t) sfp +
-                               xfs_dir2_sf_entsize_byentry(sfp, sfep)
+               /* is dir namelen 0 or does this entry extend past dir size? */
+               if (namelen == 0) {
+                       junkreason = _("is zero length");
+                       bad_sfnamelen = 1;
+               } else if ((intptr_t) sfep - (intptr_t) sfp +
+                               M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen)
                                                        > ino_dir_size)  {
+                       junkreason = _("extends past end of dir");
                        bad_sfnamelen = 1;
+               }
 
-                       if (i == num_entries - 1)  {
-                               namelen = ino_dir_size -
-                                       ((__psint_t) &sfep->name[0] -
-                                        (__psint_t) sfp);
-                               do_warn(_("size of last entry overflows space "
-                                         "left in in shortform dir %llu, "),
-                                       ino);
-                               if (!no_modify)  {
-                                       do_warn(_("resetting to %d\n"),
-                                               namelen);
-                                       sfep->namelen = namelen;
-                                       *dino_dirty = 1;
-                               } else  {
-                                       do_warn(_("would reset to %d\n"),
-                                               namelen);
-                               }
-                       } else  {
-                               do_warn(_("size of entry #%d overflows space "
-                                         "left in in shortform dir %llu\n"),
-                                       i, ino);
-                               if (!no_modify)  {
-                                       if (i == num_entries - 1)
-                                               do_warn(
-                                               _("junking entry #%d\n"),
-                                                       i);
-                                       else
-                                               do_warn(
-                                               _("junking %d entries\n"),
-                                                       num_entries - i);
-                               } else  {
-                                       if (i == num_entries - 1)
-                                               do_warn(
-                                               _("would junk entry #%d\n"),
-                                                       i);
-                                       else
-                                               do_warn(
-                                               _("would junk %d entries\n"),
-                                                       num_entries - i);
-                               }
-
-                               break;
-                       }
+               if (bad_sfnamelen) {
+                       do_warn(
+_("entry #%d %s in shortform dir %" PRIu64),
+                               i, junkreason, ino);
+                       if (!no_modify)
+                               do_warn(_(", junking %d entries\n"),
+                                       num_entries - i);
+                       else
+                               do_warn(_(", would junk %d entries\n"),
+                                       num_entries - i);
+                       /*
+                        * don't process the rest of the directory,
+                        * break out of processing loop
+                        */
+                       break;
                }
 
                /*
@@ -1072,20 +314,20 @@ process_sf_dir2(
                        /*
                         * junk entry
                         */
-                       do_warn(_("entry contains illegal character "
-                                 "in shortform dir %llu\n"),
+                       do_warn(
+_("entry contains illegal character in shortform dir %" PRIu64 "\n"),
                                ino);
                        junkit = 1;
                }
 
                if (xfs_dir2_sf_get_offset(sfep) < offset) {
-                       do_warn(_("entry contains offset out of order in "
-                                 "shortform dir %llu\n"),
+                       do_warn(
+_("entry contains offset out of order in shortform dir %" PRIu64 "\n"),
                                ino);
                        bad_offset = 1;
                }
                offset = xfs_dir2_sf_get_offset(sfep) +
-                                               xfs_dir2_data_entsize(namelen);
+                                       M_DIROPS(mp)->data_entsize(namelen);
 
                /*
                 * junk the entry by copying up the rest of the
@@ -1102,21 +344,21 @@ process_sf_dir2(
                        name[namelen] = '\0';
 
                        if (!no_modify)  {
-                               tmp_elen =
-                                       xfs_dir2_sf_entsize_byentry(sfp, sfep);
+                               tmp_elen = M_DIROPS(mp)->sf_entsize(sfp,
+                                                               sfep->namelen);
                                be64_add_cpu(&dip->di_size, -tmp_elen);
                                ino_dir_size -= tmp_elen;
 
                                tmp_sfep = (xfs_dir2_sf_entry_t *)
-                                       ((__psint_t) sfep + tmp_elen);
-                               tmp_len = max_size - ((__psint_t) tmp_sfep
-                                                       - (__psint_t) sfp);
+                                       ((intptr_t) sfep + tmp_elen);
+                               tmp_len = max_size - ((intptr_t) tmp_sfep
+                                                       - (intptr_t) sfp);
 
                                memmove(sfep, tmp_sfep, tmp_len);
 
-                               sfp->hdr.count -= 1;
+                               sfp->count -= 1;
                                num_entries--;
-                               memset((void *) ((__psint_t) sfep + tmp_len), 0,
+                               memset((void *) ((intptr_t) sfep + tmp_len), 0,
                                        tmp_elen);
 
                                /*
@@ -1136,12 +378,12 @@ process_sf_dir2(
                                *dino_dirty = 1;
                                *repair = 1;
 
-                               do_warn(_("junking entry \"%s\" in directory "
-                                         "inode %llu\n"),
+                               do_warn(
+_("junking entry \"%s\" in directory inode %" PRIu64 "\n"),
                                        name, ino);
                        } else  {
-                               do_warn(_("would have junked entry \"%s\" in "
-                                         "directory inode %llu\n"),
+                               do_warn(
+_("would have junked entry \"%s\" in directory inode %" PRIu64 "\n"),
                                        name, ino);
                        }
                } else if (lino > XFS_DIR2_MAX_SHORT_INUM)
@@ -1154,85 +396,81 @@ process_sf_dir2(
                 * calculate size based on next_sfep.
                 */
                next_sfep = (tmp_sfep == NULL)
-                       ? (xfs_dir2_sf_entry_t *) ((__psint_t) sfep
-                               + ((!bad_sfnamelen)
-                                       ? xfs_dir2_sf_entsize_byentry(sfp,
-                                               sfep)
-                                       : xfs_dir2_sf_entsize_byname(sfp,
-                                               namelen)))
+                       ? (xfs_dir2_sf_entry_t *) ((intptr_t) sfep
+                                                       + ((!bad_sfnamelen)
+                               ? M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen)
+                               : M_DIROPS(mp)->sf_entsize(sfp, namelen)))
                        : tmp_sfep;
        }
 
        /* sync up sizes and entry counts */
 
-       if (sfp->hdr.count != i) {
+       if (sfp->count != i) {
                if (no_modify) {
-                       do_warn(_("would have corrected entry count "
-                                 "in directory %llu from %d to %d\n"),
-                               ino, sfp->hdr.count, i);
+                       do_warn(
+_("would have corrected entry count in directory %" PRIu64 " from %d to %d\n"),
+                               ino, sfp->count, i);
                } else {
-                       do_warn(_("corrected entry count in directory %llu, "
-                                 "was %d, now %d\n"),
-                               ino, sfp->hdr.count, i);
-                       sfp->hdr.count = i;
+                       do_warn(
+_("corrected entry count in directory %" PRIu64 ", was %d, now %d\n"),
+                               ino, sfp->count, i);
+                       sfp->count = i;
                        *dino_dirty = 1;
                        *repair = 1;
                }
        }
 
-       if (sfp->hdr.i8count != i8)  {
+       if (sfp->i8count != i8)  {
                if (no_modify)  {
-                       do_warn(_("would have corrected i8 count in directory "
-                                 "%llu from %d to %d\n"),
-                               ino, sfp->hdr.i8count, i8);
+                       do_warn(
+_("would have corrected i8 count in directory %" PRIu64 " from %d to %d\n"),
+                               ino, sfp->i8count, i8);
                } else {
-                       do_warn(_("corrected i8 count in directory %llu, "
-                                 "was %d, now %d\n"),
-                               ino, sfp->hdr.i8count, i8);
+                       do_warn(
+_("corrected i8 count in directory %" PRIu64 ", was %d, now %d\n"),
+                               ino, sfp->i8count, i8);
                        if (i8 == 0)
-                               process_sf_dir2_fixi8(sfp, &next_sfep);
+                               process_sf_dir2_fixi8(mp, sfp, &next_sfep);
                        else
-                               sfp->hdr.i8count = i8;
+                               sfp->i8count = i8;
                        *dino_dirty = 1;
                        *repair = 1;
                }
        }
 
-       if ((__psint_t) next_sfep - (__psint_t) sfp != ino_dir_size)  {
+       if ((intptr_t)next_sfep - (intptr_t)sfp != ino_dir_size)  {
                if (no_modify)  {
-                       do_warn(_("would have corrected directory %llu size "
-                                 "from %lld to %lld\n"),
-                               ino, (__int64_t) ino_dir_size,
-                               (__int64_t)((__psint_t)next_sfep -
-                                           (__psint_t)sfp));
+                       do_warn(
+_("would have corrected directory %" PRIu64 " size from %" PRId64 " to %" PRIdPTR "\n"),
+                               ino, ino_dir_size,
+                               (intptr_t)next_sfep - (intptr_t)sfp);
                } else  {
-                       do_warn(_("corrected directory %llu size, was %lld, "
-                                 "now %lld\n"),
-                               ino, (__int64_t) ino_dir_size,
-                               (__int64_t)((__psint_t)next_sfep -
-                                           (__psint_t)sfp));
+                       do_warn(
+_("corrected directory %" PRIu64 " size, was %" PRId64 ", now %" PRIdPTR "\n"),
+                               ino, ino_dir_size,
+                               (intptr_t)next_sfep - (intptr_t)sfp);
 
                        dip->di_size = cpu_to_be64(
-                                       (__psint_t)next_sfep - (__psint_t)sfp);
+                                       (intptr_t)next_sfep - (intptr_t)sfp);
                        *dino_dirty = 1;
                        *repair = 1;
                }
        }
-       if (offset + (sfp->hdr.count + 2) * sizeof(xfs_dir2_leaf_entry_t) +
-                       sizeof(xfs_dir2_block_tail_t) > mp->m_dirblksize) {
-               do_warn(_("directory %llu offsets too high\n"), ino);
+       if (offset + (sfp->count + 2) * sizeof(xfs_dir2_leaf_entry_t) +
+                       sizeof(xfs_dir2_block_tail_t) > mp->m_dir_geo->blksize) {
+               do_warn(_("directory %" PRIu64 " offsets too high\n"), ino);
                bad_offset = 1;
        }
        if (bad_offset) {
                if (no_modify) {
-                       do_warn(_("would have corrected entry offsets in "
-                                 "directory %llu\n"),
+                       do_warn(
+_("would have corrected entry offsets in directory %" PRIu64 "\n"),
                                ino);
                } else {
-                       do_warn(_("corrected entry offsets in "
-                                 "directory %llu\n"),
+                       do_warn(
+_("corrected entry offsets in directory %" PRIu64 "\n"),
                                ino);
-                       process_sf_dir2_fixoff(dip);
+                       process_sf_dir2_fixoff(mp, dip);
                        *dino_dirty = 1;
                        *repair = 1;
                }
@@ -1241,21 +479,23 @@ process_sf_dir2(
        /*
         * check parent (..) entry
         */
-       *parent = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+       *parent = M_DIROPS(mp)->sf_get_parent_ino(sfp);
 
        /*
         * if parent entry is bogus, null it out.  we'll fix it later .
+        * If the validation fails for the root inode we fix it in
+        * the next else case.
         */
-       if (verify_inum(mp, *parent))  {
+       if (verify_inum(mp, *parent) && ino != mp->m_sb.sb_rootino)  {
 
-               do_warn(_("bogus .. inode number (%llu) in directory inode "
-                         "%llu, "),
+               do_warn(
+_("bogus .. inode number (%" PRIu64 ") in directory inode %" PRIu64 ", "),
                                *parent, ino);
                *parent = NULLFSINO;
                if (!no_modify)  {
                        do_warn(_("clearing inode number\n"));
 
-                       xfs_dir2_sf_put_inumber(sfp, &zero, &sfp->hdr.parent);
+                       M_DIROPS(mp)->sf_put_parent_ino(sfp, zero);
                        *dino_dirty = 1;
                        *repair = 1;
                } else  {
@@ -1266,16 +506,16 @@ process_sf_dir2(
                 * root directories must have .. == .
                 */
                if (!no_modify)  {
-                       do_warn(_("corrected root directory %llu .. entry, "
-                                 "was %llu, now %llu\n"),
+                       do_warn(
+_("corrected root directory %" PRIu64 " .. entry, was %" PRIu64 ", now %" PRIu64 "\n"),
                                ino, *parent, ino);
                        *parent = ino;
-                       xfs_dir2_sf_put_inumber(sfp, parent, &sfp->hdr.parent);
+                       M_DIROPS(mp)->sf_put_parent_ino(sfp, ino);
                        *dino_dirty = 1;
                        *repair = 1;
                } else  {
-                       do_warn(_("would have corrected root directory %llu .. "
-                                 "entry from %llu to %llu\n"),
+                       do_warn(
+_("would have corrected root directory %" PRIu64 " .. entry from %" PRIu64" to %" PRIu64 "\n"),
                                ino, *parent, ino);
                }
        } else if (ino == *parent && ino != mp->m_sb.sb_rootino)  {
@@ -1284,13 +524,13 @@ process_sf_dir2(
                 * to .
                 */
                *parent = NULLFSINO;
-               do_warn(_("bad .. entry in directory inode %llu, points to "
-                         "self, "),
+               do_warn(
+_("bad .. entry in directory inode %" PRIu64 ", points to self, "),
                        ino);
                if (!no_modify)  {
                        do_warn(_("clearing inode number\n"));
 
-                       xfs_dir2_sf_put_inumber(sfp, &zero, &sfp->hdr.parent);
+                       M_DIROPS(mp)->sf_put_parent_ino(sfp, zero);
                        *dino_dirty = 1;
                        *repair = 1;
                } else  {
@@ -1313,17 +553,18 @@ process_dir2_data(
        int             ino_discovery,
        char            *dirname,       /* directory pathname */
        xfs_ino_t       *parent,        /* out - NULLFSINO if entry not exist */
-       xfs_dabuf_t     *bp,
+       struct xfs_buf  *bp,
        int             *dot,           /* out - 1 if there is a dot, else 0 */
        int             *dotdot,        /* out - 1 if there's a dotdot, else 0 */
        xfs_dablk_t     da_bno,
-       char            *endptr)
+       char            *endptr,
+       int             *dirty)
 {
        int                     badbest;
        xfs_dir2_data_free_t    *bf;
        int                     clearino;
        char                    *clearreason = NULL;
-       xfs_dir2_data_t         *d;
+       struct xfs_dir2_data_hdr *d;
        xfs_dir2_data_entry_t   *dep;
        xfs_dir2_data_free_t    *dfp;
        xfs_dir2_data_unused_t  *dup;
@@ -1337,9 +578,9 @@ process_dir2_data(
        char                    *ptr;
        xfs_ino_t               ent_ino;
 
-       d = bp->data;
-       bf = d->hdr.bestfree;
-       ptr = (char *)d->u;
+       d = bp->b_addr;
+       bf = M_DIROPS(mp)->data_bestfree_p(d);
+       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
        badbest = lastfree = freeseen = 0;
        if (be16_to_cpu(bf[0].length) == 0) {
                badbest |= be16_to_cpu(bf[0].offset) != 0;
@@ -1371,7 +612,7 @@ process_dir2_data(
                                                        (char *)dup - (char *)d)
                                break;
                        badbest |= lastfree != 0;
-                       dfp = xfs_dir2_data_freefind(d, dup);
+                       dfp = xfs_dir2_data_freefind(d, bf, dup);
                        if (dfp) {
                                i = dfp - bf;
                                badbest |= (freeseen & (1 << i)) != 0;
@@ -1384,12 +625,12 @@ process_dir2_data(
                        continue;
                }
                dep = (xfs_dir2_data_entry_t *)ptr;
-               if (ptr + xfs_dir2_data_entsize(dep->namelen) > endptr)
+               if (ptr + M_DIROPS(mp)->data_entsize(dep->namelen) > endptr)
                        break;
-               if (be16_to_cpu(*xfs_dir2_data_entry_tag_p(dep)) !=
+               if (be16_to_cpu(*M_DIROPS(mp)->data_entry_tag_p(dep)) !=
                                                (char *)dep - (char *)d)
                        break;
-               ptr += xfs_dir2_data_entsize(dep->namelen);
+               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
                lastfree = 0;
        }
        /*
@@ -1397,7 +638,7 @@ process_dir2_data(
         * Phase 6 will kill this block if we don't kill the inode.
         */
        if (ptr != endptr) {
-               do_warn(_("corrupt block %u in directory inode %llu\n"),
+               do_warn(_("corrupt block %u in directory inode %" PRIu64 "\n"),
                        da_bno, ino);
                if (!no_modify)
                        do_warn(_("\twill junk block\n"));
@@ -1405,7 +646,7 @@ process_dir2_data(
                        do_warn(_("\twould junk block\n"));
                return 1;
        }
-       ptr = (char *)d->u;
+       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
        /*
         * Process the entries now.
         */
@@ -1427,7 +668,7 @@ process_dir2_data(
                 * Conditions must either set clearino to zero or set
                 * clearreason why it's being cleared.
                 */
-               if (!ino_discovery && ent_ino == BADFSINO) {
+               if (!ino_discovery && dep->name[0] == '/') {
                        /*
                         * Don't do a damned thing.  We already found this
                         * (or did it ourselves) during phase 3.
@@ -1448,6 +689,8 @@ process_dir2_data(
                        clearreason = _("user quota");
                } else if (ent_ino == mp->m_sb.sb_gquotino) {
                        clearreason = _("group quota");
+               } else if (ent_ino == mp->m_sb.sb_pquotino) {
+                       clearreason = _("project quota");
                } else {
                        irec_p = find_inode_rec(mp,
                                                XFS_INO_TO_AGNO(mp, ent_ino),
@@ -1485,20 +728,33 @@ process_dir2_data(
                ASSERT((clearino == 0 && clearreason == NULL) ||
                        (clearino != 0 && clearreason != NULL));
                if (clearino)
-                       do_warn(_("entry \"%*.*s\" at block %u offset %d in "
-                                 "directory inode %llu references %s inode "
-                                 "%llu\n"),
+                       do_warn(
+_("entry \"%*.*s\" at block %d offset %" PRIdPTR " in directory inode %" PRIu64
+  " references %s inode %" PRIu64 "\n"),
                                dep->namelen, dep->namelen, dep->name,
-                               da_bno, (char *)ptr - (char *)d, ino,
+                               da_bno, (intptr_t)ptr - (intptr_t)d, ino,
                                clearreason, ent_ino);
+
+               /*
+                * We have a special dot & dotdot fixer-upper below which can
+                * sort out the proper inode number, so don't clear it.
+                */
+               if ((dep->namelen == 1 && dep->name[0] == '.') ||
+                   (dep->namelen == 2 &&
+                    dep->name[0] == '.' && dep->name[1] == '.')) {
+                       clearino = 0;
+                       clearreason = NULL;
+               }
+
                /*
                 * If the name length is 0 (illegal) make it 1 and blast
                 * the entry.
                 */
                if (dep->namelen == 0) {
-                       do_warn(_("entry at block %u offset %d in directory "
-                                 "inode %llu has 0 namelength\n"),
-                               da_bno, (char *)ptr - (char *)d, ino);
+                       do_warn(
+_("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64
+  "has 0 namelength\n"),
+                               da_bno, (intptr_t)ptr - (intptr_t)d, ino);
                        if (!no_modify)
                                dep->namelen = 1;
                        clearino = 1;
@@ -1508,16 +764,15 @@ process_dir2_data(
                 */
                if (clearino) {
                        if (!no_modify) {
-                               do_warn(_("\tclearing inode number in entry at "
-                                         "offset %d...\n"),
-                                       (char *)ptr - (char *)d);
-                               dep->inumber = cpu_to_be64(BADFSINO);
-                               ent_ino = BADFSINO;
-                               bp->dirty = 1;
+                               do_warn(
+_("\tclearing inode number in entry at offset %" PRIdPTR "...\n"),
+                                       (intptr_t)ptr - (intptr_t)d);
+                               dep->name[0] = '/';
+                               *dirty = 1;
                        } else {
-                               do_warn(_("\twould clear inode number in entry "
-                                         "at offset %d...\n"),
-                                       (char *)ptr - (char *)d);
+                               do_warn(
+_("\twould clear inode number in entry at offset %" PRIdPTR "...\n"),
+                                       (intptr_t)ptr - (intptr_t)d);
                        }
                }
                /*
@@ -1525,23 +780,24 @@ process_dir2_data(
                 * discovery is turned on).  Otherwise, we'd complain a lot
                 * during phase 4.
                 */
-               junkit = ent_ino == BADFSINO;
+               junkit = dep->name[0] == '/';
                nm_illegal = namecheck((char *)dep->name, dep->namelen);
                if (ino_discovery && nm_illegal) {
-                       do_warn(_("entry at block %u offset %d in directory "
-                                 "inode %llu has illegal name \"%*.*s\": "),
-                               da_bno, (char *)ptr - (char *)d, ino,
+                       do_warn(
+_("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 " has illegal name \"%*.*s\": "),
+                               da_bno, (intptr_t)ptr - (intptr_t)d, ino,
                                dep->namelen, dep->namelen, dep->name);
                        junkit = 1;
                }
+
                /*
-                * Now we can mark entries with BADFSINO's bad.
+                * Ensure we write back bad entries for later processing
                 */
-               if (!no_modify && ent_ino == BADFSINO) {
-                       dep->name[0] = '/';
-                       bp->dirty = 1;
+               if (!no_modify && dep->name[0] == '/') {
+                       *dirty = 1;
                        junkit = 0;
                }
+
                /*
                 * Special .. entry processing.
                 */
@@ -1558,8 +814,8 @@ process_dir2_data(
                                if (ino == ent_ino &&
                                                ino != mp->m_sb.sb_rootino) {
                                        *parent = NULLFSINO;
-                                       do_warn(_("bad .. entry in directory "
-                                                 "inode %llu, points to self: "),
+                                       do_warn(
+_("bad .. entry in directory inode %" PRIu64 ", points to self: "),
                                                ino);
                                        junkit = 1;
                                }
@@ -1569,17 +825,34 @@ process_dir2_data(
                                 */
                                else if (ino != ent_ino &&
                                                ino == mp->m_sb.sb_rootino) {
-                                       do_warn(_("bad .. entry in root "
-                                                 "directory inode %llu, was "
-                                                 "%llu: "),
+                                       do_warn(
+_("bad .. entry in root directory inode %" PRIu64 ", was %" PRIu64 ": "),
                                                ino, ent_ino);
                                        if (!no_modify) {
                                                do_warn(_("correcting\n"));
                                                dep->inumber = cpu_to_be64(ino);
-                                               bp->dirty = 1;
+                                               *dirty = 1;
                                        } else {
                                                do_warn(_("would correct\n"));
                                        }
+                                       *parent = ino;
+                               }
+                               /*
+                                * Make sure our parent directory doesn't point
+                                * off into space.
+                                */
+                               if (!junkit &&
+                                   *parent != NULLFSINO &&
+                                   !libxfs_verify_ino(mp, *parent)) {
+                                       do_warn(
+_("bad .. entry in directory inode %" PRIu64 ", was %" PRIu64 ": "),
+                                               ino, *parent);
+                                       if (!no_modify) {
+                                               do_warn(_("correcting\n"));
+                                       } else {
+                                               do_warn(_("would correct\n"));
+                                       }
+                                       *parent = NULLFSINO;
                                }
                        }
                        /*
@@ -1589,8 +862,8 @@ process_dir2_data(
                         * seem equally valid, trash this one.
                         */
                        else {
-                               do_warn(_("multiple .. entries in directory "
-                                         "inode %llu: "),
+                               do_warn(
+_("multiple .. entries in directory inode %" PRIu64 ": "),
                                        ino);
                                junkit = 1;
                        }
@@ -1602,20 +875,20 @@ process_dir2_data(
                        if (!*dot) {
                                (*dot)++;
                                if (ent_ino != ino) {
-                                       do_warn(_("bad . entry in directory "
-                                                 "inode %llu, was %llu: "),
+                                       do_warn(
+_("bad . entry in directory inode %" PRIu64 ", was %" PRIu64 ": "),
                                                ino, ent_ino);
                                        if (!no_modify) {
                                                do_warn(_("correcting\n"));
                                                dep->inumber = cpu_to_be64(ino);
-                                               bp->dirty = 1;
+                                               *dirty = 1;
                                        } else {
                                                do_warn(_("would correct\n"));
                                        }
                                }
                        } else {
-                               do_warn(_("multiple . entries in directory "
-                                         "inode %llu: "),
+                               do_warn(
+_("multiple . entries in directory inode %" PRIu64 ": "),
                                        ino);
                                junkit = 1;
                        }
@@ -1624,8 +897,8 @@ process_dir2_data(
                 * All other entries -- make sure only . references self.
                 */
                else if (ent_ino == ino) {
-                       do_warn(_("entry \"%*.*s\" in directory inode %llu "
-                                 "points to self: "),
+                       do_warn(
+_("entry \"%*.*s\" in directory inode %" PRIu64 " points to self: "),
                                dep->namelen, dep->namelen, dep->name, ino);
                        junkit = 1;
                }
@@ -1635,7 +908,7 @@ process_dir2_data(
                if (junkit) {
                        if (!no_modify) {
                                dep->name[0] = '/';
-                               bp->dirty = 1;
+                               *dirty = 1;
                                do_warn(_("clearing entry\n"));
                        } else {
                                do_warn(_("would clear entry\n"));
@@ -1644,18 +917,20 @@ process_dir2_data(
                /*
                 * Advance to the next entry.
                 */
-               ptr += xfs_dir2_data_entsize(dep->namelen);
+               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
        }
        /*
         * Check the bestfree table.
         */
        if (freeseen != 7 || badbest) {
-               do_warn(_("bad bestfree table in block %u in directory inode "
-                         "%llu: "), da_bno, ino);
+               do_warn(
+_("bad bestfree table in block %u in directory inode %" PRIu64 ": "),
+                       da_bno, ino);
                if (!no_modify) {
                        do_warn(_("repairing table\n"));
-                       libxfs_dir2_data_freescan(mp, d, &i);
-                       bp->dirty = 1;
+                       libxfs_dir2_data_freescan_int(mp->m_dir_geo,
+                                       M_DIROPS(mp), d, &i);
+                       *dirty = 1;
                } else {
                        do_warn(_("would repair table\n"));
                }
@@ -1681,44 +956,49 @@ process_block_dir2(
        int             *dotdot,        /* out - 1 if there's a dotdot, else 0 */
        int             *repair)        /* out - 1 if something was fixed */
 {
-       xfs_dir2_block_t        *block;
+       struct xfs_dir2_data_hdr *block;
        xfs_dir2_leaf_entry_t   *blp;
        bmap_ext_t              *bmp;
-       xfs_dabuf_t             *bp;
+       struct xfs_buf          *bp;
        xfs_dir2_block_tail_t   *btp;
        int                     nex;
        int                     rval;
        bmap_ext_t              lbmp;
+       int                     dirty = 0;
 
        *repair = *dot = *dotdot = 0;
        *parent = NULLFSINO;
-       nex = blkmap_getn(blkmap, mp->m_dirdatablk, mp->m_dirblkfsbs, &bmp, &lbmp);
+       nex = blkmap_getn(blkmap, mp->m_dir_geo->datablk,
+                               mp->m_dir_geo->fsbcount, &bmp, &lbmp);
        if (nex == 0) {
-               do_warn(_("block %u for directory inode %llu is missing\n"),
-                       mp->m_dirdatablk, ino);
+               do_warn(
+_("block %u for directory inode %" PRIu64 " is missing\n"),
+                       mp->m_dir_geo->datablk, ino);
                return 1;
        }
-       bp = da_read_buf(mp, nex, bmp);
+       bp = da_read_buf(mp, nex, bmp, &xfs_dir3_block_buf_ops);
        if (bmp != &lbmp)
                free(bmp);
        if (bp == NULL) {
-               do_warn(_("can't read block %u for directory inode %llu\n"),
-                       mp->m_dirdatablk, ino);
+               do_warn(
+_("can't read block %u for directory inode %" PRIu64 "\n"),
+                       mp->m_dir_geo->datablk, ino);
                return 1;
        }
        /*
         * Verify the block
         */
-       block = bp->data;
-       if (be32_to_cpu(block->hdr.magic) != XFS_DIR2_BLOCK_MAGIC)
-               do_warn(_("bad directory block magic # %#x in block %u for "
-                         "directory inode %llu\n"),
-                       be32_to_cpu(block->hdr.magic), mp->m_dirdatablk, ino);
+       block = bp->b_addr;
+       if (!(be32_to_cpu(block->magic) == XFS_DIR2_BLOCK_MAGIC ||
+             be32_to_cpu(block->magic) == XFS_DIR3_BLOCK_MAGIC))
+               do_warn(
+_("bad directory block magic # %#x in block %u for directory inode %" PRIu64 "\n"),
+                       be32_to_cpu(block->magic), mp->m_dir_geo->datablk, ino);
        /*
         * process the data area
         * this also checks & fixes the bestfree
         */
-       btp = xfs_dir2_block_tail_p(mp, block);
+       btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block);
        blp = xfs_dir2_block_leaf_p(btp);
        /*
         * Don't let this go past the end of the block.
@@ -1726,12 +1006,15 @@ process_block_dir2(
        if ((char *)blp > (char *)btp)
                blp = (xfs_dir2_leaf_entry_t *)btp;
        rval = process_dir2_data(mp, ino, dip, ino_discovery, dirname, parent,
-               bp, dot, dotdot, mp->m_dirdatablk, (char *)blp);
-       if (bp->dirty && !no_modify) {
+               bp, dot, dotdot, mp->m_dir_geo->datablk, (char *)blp, &dirty);
+       /* If block looks ok but CRC didn't match, make sure to recompute it. */
+       if (!rval && bp->b_error == -EFSBADCRC)
+               dirty = 1;
+       if (dirty && !no_modify) {
                *repair = 1;
-               da_bwrite(mp, bp);
+               libxfs_writebuf(bp, 0);
        } else
-               da_brelse(bp);
+               libxfs_putbuf(bp);
        return rval;
 }
 
@@ -1752,28 +1035,32 @@ process_leaf_block_dir2(
 {
        int                     i;
        int                     stale;
+       struct xfs_dir2_leaf_entry *ents;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
-       for (i = stale = 0; i < be16_to_cpu(leaf->hdr.count); i++) {
-               if ((char *)&leaf->ents[i] >= (char *)leaf + mp->m_dirblksize) {
-                       do_warn(_("bad entry count in block %u of directory "
-                                 "inode %llu\n"),
+       M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = M_DIROPS(mp)->leaf_ents_p(leaf);
+
+       for (i = stale = 0; i < leafhdr.count; i++) {
+               if ((char *)&ents[i] >= (char *)leaf + mp->m_dir_geo->blksize) {
+                       do_warn(
+_("bad entry count in block %u of directory inode %" PRIu64 "\n"),
                                da_bno, ino);
                        return 1;
                }
-               if (be32_to_cpu(leaf->ents[i].address) == XFS_DIR2_NULL_DATAPTR)
+               if (be32_to_cpu(ents[i].address) == XFS_DIR2_NULL_DATAPTR)
                        stale++;
-               else if (be32_to_cpu(leaf->ents[i].hashval) < last_hashval) {
-                       do_warn(_("bad hash ordering in block %u of directory "
-                                 "inode %llu\n"),
+               else if (be32_to_cpu(ents[i].hashval) < last_hashval) {
+                       do_warn(
+_("bad hash ordering in block %u of directory inode %" PRIu64 "\n"),
                                da_bno, ino);
                        return 1;
                }
-               *next_hashval = last_hashval =
-                                       be32_to_cpu(leaf->ents[i].hashval);
+               *next_hashval = last_hashval = be32_to_cpu(ents[i].hashval);
        }
-       if (stale != be16_to_cpu(leaf->hdr.stale)) {
-               do_warn(_("bad stale count in block %u of directory "
-                         "inode %llu\n"),
+       if (stale != leafhdr.stale) {
+               do_warn(
+_("bad stale count in block %u of directory inode %" PRIu64 "\n"),
                        da_bno, ino);
                return 1;
        }
@@ -1786,11 +1073,11 @@ process_leaf_block_dir2(
 static int
 process_leaf_level_dir2(
        xfs_mount_t             *mp,
-       dir2_bt_cursor_t        *da_cursor,
+       da_bt_cursor_t          *da_cursor,
        int                     *repair)
 {
        bmap_ext_t              *bmp;
-       xfs_dabuf_t             *bp;
+       struct xfs_buf          *bp;
        int                     buf_dirty;
        xfs_dahash_t            current_hashval;
        xfs_dablk_t             da_bno;
@@ -1800,6 +1087,7 @@ process_leaf_level_dir2(
        int                     nex;
        xfs_dablk_t             prev_bno;
        bmap_ext_t              lbmp;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
        da_bno = da_cursor->level[0].bno;
        ino = da_cursor->ino;
@@ -1810,8 +1098,8 @@ process_leaf_level_dir2(
        buf_dirty = 0;
 
        do {
-               nex = blkmap_getn(da_cursor->blkmap, da_bno, mp->m_dirblkfsbs,
-                       &bmp, &lbmp);
+               nex = blkmap_getn(da_cursor->blkmap, da_bno,
+                               mp->m_dir_geo->fsbcount, &bmp, &lbmp);
                /*
                 * Directory code uses 0 as the NULL block pointer since 0
                 * is the root block and no directory block pointer can point
@@ -1820,32 +1108,32 @@ process_leaf_level_dir2(
                ASSERT(da_bno != 0);
 
                if (nex == 0) {
-                       do_warn(_("can't map block %u for directory "
-                                 "inode %llu\n"),
+                       do_warn(
+_("can't map block %u for directory inode %" PRIu64 "\n"),
                                da_bno, ino);
                        goto error_out;
                }
-               bp = da_read_buf(mp, nex, bmp);
+               bp = da_read_buf(mp, nex, bmp, &xfs_dir3_leafn_buf_ops);
                if (bmp != &lbmp)
                        free(bmp);
                bmp = NULL;
                if (bp == NULL) {
-                       do_warn(_("can't read file block %u for directory "
-                                 "inode %llu\n"),
+                       do_warn(
+_("can't read file block %u for directory inode %" PRIu64 "\n"),
                                da_bno, ino);
                        goto error_out;
                }
-               leaf = bp->data;
+               leaf = bp->b_addr;
+               M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
                /*
                 * Check magic number for leaf directory btree block.
                 */
-               if (be16_to_cpu(leaf->hdr.info.magic) !=
-                  XFS_DIR2_LEAFN_MAGIC) {
-                       do_warn(_("bad directory leaf magic # %#x for "
-                                 "directory inode %llu block %u\n"),
-                               be16_to_cpu(leaf->hdr.info.magic),
-                               ino, da_bno);
-                       da_brelse(bp);
+               if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
+                     leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) {
+                       do_warn(
+_("bad directory leaf magic # %#x for directory inode %" PRIu64 " block %u\n"),
+                               leafhdr.magic, ino, da_bno);
+                       libxfs_putbuf(bp);
                        goto error_out;
                }
                buf_dirty = 0;
@@ -1855,7 +1143,7 @@ process_leaf_level_dir2(
                 */
                if (process_leaf_block_dir2(mp, leaf, da_bno, ino,
                                current_hashval, &greatest_hashval)) {
-                       da_brelse(bp);
+                       libxfs_putbuf(bp);
                        goto error_out;
                }
                /*
@@ -1866,51 +1154,56 @@ process_leaf_level_dir2(
                da_cursor->level[0].hashval = greatest_hashval;
                da_cursor->level[0].bp = bp;
                da_cursor->level[0].bno = da_bno;
-               da_cursor->level[0].index =
-                       be16_to_cpu(leaf->hdr.count);
+               da_cursor->level[0].index = leafhdr.count;
                da_cursor->level[0].dirty = buf_dirty;
 
-               if (be32_to_cpu(leaf->hdr.info.back) != prev_bno) {
-                       do_warn(_("bad sibling back pointer for block %u in "
-                                 "directory inode %llu\n"),
+               if (leafhdr.back != prev_bno) {
+                       do_warn(
+_("bad sibling back pointer for block %u in directory inode %" PRIu64 "\n"),
                                da_bno, ino);
-                       da_brelse(bp);
+                       libxfs_putbuf(bp);
                        goto error_out;
                }
                prev_bno = da_bno;
-               da_bno = be32_to_cpu(leaf->hdr.info.forw);
+               da_bno = leafhdr.forw;
                if (da_bno != 0) {
-                       if (verify_dir2_path(mp, da_cursor, 0)) {
-                               da_brelse(bp);
+                       if (verify_da_path(mp, da_cursor, 0, XFS_DATA_FORK)) {
+                               libxfs_putbuf(bp);
                                goto error_out;
                        }
                }
                current_hashval = greatest_hashval;
+               /*
+                * If block looks ok but CRC didn't match, make sure to
+                * recompute it.
+                */
+               if (!no_modify && bp->b_error == -EFSBADCRC)
+                       buf_dirty = 1;
                ASSERT(buf_dirty == 0 || (buf_dirty && !no_modify));
                if (buf_dirty && !no_modify) {
                        *repair = 1;
-                       da_bwrite(mp, bp);
+                       libxfs_writebuf(bp, 0);
                } else
-                       da_brelse(bp);
+                       libxfs_putbuf(bp);
        } while (da_bno != 0);
-       if (verify_final_dir2_path(mp, da_cursor, 0)) {
+       if (verify_final_da_path(mp, da_cursor, 0, XFS_DATA_FORK)) {
                /*
                 * Verify the final path up (right-hand-side) if still ok.
                 */
-               do_warn(_("bad hash path in directory %llu\n"), ino);
+               do_warn(_("bad hash path in directory %" PRIu64 "\n"), ino);
                goto error_out;
        }
        /*
         * Redundant but just for testing.
         */
-       release_dir2_cursor(mp, da_cursor, 0);
+       release_da_cursor(mp, da_cursor, 0);
        return 0;
 
 error_out:
        /*
         * Release all buffers holding interior btree blocks.
         */
-       err_release_dir2_cursor(mp, da_cursor, 0);
+       err_release_da_cursor(mp, da_cursor, 0);
        if (bmp && (bmp != &lbmp))
                free(bmp);
        return 1;
@@ -1929,7 +1222,7 @@ process_node_dir2(
        int             *repair)
 {
        xfs_dablk_t             bno;
-       dir2_bt_cursor_t        da_cursor;
+       da_bt_cursor_t          da_cursor;
 
        /*
         * Try again -- traverse down left-side of tree until we hit the
@@ -1945,15 +1238,26 @@ process_node_dir2(
        /*
         * Now process interior node.
         */
-       if (traverse_int_dir2block(mp, &da_cursor, &bno) == 0)
+       if (traverse_int_dablock(mp, &da_cursor, &bno, XFS_DATA_FORK) == 0)
                return 1;
 
        /*
         * Skip directories with a root marked XFS_DIR2_LEAFN_MAGIC
+        *
+        * Be careful here: If any level of the da cursor was filled out then
+        * the directory has a da btree containing an invalid before pointer to
+        * dblock 0, and we should move on to rebuilding the directory.  If no
+        * levels in the da cursor got filled out, then we just have a single
+        * leafn block and we're done.
         */
        if (bno == 0) {
-               release_dir2_cursor(mp, &da_cursor, 0);
-               return 0;
+               if (da_cursor.active > 0) {
+                       err_release_da_cursor(mp, &da_cursor, 0);
+                       return 1;
+               } else {
+                       release_da_cursor(mp, &da_cursor, 0);
+                       return 0;
+               }
        } else {
                /*
                 * Now pass cursor and bno into leaf-block processing routine.
@@ -1984,52 +1288,62 @@ process_leaf_node_dir2(
        int             isnode)         /* node directory not leaf */
 {
        bmap_ext_t              *bmp;
-       xfs_dabuf_t             *bp;
-       xfs_dir2_data_t         *data;
-       xfs_dfiloff_t           dbno;
+       struct xfs_buf          *bp;
+       struct xfs_dir2_data_hdr *data;
+       xfs_fileoff_t           dbno;
        int                     good;
        int                     i;
-       xfs_dfiloff_t           ndbno;
+       xfs_fileoff_t           ndbno;
        int                     nex;
        int                     t;
        bmap_ext_t              lbmp;
+       int                     dirty = 0;
 
        *repair = *dot = *dotdot = good = 0;
        *parent = NULLFSINO;
-       ndbno = NULLDFILOFF;
-       while ((dbno = blkmap_next_off(blkmap, ndbno, &t)) < mp->m_dirleafblk) {
-               nex = blkmap_getn(blkmap, dbno, mp->m_dirblkfsbs, &bmp, &lbmp);
-               ndbno = dbno + mp->m_dirblkfsbs - 1;
+       ndbno = NULLFILEOFF;
+       while ((dbno = blkmap_next_off(blkmap, ndbno, &t)) < mp->m_dir_geo->leafblk) {
+               nex = blkmap_getn(blkmap, dbno, mp->m_dir_geo->fsbcount, &bmp, &lbmp);
+               /* Advance through map to last dfs block in this dir block */
+               ndbno = dbno;
+               while (ndbno < dbno + mp->m_dir_geo->fsbcount - 1) {
+                       ndbno = blkmap_next_off(blkmap, ndbno, &t);
+               }
                if (nex == 0) {
-                       do_warn(_("block %llu for directory inode %llu is "
-                                 "missing\n"),
+                       do_warn(
+_("block %" PRIu64 " for directory inode %" PRIu64 " is missing\n"),
                                dbno, ino);
                        continue;
                }
-               bp = da_read_buf(mp, nex, bmp);
+               bp = da_read_buf(mp, nex, bmp, &xfs_dir3_data_buf_ops);
                if (bmp != &lbmp)
                        free(bmp);
                if (bp == NULL) {
-                       do_warn(_("can't read block %llu for directory inode "
-                                 "%llu\n"),
+                       do_warn(
+_("can't read block %" PRIu64 " for directory inode %" PRIu64 "\n"),
                                dbno, ino);
                        continue;
                }
-               data = bp->data;
-               if (be32_to_cpu(data->hdr.magic) != XFS_DIR2_DATA_MAGIC)
-                       do_warn(_("bad directory block magic # %#x in block "
-                               "%llu for directory inode %llu\n"),
-                               be32_to_cpu(data->hdr.magic), dbno, ino);
+               data = bp->b_addr;
+               if (!(be32_to_cpu(data->magic) == XFS_DIR2_DATA_MAGIC ||
+                     be32_to_cpu(data->magic) == XFS_DIR3_DATA_MAGIC))
+                       do_warn(
+_("bad directory block magic # %#x in block %" PRIu64 " for directory inode %" PRIu64 "\n"),
+                               be32_to_cpu(data->magic), dbno, ino);
                i = process_dir2_data(mp, ino, dip, ino_discovery, dirname,
                        parent, bp, dot, dotdot, (xfs_dablk_t)dbno,
-                       (char *)data + mp->m_dirblksize);
-               if (i == 0)
+                       (char *)data + mp->m_dir_geo->blksize, &dirty);
+               if (i == 0) {
                        good++;
-               if (bp->dirty && !no_modify) {
+                       /* Maybe just CRC is wrong. Make sure we correct it. */
+                       if (bp->b_error == -EFSBADCRC)
+                               dirty = 1;
+               }
+               if (dirty && !no_modify) {
                        *repair = 1;
-                       da_bwrite(mp, bp);
+                       libxfs_writebuf(bp, 0);
                } else
-                       da_brelse(bp);
+                       libxfs_putbuf(bp);
        }
        if (good == 0)
                return 1;
@@ -2062,7 +1376,7 @@ process_dir2(
 {
        int             dot;
        int             dotdot;
-       xfs_dfiloff_t   last;
+       xfs_fileoff_t   last;
        int             repair;
        int             res;
 
@@ -2082,27 +1396,27 @@ process_dir2(
                dot = dotdot = 1;
                res = process_sf_dir2(mp, ino, dip, ino_discovery, dino_dirty,
                        dirname, parent, &repair);
-       } else if (last == mp->m_dirblkfsbs &&
+       } else if (last == mp->m_dir_geo->fsbcount &&
                        (dip->di_format == XFS_DINODE_FMT_EXTENTS ||
                        dip->di_format == XFS_DINODE_FMT_BTREE)) {
                res = process_block_dir2(mp, ino, dip, ino_discovery,
                        dino_dirty, dirname, parent, blkmap, &dot, &dotdot,
                        &repair);
-       } else if (last >= mp->m_dirleafblk + mp->m_dirblkfsbs &&
+       } else if (last >= mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount &&
                        (dip->di_format == XFS_DINODE_FMT_EXTENTS ||
                        dip->di_format == XFS_DINODE_FMT_BTREE)) {
                res = process_leaf_node_dir2(mp, ino, dip, ino_discovery,
                        dirname, parent, blkmap, &dot, &dotdot, &repair,
-                       last > mp->m_dirleafblk + mp->m_dirblkfsbs);
+                       last > mp->m_dir_geo->leafblk + mp->m_dir_geo->fsbcount);
        } else {
-               do_warn(_("bad size/format for directory %llu\n"), ino);
+               do_warn(_("bad size/format for directory %" PRIu64 "\n"), ino);
                return 1;
        }
        /*
         * bad . entries in all directories will be fixed up in phase 6
         */
        if (dot == 0) {
-               do_warn(_("no . entry for directory %llu\n"), ino);
+               do_warn(_("no . entry for directory %" PRIu64 "\n"), ino);
        }
 
        /*
@@ -2112,9 +1426,9 @@ process_dir2(
         * fixed in place since we know what it should be
         */
        if (dotdot == 0 && ino != mp->m_sb.sb_rootino) {
-               do_warn(_("no .. entry for directory %llu\n"), ino);
+               do_warn(_("no .. entry for directory %" PRIu64 "\n"), ino);
        } else if (dotdot == 0 && ino == mp->m_sb.sb_rootino) {
-               do_warn(_("no .. entry for root directory %llu\n"), ino);
+               do_warn(_("no .. entry for root directory %" PRIu64 "\n"), ino);
                need_root_dotdot = 1;
        }