]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/phase6.c
xfs_repair: detect and fix padding fields that changed with nrext64
[thirdparty/xfsprogs-dev.git] / repair / phase6.c
index d6cb0a5aba344aaeb84e01b2396fe143d3ada5c3..c04b2e0999c0a181f93b6b63a6a7dcb5453b6fef 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "libxfs.h"
 #include "threads.h"
+#include "threads.h"
 #include "prefetch.h"
 #include "avl.h"
 #include "globals.h"
@@ -26,61 +27,6 @@ static struct xfs_name               xfs_name_dot = {(unsigned char *)".",
                                                1,
                                                XFS_DIR3_FT_DIR};
 
-/*
- * When we're checking directory inodes, we're allowed to set a directory's
- * dotdot entry to zero to signal that the parent needs to be reconnected
- * during phase 6.  If we're handling a shortform directory the ifork
- * verifiers will fail, so temporarily patch out this canary so that we can
- * verify the rest of the fork and move on to fixing the dir.
- */
-static xfs_failaddr_t
-phase6_verify_dir(
-       struct xfs_inode                *ip)
-{
-       struct xfs_mount                *mp = ip->i_mount;
-       const struct xfs_dir_ops        *dops;
-       struct xfs_ifork                *ifp;
-       struct xfs_dir2_sf_hdr          *sfp;
-       xfs_failaddr_t                  fa;
-       xfs_ino_t                       old_parent;
-       bool                            parent_bypass = false;
-       int                             size;
-
-       dops = libxfs_dir_get_ops(mp, NULL);
-
-       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
-       sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
-       size = ifp->if_bytes;
-
-       /*
-        * If this is a shortform directory, phase4 may have set the parent
-        * inode to zero to indicate that it must be fixed.  Temporarily
-        * set a valid parent so that the directory verifier will pass.
-        */
-       if (size > offsetof(struct xfs_dir2_sf_hdr, parent) &&
-           size >= xfs_dir2_sf_hdr_size(sfp->i8count)) {
-               old_parent = dops->sf_get_parent_ino(sfp);
-               if (old_parent == 0) {
-                       dops->sf_put_parent_ino(sfp, mp->m_sb.sb_rootino);
-                       parent_bypass = true;
-               }
-       }
-
-       fa = libxfs_default_ifork_ops.verify_dir(ip);
-
-       /* Put it back. */
-       if (parent_bypass)
-               dops->sf_put_parent_ino(sfp, old_parent);
-
-       return fa;
-}
-
-static struct xfs_ifork_ops phase6_ifork_ops = {
-       .verify_attr    = xfs_attr_shortform_verify,
-       .verify_dir     = phase6_verify_dir,
-       .verify_symlink = xfs_symlink_shortform_verify,
-};
-
 /*
  * Data structures used to keep track of directories where the ".."
  * entries are updated. These must be rebuilt after the initial pass
@@ -120,29 +66,29 @@ add_dotdot_update(
  * 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     *nextbyaddr;    /* next in addr bucket */
+struct dir_hash_ent {
        struct dir_hash_ent     *nextbyhash;    /* next in name bucket */
        struct dir_hash_ent     *nextbyorder;   /* next in order added */
        xfs_dahash_t            hashval;        /* hash value of name */
        uint32_t                address;        /* offset of data entry */
-       xfs_ino_t               inum;           /* inode num of entry */
+       xfs_ino_t               inum;           /* inode num of entry */
        short                   junkit;         /* name starts with / */
        short                   seen;           /* have seen leaf entry */
        struct xfs_name         name;
-} dir_hash_ent_t;
+       unsigned char           namebuf[];
+};
 
-typedef struct dir_hash_tab {
+struct dir_hash_tab {
        int                     size;           /* size of hash tables */
-       int                     names_duped;    /* 1 = ent names malloced */
-       dir_hash_ent_t          *first;         /* ptr to first added entry */
-       dir_hash_ent_t          *last;          /* ptr to last added entry */
-       dir_hash_ent_t          **byhash;       /* ptr to name hash buckets */
-       dir_hash_ent_t          **byaddr;       /* ptr to addr hash buckets */
-} dir_hash_tab_t;
+       struct dir_hash_ent     *first;         /* ptr to first added entry */
+       struct dir_hash_ent     *last;          /* ptr to last added entry */
+       struct dir_hash_ent     **byhash;       /* ptr to name hash buckets */
+#define HT_UNSEEN              1
+       struct radix_tree_root  byaddr;
+};
 
 #define        DIR_HASH_TAB_SIZE(n)    \
-       (sizeof(dir_hash_tab_t) + (sizeof(dir_hash_ent_t *) * (n) * 2))
+       (sizeof(struct dir_hash_tab) + (sizeof(struct dir_hash_ent *) * (n)))
 #define        DIR_HASH_FUNC(t,a)      ((a) % (t)->size)
 
 /*
@@ -182,7 +128,6 @@ static int
 dir_read_buf(
        struct xfs_inode        *ip,
        xfs_dablk_t             bno,
-       xfs_daddr_t             mappedbno,
        struct xfs_buf          **bpp,
        const struct xfs_buf_ops *ops,
        int                     *crc_error)
@@ -190,14 +135,13 @@ dir_read_buf(
        int error;
        int error2;
 
-       error = -libxfs_da_read_buf(NULL, ip, bno, mappedbno, bpp,
-                                  XFS_DATA_FORK, ops);
+       error = -libxfs_da_read_buf(NULL, ip, bno, 0, bpp, XFS_DATA_FORK, ops);
 
        if (error != EFSBADCRC && error != EFSCORRUPTED)
                return error;
 
-       error2 = -libxfs_da_read_buf(NULL, ip, bno, mappedbno, bpp,
-                                  XFS_DATA_FORK, NULL);
+       error2 = -libxfs_da_read_buf(NULL, ip, bno, 0, bpp, XFS_DATA_FORK,
+                       NULL);
        if (error2)
                return error2;
 
@@ -211,8 +155,8 @@ dir_read_buf(
  */
 static int
 dir_hash_add(
-       xfs_mount_t             *mp,
-       dir_hash_tab_t          *hashtab,
+       struct xfs_mount        *mp,
+       struct dir_hash_tab     *hashtab,
        uint32_t                addr,
        xfs_ino_t               inum,
        int                     namelen,
@@ -220,25 +164,22 @@ dir_hash_add(
        uint8_t                 ftype)
 {
        xfs_dahash_t            hash = 0;
-       int                     byaddr;
        int                     byhash = 0;
-       dir_hash_ent_t          *p;
+       struct dir_hash_ent     *p;
        int                     dup;
        short                   junk;
        struct xfs_name         xname;
-
-       ASSERT(!hashtab->names_duped);
+       int                     error;
 
        xname.name = name;
        xname.len = namelen;
        xname.type = ftype;
 
        junk = name[0] == '/';
-       byaddr = DIR_HASH_FUNC(hashtab, addr);
        dup = 0;
 
        if (!junk) {
-               hash = mp->m_dirnameops->hashname(&xname);
+               hash = libxfs_dir2_hashname(mp, &xname);
                byhash = DIR_HASH_FUNC(hashtab, hash);
 
                /*
@@ -255,12 +196,23 @@ dir_hash_add(
                }
        }
 
-       if ((p = malloc(sizeof(*p))) == NULL)
+       /*
+        * Allocate enough space for the hash entry and the name in a single
+        * allocation so we can store our own copy of the name for later use.
+        */
+       p = calloc(1, sizeof(*p) + namelen + 1);
+       if (!p)
                do_error(_("malloc failed in dir_hash_add (%zu bytes)\n"),
                        sizeof(*p));
 
-       p->nextbyaddr = hashtab->byaddr[byaddr];
-       hashtab->byaddr[byaddr] = p;
+       error = radix_tree_insert(&hashtab->byaddr, addr, p);
+       if (error == EEXIST) {
+               do_warn(_("duplicate addrs %u in directory!\n"), addr);
+               free(p);
+               return 0;
+       }
+       radix_tree_tag_set(&hashtab->byaddr, addr, HT_UNSEEN);
+
        if (hashtab->last)
                hashtab->last->nextbyorder = p;
        else
@@ -276,38 +228,38 @@ dir_hash_add(
        p->address = addr;
        p->inum = inum;
        p->seen = 0;
-       p->name = xname;
 
+       /* Set up the name in the region trailing the hash entry. */
+       memcpy(p->namebuf, name, namelen);
+       p->name.name = p->namebuf;
+       p->name.len = namelen;
+       p->name.type = ftype;
        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)
+/* Mark an existing directory hashtable entry as junk. */
+static void
+dir_hash_junkit(
+       struct dir_hash_tab     *hashtab,
+       xfs_dir2_dataptr_t      addr)
 {
-       int             i;
-       dir_hash_ent_t  *p;
+       struct dir_hash_ent     *p;
 
-       for (i = 0; i < hashtab->size; i++) {
-               for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
-                       if (p->seen == 0)
-                               return 1;
-               }
-       }
-       return 0;
+       p = radix_tree_lookup(&hashtab->byaddr, addr);
+       assert(p != NULL);
+
+       p->junkit = 1;
+       p->namebuf[0] = '/';
 }
 
 static int
 dir_hash_check(
-       dir_hash_tab_t  *hashtab,
-       xfs_inode_t     *ip,
-       int             seeval)
+       struct dir_hash_tab     *hashtab,
+       struct xfs_inode        *ip,
+       int                     seeval)
 {
-       static char     *seevalstr[DIR_HASH_CK_TOTAL];
-       static int      done;
+       static char             *seevalstr[DIR_HASH_CK_TOTAL];
+       static int              done;
 
        if (!done) {
                seevalstr[DIR_HASH_CK_OK] = _("ok");
@@ -319,7 +271,8 @@ dir_hash_check(
                done = 1;
        }
 
-       if (seeval == DIR_HASH_CK_OK && dir_hash_unseen(hashtab))
+       if (seeval == DIR_HASH_CK_OK &&
+           radix_tree_tagged(&hashtab->byaddr, HT_UNSEEN))
                seeval = DIR_HASH_CK_NOLEAF;
        if (seeval == DIR_HASH_CK_OK)
                return 0;
@@ -334,83 +287,92 @@ dir_hash_check(
 
 static void
 dir_hash_done(
-       dir_hash_tab_t  *hashtab)
+       struct dir_hash_tab     *hashtab)
 {
-       int             i;
-       dir_hash_ent_t  *n;
-       dir_hash_ent_t  *p;
+       int                     i;
+       struct dir_hash_ent     *n;
+       struct dir_hash_ent     *p;
 
        for (i = 0; i < hashtab->size; i++) {
-               for (p = hashtab->byaddr[i]; p; p = n) {
-                       n = p->nextbyaddr;
-                       if (hashtab->names_duped)
-                               free((void *)p->name.name);
+               for (p = hashtab->byhash[i]; p; p = n) {
+                       n = p->nextbyhash;
+                       radix_tree_delete(&hashtab->byaddr, p->address);
                        free(p);
                }
        }
        free(hashtab);
 }
 
-static dir_hash_tab_t *
+/*
+ * Create a directory hash index structure based on the size of the directory we
+ * are about to try to repair. The size passed in is the size of the data
+ * segment of the directory in bytes, so we don't really know exactly how many
+ * entries are in it. Hence assume an entry size of around 64 bytes - that's a
+ * name length of 40+ bytes so should cover a most situations with really large
+ * directories.
+ */
+static struct dir_hash_tab *
 dir_hash_init(
-       xfs_fsize_t     size)
+       xfs_fsize_t             size)
 {
-       dir_hash_tab_t  *hashtab;
-       int             hsize;
+       struct dir_hash_tab     *hashtab = NULL;
+       int                     hsize;
 
-       hsize = size / (16 * 4);
-       if (hsize > 65536)
-               hsize = 63336;
-       else if (hsize < 16)
+       hsize = size / 64;
+       if (hsize < 16)
                hsize = 16;
-       if ((hashtab = calloc(DIR_HASH_TAB_SIZE(hsize), 1)) == NULL)
+
+       /*
+        * Try to allocate as large a hash table as possible. Failure to
+        * allocate isn't fatal, it will just result in slower performance as we
+        * reduce the size of the table.
+        */
+       while (hsize >= 16) {
+               hashtab = calloc(DIR_HASH_TAB_SIZE(hsize), 1);
+               if (hashtab)
+                       break;
+               hsize /= 2;
+       }
+       if (!hashtab)
                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);
+       hashtab->byhash = (struct dir_hash_ent **)((char *)hashtab +
+               sizeof(struct dir_hash_tab));
+       INIT_RADIX_TREE(&hashtab->byaddr, 0);
        return hashtab;
 }
 
 static int
 dir_hash_see(
-       dir_hash_tab_t          *hashtab,
+       struct dir_hash_tab     *hashtab,
        xfs_dahash_t            hash,
        xfs_dir2_dataptr_t      addr)
 {
-       int                     i;
-       dir_hash_ent_t          *p;
-
-       i = DIR_HASH_FUNC(hashtab, addr);
-       for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
-               if (p->address != addr)
-                       continue;
-               if (p->seen)
-                       return DIR_HASH_CK_DUPLEAF;
-               if (p->junkit == 0 && p->hashval != hash)
-                       return DIR_HASH_CK_BADHASH;
-               p->seen = 1;
-               return DIR_HASH_CK_OK;
-       }
-       return DIR_HASH_CK_NODATA;
+       struct dir_hash_ent     *p;
+
+       p = radix_tree_lookup(&hashtab->byaddr, addr);
+       if (!p)
+               return DIR_HASH_CK_NODATA;
+       if (!radix_tree_tag_get(&hashtab->byaddr, addr, HT_UNSEEN))
+               return DIR_HASH_CK_DUPLEAF;
+       if (p->junkit == 0 && p->hashval != hash)
+               return DIR_HASH_CK_BADHASH;
+       radix_tree_tag_clear(&hashtab->byaddr, addr, HT_UNSEEN);
+       return DIR_HASH_CK_OK;
 }
 
 static void
 dir_hash_update_ftype(
-       dir_hash_tab_t          *hashtab,
+       struct dir_hash_tab     *hashtab,
        xfs_dir2_dataptr_t      addr,
        uint8_t                 ftype)
 {
-       int                     i;
-       dir_hash_ent_t          *p;
+       struct dir_hash_ent     *p;
 
-       i = DIR_HASH_FUNC(hashtab, addr);
-       for (p = hashtab->byaddr[i]; p; p = p->nextbyaddr) {
-               if (p->address != addr)
-                       continue;
-               p->name.type = ftype;
-       }
+       p = radix_tree_lookup(&hashtab->byaddr, addr);
+       if (!p)
+               return;
+       p->name.type = ftype;
 }
 
 /*
@@ -419,7 +381,7 @@ dir_hash_update_ftype(
  */
 static int
 dir_hash_see_all(
-       dir_hash_tab_t          *hashtab,
+       struct dir_hash_tab     *hashtab,
        xfs_dir2_leaf_entry_t   *ents,
        int                     count,
        int                     stale)
@@ -442,67 +404,43 @@ dir_hash_see_all(
 }
 
 /*
- * Convert name pointers into locally allocated memory.
- * This must only be done after all the entries have been added.
- */
-static void
-dir_hash_dup_names(dir_hash_tab_t *hashtab)
-{
-       unsigned char           *name;
-       dir_hash_ent_t          *p;
-
-       if (hashtab->names_duped)
-               return;
-
-       for (p = hashtab->first; p; p = p->nextbyorder) {
-               name = malloc(p->name.len);
-               memcpy(name, p->name.name, p->name.len);
-               p->name.name = name;
-       }
-       hashtab->names_duped = 1;
-}
-
-/*
- * Given a block number in a fork, return the next valid block number
- * (not a hole).
- * If this is the last block number then NULLFILEOFF is returned.
- *
- * This was originally in the kernel, but only used in xfs_repair.
+ * Given a block number in a fork, return the next valid block number (not a
+ * hole).  If this is the last block number then NULLFILEOFF is returned.
  */
 static int
 bmap_next_offset(
-       xfs_trans_t     *tp,                    /* transaction pointer */
-       xfs_inode_t     *ip,                    /* incore inode */
-       xfs_fileoff_t   *bnop,                  /* current block */
-       int             whichfork)              /* data or attr fork */
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *bnop)
 {
-       xfs_fileoff_t   bno;                    /* current block */
-       int             error;                  /* error return value */
-       xfs_bmbt_irec_t got;                    /* current extent value */
-       struct xfs_ifork        *ifp;           /* inode fork pointer */
+       xfs_fileoff_t           bno;
+       int                     error;
+       struct xfs_bmbt_irec    got;
        struct xfs_iext_cursor  icur;
 
-       if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
-           XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
-           XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
-              return EIO;
-       if (XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL) {
+       switch (ip->i_df.if_format) {
+       case XFS_DINODE_FMT_LOCAL:
                *bnop = NULLFILEOFF;
                return 0;
+       case XFS_DINODE_FMT_BTREE:
+       case XFS_DINODE_FMT_EXTENTS:
+               break;
+       default:
+               return EIO;
        }
-       ifp = XFS_IFORK_PTR(ip, whichfork);
-       if (!(ifp->if_flags & XFS_IFEXTENTS) &&
-           (error = -libxfs_iread_extents(tp, ip, whichfork)))
+
+        /* Read extent map. */
+       error = -libxfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+       if (error)
                return error;
+
        bno = *bnop + 1;
-       if (!libxfs_iext_lookup_extent(ip, ifp, bno, &icur, &got))
+       if (!libxfs_iext_lookup_extent(ip, &ip->i_df, bno, &icur, &got))
                *bnop = NULLFILEOFF;
        else
                *bnop = got.br_startoff < bno ? bno : got.br_startoff;
        return 0;
 }
 
-
 static void
 res_failed(
        int     err)
@@ -513,7 +451,23 @@ res_failed(
                do_error(_("xfs_trans_reserve returned %d\n"), err);
 }
 
-void
+static inline void
+reset_inode_fields(struct xfs_inode *ip)
+{
+       ip->i_projid = 0;
+       ip->i_disk_size = 0;
+       ip->i_nblocks = 0;
+       ip->i_extsize = 0;
+       ip->i_cowextsize = 0;
+       ip->i_flushiter = 0;
+       ip->i_forkoff = 0;
+       ip->i_diflags = 0;
+       ip->i_diflags2 = 0;
+       ip->i_crtime.tv_sec = 0;
+       ip->i_crtime.tv_nsec = 0;
+}
+
+static void
 mk_rbmino(xfs_mount_t *mp)
 {
        xfs_trans_t     *tp;
@@ -524,7 +478,6 @@ mk_rbmino(xfs_mount_t *mp)
        int             error;
        xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
-       int             vers;
        int             times;
        uint            blocks;
 
@@ -535,27 +488,26 @@ mk_rbmino(xfs_mount_t *mp)
        if (i)
                res_failed(i);
 
-       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 0, &ip);
+       error = -libxfs_iget(mp, tp, mp->m_sb.sb_rbmino, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime bitmap inode -- error - %d\n"),
                        error);
        }
 
-       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = S_IFREG;
-       ip->i_d.di_version = vers;
-       ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
-       ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
+       if (ip->i_afp)
+               ip->i_afp->if_format = XFS_DINODE_FMT_EXTENTS;
 
        set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (ip->i_d.di_version == 3) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -563,17 +515,19 @@ mk_rbmino(xfs_mount_t *mp)
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
-       ip->i_d.di_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
+       ip->i_disk_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
 
        /*
         * commit changes
         */
+       libxfs_trans_ijoin(tp, ip, 0);
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
 
        /*
         * then allocate blocks for file and fill with zeroes (stolen
@@ -604,14 +558,19 @@ mk_rbmino(xfs_mount_t *mp)
                        bno += ep->br_blockcount;
                }
        }
-       libxfs_trans_commit(tp);
-       IRELE(ip);
+       error = -libxfs_trans_commit(tp);
+       if (error) {
+               do_error(
+               _("allocation of the realtime bitmap failed, error = %d\n"),
+                       error);
+       }
+       libxfs_irele(ip);
 }
 
 static int
 fill_rbmino(xfs_mount_t *mp)
 {
-       xfs_buf_t       *bp;
+       struct xfs_buf  *bp;
        xfs_trans_t     *tp;
        xfs_inode_t     *ip;
        xfs_rtword_t    *bmp;
@@ -627,7 +586,7 @@ fill_rbmino(xfs_mount_t *mp)
        if (error)
                res_failed(error);
 
-       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 0, &ip);
+       error = -libxfs_iget(mp, tp, mp->m_sb.sb_rbmino, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime bitmap inode -- error - %d\n"),
@@ -668,15 +627,18 @@ _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime bitmap inode %
                bno++;
        }
 
-       libxfs_trans_commit(tp);
-       IRELE(ip);
+       libxfs_trans_ijoin(tp, ip, 0);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
+       libxfs_irele(ip);
        return(0);
 }
 
 static int
 fill_rsumino(xfs_mount_t *mp)
 {
-       xfs_buf_t       *bp;
+       struct xfs_buf  *bp;
        xfs_trans_t     *tp;
        xfs_inode_t     *ip;
        xfs_suminfo_t   *smp;
@@ -694,7 +656,7 @@ fill_rsumino(xfs_mount_t *mp)
        if (error)
                res_failed(error);
 
-       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 0, &ip);
+       error = -libxfs_iget(mp, tp, mp->m_sb.sb_rsumino, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime summary inode -- error - %d\n"),
@@ -724,7 +686,7 @@ fill_rsumino(xfs_mount_t *mp)
                        do_warn(
 _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime summary inode %" PRIu64 "\n"),
                                bno, map.br_startblock, mp->m_sb.sb_rsumino);
-                       IRELE(ip);
+                       libxfs_irele(ip);
                        return(1);
                }
 
@@ -736,8 +698,11 @@ _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime summary inode
                bno++;
        }
 
-       libxfs_trans_commit(tp);
-       IRELE(ip);
+       libxfs_trans_ijoin(tp, ip, 0);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
+       libxfs_irele(ip);
        return(0);
 }
 
@@ -753,7 +718,6 @@ mk_rsumino(xfs_mount_t *mp)
        int             nsumblocks;
        xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
-       int             vers;
        int             times;
        uint            blocks;
 
@@ -764,27 +728,26 @@ mk_rsumino(xfs_mount_t *mp)
        if (i)
                res_failed(i);
 
-       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 0, &ip);
+       error = -libxfs_iget(mp, tp, mp->m_sb.sb_rsumino, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime summary inode -- error - %d\n"),
                        error);
        }
 
-       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = S_IFREG;
-       ip->i_d.di_version = vers;
-       ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
-       ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
+       if (ip->i_afp)
+               ip->i_afp->if_format = XFS_DINODE_FMT_EXTENTS;
 
        set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (ip->i_d.di_version == 3) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -792,17 +755,19 @@ mk_rsumino(xfs_mount_t *mp)
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
-       ip->i_d.di_size = mp->m_rsumsize;
+       ip->i_disk_size = mp->m_rsumsize;
 
        /*
         * commit changes
         */
+       libxfs_trans_ijoin(tp, ip, 0);
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
 
        /*
         * then allocate blocks for file and fill with zeroes (stolen
@@ -833,8 +798,13 @@ mk_rsumino(xfs_mount_t *mp)
                        bno += ep->br_blockcount;
                }
        }
-       libxfs_trans_commit(tp);
-       IRELE(ip);
+       error = -libxfs_trans_commit(tp);
+       if (error) {
+               do_error(
+       _("allocation of the realtime summary ino failed, error = %d\n"),
+                       error);
+       }
+       libxfs_irele(ip);
 }
 
 /*
@@ -849,7 +819,6 @@ mk_root_dir(xfs_mount_t *mp)
        int             error;
        const mode_t    mode = 0755;
        ino_tree_node_t *irec;
-       int             vers;
        int             times;
 
        ip = NULL;
@@ -857,7 +826,7 @@ mk_root_dir(xfs_mount_t *mp)
        if (i)
                res_failed(i);
 
-       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rootino, 0, 0, &ip);
+       error = -libxfs_iget(mp, tp, mp->m_sb.sb_rootino, 0, &ip);
        if (error) {
                do_error(_("could not iget root inode -- error - %d\n"), error);
        }
@@ -865,41 +834,41 @@ mk_root_dir(xfs_mount_t *mp)
        /*
         * take care of the core -- initialization from xfs_ialloc()
         */
-       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = mode|S_IFDIR;
-       ip->i_d.di_version = vers;
-       ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
-       ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
+       if (ip->i_afp)
+               ip->i_afp->if_format = XFS_DINODE_FMT_EXTENTS;
 
-       set_nlink(VFS_I(ip), 1);        /* account for . */
+       set_nlink(VFS_I(ip), 2);        /* account for . and .. */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (ip->i_d.di_version == 3) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
-
+       libxfs_trans_ijoin(tp, ip, 0);
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
        /*
         * initialize the directory
         */
-       ip->d_ops = mp->m_dir_inode_ops;
        libxfs_dir_init(tp, ip, ip);
 
-       libxfs_trans_commit(tp);
-       IRELE(ip);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(_("%s: commit failed, error %d\n"), __func__, error);
+
+       libxfs_irele(ip);
 
        irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
                                XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino));
@@ -931,8 +900,7 @@ mk_orphanage(xfs_mount_t *mp)
         * would have been cleared in phase3 and phase4.
         */
 
-       i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
-                       &xfs_default_ifork_ops);
+       i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip);
        if (i)
                do_error(_("%d - couldn't iget root inode to obtain %s\n"),
                        i, ORPHANAGE);
@@ -956,13 +924,12 @@ mk_orphanage(xfs_mount_t *mp)
         * use iget/ijoin instead of trans_iget because the ialloc
         * wrapper can commit the transaction and start a new one
         */
-/*     i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
-                       &xfs_default_ifork_ops);
+/*     i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip);
        if (i)
                do_error(_("%d - couldn't iget root inode to make %s\n"),
                        i, ORPHANAGE);*/
 
-       error = -libxfs_inode_alloc(&tp, pip, mode|S_IFDIR,
+       error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFDIR,
                                        1, 0, &zerocr, &zerofsx, &ip);
        if (error) {
                do_error(_("%s inode allocation failed %d\n"),
@@ -998,6 +965,7 @@ mk_orphanage(xfs_mount_t *mp)
         */
        set_inode_used(irec, ino_offset);
        add_inode_ref(irec, ino_offset);
+       add_inode_reached(irec, ino_offset);
 
        /*
         * now that we know the transaction will stay around,
@@ -1016,22 +984,25 @@ mk_orphanage(xfs_mount_t *mp)
 
        /*
         * bump up the link count in the root directory to account
-        * for .. in the new directory
+        * for .. in the new directory, and update the irec copy of the
+        * on-disk nlink so we don't fail the link count check later.
         */
        inc_nlink(VFS_I(pip));
-       add_inode_ref(find_inode_rec(mp,
-                               XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
-                               XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino)), 0);
-
-
+       irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rootino),
+                                 XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rootino));
+       add_inode_ref(irec, 0);
+       set_inode_disk_nlinks(irec, 0, get_inode_disk_nlinks(irec, 0) + 1);
 
        libxfs_trans_log_inode(tp, pip, XFS_ILOG_CORE);
        libxfs_dir_init(tp, ip, pip);
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       libxfs_trans_commit(tp);
-       IRELE(ip);
-       IRELE(pip);
-       add_inode_reached(irec,ino_offset);
+       error = -libxfs_trans_commit(tp);
+       if (error) {
+               do_error(_("%s directory creation failed -- bmapf error %d\n"),
+                       ORPHANAGE, error);
+       }
+       libxfs_irele(ip);
+       libxfs_irele(pip);
 
        return(ino);
 }
@@ -1061,8 +1032,7 @@ mv_orphanage(
        xname.len = snprintf((char *)fname, sizeof(fname), "%llu",
                                (unsigned long long)ino);
 
-       err = -libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip,
-                       &xfs_default_ifork_ops);
+       err = -libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip);
        if (err)
                do_error(_("%d - couldn't iget orphanage inode\n"), err);
        /*
@@ -1075,7 +1045,7 @@ mv_orphanage(
                                        (unsigned long long)ino, ++incr);
 
        /* Orphans may not have a proper parent, so use custom ops here */
-       err = -libxfs_iget(mp, NULL, ino, 0, &ino_p, &phase6_ifork_ops);
+       err = -libxfs_iget(mp, NULL, ino, 0, &ino_p);
        if (err)
                do_error(_("%d - couldn't iget disconnected inode\n"), err);
 
@@ -1097,9 +1067,7 @@ mv_orphanage(
                        err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_rename,
                                                  nres, 0, 0, &tp);
                        if (err)
-                               do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                                       err);
+                               res_failed(err);
 
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1108,8 +1076,7 @@ mv_orphanage(
                                                ino, nres);
                        if (err)
                                do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                                       ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
 
                        if (irec)
                                add_inode_ref(irec, ino_offset);
@@ -1121,19 +1088,19 @@ mv_orphanage(
                                        orphanage_ino, nres);
                        if (err)
                                do_error(
-       _("creation of .. entry failed (%d), filesystem may be out of space\n"),
-                                       err);
+       _("creation of .. entry failed (%d)\n"), err);
 
                        inc_nlink(VFS_I(ino_p));
                        libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
-                       libxfs_trans_commit(tp);
+                       err = -libxfs_trans_commit(tp);
+                       if (err)
+                               do_error(
+       _("creation of .. entry failed (%d)\n"), err);
                } else  {
                        err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_rename,
                                                  nres, 0, 0, &tp);
                        if (err)
-                               do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                                       err);
+                               res_failed(err);
 
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1143,8 +1110,7 @@ mv_orphanage(
                                                ino, nres);
                        if (err)
                                do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                                       ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
 
                        if (irec)
                                add_inode_ref(irec, ino_offset);
@@ -1162,11 +1128,13 @@ mv_orphanage(
                                                nres);
                                if (err)
                                        do_error(
-       _("name replace op failed (%d), filesystem may be out of space\n"),
-                                               err);
+       _("name replace op failed (%d)\n"), err);
                        }
 
-                       libxfs_trans_commit(tp);
+                       err = -libxfs_trans_commit(tp);
+                       if (err)
+                               do_error(
+       _("orphanage name replace op failed (%d)\n"), err);
                }
 
        } else  {
@@ -1180,9 +1148,7 @@ mv_orphanage(
                err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove,
                                          nres, 0, 0, &tp);
                if (err)
-                       do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                               err);
+                       res_failed(err);
 
                libxfs_trans_ijoin(tp, orphanage_ip, 0);
                libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1191,16 +1157,18 @@ mv_orphanage(
                                                nres);
                if (err)
                        do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                               ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
                ASSERT(err == 0);
 
                set_nlink(VFS_I(ino_p), 1);
                libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
-               libxfs_trans_commit(tp);
+               err = -libxfs_trans_commit(tp);
+               if (err)
+                       do_error(
+       _("orphanage name create failed (%d)\n"), err);
        }
-       IRELE(ino_p);
-       IRELE(orphanage_ip);
+       libxfs_irele(ino_p);
+       libxfs_irele(orphanage_ip);
 }
 
 static int
@@ -1233,23 +1201,21 @@ dir_binval(
        struct xfs_ifork        *ifp;
        struct xfs_da_geometry  *geo;
        struct xfs_buf          *bp;
-       xfs_dablk_t             dabno, end_dabno;
+       xfs_dablk_t             dabno;
        int                     error = 0;
 
-       if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
-           ip->i_d.di_format != XFS_DINODE_FMT_BTREE)
+       if (ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS &&
+           ip->i_df.if_format != XFS_DINODE_FMT_BTREE)
                return 0;
 
        geo = tp->t_mountp->m_dir_geo;
        ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
        for_each_xfs_iext(ifp, &icur, &rec) {
-               dabno = xfs_dir2_db_to_da(geo, rec.br_startoff +
-                               geo->fsbcount - 1);
-               end_dabno = xfs_dir2_db_to_da(geo, rec.br_startoff +
-                               rec.br_blockcount);
-               for (; dabno <= end_dabno; dabno += geo->fsbcount) {
+               for (dabno = roundup(rec.br_startoff, geo->fsbcount);
+                    dabno < rec.br_startoff + rec.br_blockcount;
+                    dabno += geo->fsbcount) {
                        bp = NULL;
-                       error = -libxfs_da_get_buf(tp, ip, dabno, -2, &bp,
+                       error = -libxfs_da_get_buf(tp, ip, dabno, &bp,
                                        whichfork);
                        if (error)
                                return error;
@@ -1270,20 +1236,20 @@ dir_binval(
 
 static void
 longform_dir2_rebuild(
-       xfs_mount_t             *mp,
+       struct xfs_mount        *mp,
        xfs_ino_t               ino,
-       xfs_inode_t             *ip,
-       ino_tree_node_t         *irec,
+       struct xfs_inode        *ip,
+       struct ino_tree_node    *irec,
        int                     ino_offset,
-       dir_hash_tab_t          *hashtab)
+       struct dir_hash_tab     *hashtab)
 {
        int                     error;
        int                     nres;
-       xfs_trans_t             *tp;
+       struct xfs_trans        *tp;
        xfs_fileoff_t           lastblock;
-       xfs_inode_t             pip;
-       dir_hash_ent_t          *p;
-       int                     done;
+       struct xfs_inode        pip;
+       struct dir_hash_ent     *p;
+       int                     done = 0;
 
        /*
         * trash directory completely and rebuild from scratch using the
@@ -1311,21 +1277,33 @@ longform_dir2_rebuild(
 
        error = dir_binval(tp, ip, XFS_DATA_FORK);
        if (error)
-               res_failed(error);
+               do_error(_("error %d invalidating directory %llu blocks\n"),
+                               error, (unsigned long long)ip->i_ino);
 
        if ((error = -libxfs_bmap_last_offset(ip, &lastblock, XFS_DATA_FORK)))
                do_error(_("xfs_bmap_last_offset failed -- error - %d\n"),
                        error);
 
        /* free all data, leaf, node and freespace blocks */
-       error = -libxfs_bunmapi(tp, ip, 0, lastblock, XFS_BMAPI_METADATA, 0,
-                               &done);
-       if (error) {
-               do_warn(_("xfs_bunmapi failed -- error - %d\n"), error);
-               goto out_bmap_cancel;
-       }
-
-       ASSERT(done);
+       while (!done) {
+              error = -libxfs_bunmapi(tp, ip, 0, lastblock, XFS_BMAPI_METADATA,
+                                      0, &done);
+              if (error) {
+                      do_warn(_("xfs_bunmapi failed -- error - %d\n"), error);
+                      goto out_bmap_cancel;
+              }
+              error = -libxfs_defer_finish(&tp);
+              if (error) {
+                      do_warn(("defer_finish failed -- error - %d\n"), error);
+                      goto out_bmap_cancel;
+              }
+              /*
+               * Close out trans and start the next one in the chain.
+               */
+              error = -libxfs_trans_roll_inode(&tp, ip);
+              if (error)
+                       goto out_bmap_cancel;
+        }
 
        error = -libxfs_dir_init(tp, ip, &pip);
        if (error) {
@@ -1333,7 +1311,10 @@ longform_dir2_rebuild(
                goto out_bmap_cancel;
        }
 
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(
+       _("dir init failed (%d)\n"), error);
 
        if (ino == mp->m_sb.sb_rootino)
                need_root_dotdot = 0;
@@ -1359,12 +1340,14 @@ longform_dir2_rebuild(
                                                nres);
                if (error) {
                        do_warn(
-_("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"),
-                               ino, error);
+_("name create failed in ino %" PRIu64 " (%d)\n"), ino, error);
                        goto out_bmap_cancel;
                }
 
-               libxfs_trans_commit(tp);
+               error = -libxfs_trans_commit(tp);
+               if (error)
+                       do_error(
+_("name create failed (%d) during rebuild\n"), error);
        }
 
        return;
@@ -1397,6 +1380,7 @@ dir2_kill_block(
                res_failed(error);
        libxfs_trans_ijoin(tp, ip, 0);
        libxfs_trans_bjoin(tp, bp);
+       libxfs_trans_bhold(tp, bp);
        memset(&args, 0, sizeof(args));
        args.dp = ip;
        args.trans = tp;
@@ -1410,7 +1394,52 @@ dir2_kill_block(
        if (error)
                do_error(_("shrink_inode failed inode %" PRIu64 " block %u\n"),
                        ip->i_ino, da_bno);
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(
+_("directory shrink failed (%d)\n"), error);
+}
+
+static inline void
+check_longform_ftype(
+       struct xfs_mount        *mp,
+       struct xfs_inode        *ip,
+       xfs_dir2_data_entry_t   *dep,
+       ino_tree_node_t         *irec,
+       int                     ino_offset,
+       struct dir_hash_tab     *hashtab,
+       xfs_dir2_dataptr_t      addr,
+       struct xfs_da_args      *da,
+       struct xfs_buf          *bp)
+{
+       xfs_ino_t               inum = be64_to_cpu(dep->inumber);
+       uint8_t                 dir_ftype;
+       uint8_t                 ino_ftype;
+
+       if (!xfs_has_ftype(mp))
+               return;
+
+       dir_ftype = libxfs_dir2_data_get_ftype(mp, dep);
+       ino_ftype = get_inode_ftype(irec, ino_offset);
+
+       if (dir_ftype == ino_ftype)
+               return;
+
+       if (no_modify) {
+               do_warn(
+_("would fix ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
+                       dir_ftype, ino_ftype,
+                       ip->i_ino, inum);
+               return;
+       }
+
+       do_warn(
+_("fixing ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
+               dir_ftype, ino_ftype,
+               ip->i_ino, inum);
+       libxfs_dir2_data_put_ftype(mp, dep, ino_ftype);
+       libxfs_dir2_data_log_entry(da, bp, dep);
+       dir_hash_update_ftype(hashtab, addr, ino_ftype);
 }
 
 /*
@@ -1419,21 +1448,20 @@ dir2_kill_block(
  */
 static void
 longform_dir2_entry_check_data(
-       xfs_mount_t             *mp,
-       xfs_inode_t             *ip,
+       struct xfs_mount        *mp,
+       struct xfs_inode        *ip,
        int                     *num_illegal,
        int                     *need_dot,
-       ino_tree_node_t         *current_irec,
+       struct ino_tree_node    *current_irec,
        int                     current_ino_offset,
-       struct xfs_buf          **bpp,
-       dir_hash_tab_t          *hashtab,
+       struct xfs_buf          *bp,
+       struct dir_hash_tab     *hashtab,
        freetab_t               **freetabp,
        xfs_dablk_t             da_bno,
        int                     isblock)
 {
        xfs_dir2_dataptr_t      addr;
        xfs_dir2_leaf_entry_t   *blp;
-       struct xfs_buf          *bp;
        xfs_dir2_block_tail_t   *btp;
        struct xfs_dir2_data_hdr *d;
        xfs_dir2_db_t           db;
@@ -1464,9 +1492,8 @@ longform_dir2_entry_check_data(
        };
 
 
-       bp = *bpp;
        d = bp->b_addr;
-       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
+       ptr = (char *)d + mp->m_dir_geo->data_entry_offset;
        nbad = 0;
        needscan = needlog = 0;
        junkit = 0;
@@ -1477,13 +1504,13 @@ longform_dir2_entry_check_data(
                endptr = (char *)blp;
                if (endptr > (char *)btp)
                        endptr = (char *)btp;
-               if (xfs_sb_version_hascrc(&mp->m_sb))
+               if (xfs_has_crc(mp))
                        wantmagic = XFS_DIR3_BLOCK_MAGIC;
                else
                        wantmagic = XFS_DIR2_BLOCK_MAGIC;
        } else {
                endptr = (char *)d + mp->m_dir_geo->blksize;
-               if (xfs_sb_version_hascrc(&mp->m_sb))
+               if (xfs_has_crc(mp))
                        wantmagic = XFS_DIR3_DATA_MAGIC;
                else
                        wantmagic = XFS_DIR2_DATA_MAGIC;
@@ -1526,7 +1553,7 @@ longform_dir2_entry_check_data(
                                break;
 
                        /* check for block with no data entries */
-                       if ((ptr == (char *)M_DIROPS(mp)->data_entry_p(d)) &&
+                       if ((ptr == (char *)d + mp->m_dir_geo->data_entry_offset) &&
                            (ptr + be16_to_cpu(dup->length) >= endptr)) {
                                junkit = 1;
                                *num_illegal += 1;
@@ -1541,12 +1568,12 @@ longform_dir2_entry_check_data(
 
                /* validate data entry size */
                dep = (xfs_dir2_data_entry_t *)ptr;
-               if (ptr + M_DIROPS(mp)->data_entsize(dep->namelen) > endptr)
+               if (ptr + libxfs_dir2_data_entsize(mp, dep->namelen) > endptr)
                        break;
-               if (be16_to_cpu(*M_DIROPS(mp)->data_entry_tag_p(dep)) !=
+               if (be16_to_cpu(*libxfs_dir2_data_entry_tag_p(mp, dep)) !=
                                                (char *)dep - (char *)d)
                        break;
-               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
+               ptr += libxfs_dir2_data_entsize(mp, dep->namelen);
        }
 
        /* did we find an empty or corrupt block? */
@@ -1565,10 +1592,8 @@ longform_dir2_entry_check_data(
                        dir2_kill_block(mp, ip, da_bno, bp);
                } else {
                        do_warn(_("would junk block\n"));
-                       libxfs_putbuf(bp);
                }
                freetab->ents[db].v = NULLDATAOFF;
-               *bpp = NULL;
                return;
        }
 
@@ -1595,7 +1620,7 @@ longform_dir2_entry_check_data(
                        do_warn(_("would fix magic # to %#x\n"), wantmagic);
        }
        lastfree = 0;
-       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
+       ptr = (char *)d + mp->m_dir_geo->data_entry_offset;
        /*
         * look at each entry.  reference inode pointed to by each
         * entry in the incore inode tree.
@@ -1635,7 +1660,7 @@ longform_dir2_entry_check_data(
                addr = xfs_dir2_db_off_to_dataptr(mp->m_dir_geo, db,
                                                  ptr - (char *)d);
                dep = (xfs_dir2_data_entry_t *)ptr;
-               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
+               ptr += libxfs_dir2_data_entsize(mp, dep->namelen);
                inum = be64_to_cpu(dep->inumber);
                lastfree = 0;
                /*
@@ -1713,7 +1738,7 @@ longform_dir2_entry_check_data(
                 * check for duplicate names in directory.
                 */
                if (!dir_hash_add(mp, hashtab, addr, inum, dep->namelen,
-                               dep->name, M_DIROPS(mp)->data_get_ftype(dep))) {
+                               dep->name, libxfs_dir2_data_get_ftype(mp, dep))) {
                        nbad++;
                        if (entry_junked(
        _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
@@ -1749,13 +1774,19 @@ longform_dir2_entry_check_data(
                                if (entry_junked(
        _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is not in the the first block"), fname,
                                                inum, ip->i_ino)) {
+                                       dir_hash_junkit(hashtab, addr);
                                        dep->name[0] = '/';
                                        libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
+
+                       if (!nbad)
+                               check_longform_ftype(mp, ip, dep, irec,
+                                               ino_offset, hashtab, addr, &da,
+                                               bp);
                        continue;
                }
-               ASSERT(no_modify || !verify_inum(mp, inum));
+               ASSERT(no_modify || libxfs_verify_dir_ino(mp, inum));
                /*
                 * special case the . entry.  we know there's only one
                 * '.' and only '.' points to itself because bogus entries
@@ -1770,52 +1801,34 @@ longform_dir2_entry_check_data(
                               (dep->name[0] == '.' && dep->namelen == 1));
                        add_inode_ref(current_irec, current_ino_offset);
                        if (da_bno != 0 ||
-                           dep != M_DIROPS(mp)->data_entry_p(d)) {
+                           dep != (void *)d + mp->m_dir_geo->data_entry_offset) {
                                /* "." should be the first entry */
                                nbad++;
                                if (entry_junked(
        _("entry \"%s\" in dir %" PRIu64 " is not the first entry"),
                                                fname, inum, ip->i_ino)) {
+                                       dir_hash_junkit(hashtab, addr);
                                        dep->name[0] = '/';
                                        libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
+
+                       if (!nbad)
+                               check_longform_ftype(mp, ip, dep, irec,
+                                               ino_offset, hashtab, addr, &da,
+                                               bp);
                        *need_dot = 0;
                        continue;
                }
                /*
                 * skip entries with bogus inumbers if we're in no modify mode
                 */
-               if (no_modify && verify_inum(mp, inum))
+               if (no_modify && !libxfs_verify_dir_ino(mp, inum))
                        continue;
 
                /* validate ftype field if supported */
-               if (xfs_sb_version_hasftype(&mp->m_sb)) {
-                       uint8_t dir_ftype;
-                       uint8_t ino_ftype;
-
-                       dir_ftype = M_DIROPS(mp)->data_get_ftype(dep);
-                       ino_ftype = get_inode_ftype(irec, ino_offset);
-
-                       if (dir_ftype != ino_ftype) {
-                               if (no_modify) {
-                                       do_warn(
-       _("would fix ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
-                                               dir_ftype, ino_ftype,
-                                               ip->i_ino, inum);
-                               } else {
-                                       do_warn(
-       _("fixing ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
-                                               dir_ftype, ino_ftype,
-                                               ip->i_ino, inum);
-                                       M_DIROPS(mp)->data_put_ftype(dep,
-                                                               ino_ftype);
-                                       libxfs_dir2_data_log_entry(&da, bp, dep);
-                                       dir_hash_update_ftype(hashtab, addr,
-                                                             ino_ftype);
-                               }
-                       }
-               }
+               check_longform_ftype(mp, ip, dep, irec, ino_offset, hashtab,
+                               addr, &da, bp);
 
                /*
                 * check easy case first, regular inode, just bump
@@ -1865,6 +1878,7 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
                                orphanage_ino = 0;
                        nbad++;
                        if (!no_modify)  {
+                               dir_hash_junkit(hashtab, addr);
                                dep->name[0] = '/';
                                libxfs_dir2_data_log_entry(&da, bp, dep);
                                if (verbose)
@@ -1879,14 +1893,16 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
        }
        *num_illegal += nbad;
        if (needscan)
-               libxfs_dir2_data_freescan_int(mp->m_dir_geo, M_DIROPS(mp),
-                               d, &i);
+               libxfs_dir2_data_freescan(mp, d, &i);
        if (needlog)
                libxfs_dir2_data_log_header(&da, bp);
-       libxfs_trans_commit(tp);
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(
+_("directory block fixing failed (%d)\n"), error);
 
        /* record the largest free space in the freetab for later checking */
-       bf = M_DIROPS(mp)->data_bestfree_p(d);
+       bf = libxfs_dir2_data_bestfree_p(mp, d);
        freetab->ents[db].v = be16_to_cpu(bf[0].length);
        freetab->ents[db].s = 0;
 }
@@ -1906,21 +1922,21 @@ __check_dir3_header(
        if (be64_to_cpu(owner) != ino) {
                do_warn(
 _("expected owner inode %" PRIu64 ", got %llu, directory block %" PRIu64 "\n"),
-                       ino, (unsigned long long)be64_to_cpu(owner), bp->b_bn);
+                       ino, (unsigned long long)be64_to_cpu(owner), xfs_buf_daddr(bp));
                return 1;
        }
        /* verify block number */
-       if (be64_to_cpu(blkno) != bp->b_bn) {
+       if (be64_to_cpu(blkno) != xfs_buf_daddr(bp)) {
                do_warn(
 _("expected block %" PRIu64 ", got %llu, directory inode %" PRIu64 "\n"),
-                       bp->b_bn, (unsigned long long)be64_to_cpu(blkno), ino);
+                       xfs_buf_daddr(bp), (unsigned long long)be64_to_cpu(blkno), ino);
                return 1;
        }
        /* verify uuid */
        if (platform_uuid_compare(uuid, &mp->m_sb.sb_meta_uuid) != 0) {
                do_warn(
 _("wrong FS UUID, directory inode %" PRIu64 " block %" PRIu64 "\n"),
-                       ino, bp->b_bn);
+                       ino, xfs_buf_daddr(bp));
                return 1;
        }
 
@@ -1956,10 +1972,10 @@ check_dir3_header(
  */
 static int
 longform_dir2_check_leaf(
-       xfs_mount_t             *mp,
-       xfs_inode_t             *ip,
-       dir_hash_tab_t          *hashtab,
-       freetab_t               *freetab)
+       struct xfs_mount        *mp,
+       struct xfs_inode        *ip,
+       struct dir_hash_tab     *hashtab,
+       struct freetab          *freetab)
 {
        int                     badtail;
        __be16                  *bestsp;
@@ -1975,8 +1991,7 @@ longform_dir2_check_leaf(
        int                     fixit = 0;
 
        da_bno = mp->m_dir_geo->leafblk;
-       error = dir_read_buf(ip, da_bno, -1, &bp, &xfs_dir3_leaf1_buf_ops,
-                            &fixit);
+       error = dir_read_buf(ip, da_bno, &bp, &xfs_dir3_leaf1_buf_ops, &fixit);
        if (error == EFSBADCRC || error == EFSCORRUPTED || fixit) {
                do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad CRC\n"),
@@ -1990,35 +2005,34 @@ longform_dir2_check_leaf(
        }
 
        leaf = bp->b_addr;
-       M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = M_DIROPS(mp)->leaf_ents_p(leaf);
+       libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf);
+       ents = leafhdr.ents;
        ltp = xfs_dir2_leaf_tail_p(mp->m_dir_geo, leaf);
        bestsp = xfs_dir2_leaf_bests_p(ltp);
        if (!(leafhdr.magic == XFS_DIR2_LEAF1_MAGIC ||
              leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) ||
                                leafhdr.forw || leafhdr.back ||
                                leafhdr.count < leafhdr.stale ||
-                               leafhdr.count >
-                                       M_DIROPS(mp)->leaf_max_ents(mp->m_dir_geo) ||
+                               leafhdr.count > mp->m_dir_geo->leaf_max_ents ||
                                (char *)&ents[leafhdr.count] > (char *)bestsp) {
                do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad header\n"),
                        da_bno, ip->i_ino);
-               libxfs_putbuf(bp);
+               libxfs_buf_relse(bp);
                return 1;
        }
 
        if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) {
                error = check_da3_header(mp, bp, ip->i_ino);
                if (error) {
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        return error;
                }
        }
 
        seeval = dir_hash_see_all(hashtab, ents, leafhdr.count, leafhdr.stale);
        if (dir_hash_check(hashtab, ip, seeval)) {
-               libxfs_putbuf(bp);
+               libxfs_buf_relse(bp);
                return 1;
        }
        badtail = freetab->nents != be32_to_cpu(ltp->bestcount);
@@ -2030,10 +2044,10 @@ longform_dir2_check_leaf(
                do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad tail\n"),
                        da_bno, ip->i_ino);
-               libxfs_putbuf(bp);
+               libxfs_buf_relse(bp);
                return 1;
        }
-       libxfs_putbuf(bp);
+       libxfs_buf_relse(bp);
        return fixit;
 }
 
@@ -2043,10 +2057,10 @@ longform_dir2_check_leaf(
  */
 static int
 longform_dir2_check_node(
-       xfs_mount_t             *mp,
-       xfs_inode_t             *ip,
-       dir_hash_tab_t          *hashtab,
-       freetab_t               *freetab)
+       struct xfs_mount        *mp,
+       struct xfs_inode        *ip,
+       struct dir_hash_tab     *hashtab,
+       struct freetab          *freetab)
 {
        struct xfs_buf          *bp;
        xfs_dablk_t             da_bno;
@@ -2068,7 +2082,7 @@ longform_dir2_check_node(
                        next_da_bno != NULLFILEOFF && da_bno < mp->m_dir_geo->freeblk;
                        da_bno = (xfs_dablk_t)next_da_bno) {
                next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
-               if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK))
+               if (bmap_next_offset(ip, &next_da_bno))
                        break;
 
                /*
@@ -2078,8 +2092,8 @@ longform_dir2_check_node(
                 * a node block, then we'll skip it below based on a magic
                 * number check.
                 */
-               error = dir_read_buf(ip, da_bno, -1, &bp,
-                                    &xfs_da3_node_buf_ops, &fixit);
+               error = dir_read_buf(ip, da_bno, &bp, &xfs_da3_node_buf_ops,
+                               &fixit);
                if (error) {
                        do_warn(
        _("can't read leaf block %u for directory inode %" PRIu64 ", error %d\n"),
@@ -2087,8 +2101,8 @@ longform_dir2_check_node(
                        return 1;
                }
                leaf = bp->b_addr;
-               M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
-               ents = M_DIROPS(mp)->leaf_ents_p(leaf);
+               libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf);
+               ents = leafhdr.ents;
                if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
                      leafhdr.magic == XFS_DIR3_LEAFN_MAGIC ||
                      leafhdr.magic == XFS_DA_NODE_MAGIC ||
@@ -2096,7 +2110,7 @@ longform_dir2_check_node(
                        do_warn(
        _("unknown magic number %#x for block %u in directory inode %" PRIu64 "\n"),
                                leafhdr.magic, da_bno, ip->i_ino);
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        return 1;
                }
 
@@ -2105,7 +2119,7 @@ longform_dir2_check_node(
                    leafhdr.magic == XFS_DA3_NODE_MAGIC) {
                        error = check_da3_header(mp, bp, ip->i_ino);
                        if (error) {
-                               libxfs_putbuf(bp);
+                               libxfs_buf_relse(bp);
                                return error;
                        }
                }
@@ -2113,7 +2127,7 @@ longform_dir2_check_node(
                /* ignore nodes */
                if (leafhdr.magic == XFS_DA_NODE_MAGIC ||
                    leafhdr.magic == XFS_DA3_NODE_MAGIC) {
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        continue;
                }
 
@@ -2122,17 +2136,17 @@ longform_dir2_check_node(
                 * the right ops on the buffer for when we write it back out.
                 */
                bp->b_ops = &xfs_dir3_leafn_buf_ops;
-               if (leafhdr.count > M_DIROPS(mp)->leaf_max_ents(mp->m_dir_geo) ||
+               if (leafhdr.count > mp->m_dir_geo->leaf_max_ents ||
                    leafhdr.count < leafhdr.stale) {
                        do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad header\n"),
                                da_bno, ip->i_ino);
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        return 1;
                }
                seeval = dir_hash_see_all(hashtab, ents,
                                        leafhdr.count, leafhdr.stale);
-               libxfs_putbuf(bp);
+               libxfs_buf_relse(bp);
                if (seeval != DIR_HASH_CK_OK)
                        return 1;
        }
@@ -2143,11 +2157,11 @@ longform_dir2_check_node(
             next_da_bno != NULLFILEOFF;
             da_bno = (xfs_dablk_t)next_da_bno) {
                next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
-               if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK))
+               if (bmap_next_offset(ip, &next_da_bno))
                        break;
 
-               error = dir_read_buf(ip, da_bno, -1, &bp,
-                                    &xfs_dir3_free_buf_ops, &fixit);
+               error = dir_read_buf(ip, da_bno, &bp, &xfs_dir3_free_buf_ops,
+                               &fixit);
                if (error) {
                        do_warn(
        _("can't read freespace block %u for directory inode %" PRIu64 ", error %d\n"),
@@ -2155,26 +2169,26 @@ longform_dir2_check_node(
                        return 1;
                }
                free = bp->b_addr;
-               M_DIROPS(mp)->free_hdr_from_disk(&freehdr, free);
-               bests = M_DIROPS(mp)->free_bests_p(free);
+               libxfs_dir2_free_hdr_from_disk(mp, &freehdr, free);
+               bests = freehdr.bests;
                fdb = xfs_dir2_da_to_db(mp->m_dir_geo, da_bno);
                if (!(freehdr.magic == XFS_DIR2_FREE_MAGIC ||
                      freehdr.magic == XFS_DIR3_FREE_MAGIC) ||
                    freehdr.firstdb !=
                        (fdb - xfs_dir2_byte_to_db(mp->m_dir_geo, XFS_DIR2_FREE_OFFSET)) *
-                       M_DIROPS(mp)->free_max_bests(mp->m_dir_geo) ||
+                       mp->m_dir_geo->free_max_bests ||
                    freehdr.nvalid < freehdr.nused) {
                        do_warn(
        _("free block %u for directory inode %" PRIu64 " bad header\n"),
                                da_bno, ip->i_ino);
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        return 1;
                }
 
                if (freehdr.magic == XFS_DIR3_FREE_MAGIC) {
                        error = check_dir3_header(mp, bp, ip->i_ino);
                        if (error) {
-                               libxfs_putbuf(bp);
+                               libxfs_buf_relse(bp);
                                return error;
                        }
                }
@@ -2185,7 +2199,7 @@ longform_dir2_check_node(
                                do_warn(
        _("free block %u entry %i for directory ino %" PRIu64 " bad\n"),
                                        da_bno, i, ip->i_ino);
-                               libxfs_putbuf(bp);
+                               libxfs_buf_relse(bp);
                                return 1;
                        }
                        used += be16_to_cpu(bests[i]) != NULLDATAOFF;
@@ -2195,10 +2209,10 @@ longform_dir2_check_node(
                        do_warn(
        _("free block %u for directory inode %" PRIu64 " bad nused\n"),
                                da_bno, ip->i_ino);
-                       libxfs_putbuf(bp);
+                       libxfs_buf_relse(bp);
                        return 1;
                }
-               libxfs_putbuf(bp);
+               libxfs_buf_relse(bp);
        }
        for (i = 0; i < freetab->nents; i++) {
                if ((freetab->ents[i].s == 0) &&
@@ -2218,47 +2232,41 @@ longform_dir2_check_node(
  * (ie. get libxfs to do all the grunt work)
  */
 static void
-longform_dir2_entry_check(xfs_mount_t  *mp,
-                       xfs_ino_t       ino,
-                       xfs_inode_t     *ip,
-                       int             *num_illegal,
-                       int             *need_dot,
-                       ino_tree_node_t *irec,
-                       int             ino_offset,
-                       dir_hash_tab_t  *hashtab)
+longform_dir2_entry_check(
+       struct xfs_mount        *mp,
+       xfs_ino_t               ino,
+       struct xfs_inode        *ip,
+       int                     *num_illegal,
+       int                     *need_dot,
+       struct ino_tree_node    *irec,
+       int                     ino_offset,
+       struct dir_hash_tab     *hashtab)
 {
-       struct xfs_buf          **bplist;
+       struct xfs_buf          *bp = NULL;
        xfs_dablk_t             da_bno;
        freetab_t               *freetab;
-       int                     num_bps;
        int                     i;
        int                     isblock;
        int                     isleaf;
        xfs_fileoff_t           next_da_bno;
        int                     seeval;
        int                     fixit = 0;
-       xfs_dir2_db_t           db;
        struct xfs_da_args      args;
 
        *need_dot = 1;
-       freetab = malloc(FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
+       freetab = malloc(FREETAB_SIZE(ip->i_disk_size / mp->m_dir_geo->blksize));
        if (!freetab) {
                do_error(_("malloc failed in %s (%" PRId64 " bytes)\n"),
                        __func__,
-                       FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
+                       FREETAB_SIZE(ip->i_disk_size / mp->m_dir_geo->blksize));
                exit(1);
        }
-       freetab->naents = ip->i_d.di_size / mp->m_dir_geo->blksize;
+       freetab->naents = ip->i_disk_size / mp->m_dir_geo->blksize;
        freetab->nents = 0;
        for (i = 0; i < freetab->naents; i++) {
                freetab->ents[i].v = NULLDATAOFF;
                freetab->ents[i].s = 0;
        }
-       num_bps = freetab->naents;
-       bplist = calloc(num_bps, sizeof(struct xfs_buf*));
-       if (!bplist)
-               do_error(_("calloc failed in %s (%zu bytes)\n"),
-                       __func__, num_bps * sizeof(struct xfs_buf*));
 
        /* is this a block, leaf, or node directory? */
        args.dp = ip;
@@ -2275,7 +2283,7 @@ longform_dir2_entry_check(xfs_mount_t     *mp,
                struct xfs_dir2_data_hdr *d;
 
                next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
-               if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK)) {
+               if (bmap_next_offset(ip, &next_da_bno)) {
                        /*
                         * if this is the first block, there isn't anything we
                         * can recover so we just trash it.
@@ -2287,23 +2295,12 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
                        break;
                }
 
-               db = xfs_dir2_da_to_db(mp->m_dir_geo, da_bno);
-               if (db >= num_bps) {
-                       /* more data blocks than expected */
-                       num_bps = db + 1;
-                       bplist = realloc(bplist, num_bps * sizeof(struct xfs_buf*));
-                       if (!bplist)
-                               do_error(_("realloc failed in %s (%zu bytes)\n"),
-                                       __func__,
-                                       num_bps * sizeof(struct xfs_buf*));
-               }
-
                if (isblock)
                        ops = &xfs_dir3_block_buf_ops;
                else
                        ops = &xfs_dir3_data_buf_ops;
 
-               error = dir_read_buf(ip, da_bno, -1, &bplist[db], ops, &fixit);
+               error = dir_read_buf(ip, da_bno, &bp, ops, &fixit);
                if (error) {
                        do_warn(
        _("can't read data block %u for directory inode %" PRIu64 " error %d\n"),
@@ -2323,21 +2320,25 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
                }
 
                /* check v5 metadata */
-               d = bplist[db]->b_addr;
+               d = bp->b_addr;
                if (be32_to_cpu(d->magic) == XFS_DIR3_BLOCK_MAGIC ||
                    be32_to_cpu(d->magic) == XFS_DIR3_DATA_MAGIC) {
-                       struct xfs_buf           *bp = bplist[db];
-
                        error = check_dir3_header(mp, bp, ino);
                        if (error) {
                                fixit++;
+                               if (isblock)
+                                       goto out_fix;
                                continue;
                        }
                }
 
                longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
-                               irec, ino_offset, &bplist[db], hashtab,
+                               irec, ino_offset, bp, hashtab,
                                &freetab, da_bno, isblock);
+               if (isblock)
+                       break;
+
+               libxfs_buf_relse(bp);
        }
        fixit |= (*num_illegal != 0) || dir2_is_badino(ino) || *need_dot;
 
@@ -2348,7 +2349,7 @@ longform_dir2_entry_check(xfs_mount_t     *mp,
                        xfs_dir2_block_tail_t   *btp;
                        xfs_dir2_leaf_entry_t   *blp;
 
-                       block = bplist[0]->b_addr;
+                       block = bp->b_addr;
                        btp = xfs_dir2_block_tail_p(mp->m_dir_geo, block);
                        blp = xfs_dir2_block_leaf_p(btp);
                        seeval = dir_hash_see_all(hashtab, blp,
@@ -2365,21 +2366,19 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
                }
        }
 out_fix:
+       if (isblock && bp)
+               libxfs_buf_relse(bp);
+
        if (!no_modify && (fixit || dotdot_update)) {
-               dir_hash_dup_names(hashtab);
-               for (i = 0; i < num_bps; i++)
-                       if (bplist[i])
-                               libxfs_putbuf(bplist[i]);
                longform_dir2_rebuild(mp, ino, ip, irec, ino_offset, hashtab);
                *num_illegal = 0;
                *need_dot = 0;
        } else {
-               for (i = 0; i < num_bps; i++)
-                       if (bplist[i])
-                               libxfs_putbuf(bplist[i]);
+               if (fixit || dotdot_update)
+                       do_warn(
+       _("would rebuild directory inode %" PRIu64 "\n"), ino);
        }
 
-       free(bplist);
        free(freetab);
 }
 
@@ -2405,8 +2404,8 @@ shortform_dir2_junk(
        if (lino == orphanage_ino)
                orphanage_ino = 0;
 
-       next_elen = M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen);
-       next_sfep = M_DIROPS(mp)->sf_nextentry(sfp, sfep);
+       next_elen = libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen);
+       next_sfep = libxfs_dir2_sf_nextentry(mp, sfp, sfep);
 
        /*
         * if we are just checking, simply return the pointer to the next entry
@@ -2444,13 +2443,14 @@ shortform_dir2_junk(
 }
 
 static void
-shortform_dir2_entry_check(xfs_mount_t *mp,
-                       xfs_ino_t       ino,
-                       xfs_inode_t     *ip,
-                       int             *ino_dirty,
-                       ino_tree_node_t *current_irec,
-                       int             current_ino_offset,
-                       dir_hash_tab_t  *hashtab)
+shortform_dir2_entry_check(
+       struct xfs_mount        *mp,
+       xfs_ino_t               ino,
+       struct xfs_inode        *ip,
+       int                     *ino_dirty,
+       struct ino_tree_node    *current_irec,
+       int                     current_ino_offset,
+       struct dir_hash_tab     *hashtab)
 {
        xfs_ino_t               lino;
        xfs_ino_t               parent;
@@ -2474,7 +2474,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
        bytes_deleted = 0;
 
        max_size = ifp->if_bytes;
-       ASSERT(ip->i_d.di_size <= ifp->if_bytes);
+       ASSERT(ip->i_disk_size <= ifp->if_bytes);
 
        /*
         * if just rebuild a directory due to a "..", update and return
@@ -2489,7 +2489,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                        do_warn(
        _("setting .. in sf dir inode %" PRIu64 " to %" PRIu64 "\n"),
                                ino, parent);
-                       M_DIROPS(mp)->sf_put_parent_ino(sfp, parent);
+                       libxfs_dir2_sf_put_parent_ino(sfp, parent);
                        *ino_dirty = 1;
                }
                return;
@@ -2506,7 +2506,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
        /*
         * Initialise i8 counter -- the parent inode number counts as well.
         */
-       i8 = M_DIROPS(mp)->sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM;
+       i8 = libxfs_dir2_sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM;
 
        /*
         * now run through entries, stop at first bad entry, don't need
@@ -2520,7 +2520,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                        sfep = next_sfep, i++)  {
                bad_sfnamelen = 0;
 
-               lino = M_DIROPS(mp)->sf_get_ino(sfp, sfep);
+               lino = libxfs_dir2_sf_get_ino(mp, sfp, sfep);
 
                namelen = sfep->namelen;
 
@@ -2538,7 +2538,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                        bad_sfnamelen = 1;
 
                        if (i == sfp->count - 1)  {
-                               namelen = ip->i_d.di_size -
+                               namelen = ip->i_disk_size -
                                        ((intptr_t) &sfep->name[0] -
                                         (intptr_t) sfp);
                        } else  {
@@ -2549,12 +2549,12 @@ shortform_dir2_entry_check(xfs_mount_t  *mp,
                                break;
                        }
                } else if (no_modify && (intptr_t) sfep - (intptr_t) sfp +
-                               + M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen)
-                               > ip->i_d.di_size)  {
+                               + libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen)
+                               > ip->i_disk_size)  {
                        bad_sfnamelen = 1;
 
                        if (i == sfp->count - 1)  {
-                               namelen = ip->i_d.di_size -
+                               namelen = ip->i_disk_size -
                                        ((intptr_t) &sfep->name[0] -
                                         (intptr_t) sfp);
                        } else  {
@@ -2570,15 +2570,15 @@ shortform_dir2_entry_check(xfs_mount_t  *mp,
                fname[sfep->namelen] = '\0';
 
                ASSERT(no_modify || (lino != NULLFSINO && lino != 0));
-               ASSERT(no_modify || !verify_inum(mp, lino));
+               ASSERT(no_modify || libxfs_verify_dir_ino(mp, lino));
 
                /*
                 * Also skip entries with bogus inode numbers if we're
                 * in no modify mode.
                 */
 
-               if (no_modify && verify_inum(mp, lino))  {
-                       next_sfep = M_DIROPS(mp)->sf_nextentry(sfp, sfep);
+               if (no_modify && !libxfs_verify_dir_ino(mp, lino))  {
+                       next_sfep = libxfs_dir2_sf_nextentry(mp, sfp, sfep);
                        continue;
                }
 
@@ -2640,7 +2640,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                if (!dir_hash_add(mp, hashtab, (xfs_dir2_dataptr_t)
                                (sfep - xfs_dir2_sf_firstentry(sfp)),
                                lino, sfep->namelen, sfep->name,
-                               M_DIROPS(mp)->sf_get_ftype(sfep))) {
+                               libxfs_dir2_sf_get_ftype(mp, sfep))) {
                        do_warn(
 _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                                fname, lino, ino);
@@ -2701,11 +2701,11 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                }
 
                /* validate ftype field if supported */
-               if (xfs_sb_version_hasftype(&mp->m_sb)) {
+               if (xfs_has_ftype(mp)) {
                        uint8_t dir_ftype;
                        uint8_t ino_ftype;
 
-                       dir_ftype = M_DIROPS(mp)->sf_get_ftype(sfep);
+                       dir_ftype = libxfs_dir2_sf_get_ftype(mp, sfep);
                        ino_ftype = get_inode_ftype(irec, ino_offset);
 
                        if (dir_ftype != ino_ftype) {
@@ -2719,7 +2719,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
        _("fixing ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
                                                dir_ftype, ino_ftype,
                                                ino, lino);
-                                       M_DIROPS(mp)->sf_put_ftype(sfep,
+                                       libxfs_dir2_sf_put_ftype(mp, sfep,
                                                                ino_ftype);
                                        dir_hash_update_ftype(hashtab,
                        (xfs_dir2_dataptr_t)(sfep - xfs_dir2_sf_firstentry(sfp)),
@@ -2740,8 +2740,8 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                ASSERT(no_modify || bad_sfnamelen == 0);
                next_sfep = (struct xfs_dir2_sf_entry *)((intptr_t)sfep +
                              (bad_sfnamelen
-                               ? M_DIROPS(mp)->sf_entsize(sfp, namelen)
-                               : M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen)));
+                               ? libxfs_dir2_sf_entsize(mp, sfp, namelen)
+                               : libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen)));
        }
 
        if (sfp->i8count != i8) {
@@ -2772,17 +2772,17 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
        if (*ino_dirty && bytes_deleted > 0)  {
                ASSERT(!no_modify);
                libxfs_idata_realloc(ip, -bytes_deleted, XFS_DATA_FORK);
-               ip->i_d.di_size -= bytes_deleted;
+               ip->i_disk_size -= bytes_deleted;
        }
 
-       if (ip->i_d.di_size != ip->i_df.if_bytes)  {
+       if (ip->i_disk_size != ip->i_df.if_bytes)  {
                ASSERT(ip->i_df.if_bytes == (xfs_fsize_t)
                                ((intptr_t) next_sfep - (intptr_t) sfp));
-               ip->i_d.di_size = (xfs_fsize_t)
+               ip->i_disk_size = (xfs_fsize_t)
                                ((intptr_t) next_sfep - (intptr_t) sfp);
                do_warn(
        _("setting size to %" PRId64 " bytes to reflect junked entries\n"),
-                       ip->i_d.di_size);
+                       ip->i_disk_size);
                *ino_dirty = 1;
        }
 }
@@ -2792,15 +2792,15 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
  */
 static void
 process_dir_inode(
-       xfs_mount_t             *mp,
+       struct xfs_mount        *mp,
        xfs_agnumber_t          agno,
-       ino_tree_node_t         *irec,
+       struct ino_tree_node    *irec,
        int                     ino_offset)
 {
        xfs_ino_t               ino;
-       xfs_inode_t             *ip;
-       xfs_trans_t             *tp;
-       dir_hash_tab_t          *hashtab;
+       struct xfs_inode        *ip;
+       struct xfs_trans        *tp;
+       struct dir_hash_tab     *hashtab;
        int                     need_dot;
        int                     dirty, num_illegal, error, nres;
 
@@ -2814,7 +2814,7 @@ process_dir_inode(
 
        ASSERT(!is_inode_refchecked(irec, ino_offset) || dotdot_update);
 
-       error = -libxfs_iget(mp, NULL, ino, 0, &ip, &phase6_ifork_ops);
+       error = -libxfs_iget(mp, NULL, ino, 0, &ip);
        if (error) {
                if (!no_modify)
                        do_error(
@@ -2856,12 +2856,12 @@ process_dir_inode(
 
        add_inode_refchecked(irec, ino_offset);
 
-       hashtab = dir_hash_init(ip->i_d.di_size);
+       hashtab = dir_hash_init(ip->i_disk_size);
 
        /*
         * look for bogus entries
         */
-       switch (ip->i_d.di_format)  {
+       switch (ip->i_df.if_format)  {
                case XFS_DINODE_FMT_EXTENTS:
                case XFS_DINODE_FMT_BTREE:
                        /*
@@ -2899,7 +2899,12 @@ process_dir_inode(
                        if (dirty)  {
                                libxfs_trans_log_inode(tp, ip,
                                        XFS_ILOG_CORE | XFS_ILOG_DDATA);
-                               libxfs_trans_commit(tp);
+                               error = -libxfs_trans_commit(tp);
+                               if (error)
+                                       do_error(
+_("error %d fixing shortform directory %llu\n"),
+                                               error,
+                                               (unsigned long long)ip->i_ino);
                        } else  {
                                libxfs_trans_cancel(tp);
                        }
@@ -2921,7 +2926,7 @@ process_dir_inode(
         * if it has to move them around.
         */
        if (!no_modify && need_root_dotdot && ino == mp->m_sb.sb_rootino)  {
-               ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_LOCAL);
+               ASSERT(ip->i_df.if_format != XFS_DINODE_FMT_LOCAL);
 
                do_warn(_("recreating root directory .. entry\n"));
 
@@ -2940,7 +2945,10 @@ process_dir_inode(
        _("can't make \"..\" entry in root inode %" PRIu64 ", createname error %d\n"), ino, error);
 
                libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-               libxfs_trans_commit(tp);
+               error = -libxfs_trans_commit(tp);
+               if (error)
+                       do_error(
+       _("root inode \"..\" entry recreation failed (%d)\n"), error);
 
                need_root_dotdot = 0;
        } else if (need_root_dotdot && ino == mp->m_sb.sb_rootino)  {
@@ -2970,7 +2978,7 @@ process_dir_inode(
                        do_warn(
        _("would create missing \".\" entry in dir ino %" PRIu64 "\n"),
                                ino);
-               } else if (ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)  {
+               } else if (ip->i_df.if_format != XFS_DINODE_FMT_LOCAL)  {
                        /*
                         * need to create . entry in longform dir.
                         */
@@ -2993,10 +3001,13 @@ process_dir_inode(
                                        ino, error);
 
                        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-                       libxfs_trans_commit(tp);
+                       error = -libxfs_trans_commit(tp);
+                       if (error)
+                               do_error(
+       _("root inode \".\" entry recreation failed (%d)\n"), error);
                }
        }
-       IRELE(ip);
+       libxfs_irele(ip);
 }
 
 /*
@@ -3099,20 +3110,44 @@ check_for_orphaned_inodes(
 }
 
 static void
-traverse_function(
+do_dir_inode(
        struct workqueue        *wq,
-       xfs_agnumber_t          agno,
+       xfs_agnumber_t          agno,
        void                    *arg)
 {
-       ino_tree_node_t         *irec;
+       struct ino_tree_node    *irec = arg;
        int                     i;
+
+       for (i = 0; i < XFS_INODES_PER_CHUNK; i++)  {
+               if (inode_isadir(irec, i))
+                       process_dir_inode(wq->wq_ctx, agno, irec, i);
+       }
+}
+
+static void
+traverse_function(
+       struct workqueue        *wq,
+       xfs_agnumber_t          agno,
+       void                    *arg)
+{
+       struct ino_tree_node    *irec;
        prefetch_args_t         *pf_args = arg;
+       struct workqueue        lwq;
+       struct xfs_mount        *mp = wq->wq_ctx;
 
        wait_for_inode_prefetch(pf_args);
 
        if (verbose)
                do_log(_("        - agno = %d\n"), agno);
 
+       /*
+        * The more AGs we have in flight at once, the fewer processing threads
+        * per AG. This means we don't overwhelm the machine with hundreds of
+        * threads when we start acting on lots of AGs at once. We just want
+        * enough that we can keep multiple CPUs busy across multiple AGs.
+        */
+       workqueue_create_bound(&lwq, mp, ag_stride, 1000);
+
        for (irec = findfirst_inode_rec(agno); irec; irec = next_ino_rec(irec)) {
                if (irec->ino_isa_dir == 0)
                        continue;
@@ -3120,18 +3155,19 @@ traverse_function(
                if (pf_args) {
                        sem_post(&pf_args->ra_count);
 #ifdef XR_PF_TRACE
+                       {
+                       int     i;
                        sem_getvalue(&pf_args->ra_count, &i);
                        pftrace(
                "processing inode chunk %p in AG %d (sem count = %d)",
                                irec, agno, i);
+                       }
 #endif
                }
 
-               for (i = 0; i < XFS_INODES_PER_CHUNK; i++)  {
-                       if (inode_isadir(irec, i))
-                               process_dir_inode(wq->wq_ctx, agno, irec, i);
-               }
+               queue_work(&lwq, do_dir_inode, agno, irec);
        }
+       destroy_work_queue(&lwq);
        cleanup_inode_prefetch(pf_args);
 }
 
@@ -3159,7 +3195,7 @@ static void
 traverse_ags(
        struct xfs_mount        *mp)
 {
-       do_inode_prefetch(mp, 0, traverse_function, false, true);
+       do_inode_prefetch(mp, ag_stride, traverse_function, false, true);
 }
 
 void