]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/phase6.c
xfs: remove xfs_bmapi_write() dfops param
[thirdparty/xfsprogs-dev.git] / repair / phase6.c
index d2d4a445ceef292683bff2d02126bc7d8a5a0917..57d00989682752f71e57c9365f73ffa8a1b26c84 100644 (file)
@@ -1,22 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (c) 2000-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 "threads.h"
+#include "prefetch.h"
 #include "avl.h"
 #include "globals.h"
 #include "agheader.h"
@@ -25,9 +15,7 @@
 #include "protos.h"
 #include "err_protos.h"
 #include "dinode.h"
-#include "prefetch.h"
 #include "progress.h"
-#include "threads.h"
 #include "versions.h"
 
 static struct cred             zerocr;
@@ -38,18 +26,73 @@ 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
  */
 typedef struct dotdot_update {
-       struct dotdot_update    *next;
+       struct list_head        list;
        ino_tree_node_t         *irec;
        xfs_agnumber_t          agno;
        int                     ino_offset;
 } dotdot_update_t;
 
-static dotdot_update_t         *dotdot_update_list;
+static LIST_HEAD(dotdot_update_list);
 static int                     dotdot_update;
 
 static void
@@ -64,12 +107,12 @@ add_dotdot_update(
                do_error(_("malloc failed add_dotdot_update (%zu bytes)\n"),
                        sizeof(dotdot_update_t));
 
-       dir->next = dotdot_update_list;
+       INIT_LIST_HEAD(&dir->list);
        dir->irec = irec;
        dir->agno = agno;
        dir->ino_offset = ino_offset;
 
-       dotdot_update_list = dir;
+       list_add(&dir->list, &dotdot_update_list);
 }
 
 /*
@@ -82,7 +125,7 @@ typedef 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 */
+       uint32_t                address;        /* offset of data entry */
        xfs_ino_t               inum;           /* inode num of entry */
        short                   junkit;         /* name starts with / */
        short                   seen;           /* have seen leaf entry */
@@ -124,6 +167,45 @@ typedef struct freetab {
 #define        DIR_HASH_CK_BADSTALE    5
 #define        DIR_HASH_CK_TOTAL       6
 
+/*
+ * Need to handle CRC and validation errors specially here. If there is a
+ * validator error, re-read without the verifier so that we get a buffer we can
+ * check and repair. Re-attach the ops to the buffer after the read so that when
+ * it is rewritten the CRC is recalculated.
+ *
+ * If the buffer was not read, we return an error. If the buffer was read but
+ * had a CRC or corruption error, we reread it without the verifier and if it is
+ * read successfully we increment *crc_error and return 0. Otherwise we
+ * return the read error.
+ */
+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)
+{
+       int error;
+       int error2;
+
+       error = -libxfs_da_read_buf(NULL, ip, bno, mappedbno, 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);
+       if (error2)
+               return error2;
+
+       (*crc_error)++;
+       (*bpp)->b_ops = ops;
+       return 0;
+}
+
 /*
  * Returns 0 if the name already exists (ie. a duplicate)
  */
@@ -131,10 +213,11 @@ static int
 dir_hash_add(
        xfs_mount_t             *mp,
        dir_hash_tab_t          *hashtab,
-       __uint32_t              addr,
+       uint32_t                addr,
        xfs_ino_t               inum,
        int                     namelen,
-       unsigned char           *name)
+       unsigned char           *name,
+       uint8_t                 ftype)
 {
        xfs_dahash_t            hash = 0;
        int                     byaddr;
@@ -148,6 +231,7 @@ dir_hash_add(
 
        xname.name = name;
        xname.len = namelen;
+       xname.type = ftype;
 
        junk = name[0] == '/';
        byaddr = DIR_HASH_FUNC(hashtab, addr);
@@ -312,6 +396,23 @@ dir_hash_see(
        return DIR_HASH_CK_NODATA;
 }
 
+static void
+dir_hash_update_ftype(
+       dir_hash_tab_t          *hashtab,
+       xfs_dir2_dataptr_t      addr,
+       uint8_t                 ftype)
+{
+       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;
+               p->name.type = ftype;
+       }
+}
+
 /*
  * checks to make sure leafs match a data entry, and that the stale
  * count is valid.
@@ -376,12 +477,10 @@ bmap_next_offset(
        int             whichfork)              /* data or attr fork */
 {
        xfs_fileoff_t   bno;                    /* current block */
-       int             eof;                    /* hit end of file */
        int             error;                  /* error return value */
        xfs_bmbt_irec_t got;                    /* current extent value */
        xfs_ifork_t     *ifp;                   /* inode fork pointer */
-       xfs_extnum_t    lastx;                  /* last extent used */
-       xfs_bmbt_irec_t prev;                   /* previous extent value */
+       struct xfs_iext_cursor  icur;
 
        if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE &&
            XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -393,11 +492,10 @@ bmap_next_offset(
        }
        ifp = XFS_IFORK_PTR(ip, whichfork);
        if (!(ifp->if_flags & XFS_IFEXTENTS) &&
-           (error = xfs_iread_extents(tp, ip, whichfork)))
+           (error = -libxfs_iread_extents(tp, ip, whichfork)))
                return error;
        bno = *bnop + 1;
-       xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev);
-       if (eof)
+       if (!libxfs_iext_lookup_extent(ip, ifp, bno, &icur, &got))
                *bnop = NULLFILEOFF;
        else
                *bnop = got.br_startoff < bno ? bno : got.br_startoff;
@@ -424,51 +522,42 @@ mk_rbmino(xfs_mount_t *mp)
        xfs_fsblock_t   first;
        int             i;
        int             nmap;
-       int             committed;
        int             error;
-       xfs_bmap_free_t flist;
-       xfs_dfiloff_t   bno;
+       struct xfs_defer_ops    dfops;
+       xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
        int             vers;
        int             times;
-       struct xfs_trans_res tres = {0};
+       uint            blocks;
 
        /*
         * first set up inode
         */
-       tp = libxfs_trans_alloc(mp, 0);
-
-       i = libxfs_trans_reserve(tp, &tres, 10, 0);
+       i = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (i)
                res_failed(i);
 
-       error = libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 0, &ip);
+       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 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 : 1;
-       memset(&ip->i_d, 0, xfs_icdinode_size(vers));
+       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
+       memset(&ip->i_d, 0, sizeof(ip->i_d));
 
-       ip->i_d.di_magic = XFS_DINODE_MAGIC;
-       ip->i_d.di_mode = S_IFREG;
+       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_d.di_nlink = 1;           /* account for sb ptr */
+       set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
        if (ip->i_d.di_version == 3) {
-               ip->i_d.di_crc = 0;
-               ip->i_d.di_changecount = 1;
-               ip->i_d.di_lsn = 0;
+               VFS_I(ip)->i_version = 1;
                ip->i_d.di_flags2 = 0;
-               ip->i_d.di_ino = mp->m_sb.sb_rbmino;
-               memset(&(ip->i_d.di_pad2[0]), 0, sizeof(ip->i_d.di_pad2));
-               platform_uuid_copy(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid);
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -478,7 +567,7 @@ mk_rbmino(xfs_mount_t *mp)
         */
        ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
-       ip->i_df.if_u1.if_extents = NULL;
+       ip->i_df.if_u1.if_root = NULL;
 
        ip->i_d.di_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
 
@@ -486,28 +575,27 @@ mk_rbmino(xfs_mount_t *mp)
         * commit changes
         */
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       libxfs_trans_ihold(tp, ip);
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
 
        /*
         * then allocate blocks for file and fill with zeroes (stolen
         * from mkfs)
         */
-       tp = libxfs_trans_alloc(mp, 0);
-       error = libxfs_trans_reserve(tp, &tres, mp->m_sb.sb_rbmblocks +
-                               (XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1), 0);
+       blocks = mp->m_sb.sb_rbmblocks +
+                       XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1;
+       error = -libxfs_trans_alloc_rollable(mp, blocks, &tp);
        if (error)
                res_failed(error);
 
        libxfs_trans_ijoin(tp, ip, 0);
        bno = 0;
-       xfs_bmap_init(&flist, &first);
+       libxfs_defer_init(&dfops, &first);
+       tp->t_dfops = &dfops;
        while (bno < mp->m_sb.sb_rbmblocks) {
                nmap = XFS_BMAP_MAX_NMAP;
-               error = libxfs_bmapi_write(tp, ip, bno,
+               error = -libxfs_bmapi_write(tp, ip, bno,
                          (xfs_extlen_t)(mp->m_sb.sb_rbmblocks - bno),
-                         0, &first, mp->m_sb.sb_rbmblocks,
-                         map, &nmap, &flist);
+                         0, &first, mp->m_sb.sb_rbmblocks, map, &nmap);
                if (error) {
                        do_error(
                        _("couldn't allocate realtime bitmap, error = %d\n"),
@@ -520,13 +608,15 @@ mk_rbmino(xfs_mount_t *mp)
                        bno += ep->br_blockcount;
                }
        }
-       error = libxfs_bmap_finish(&tp, &flist, &committed);
+       libxfs_defer_ijoin(&dfops, ip);
+       error = -libxfs_defer_finish(&tp, &dfops);
        if (error) {
                do_error(
                _("allocation of the realtime bitmap failed, error = %d\n"),
                        error);
        }
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       IRELE(ip);
 }
 
 static int
@@ -539,20 +629,17 @@ fill_rbmino(xfs_mount_t *mp)
        xfs_fsblock_t   first;
        int             nmap;
        int             error;
-       xfs_dfiloff_t   bno;
+       xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map;
-       struct xfs_trans_res tres = {0};
 
        bmp = btmcompute;
        bno = 0;
 
-       tp = libxfs_trans_alloc(mp, 0);
-
-       error = libxfs_trans_reserve(tp, &tres, 10, 0);
+       error = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (error)
                res_failed(error);
 
-       error = libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 0, &ip);
+       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rbmino, 0, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime bitmap inode -- error - %d\n"),
@@ -565,8 +652,8 @@ fill_rbmino(xfs_mount_t *mp)
                 * fill the file one block at a time
                 */
                nmap = 1;
-               error = libxfs_bmapi_write(tp, ip, bno, 1, 0,
-                                       &first, 1, &map, &nmap, NULL);
+               error = -libxfs_bmapi_write(tp, ip, bno, 1, 0,
+                                       &first, 1, &map, &nmap);
                if (error || nmap != 1) {
                        do_error(
        _("couldn't map realtime bitmap block %" PRIu64 ", error = %d\n"),
@@ -575,7 +662,7 @@ fill_rbmino(xfs_mount_t *mp)
 
                ASSERT(map.br_startblock != HOLESTARTBLOCK);
 
-               error = libxfs_trans_read_buf(
+               error = -libxfs_trans_read_buf(
                                mp, tp, mp->m_dev,
                                XFS_FSB_TO_DADDR(mp, map.br_startblock),
                                XFS_FSB_TO_BB(mp, 1), 1, &bp, NULL);
@@ -587,15 +674,16 @@ _("can't access block %" PRIu64 " (fsbno %" PRIu64 ") of realtime bitmap inode %
                        return(1);
                }
 
-               memmove(XFS_BUF_PTR(bp), bmp, mp->m_sb.sb_blocksize);
+               memmove(bp->b_addr, bmp, mp->m_sb.sb_blocksize);
 
                libxfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
 
-               bmp = (xfs_rtword_t *)((__psint_t) bmp + mp->m_sb.sb_blocksize);
+               bmp = (xfs_rtword_t *)((intptr_t) bmp + mp->m_sb.sb_blocksize);
                bno++;
        }
 
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       IRELE(ip);
        return(0);
 }
 
@@ -609,22 +697,19 @@ fill_rsumino(xfs_mount_t *mp)
        xfs_fsblock_t   first;
        int             nmap;
        int             error;
-       xfs_dfiloff_t   bno;
-       xfs_dfiloff_t   end_bno;
+       xfs_fileoff_t   bno;
+       xfs_fileoff_t   end_bno;
        xfs_bmbt_irec_t map;
-       struct xfs_trans_res tres = {0};
 
        smp = sumcompute;
        bno = 0;
        end_bno = mp->m_rsumsize >> mp->m_sb.sb_blocklog;
 
-       tp = libxfs_trans_alloc(mp, 0);
-
-       error = libxfs_trans_reserve(tp, &tres, 10, 0);
+       error = -libxfs_trans_alloc_rollable(mp, 10, &tp);
        if (error)
                res_failed(error);
 
-       error = libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 0, &ip);
+       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 0, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime summary inode -- error - %d\n"),
@@ -637,8 +722,8 @@ fill_rsumino(xfs_mount_t *mp)
                 * fill the file one block at a time
                 */
                nmap = 1;
-               error = libxfs_bmapi_write(tp, ip, bno, 1, 0,
-                                       &first, 1, &map, &nmap, NULL);
+               error = -libxfs_bmapi_write(tp, ip, bno, 1, 0,
+                                       &first, 1, &map, &nmap);
                if (error || nmap != 1) {
                        do_error(
        _("couldn't map realtime summary inode block %" PRIu64 ", error = %d\n"),
@@ -647,7 +732,7 @@ fill_rsumino(xfs_mount_t *mp)
 
                ASSERT(map.br_startblock != HOLESTARTBLOCK);
 
-               error = libxfs_trans_read_buf(
+               error = -libxfs_trans_read_buf(
                                mp, tp, mp->m_dev,
                                XFS_FSB_TO_DADDR(mp, map.br_startblock),
                                XFS_FSB_TO_BB(mp, 1), 1, &bp, NULL);
@@ -656,18 +741,20 @@ 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);
                        return(1);
                }
 
-               memmove(XFS_BUF_PTR(bp), smp, mp->m_sb.sb_blocksize);
+               memmove(bp->b_addr, smp, mp->m_sb.sb_blocksize);
 
                libxfs_trans_log_buf(tp, bp, 0, mp->m_sb.sb_blocksize - 1);
 
-               smp = (xfs_suminfo_t *)((__psint_t)smp + mp->m_sb.sb_blocksize);
+               smp = (xfs_suminfo_t *)((intptr_t)smp + mp->m_sb.sb_blocksize);
                bno++;
        }
 
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       IRELE(ip);
        return(0);
 }
 
@@ -680,52 +767,43 @@ mk_rsumino(xfs_mount_t *mp)
        xfs_fsblock_t   first;
        int             i;
        int             nmap;
-       int             committed;
        int             error;
        int             nsumblocks;
-       xfs_bmap_free_t flist;
-       xfs_dfiloff_t   bno;
+       struct xfs_defer_ops    dfops;
+       xfs_fileoff_t   bno;
        xfs_bmbt_irec_t map[XFS_BMAP_MAX_NMAP];
        int             vers;
        int             times;
-       struct xfs_trans_res tres = {0};
+       uint            blocks;
 
        /*
         * first set up inode
         */
-       tp = libxfs_trans_alloc(mp, 0);
-
-       i = libxfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 10, 0);
+       i = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 10, 0, 0, &tp);
        if (i)
                res_failed(i);
 
-       error = libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 0, &ip);
+       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rsumino, 0, 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 : 1;
-       memset(&ip->i_d, 0, xfs_icdinode_size(vers));
+       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
+       memset(&ip->i_d, 0, sizeof(ip->i_d));
 
-       ip->i_d.di_magic = XFS_DINODE_MAGIC;
-       ip->i_d.di_mode = S_IFREG;
+       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_d.di_nlink = 1;           /* account for sb ptr */
+       set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
        if (ip->i_d.di_version == 3) {
-               ip->i_d.di_crc = 0;
-               ip->i_d.di_changecount = 1;
-               ip->i_d.di_lsn = 0;
+               VFS_I(ip)->i_version = 1;
                ip->i_d.di_flags2 = 0;
-               ip->i_d.di_ino = mp->m_sb.sb_rsumino;
-               memset(&(ip->i_d.di_pad2[0]), 0, sizeof(ip->i_d.di_pad2));
-               platform_uuid_copy(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid);
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -735,7 +813,7 @@ mk_rsumino(xfs_mount_t *mp)
         */
        ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
-       ip->i_df.if_u1.if_extents = NULL;
+       ip->i_df.if_u1.if_root = NULL;
 
        ip->i_d.di_size = mp->m_rsumsize;
 
@@ -743,33 +821,27 @@ mk_rsumino(xfs_mount_t *mp)
         * commit changes
         */
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-       libxfs_trans_ihold(tp, ip);
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
 
        /*
         * then allocate blocks for file and fill with zeroes (stolen
         * from mkfs)
         */
-       tp = libxfs_trans_alloc(mp, 0);
-       xfs_bmap_init(&flist, &first);
-
        nsumblocks = mp->m_rsumsize >> mp->m_sb.sb_blocklog;
-       tres.tr_logres = BBTOB(128);
-       tres.tr_logcount = XFS_DEFAULT_PERM_LOG_COUNT;
-       tres.tr_logflags = XFS_TRANS_PERM_LOG_RES;
-       error = libxfs_trans_reserve(tp, &tres, mp->m_sb.sb_rbmblocks +
-                             (XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1), 0);
+       blocks = nsumblocks + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) - 1;
+       error = -libxfs_trans_alloc_rollable(mp, blocks, &tp);
        if (error)
                res_failed(error);
 
        libxfs_trans_ijoin(tp, ip, 0);
        bno = 0;
-       xfs_bmap_init(&flist, &first);
+       libxfs_defer_init(&dfops, &first);
+       tp->t_dfops = &dfops;
        while (bno < nsumblocks) {
                nmap = XFS_BMAP_MAX_NMAP;
-               error = libxfs_bmapi_write(tp, ip, bno,
+               error = -libxfs_bmapi_write(tp, ip, bno,
                          (xfs_extlen_t)(nsumblocks - bno),
-                         0, &first, nsumblocks, map, &nmap, &flist);
+                         0, &first, nsumblocks, map, &nmap);
                if (error) {
                        do_error(
                _("couldn't allocate realtime summary inode, error = %d\n"),
@@ -782,13 +854,15 @@ mk_rsumino(xfs_mount_t *mp)
                        bno += ep->br_blockcount;
                }
        }
-       error = libxfs_bmap_finish(&tp, &flist, &committed);
+       libxfs_defer_ijoin(&dfops, ip);
+       error = -libxfs_defer_finish(&tp, &dfops);
        if (error) {
                do_error(
        _("allocation of the realtime summary ino failed, error = %d\n"),
                        error);
        }
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       IRELE(ip);
 }
 
 /*
@@ -806,16 +880,12 @@ mk_root_dir(xfs_mount_t *mp)
        int             vers;
        int             times;
 
-       ASSERT(xfs_sb_version_hasdirv2(&mp->m_sb));
-
-       tp = libxfs_trans_alloc(mp, 0);
        ip = NULL;
-
-       i = libxfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 10, 0);
+       i = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 10, 0, 0, &tp);
        if (i)
                res_failed(i);
 
-       error = libxfs_trans_iget(mp, tp, mp->m_sb.sb_rootino, 0, 0, &ip);
+       error = -libxfs_trans_iget(mp, tp, mp->m_sb.sb_rootino, 0, 0, &ip);
        if (error) {
                do_error(_("could not iget root inode -- error - %d\n"), error);
        }
@@ -823,26 +893,20 @@ 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 : 1;
-       memset(&ip->i_d, 0, xfs_icdinode_size(vers));
+       vers = xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
+       memset(&ip->i_d, 0, sizeof(ip->i_d));
 
-       ip->i_d.di_magic = XFS_DINODE_MAGIC;
-       ip->i_d.di_mode = (__uint16_t) mode|S_IFDIR;
+       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_d.di_nlink = 1;           /* account for . */
+       set_nlink(VFS_I(ip), 1);        /* account for . */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
        if (ip->i_d.di_version == 3) {
-               ip->i_d.di_crc = 0;
-               ip->i_d.di_changecount = 1;
-               ip->i_d.di_lsn = 0;
+               VFS_I(ip)->i_version = 1;
                ip->i_d.di_flags2 = 0;
-               ip->i_d.di_ino = mp->m_sb.sb_rootino;
-               memset(&(ip->i_d.di_pad2[0]), 0, sizeof(ip->i_d.di_pad2));
-               platform_uuid_copy(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid);
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -854,14 +918,18 @@ mk_root_dir(xfs_mount_t *mp)
         */
        ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = ip->i_df.if_real_bytes = 0;
-       ip->i_df.if_u1.if_extents = NULL;
+       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, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       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));
@@ -883,22 +951,21 @@ mk_orphanage(xfs_mount_t *mp)
        ino_tree_node_t *irec;
        int             ino_offset = 0;
        int             i;
-       int             committed;
        int             error;
-       xfs_bmap_free_t flist;
+       struct xfs_defer_ops    dfops;
        const int       mode = 0755;
        int             nres;
        struct xfs_name xname;
 
-       ASSERT(xfs_sb_version_hasdirv2(&mp->m_sb));
-
        /*
         * check for an existing lost+found first, if it exists, return
         * its inode. Otherwise, we can create it. Bad lost+found inodes
         * would have been cleared in phase3 and phase4.
         */
 
-       if ((i = libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip, 0)))
+       i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
+                       &xfs_default_ifork_ops);
+       if (i)
                do_error(_("%d - couldn't iget root inode to obtain %s\n"),
                        i, ORPHANAGE);
 
@@ -912,12 +979,9 @@ mk_orphanage(xfs_mount_t *mp)
        /*
         * could not be found, create it
         */
-
-       tp = libxfs_trans_alloc(mp, 0);
-       xfs_bmap_init(&flist, &first);
-
+       libxfs_defer_init(&dfops, &first);
        nres = XFS_MKDIR_SPACE_RES(mp, xname.len);
-       i = libxfs_trans_reserve(tp, &M_RES(mp)->tr_mkdir, nres, 0);
+       i = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir, nres, 0, 0, &tp);
        if (i)
                res_failed(i);
 
@@ -925,17 +989,19 @@ 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
         */
-/*     if ((i = libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip, 0)))
+/*     i = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &pip,
+                       &xfs_default_ifork_ops);
+       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_inode_alloc(&tp, pip, mode|S_IFDIR,
                                        1, 0, &zerocr, &zerofsx, &ip);
        if (error) {
                do_error(_("%s inode allocation failed %d\n"),
                        ORPHANAGE, error);
        }
-       ip->i_d.di_nlink++;             /* account for . */
+       inc_nlink(VFS_I(ip));           /* account for . */
        ino = ip->i_ino;
 
        irec = find_inode_rec(mp,
@@ -975,9 +1041,9 @@ mk_orphanage(xfs_mount_t *mp)
        /*
         * create the actual entry
         */
-       error = libxfs_dir_createname(tp, pip, &xname, ip->i_ino, &first,
-                                       &flist, nres);
-       if (error) 
+       error = -libxfs_dir_createname(tp, pip, &xname, ip->i_ino, &first,
+                                       nres);
+       if (error)
                do_error(
                _("can't make %s, createname error %d\n"),
                        ORPHANAGE, error);
@@ -986,7 +1052,7 @@ mk_orphanage(xfs_mount_t *mp)
         * bump up the link count in the root directory to account
         * for .. in the new directory
         */
-       pip->i_d.di_nlink++;
+       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);
@@ -997,14 +1063,17 @@ mk_orphanage(xfs_mount_t *mp)
        libxfs_dir_init(tp, ip, pip);
        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-       error = libxfs_bmap_finish(&tp, &flist, &committed);
+       libxfs_defer_ijoin(&dfops, ip);
+       error = -libxfs_defer_finish(&tp, &dfops);
        if (error) {
                do_error(_("%s directory creation failed -- bmapf error %d\n"),
                        ORPHANAGE, error);
        }
 
 
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       libxfs_trans_commit(tp);
+       IRELE(ip);
+       IRELE(pip);
        add_inode_reached(irec,ino_offset);
 
        return(ino);
@@ -1024,9 +1093,8 @@ mv_orphanage(
        xfs_inode_t             *ino_p;
        xfs_trans_t             *tp;
        xfs_fsblock_t           first;
-       xfs_bmap_free_t         flist;
+       struct xfs_defer_ops            dfops;
        int                     err;
-       int                     committed;
        unsigned char           fname[MAXPATHLEN + 1];
        int                     nres;
        int                     incr;
@@ -1034,15 +1102,12 @@ mv_orphanage(
        int                     ino_offset = 0;
        struct xfs_name         xname;
 
-       ASSERT(xfs_sb_version_hasdirv2(&mp->m_sb));
-
        xname.name = fname;
        xname.len = snprintf((char *)fname, sizeof(fname), "%llu",
                                (unsigned long long)ino);
-       /* XXX use xfs_mode_to_ftype[] when userspace gains it */
-       xname.type = XFS_DIR3_FT_UNKNOWN;
 
-       err = libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip, 0);
+       err = -libxfs_iget(mp, NULL, orphanage_ino, 0, &orphanage_ip,
+                       &xfs_default_ifork_ops);
        if (err)
                do_error(_("%d - couldn't iget orphanage inode\n"), err);
        /*
@@ -1054,11 +1119,13 @@ mv_orphanage(
                xname.len = snprintf((char *)fname, sizeof(fname), "%llu.%d",
                                        (unsigned long long)ino, ++incr);
 
-       tp = libxfs_trans_alloc(mp, 0);
-
-       if ((err = libxfs_iget(mp, NULL, ino, 0, &ino_p, 0)))
+       /* Orphans may not have a proper parent, so use custom ops here */
+       err = -libxfs_iget(mp, NULL, ino, 0, &ino_p, &phase6_ifork_ops);
+       if (err)
                do_error(_("%d - couldn't iget disconnected inode\n"), err);
 
+       xname.type = libxfs_mode_to_ftype(VFS_I(ino_p)->i_mode);
+
        if (isa_dir)  {
                irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, orphanage_ino),
                                XFS_INO_TO_AGINO(mp, orphanage_ino));
@@ -1067,13 +1134,13 @@ mv_orphanage(
                                        irec->ino_startnum;
                nres = XFS_DIRENTER_SPACE_RES(mp, fnamelen) +
                       XFS_DIRENTER_SPACE_RES(mp, 2);
-               err = libxfs_dir_lookup(tp, ino_p, &xfs_name_dotdot,
+               err = -libxfs_dir_lookup(NULL, ino_p, &xfs_name_dotdot,
                                        &entry_ino_num, NULL);
                if (err) {
                        ASSERT(err == ENOENT);
 
-                       err = libxfs_trans_reserve(tp, &M_RES(mp)->tr_rename,
-                                                  nres, 0);
+                       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"),
@@ -1082,9 +1149,9 @@ mv_orphanage(
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
 
-                       xfs_bmap_init(&flist, &first);
-                       err = libxfs_dir_createname(tp, orphanage_ip, &xname,
-                                               ino, &first, &flist, nres);
+                       libxfs_defer_init(&dfops, &first);
+                       err = -libxfs_dir_createname(tp, orphanage_ip, &xname,
+                                               ino, &first, nres);
                        if (err)
                                do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
@@ -1093,30 +1160,30 @@ mv_orphanage(
                        if (irec)
                                add_inode_ref(irec, ino_offset);
                        else
-                               orphanage_ip->i_d.di_nlink++;
+                               inc_nlink(VFS_I(orphanage_ip));
                        libxfs_trans_log_inode(tp, orphanage_ip, XFS_ILOG_CORE);
 
-                       err = libxfs_dir_createname(tp, ino_p, &xfs_name_dotdot,
-                                       orphanage_ino, &first, &flist, nres);
+                       err = -libxfs_dir_createname(tp, ino_p, &xfs_name_dotdot,
+                                       orphanage_ino, &first, nres);
                        if (err)
                                do_error(
        _("creation of .. entry failed (%d), filesystem may be out of space\n"),
                                        err);
 
-                       ino_p->i_d.di_nlink++;
+                       inc_nlink(VFS_I(ino_p));
                        libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
 
-                       err = libxfs_bmap_finish(&tp, &flist, &committed);
+                       libxfs_defer_ijoin(&dfops, ino_p);
+                       err = -libxfs_defer_finish(&tp, &dfops);
                        if (err)
                                do_error(
        _("bmap finish failed (err - %d), filesystem may be out of space\n"),
                                        err);
 
-                       libxfs_trans_commit(tp,
-                               XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+                       libxfs_trans_commit(tp);
                } else  {
-                       err = libxfs_trans_reserve(tp, &M_RES(mp)->tr_rename,
-                                                  nres, 0);
+                       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"),
@@ -1125,10 +1192,10 @@ mv_orphanage(
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
 
-                       xfs_bmap_init(&flist, &first);
+                       libxfs_defer_init(&dfops, &first);
 
-                       err = libxfs_dir_createname(tp, orphanage_ip, &xname,
-                                               ino, &first, &flist, nres);
+                       err = -libxfs_dir_createname(tp, orphanage_ip, &xname,
+                                               ino, &first, nres);
                        if (err)
                                do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
@@ -1137,7 +1204,7 @@ mv_orphanage(
                        if (irec)
                                add_inode_ref(irec, ino_offset);
                        else
-                               orphanage_ip->i_d.di_nlink++;
+                               inc_nlink(VFS_I(orphanage_ip));
                        libxfs_trans_log_inode(tp, orphanage_ip, XFS_ILOG_CORE);
 
                        /*
@@ -1145,23 +1212,23 @@ mv_orphanage(
                         * to us.  that'll pop a libxfs/kernel ASSERT.
                         */
                        if (entry_ino_num != orphanage_ino)  {
-                               err = libxfs_dir_replace(tp, ino_p,
+                               err = -libxfs_dir_replace(tp, ino_p,
                                                &xfs_name_dotdot, orphanage_ino,
-                                               &first, &flist, nres);
+                                               &first, nres);
                                if (err)
                                        do_error(
        _("name replace op failed (%d), filesystem may be out of space\n"),
                                                err);
                        }
 
-                       err = libxfs_bmap_finish(&tp, &flist, &committed);
+                       libxfs_defer_ijoin(&dfops, ino_p);
+                       err = -libxfs_defer_finish(&tp, &dfops);
                        if (err)
                                do_error(
        _("bmap finish failed (%d), filesystem may be out of space\n"),
                                        err);
 
-                       libxfs_trans_commit(tp,
-                               XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+                       libxfs_trans_commit(tp);
                }
 
        } else  {
@@ -1172,8 +1239,8 @@ mv_orphanage(
                 * also accounted for in the create
                 */
                nres = XFS_DIRENTER_SPACE_RES(mp, xname.len);
-               err = libxfs_trans_reserve(tp, &M_RES(mp)->tr_remove,
-                                          nres, 0);
+               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"),
@@ -1182,26 +1249,29 @@ mv_orphanage(
                libxfs_trans_ijoin(tp, orphanage_ip, 0);
                libxfs_trans_ijoin(tp, ino_p, 0);
 
-               xfs_bmap_init(&flist, &first);
-               err = libxfs_dir_createname(tp, orphanage_ip, &xname, ino,
-                                               &first, &flist, nres);
+               libxfs_defer_init(&dfops, &first);
+               err = -libxfs_dir_createname(tp, orphanage_ip, &xname, ino,
+                                               &first, nres);
                if (err)
                        do_error(
        _("name create failed in %s (%d), filesystem may be out of space\n"),
                                ORPHANAGE, err);
                ASSERT(err == 0);
 
-               ino_p->i_d.di_nlink = 1;
+               set_nlink(VFS_I(ino_p), 1);
                libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
 
-               err = libxfs_bmap_finish(&tp, &flist, &committed);
+               libxfs_defer_ijoin(&dfops, ino_p);
+               err = -libxfs_defer_finish(&tp, &dfops);
                if (err)
                        do_error(
        _("bmap finish failed (%d), filesystem may be out of space\n"),
                                err);
 
-               libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+               libxfs_trans_commit(tp);
        }
+       IRELE(ino_p);
+       IRELE(orphanage_ip);
 }
 
 static int
@@ -1222,6 +1292,48 @@ entry_junked(
        return !no_modify;
 }
 
+/* Find and invalidate all the directory's buffers. */
+static int
+dir_binval(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       int                     whichfork)
+{
+       struct xfs_iext_cursor  icur;
+       struct xfs_bmbt_irec    rec;
+       struct xfs_ifork        *ifp;
+       struct xfs_da_geometry  *geo;
+       struct xfs_buf          *bp;
+       xfs_dablk_t             dabno, end_dabno;
+       int                     error = 0;
+
+       if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
+           ip->i_d.di_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) {
+                       bp = NULL;
+                       error = -libxfs_da_get_buf(tp, ip, dabno, -2, &bp,
+                                       whichfork);
+                       if (error)
+                               return error;
+                       if (!bp)
+                               continue;
+                       libxfs_trans_binval(tp, bp);
+                       libxfs_trans_brelse(tp, bp);
+               }
+       }
+
+       return error;
+}
+
 /*
  * Unexpected failure during the rebuild will leave the entries in
  * lost+found on the next run
@@ -1241,10 +1353,9 @@ longform_dir2_rebuild(
        xfs_trans_t             *tp;
        xfs_fileoff_t           lastblock;
        xfs_fsblock_t           firstblock;
-       xfs_bmap_free_t         flist;
+       struct xfs_defer_ops            dfops;
        xfs_inode_t             pip;
        dir_hash_ent_t          *p;
-       int                     committed;
        int                     done;
 
        /*
@@ -1261,27 +1372,29 @@ longform_dir2_rebuild(
         * for the libxfs_dir_init() call).
         */
        pip.i_ino = get_inode_parent(irec, ino_offset);
-       if (pip.i_ino == NULLFSINO)
+       if (pip.i_ino == NULLFSINO ||
+           libxfs_dir_ino_validate(mp, pip.i_ino))
                pip.i_ino = mp->m_sb.sb_rootino;
 
-       xfs_bmap_init(&flist, &firstblock);
+       libxfs_defer_init(&dfops, &firstblock);
 
-       tp = libxfs_trans_alloc(mp, 0);
        nres = XFS_REMOVE_SPACE_RES(mp);
-       error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_remove, nres, 0);
+       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
        if (error)
                res_failed(error);
        libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
 
-       if ((error = libxfs_bmap_last_offset(tp, ip, &lastblock,
-                                               XFS_DATA_FORK)))
+       error = dir_binval(tp, ip, XFS_DATA_FORK);
+       if (error)
+               res_failed(error);
+
+       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,
-                               &firstblock, &flist, &done);
+       error = -libxfs_bunmapi(tp, ip, 0, lastblock, XFS_BMAPI_METADATA, 0,
+                               &firstblock, &dfops, &done);
        if (error) {
                do_warn(_("xfs_bunmapi failed -- error - %d\n"), error);
                goto out_bmap_cancel;
@@ -1289,11 +1402,19 @@ longform_dir2_rebuild(
 
        ASSERT(done);
 
-       libxfs_dir_init(tp, ip, &pip);
+       error = -libxfs_dir_init(tp, ip, &pip);
+       if (error) {
+               do_warn(_("xfs_dir_init failed -- error - %d\n"), error);
+               goto out_bmap_cancel;
+       }
+
+       libxfs_defer_ijoin(&dfops, ip);
+       error = -libxfs_defer_finish(&tp, &dfops);
 
-       error = libxfs_bmap_finish(&tp, &flist, &committed);
+       libxfs_trans_commit(tp);
 
-       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+       if (ino == mp->m_sb.sb_rootino)
+               need_root_dotdot = 0;
 
        /* go through the hash list and re-add the inodes */
 
@@ -1304,19 +1425,17 @@ longform_dir2_rebuild(
                                                p->name.name[1] == '.'))))
                        continue;
 
-               tp = libxfs_trans_alloc(mp, 0);
                nres = XFS_CREATE_SPACE_RES(mp, p->name.len);
-               error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_create,
-                                            nres, 0);
+               error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_create,
+                                           nres, 0, 0, &tp);
                if (error)
                        res_failed(error);
 
                libxfs_trans_ijoin(tp, ip, 0);
-               libxfs_trans_ihold(tp, ip);
 
-               xfs_bmap_init(&flist, &firstblock);
-               error = libxfs_dir_createname(tp, ip, &p->name, p->inum,
-                                               &firstblock, &flist, nres);
+               libxfs_defer_init(&dfops, &firstblock);
+               error = -libxfs_dir_createname(tp, ip, &p->name, p->inum,
+                                               &firstblock, nres);
                if (error) {
                        do_warn(
 _("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"),
@@ -1324,7 +1443,8 @@ _("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"
                        goto out_bmap_cancel;
                }
 
-               error = libxfs_bmap_finish(&tp, &flist, &committed);
+               libxfs_defer_ijoin(&dfops, ip);
+               error = -libxfs_defer_finish(&tp, &dfops);
                if (error) {
                        do_warn(
        _("bmap finish failed (%d), filesystem may be out of space\n"),
@@ -1332,15 +1452,14 @@ _("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"
                        goto out_bmap_cancel;
                }
 
-               libxfs_trans_commit(tp,
-                               XFS_TRANS_RELEASE_LOG_RES|XFS_TRANS_SYNC);
+               libxfs_trans_commit(tp);
        }
 
        return;
 
 out_bmap_cancel:
-       libxfs_bmap_cancel(&flist);
-       libxfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT);
+       libxfs_defer_cancel(&dfops);
+       libxfs_trans_cancel(tp);
        return;
 }
 
@@ -1357,38 +1476,36 @@ dir2_kill_block(
        struct xfs_buf  *bp)
 {
        xfs_da_args_t   args;
-       int             committed;
        int             error;
        xfs_fsblock_t   firstblock;
-       xfs_bmap_free_t flist;
+       struct xfs_defer_ops    dfops;
        int             nres;
        xfs_trans_t     *tp;
 
-       tp = libxfs_trans_alloc(mp, 0);
        nres = XFS_REMOVE_SPACE_RES(mp);
-       error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_remove, nres, 0);
+       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
        if (error)
                res_failed(error);
        libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
        libxfs_trans_bjoin(tp, bp);
        memset(&args, 0, sizeof(args));
-       xfs_bmap_init(&flist, &firstblock);
+       libxfs_defer_init(&dfops, &firstblock);
        args.dp = ip;
        args.trans = tp;
        args.firstblock = &firstblock;
-       args.flist = &flist;
        args.whichfork = XFS_DATA_FORK;
-       if (da_bno >= mp->m_dirleafblk && da_bno < mp->m_dirfreeblk)
-               error = libxfs_da_shrink_inode(&args, da_bno, bp);
+       args.geo = mp->m_dir_geo;
+       if (da_bno >= mp->m_dir_geo->leafblk && da_bno < mp->m_dir_geo->freeblk)
+               error = -libxfs_da_shrink_inode(&args, da_bno, bp);
        else
-               error = libxfs_dir2_shrink_inode(&args,
-                               xfs_dir2_da_to_db(mp, da_bno), bp);
+               error = -libxfs_dir2_shrink_inode(&args,
+                               xfs_dir2_da_to_db(mp->m_dir_geo, da_bno), bp);
        if (error)
                do_error(_("shrink_inode failed inode %" PRIu64 " block %u\n"),
                        ip->i_ino, da_bno);
-       libxfs_bmap_finish(&tp, &flist, &committed);
-       libxfs_trans_commit(tp, 0);
+       libxfs_defer_ijoin(&dfops, ip);
+       libxfs_defer_finish(&tp, &dfops);
+       libxfs_trans_commit(tp);
 }
 
 /*
@@ -1413,7 +1530,6 @@ longform_dir2_entry_check_data(
        xfs_dir2_leaf_entry_t   *blp;
        struct xfs_buf          *bp;
        xfs_dir2_block_tail_t   *btp;
-       int                     committed;
        struct xfs_dir2_data_hdr *d;
        xfs_dir2_db_t           db;
        xfs_dir2_data_entry_t   *dep;
@@ -1422,7 +1538,7 @@ longform_dir2_entry_check_data(
        char                    *endptr;
        int                     error;
        xfs_fsblock_t           firstblock;
-       xfs_bmap_free_t         flist;
+       struct xfs_defer_ops            dfops;
        char                    fname[MAXNAMELEN + 1];
        freetab_t               *freetab;
        int                     i;
@@ -1439,16 +1555,21 @@ longform_dir2_entry_check_data(
        char                    *ptr;
        xfs_trans_t             *tp;
        int                     wantmagic;
+       struct xfs_da_args      da = {
+               .dp = ip,
+               .geo = mp->m_dir_geo,
+       };
+
 
        bp = *bpp;
        d = bp->b_addr;
-       ptr = (char *)xfs_dir3_data_entry_p(d);
+       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
        nbad = 0;
        needscan = needlog = 0;
        junkit = 0;
        freetab = *freetabp;
        if (isblock) {
-               btp = xfs_dir2_block_tail_p(mp, (struct xfs_dir2_data_hdr *)d);
+               btp = xfs_dir2_block_tail_p(mp->m_dir_geo, d);
                blp = xfs_dir2_block_leaf_p(btp);
                endptr = (char *)blp;
                if (endptr > (char *)btp)
@@ -1458,13 +1579,13 @@ longform_dir2_entry_check_data(
                else
                        wantmagic = XFS_DIR2_BLOCK_MAGIC;
        } else {
-               endptr = (char *)d + mp->m_dirblksize;
+               endptr = (char *)d + mp->m_dir_geo->blksize;
                if (xfs_sb_version_hascrc(&mp->m_sb))
                        wantmagic = XFS_DIR3_DATA_MAGIC;
                else
                        wantmagic = XFS_DIR2_DATA_MAGIC;
        }
-       db = xfs_dir2_da_to_db(mp, da_bno);
+       db = xfs_dir2_da_to_db(mp->m_dir_geo, da_bno);
 
        /* check for data block beyond expected end */
        if (freetab->naents <= db) {
@@ -1472,9 +1593,8 @@ longform_dir2_entry_check_data(
 
                *freetabp = freetab = realloc(freetab, FREETAB_SIZE(db + 1));
                if (!freetab) {
-                       do_error(
-       _("realloc failed in longform_dir2_entry_check_data (%zu bytes)\n"),
-                               FREETAB_SIZE(db + 1));
+                       do_error(_("realloc failed in %s (%zu bytes)\n"),
+                               __func__, FREETAB_SIZE(db + 1));
                }
                e.v = NULLDATAOFF;
                e.s = 0;
@@ -1503,7 +1623,7 @@ longform_dir2_entry_check_data(
                                break;
 
                        /* check for block with no data entries */
-                       if ((ptr == (char *)xfs_dir3_data_entry_p(d)) &&
+                       if ((ptr == (char *)M_DIROPS(mp)->data_entry_p(d)) &&
                            (ptr + be16_to_cpu(dup->length) >= endptr)) {
                                junkit = 1;
                                *num_illegal += 1;
@@ -1518,12 +1638,12 @@ longform_dir2_entry_check_data(
 
                /* validate data entry size */
                dep = (xfs_dir2_data_entry_t *)ptr;
-               if (ptr + xfs_dir3_data_entsize(mp, dep->namelen) > endptr)
+               if (ptr + M_DIROPS(mp)->data_entsize(dep->namelen) > endptr)
                        break;
-               if (be16_to_cpu(*xfs_dir3_data_entry_tag_p(mp, dep)) !=
+               if (be16_to_cpu(*M_DIROPS(mp)->data_entry_tag_p(dep)) !=
                                                (char *)dep - (char *)d)
                        break;
-               ptr += xfs_dir3_data_entsize(mp, dep->namelen);
+               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
        }
 
        /* did we find an empty or corrupt block? */
@@ -1553,15 +1673,14 @@ longform_dir2_entry_check_data(
        if (freetab->nents < db + 1)
                freetab->nents = db + 1;
 
-       tp = libxfs_trans_alloc(mp, 0);
-       error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_remove, 0, 0);
+       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, 0, 0, 0, &tp);
        if (error)
                res_failed(error);
+       da.trans = tp;
        libxfs_trans_ijoin(tp, ip, 0);
-       libxfs_trans_ihold(tp, ip);
        libxfs_trans_bjoin(tp, bp);
        libxfs_trans_bhold(tp, bp);
-       xfs_bmap_init(&flist, &firstblock);
+       libxfs_defer_init(&dfops, &firstblock);
        if (be32_to_cpu(d->magic) != wantmagic) {
                do_warn(
        _("bad directory block magic # %#x for directory inode %" PRIu64 " block %d: "),
@@ -1574,7 +1693,7 @@ longform_dir2_entry_check_data(
                        do_warn(_("would fix magic # to %#x\n"), wantmagic);
        }
        lastfree = 0;
-       ptr = (char *)xfs_dir3_data_entry_p(d);
+       ptr = (char *)M_DIROPS(mp)->data_entry_p(d);
        /*
         * look at each entry.  reference inode pointed to by each
         * entry in the incore inode tree.
@@ -1595,12 +1714,13 @@ longform_dir2_entry_check_data(
        _("directory inode %" PRIu64 " block %u has consecutive free entries: "),
                                        ip->i_ino, da_bno);
                                if (!no_modify) {
+
                                        do_warn(_("joining together\n"));
                                        len = be16_to_cpu(dup->length);
-                                       libxfs_dir2_data_use_free(tp, bp, dup,
+                                       libxfs_dir2_data_use_free(&da, bp, dup,
                                                ptr - (char *)d, len, &needlog,
                                                &needscan);
-                                       libxfs_dir2_data_make_free(tp, bp,
+                                       libxfs_dir2_data_make_free(&da, bp,
                                                ptr - (char *)d, len, &needlog,
                                                &needscan);
                                } else
@@ -1610,9 +1730,10 @@ longform_dir2_entry_check_data(
                        lastfree = 1;
                        continue;
                }
-               addr = xfs_dir2_db_off_to_dataptr(mp, db, ptr - (char *)d);
+               addr = xfs_dir2_db_off_to_dataptr(mp->m_dir_geo, db,
+                                                 ptr - (char *)d);
                dep = (xfs_dir2_data_entry_t *)ptr;
-               ptr += xfs_dir3_data_entsize(mp, dep->namelen);
+               ptr += M_DIROPS(mp)->data_entsize(dep->namelen);
                inum = be64_to_cpu(dep->inumber);
                lastfree = 0;
                /*
@@ -1623,7 +1744,7 @@ longform_dir2_entry_check_data(
                if (dep->name[0] == '/')  {
                        nbad++;
                        if (!no_modify)
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
                        continue;
                }
 
@@ -1639,7 +1760,7 @@ longform_dir2_entry_check_data(
        _("entry \"%s\" in directory inode %" PRIu64 " points to non-existent inode %" PRIu64 ""),
                                        fname, ip->i_ino, inum)) {
                                dep->name[0] = '/';
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
                        }
                        continue;
                }
@@ -1656,7 +1777,7 @@ longform_dir2_entry_check_data(
        _("entry \"%s\" in directory inode %" PRIu64 " points to free inode %" PRIu64),
                                        fname, ip->i_ino, inum)) {
                                dep->name[0] = '/';
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
                        }
                        continue;
                }
@@ -1674,7 +1795,7 @@ longform_dir2_entry_check_data(
        _("%s (ino %" PRIu64 ") in root (%" PRIu64 ") is not a directory"),
                                                ORPHANAGE, inum, ip->i_ino)) {
                                        dep->name[0] = '/';
-                                       libxfs_dir2_data_log_entry(tp, bp, dep);
+                                       libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                                continue;
                        }
@@ -1685,17 +1806,18 @@ longform_dir2_entry_check_data(
                        if (!orphanage_ino)
                                orphanage_ino = inum;
                }
+
                /*
                 * check for duplicate names in directory.
                 */
                if (!dir_hash_add(mp, hashtab, addr, inum, dep->namelen,
-                                                       dep->name)) {
+                               dep->name, M_DIROPS(mp)->data_get_ftype(dep))) {
                        nbad++;
                        if (entry_junked(
        _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                                        fname, inum, ip->i_ino)) {
                                dep->name[0] = '/';
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
                        }
                        if (inum == orphanage_ino)
                                orphanage_ino = 0;
@@ -1726,7 +1848,7 @@ longform_dir2_entry_check_data(
        _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is not in the the first block"), fname,
                                                inum, ip->i_ino)) {
                                        dep->name[0] = '/';
-                                       libxfs_dir2_data_log_entry(tp, bp, dep);
+                                       libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
                        continue;
@@ -1742,17 +1864,18 @@ longform_dir2_entry_check_data(
                 * of when directory is moved to orphanage.
                 */
                if (ip->i_ino == inum)  {
-                       ASSERT(dep->name[0] == '.' && dep->namelen == 1);
+                       ASSERT(no_modify ||
+                              (dep->name[0] == '.' && dep->namelen == 1));
                        add_inode_ref(current_irec, current_ino_offset);
                        if (da_bno != 0 ||
-                           dep != xfs_dir3_data_entry_p(d)) {
+                           dep != M_DIROPS(mp)->data_entry_p(d)) {
                                /* "." should be the first entry */
                                nbad++;
                                if (entry_junked(
        _("entry \"%s\" in dir %" PRIu64 " is not the first entry"),
                                                fname, inum, ip->i_ino)) {
                                        dep->name[0] = '/';
-                                       libxfs_dir2_data_log_entry(tp, bp, dep);
+                                       libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
                        *need_dot = 0;
@@ -1763,6 +1886,35 @@ longform_dir2_entry_check_data(
                 */
                if (no_modify && verify_inum(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 easy case first, regular inode, just bump
                 * the link count and continue
@@ -1809,11 +1961,10 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
                if (junkit)  {
                        if (inum == orphanage_ino)
                                orphanage_ino = 0;
-                       junkit = 0;
                        nbad++;
                        if (!no_modify)  {
                                dep->name[0] = '/';
-                               libxfs_dir2_data_log_entry(tp, bp, dep);
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
                                if (verbose)
                                        do_warn(
                                        _("\twill clear entry \"%s\"\n"),
@@ -1826,18 +1977,80 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
        }
        *num_illegal += nbad;
        if (needscan)
-               libxfs_dir2_data_freescan(mp, d, &needlog);
+               libxfs_dir2_data_freescan_int(mp->m_dir_geo, M_DIROPS(mp),
+                               d, &i);
        if (needlog)
-               libxfs_dir2_data_log_header(tp, bp);
-       libxfs_bmap_finish(&tp, &flist, &committed);
-       libxfs_trans_commit(tp, 0);
+               libxfs_dir2_data_log_header(&da, bp);
+       libxfs_defer_ijoin(&dfops, ip);
+       libxfs_defer_finish(&tp, &dfops);
+       libxfs_trans_commit(tp);
 
        /* record the largest free space in the freetab for later checking */
-       bf = xfs_dir3_data_bestfree_p(d);
+       bf = M_DIROPS(mp)->data_bestfree_p(d);
        freetab->ents[db].v = be16_to_cpu(bf[0].length);
        freetab->ents[db].s = 0;
 }
 
+/* check v5 metadata */
+static int
+__check_dir3_header(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp,
+       xfs_ino_t               ino,
+       __be64                  owner,
+       __be64                  blkno,
+       uuid_t                  *uuid)
+{
+
+       /* verify owner */
+       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);
+               return 1;
+       }
+       /* verify block number */
+       if (be64_to_cpu(blkno) != bp->b_bn) {
+               do_warn(
+_("expected block %" PRIu64 ", got %llu, directory inode %" PRIu64 "\n"),
+                       bp->b_bn, (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);
+               return 1;
+       }
+
+       return 0;
+}
+
+static int
+check_da3_header(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp,
+       xfs_ino_t               ino)
+{
+       struct xfs_da3_blkinfo  *info = bp->b_addr;
+
+       return __check_dir3_header(mp, bp, ino, info->owner, info->blkno,
+                                  &info->uuid);
+}
+
+static int
+check_dir3_header(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp,
+       xfs_ino_t               ino)
+{
+       struct xfs_dir3_blk_hdr *info = bp->b_addr;
+
+       return __check_dir3_header(mp, bp, ino, info->owner, info->blkno,
+                                  &info->uuid);
+}
+
 /*
  * Check contents of leaf-form block.
  */
@@ -1858,26 +2071,35 @@ longform_dir2_check_leaf(
        int                     seeval;
        struct xfs_dir2_leaf_entry *ents;
        struct xfs_dir3_icleaf_hdr leafhdr;
+       int                     error;
+       int                     fixit = 0;
 
-       da_bno = mp->m_dirleafblk;
-       if (libxfs_da_read_buf(NULL, ip, da_bno, -1, &bp, XFS_DATA_FORK,
-                               &xfs_dir3_leaf1_buf_ops)) {
-               do_error(
-       _("can't read block %u for directory inode %" PRIu64 "\n"),
+       da_bno = mp->m_dir_geo->leafblk;
+       error = dir_read_buf(ip, da_bno, -1, &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"),
                        da_bno, ip->i_ino);
+               return 1;
+       } else if (error) {
+               do_error(
+       _("can't read block %u for directory inode %" PRIu64 ", error %d\n"),
+                       da_bno, ip->i_ino, error);
                /* NOTREACHED */
        }
+
        leaf = bp->b_addr;
-       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-       ents = xfs_dir3_leaf_ents_p(leaf);
-       ltp = xfs_dir2_leaf_tail_p(mp, leaf);
+       M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = M_DIROPS(mp)->leaf_ents_p(leaf);
+       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 >
-                                       xfs_dir3_max_leaf_ents(mp, leaf) ||
+                                       M_DIROPS(mp)->leaf_max_ents(mp->m_dir_geo) ||
                                (char *)&ents[leafhdr.count] > (char *)bestsp) {
                do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad header\n"),
@@ -1885,6 +2107,15 @@ longform_dir2_check_leaf(
                libxfs_putbuf(bp);
                return 1;
        }
+
+       if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) {
+               error = check_da3_header(mp, bp, ip->i_ino);
+               if (error) {
+                       libxfs_putbuf(bp);
+                       return error;
+               }
+       }
+
        seeval = dir_hash_see_all(hashtab, ents, leafhdr.count, leafhdr.stale);
        if (dir_hash_check(hashtab, ip, seeval)) {
                libxfs_putbuf(bp);
@@ -1903,7 +2134,7 @@ longform_dir2_check_leaf(
                return 1;
        }
        libxfs_putbuf(bp);
-       return 0;
+       return fixit;
 }
 
 /*
@@ -1930,11 +2161,13 @@ longform_dir2_check_node(
        struct xfs_dir3_icleaf_hdr leafhdr;
        struct xfs_dir3_icfree_hdr freehdr;
        __be16                  *bests;
+       int                     error;
+       int                     fixit = 0;
 
-       for (da_bno = mp->m_dirleafblk, next_da_bno = 0;
-                       next_da_bno != NULLFILEOFF && da_bno < mp->m_dirfreeblk;
+       for (da_bno = mp->m_dir_geo->leafblk, next_da_bno = 0;
+                       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_dirblkfsbs - 1;
+               next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
                if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK))
                        break;
 
@@ -1945,30 +2178,51 @@ longform_dir2_check_node(
                 * a node block, then we'll skip it below based on a magic
                 * number check.
                 */
-               if (libxfs_da_read_buf(NULL, ip, da_bno, -1, &bp,
-                               XFS_DATA_FORK, &xfs_da3_node_buf_ops)) {
+               error = dir_read_buf(ip, da_bno, -1, &bp,
+                                    &xfs_da3_node_buf_ops, &fixit);
+               if (error) {
                        do_warn(
-       _("can't read leaf block %u for directory inode %" PRIu64 "\n"),
-                               da_bno, ip->i_ino);
+       _("can't read leaf block %u for directory inode %" PRIu64 ", error %d\n"),
+                               da_bno, ip->i_ino, error);
                        return 1;
                }
                leaf = bp->b_addr;
-               xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
-               ents = xfs_dir3_leaf_ents_p(leaf);
+               M_DIROPS(mp)->leaf_hdr_from_disk(&leafhdr, leaf);
+               ents = M_DIROPS(mp)->leaf_ents_p(leaf);
                if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
-                     leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) {
-                       if (leafhdr.magic == XFS_DA_NODE_MAGIC ||
-                           leafhdr.magic == XFS_DA3_NODE_MAGIC) {
-                               libxfs_putbuf(bp);
-                               continue;
-                       }
+                     leafhdr.magic == XFS_DIR3_LEAFN_MAGIC ||
+                     leafhdr.magic == XFS_DA_NODE_MAGIC ||
+                     leafhdr.magic == XFS_DA3_NODE_MAGIC)) {
                        do_warn(
        _("unknown magic number %#x for block %u in directory inode %" PRIu64 "\n"),
                                leafhdr.magic, da_bno, ip->i_ino);
                        libxfs_putbuf(bp);
                        return 1;
                }
-               if (leafhdr.count > xfs_dir3_max_leaf_ents(mp, leaf) ||
+
+               /* check v5 metadata */
+               if (leafhdr.magic == XFS_DIR3_LEAFN_MAGIC ||
+                   leafhdr.magic == XFS_DA3_NODE_MAGIC) {
+                       error = check_da3_header(mp, bp, ip->i_ino);
+                       if (error) {
+                               libxfs_putbuf(bp);
+                               return error;
+                       }
+               }
+
+               /* ignore nodes */
+               if (leafhdr.magic == XFS_DA_NODE_MAGIC ||
+                   leafhdr.magic == XFS_DA3_NODE_MAGIC) {
+                       libxfs_putbuf(bp);
+                       continue;
+               }
+
+               /*
+                * If there's a validator error, we need to ensure that we got
+                * 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) ||
                    leafhdr.count < leafhdr.stale) {
                        do_warn(
        _("leaf block %u for directory inode %" PRIu64 " bad header\n"),
@@ -1985,35 +2239,45 @@ longform_dir2_check_node(
        if (dir_hash_check(hashtab, ip, seeval))
                return 1;
 
-       for (da_bno = mp->m_dirfreeblk, next_da_bno = 0;
+       for (da_bno = mp->m_dir_geo->freeblk, next_da_bno = 0;
             next_da_bno != NULLFILEOFF;
             da_bno = (xfs_dablk_t)next_da_bno) {
-               next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
+               next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
                if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK))
                        break;
-               if (libxfs_da_read_buf(NULL, ip, da_bno, -1, &bp,
-                               XFS_DATA_FORK, &xfs_dir3_free_buf_ops)) {
+
+               error = dir_read_buf(ip, da_bno, -1, &bp,
+                                    &xfs_dir3_free_buf_ops, &fixit);
+               if (error) {
                        do_warn(
-       _("can't read freespace block %u for directory inode %" PRIu64 "\n"),
-                               da_bno, ip->i_ino);
+       _("can't read freespace block %u for directory inode %" PRIu64 ", error %d\n"),
+                               da_bno, ip->i_ino, error);
                        return 1;
                }
                free = bp->b_addr;
-               xfs_dir3_free_hdr_from_disk(&freehdr, free);
-               bests = xfs_dir3_free_bests_p(mp, free);
-               fdb = xfs_dir2_da_to_db(mp, da_bno);
+               M_DIROPS(mp)->free_hdr_from_disk(&freehdr, free);
+               bests = M_DIROPS(mp)->free_bests_p(free);
+               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_FREE_FIRSTDB(mp)) *
-                                               xfs_dir3_free_max_bests(mp) ||
-                               freehdr.nvalid < freehdr.nused) {
+                   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) ||
+                   freehdr.nvalid < freehdr.nused) {
                        do_warn(
        _("free block %u for directory inode %" PRIu64 " bad header\n"),
                                da_bno, ip->i_ino);
                        libxfs_putbuf(bp);
                        return 1;
                }
+
+               if (freehdr.magic == XFS_DIR3_FREE_MAGIC) {
+                       error = check_dir3_header(mp, bp, ip->i_ino);
+                       if (error) {
+                               libxfs_putbuf(bp);
+                               return error;
+                       }
+               }
                for (i = used = 0; i < freehdr.nvalid; i++) {
                        if (i + freehdr.firstdb >= freetab->nents ||
                                        freetab->ents[i + freehdr.firstdb].v !=
@@ -2045,7 +2309,7 @@ longform_dir2_check_node(
                        return 1;
                }
        }
-       return 0;
+       return fixit;
 }
 
 /*
@@ -2074,16 +2338,17 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
        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_dirblksize));
+       freetab = malloc(FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
        if (!freetab) {
-               do_error(
-               _("malloc failed in longform_dir2_entry_check (%" PRId64 " bytes)\n"),
-                       FREETAB_SIZE(ip->i_d.di_size / mp->m_dirblksize));
+               do_error(_("malloc failed in %s (%" PRId64 " bytes)\n"),
+                       __func__,
+                       FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
                exit(1);
        }
-       freetab->naents = ip->i_d.di_size / mp->m_dirblksize;
+       freetab->naents = ip->i_d.di_size / mp->m_dir_geo->blksize;
        freetab->nents = 0;
        for (i = 0; i < freetab->naents; i++) {
                freetab->ents[i].v = NULLDATAOFF;
@@ -2091,27 +2356,45 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
        }
        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? */
-       libxfs_dir2_isblock(NULL, ip, &isblock);
-       libxfs_dir2_isleaf(NULL, ip, &isleaf);
+       args.dp = ip;
+       args.geo = mp->m_dir_geo;
+       libxfs_dir2_isblock(&args, &isblock);
+       libxfs_dir2_isleaf(&args, &isleaf);
 
        /* check directory "data" blocks (ie. name/inode pairs) */
        for (da_bno = 0, next_da_bno = 0;
-            next_da_bno != NULLFILEOFF && da_bno < mp->m_dirleafblk;
+            next_da_bno != NULLFILEOFF && da_bno < mp->m_dir_geo->leafblk;
             da_bno = (xfs_dablk_t)next_da_bno) {
                const struct xfs_buf_ops *ops;
+               int                      error;
+               struct xfs_dir2_data_hdr *d;
 
-               next_da_bno = da_bno + mp->m_dirblkfsbs - 1;
-               if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK))
+               next_da_bno = da_bno + mp->m_dir_geo->fsbcount - 1;
+               if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK)) {
+                       /*
+                        * if this is the first block, there isn't anything we
+                        * can recover so we just trash it.
+                        */
+                        if (da_bno == 0) {
+                               fixit++;
+                               goto out_fix;
+                       }
                        break;
-               db = xfs_dir2_da_to_db(mp, da_bno);
+               }
+
+               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 longform_dir2_entry_check (%zu bytes)\n"),
+                               do_error(_("realloc failed in %s (%zu bytes)\n"),
+                                       __func__,
                                        num_bps * sizeof(struct xfs_buf*));
                }
 
@@ -2119,11 +2402,12 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
                        ops = &xfs_dir3_block_buf_ops;
                else
                        ops = &xfs_dir3_data_buf_ops;
-               if (libxfs_da_read_buf(NULL, ip, da_bno, -1, &bplist[db],
-                                      XFS_DATA_FORK, ops)) {
+
+               error = dir_read_buf(ip, da_bno, -1, &bplist[db], ops, &fixit);
+               if (error) {
                        do_warn(
-       _("can't read data block %u for directory inode %" PRIu64 "\n"),
-                               da_bno, ino);
+       _("can't read data block %u for directory inode %" PRIu64 " error %d\n"),
+                               da_bno, ino, error);
                        *num_illegal += 1;
 
                        /*
@@ -2137,11 +2421,25 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
                        }
                        continue;
                }
+
+               /* check v5 metadata */
+               d = bplist[db]->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++;
+                               continue;
+                       }
+               }
+
                longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot,
                                irec, ino_offset, &bplist[db], hashtab,
                                &freetab, da_bno, isblock);
        }
-       fixit = (*num_illegal != 0) || dir2_is_badino(ino) || *need_dot;
+       fixit |= (*num_illegal != 0) || dir2_is_badino(ino) || *need_dot;
 
        if (!dotdot_update) {
                /* check btree and freespace */
@@ -2151,7 +2449,7 @@ longform_dir2_entry_check(xfs_mount_t     *mp,
                        xfs_dir2_leaf_entry_t   *blp;
 
                        block = bplist[0]->b_addr;
-                       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);
                        seeval = dir_hash_see_all(hashtab, blp,
                                                be32_to_cpu(btp->count),
@@ -2169,14 +2467,14 @@ longform_dir2_entry_check(xfs_mount_t   *mp,
 out_fix:
        if (!no_modify && (fixit || dotdot_update)) {
                dir_hash_dup_names(hashtab);
-               for (i = 0; i < freetab->naents; i++)
+               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 < freetab->naents; i++)
+               for (i = 0; i < num_bps; i++)
                        if (bplist[i])
                                libxfs_putbuf(bplist[i]);
        }
@@ -2189,6 +2487,62 @@ out_fix:
  * shortform directory v2 processing routines -- entry verification and
  * bad entry deletion (pruning).
  */
+static struct xfs_dir2_sf_entry *
+shortform_dir2_junk(
+       struct xfs_mount        *mp,
+       struct xfs_dir2_sf_hdr  *sfp,
+       struct xfs_dir2_sf_entry *sfep,
+       xfs_ino_t               lino,
+       int                     *max_size,
+       int                     *index,
+       int                     *bytes_deleted,
+       int                     *ino_dirty)
+{
+       struct xfs_dir2_sf_entry *next_sfep;
+       int                     next_len;
+       int                     next_elen;
+
+       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);
+
+       /*
+        * if we are just checking, simply return the pointer to the next entry
+        * here so that the checking loop can continue.
+        */
+       if (no_modify) {
+               do_warn(_("would junk entry\n"));
+               return next_sfep;
+       }
+
+       /*
+        * now move all the remaining entries down over the junked entry and
+        * clear the newly unused bytes at the tail of the directory region.
+        */
+       next_len = *max_size - ((intptr_t)next_sfep - (intptr_t)sfp);
+       *max_size -= next_elen;
+       *bytes_deleted += next_elen;
+
+       memmove(sfep, next_sfep, next_len);
+       memset((void *)((intptr_t)sfep + next_len), 0, next_elen);
+       sfp->count -= 1;
+       *ino_dirty = 1;
+
+       /*
+        * WARNING:  drop the index i by one so it matches the decremented count
+        * for accurate comparisons in the loop test
+        */
+       (*index)--;
+
+       if (verbose)
+               do_warn(_("junking entry\n"));
+       else
+               do_warn("\n");
+       return sfep;
+}
+
 static void
 shortform_dir2_entry_check(xfs_mount_t *mp,
                        xfs_ino_t       ino,
@@ -2201,15 +2555,13 @@ shortform_dir2_entry_check(xfs_mount_t  *mp,
        xfs_ino_t               lino;
        xfs_ino_t               parent;
        struct xfs_dir2_sf_hdr  *sfp;
-       xfs_dir2_sf_entry_t     *sfep, *next_sfep, *tmp_sfep;
-       xfs_ifork_t             *ifp;
-       ino_tree_node_t         *irec;
+       struct xfs_dir2_sf_entry *sfep;
+       struct xfs_dir2_sf_entry *next_sfep;
+       struct xfs_ifork        *ifp;
+       struct ino_tree_node    *irec;
        int                     max_size;
        int                     ino_offset;
        int                     i;
-       int                     junkit;
-       int                     tmp_len;
-       int                     tmp_elen;
        int                     bad_sfnamelen;
        int                     namelen;
        int                     bytes_deleted;
@@ -2237,7 +2589,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                        do_warn(
        _("setting .. in sf dir inode %" PRIu64 " to %" PRIu64 "\n"),
                                ino, parent);
-                       xfs_dir2_sf_put_parent_ino(sfp, parent);
+                       M_DIROPS(mp)->sf_put_parent_ino(sfp, parent);
                        *ino_dirty = 1;
                }
                return;
@@ -2254,7 +2606,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
        /*
         * Initialise i8 counter -- the parent inode number counts as well.
         */
-       i8 = xfs_dir2_sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM;
+       i8 = M_DIROPS(mp)->sf_get_parent_ino(sfp) > XFS_DIR2_MAX_SHORT_INUM;
 
        /*
         * now run through entries, stop at first bad entry, don't need
@@ -2264,13 +2616,11 @@ shortform_dir2_entry_check(xfs_mount_t  *mp,
        sfep = next_sfep = xfs_dir2_sf_firstentry(sfp);
 
        for (i = 0; i < sfp->count && max_size >
-                                       (__psint_t)next_sfep - (__psint_t)sfp;
+                                       (intptr_t)next_sfep - (intptr_t)sfp;
                        sfep = next_sfep, i++)  {
-               junkit = 0;
                bad_sfnamelen = 0;
-               tmp_sfep = NULL;
 
-               lino = xfs_dir3_sfe_get_ino(mp, sfp, sfep);
+               lino = M_DIROPS(mp)->sf_get_ino(sfp, sfep);
 
                namelen = sfep->namelen;
 
@@ -2289,8 +2639,8 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
 
                        if (i == sfp->count - 1)  {
                                namelen = ip->i_d.di_size -
-                                       ((__psint_t) &sfep->name[0] -
-                                        (__psint_t) sfp);
+                                       ((intptr_t) &sfep->name[0] -
+                                        (intptr_t) sfp);
                        } else  {
                                /*
                                 * don't process the rest of the directory,
@@ -2298,15 +2648,15 @@ shortform_dir2_entry_check(xfs_mount_t  *mp,
                                 */
                                break;
                        }
-               } else if (no_modify && (__psint_t) sfep - (__psint_t) sfp +
-                               + xfs_dir3_sf_entsize(mp, sfp, sfep->namelen)
+               } else if (no_modify && (intptr_t) sfep - (intptr_t) sfp +
+                               + M_DIROPS(mp)->sf_entsize(sfp, sfep->namelen)
                                > ip->i_d.di_size)  {
                        bad_sfnamelen = 1;
 
                        if (i == sfp->count - 1)  {
                                namelen = ip->i_d.di_size -
-                                       ((__psint_t) &sfep->name[0] -
-                                        (__psint_t) sfp);
+                                       ((intptr_t) &sfep->name[0] -
+                                        (intptr_t) sfp);
                        } else  {
                                /*
                                 * don't process the rest of the directory,
@@ -2328,8 +2678,7 @@ shortform_dir2_entry_check(xfs_mount_t    *mp,
                 */
 
                if (no_modify && verify_inum(mp, lino))  {
-                       next_sfep = (xfs_dir2_sf_entry_t *)((__psint_t)sfep +
-                               xfs_dir3_sf_entsize(mp, sfp, sfep->namelen));
+                       next_sfep = M_DIROPS(mp)->sf_nextentry(sfp, sfep);
                        continue;
                }
 
@@ -2340,7 +2689,10 @@ shortform_dir2_entry_check(xfs_mount_t   *mp,
                        do_warn(
        _("entry \"%s\" in shortform directory %" PRIu64 " references non-existent inode %" PRIu64 "\n"),
                                fname, ino, lino);
-                       goto do_junkit;
+                       next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino,
+                                               &max_size, &i, &bytes_deleted,
+                                               ino_dirty);
+                       continue;
                }
 
                ino_offset = XFS_INO_TO_AGINO(mp, lino) - irec->ino_startnum;
@@ -2354,7 +2706,10 @@ shortform_dir2_entry_check(xfs_mount_t   *mp,
                        do_warn(
        _("entry \"%s\" in shortform directory inode %" PRIu64 " points to free inode %" PRIu64 "\n"),
                                fname, ino, lino);
-                       goto do_junkit;
+                       next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino,
+                                               &max_size, &i, &bytes_deleted,
+                                               ino_dirty);
+                       continue;
                }
                /*
                 * check if this inode is lost+found dir in the root
@@ -2367,7 +2722,10 @@ shortform_dir2_entry_check(xfs_mount_t   *mp,
                                do_warn(
        _("%s (ino %" PRIu64 ") in root (%" PRIu64 ") is not a directory"),
                                        ORPHANAGE, lino, ino);
-                               goto do_junkit;
+                               next_sfep = shortform_dir2_junk(mp, sfp, sfep,
+                                               lino, &max_size, &i,
+                                               &bytes_deleted, ino_dirty);
+                               continue;
                        }
                        /*
                         * if this is a dup, it will be picked up below,
@@ -2381,11 +2739,15 @@ 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)) {
+                               lino, sfep->namelen, sfep->name,
+                               M_DIROPS(mp)->sf_get_ftype(sfep))) {
                        do_warn(
 _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                                fname, lino, ino);
-                       goto do_junkit;
+                       next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino,
+                                               &max_size, &i, &bytes_deleted,
+                                               ino_dirty);
+                       continue;
                }
 
                if (!inode_isadir(irec, ino_offset))  {
@@ -2403,11 +2765,14 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                         * the .. in the child, blow out the entry
                         */
                        if (is_inode_reached(irec, ino_offset))  {
-                               junkit = 1;
                                do_warn(
        _("entry \"%s\" in directory inode %" PRIu64
          " references already connected inode %" PRIu64 ".\n"),
                                        fname, ino, lino);
+                               next_sfep = shortform_dir2_junk(mp, sfp, sfep,
+                                               lino, &max_size, &i,
+                                               &bytes_deleted, ino_dirty);
+                               continue;
                        } else if (parent == ino)  {
                                add_inode_reached(irec, ino_offset);
                                add_inode_ref(current_irec, current_ino_offset);
@@ -2423,76 +2788,60 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                                add_dotdot_update(XFS_INO_TO_AGNO(mp, lino),
                                                        irec, ino_offset);
                        } else  {
-                               junkit = 1;
                                do_warn(
        _("entry \"%s\" in directory inode %" PRIu64
          " not consistent with .. value (%" PRIu64
          ") in inode %" PRIu64 ",\n"),
                                        fname, ino, parent, lino);
+                               next_sfep = shortform_dir2_junk(mp, sfp, sfep,
+                                               lino, &max_size, &i,
+                                               &bytes_deleted, ino_dirty);
+                               continue;
                        }
                }
 
-               if (junkit)  {
-do_junkit:
-                       if (lino == orphanage_ino)
-                               orphanage_ino = 0;
-                       if (!no_modify)  {
-                               tmp_elen = xfs_dir3_sf_entsize(mp, sfp,
-                                                               sfep->namelen);
-                               tmp_sfep = (xfs_dir2_sf_entry_t *)
-                                       ((__psint_t) sfep + tmp_elen);
-                               tmp_len = max_size - ((__psint_t) tmp_sfep
-                                                       - (__psint_t) sfp);
-                               max_size -= tmp_elen;
-                               bytes_deleted += tmp_elen;
-
-                               memmove(sfep, tmp_sfep, tmp_len);
-
-                               sfp->count -= 1;
-                               memset((void *)((__psint_t)sfep + tmp_len), 0,
-                                               tmp_elen);
-
-                               /*
-                                * set the tmp value to the current
-                                * pointer so we'll process the entry
-                                * we just moved up
-                                */
-                               tmp_sfep = sfep;
-
-                               /*
-                                * WARNING:  drop the index i by one
-                                * so it matches the decremented count for
-                                * accurate comparisons in the loop test
-                                */
-                               i--;
+               /* validate ftype field if supported */
+               if (xfs_sb_version_hasftype(&mp->m_sb)) {
+                       uint8_t dir_ftype;
+                       uint8_t ino_ftype;
 
-                               *ino_dirty = 1;
+                       dir_ftype = M_DIROPS(mp)->sf_get_ftype(sfep);
+                       ino_ftype = get_inode_ftype(irec, ino_offset);
 
-                               if (verbose)
-                                       do_warn(_("junking entry\n"));
-                               else
-                                       do_warn("\n");
-                       } else  {
-                               do_warn(_("would junk entry\n"));
+                       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,
+                                               ino, lino);
+                               } else {
+                                       do_warn(
+       _("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,
+                                                               ino_ftype);
+                                       dir_hash_update_ftype(hashtab,
+                       (xfs_dir2_dataptr_t)(sfep - xfs_dir2_sf_firstentry(sfp)),
+                                                             ino_ftype);
+                                       *ino_dirty = 1;
+                               }
                        }
-               } else if (lino > XFS_DIR2_MAX_SHORT_INUM)
+               }
+
+               if (lino > XFS_DIR2_MAX_SHORT_INUM)
                        i8++;
 
                /*
-                * go onto next entry unless we've just junked an
-                * entry in which the current entry pointer points
-                * to an unprocessed entry.  have to take into entries
-                * with bad namelen into account in no modify mode since we
-                * calculate size based on next_sfep.
+                * go onto next entry - we have to take entries with bad namelen
+                * into account in no modify mode since we calculate size based
+                * on next_sfep.
                 */
                ASSERT(no_modify || bad_sfnamelen == 0);
-
-               next_sfep = (tmp_sfep == NULL)
-                       ? (xfs_dir2_sf_entry_t *) ((__psint_t) sfep
-                                                       + ((!bad_sfnamelen)
-                               ? xfs_dir3_sf_entsize(mp, sfp, sfep->namelen)
-                               : xfs_dir3_sf_entsize(mp, sfp, namelen)))
-                       : tmp_sfep;
+               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)));
        }
 
        if (sfp->i8count != i8) {
@@ -2501,11 +2850,13 @@ do_junkit:
                                ino);
                } else {
                        if (i8 == 0) {
+                               struct xfs_dir2_sf_entry *tmp_sfep;
+
                                tmp_sfep = next_sfep;
                                process_sf_dir2_fixi8(mp, sfp, &tmp_sfep);
                                bytes_deleted +=
-                                       (__psint_t)next_sfep -
-                                       (__psint_t)tmp_sfep;
+                                       (intptr_t)next_sfep -
+                                       (intptr_t)tmp_sfep;
                                next_sfep = tmp_sfep;
                        } else
                                sfp->i8count = i8;
@@ -2518,8 +2869,7 @@ do_junkit:
        /*
         * sync up sizes if required
         */
-       if (*ino_dirty)  {
-               ASSERT(bytes_deleted > 0);
+       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;
@@ -2527,9 +2877,9 @@ do_junkit:
 
        if (ip->i_d.di_size != ip->i_df.if_bytes)  {
                ASSERT(ip->i_df.if_bytes == (xfs_fsize_t)
-                               ((__psint_t) next_sfep - (__psint_t) sfp));
+                               ((intptr_t) next_sfep - (intptr_t) sfp));
                ip->i_d.di_size = (xfs_fsize_t)
-                               ((__psint_t) next_sfep - (__psint_t) sfp);
+                               ((intptr_t) next_sfep - (intptr_t) sfp);
                do_warn(
        _("setting size to %" PRId64 " bytes to reflect junked entries\n"),
                        ip->i_d.di_size);
@@ -2548,12 +2898,12 @@ process_dir_inode(
        int                     ino_offset)
 {
        xfs_ino_t               ino;
-       xfs_bmap_free_t         flist;
+       struct xfs_defer_ops            dfops;
        xfs_fsblock_t           first;
        xfs_inode_t             *ip;
        xfs_trans_t             *tp;
        dir_hash_tab_t          *hashtab;
-       int                     need_dot, committed;
+       int                     need_dot;
        int                     dirty, num_illegal, error, nres;
 
        ino = XFS_AGINO_TO_INO(mp, agno, irec->ino_startnum + ino_offset);
@@ -2566,7 +2916,7 @@ process_dir_inode(
 
        ASSERT(!is_inode_refchecked(irec, ino_offset) || dotdot_update);
 
-       error = libxfs_iget(mp, NULL, ino, 0, &ip, 0);
+       error = -libxfs_iget(mp, NULL, ino, 0, &ip, &phase6_ifork_ops);
        if (error) {
                if (!no_modify)
                        do_error(
@@ -2629,7 +2979,6 @@ process_dir_inode(
                        break;
 
                case XFS_DINODE_FMT_LOCAL:
-                       tp = libxfs_trans_alloc(mp, 0);
                        /*
                         * using the remove reservation is overkill
                         * since at most we'll only need to log the
@@ -2637,13 +2986,12 @@ process_dir_inode(
                         * new define in ourselves.
                         */
                        nres = no_modify ? 0 : XFS_REMOVE_SPACE_RES(mp);
-                       error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_remove,
-                                                    nres, 0);
+                       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove,
+                                                   nres, 0, 0, &tp);
                        if (error)
                                res_failed(error);
 
                        libxfs_trans_ijoin(tp, ip, 0);
-                       libxfs_trans_ihold(tp, ip);
 
                        shortform_dir2_entry_check(mp, ino, ip, &dirty,
                                                irec, ino_offset,
@@ -2653,12 +3001,9 @@ process_dir_inode(
                        if (dirty)  {
                                libxfs_trans_log_inode(tp, ip,
                                        XFS_ILOG_CORE | XFS_ILOG_DDATA);
-                               libxfs_trans_commit(tp,
-                                       XFS_TRANS_RELEASE_LOG_RES |
-                                       XFS_TRANS_SYNC);
+                               libxfs_trans_commit(tp);
                        } else  {
-                               libxfs_trans_cancel(tp,
-                                       XFS_TRANS_RELEASE_LOG_RES);
+                               libxfs_trans_cancel(tp);
                        }
                        break;
 
@@ -2682,31 +3027,28 @@ process_dir_inode(
 
                do_warn(_("recreating root directory .. entry\n"));
 
-               tp = libxfs_trans_alloc(mp, 0);
-               ASSERT(tp != NULL);
-
                nres = XFS_MKDIR_SPACE_RES(mp, 2);
-               error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_mkdir, nres, 0);
+               error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir,
+                                           nres, 0, 0, &tp);
                if (error)
                        res_failed(error);
 
                libxfs_trans_ijoin(tp, ip, 0);
-               libxfs_trans_ihold(tp, ip);
 
-               xfs_bmap_init(&flist, &first);
+               libxfs_defer_init(&dfops, &first);
 
-               error = libxfs_dir_createname(tp, ip, &xfs_name_dotdot,
-                                       ip->i_ino, &first, &flist, nres);
+               error = -libxfs_dir_createname(tp, ip, &xfs_name_dotdot,
+                                       ip->i_ino, &first, nres);
                if (error)
                        do_error(
        _("can't make \"..\" entry in root inode %" PRIu64 ", createname error %d\n"), ino, error);
 
                libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-               error = libxfs_bmap_finish(&tp, &flist, &committed);
+               libxfs_defer_ijoin(&dfops, ip);
+               error = -libxfs_defer_finish(&tp, &dfops);
                ASSERT(error == 0);
-               libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES |
-                                                       XFS_TRANS_SYNC);
+               libxfs_trans_commit(tp);
 
                need_root_dotdot = 0;
        } else if (need_root_dotdot && ino == mp->m_sb.sb_rootino)  {
@@ -2743,22 +3085,18 @@ process_dir_inode(
                        do_warn(
        _("creating missing \".\" entry in dir ino %" PRIu64 "\n"), ino);
 
-                       tp = libxfs_trans_alloc(mp, 0);
-                       ASSERT(tp != NULL);
-
                        nres = XFS_MKDIR_SPACE_RES(mp, 1);
-                       error = libxfs_trans_reserve(tp, &M_RES(mp)->tr_mkdir,
-                                                    nres, 0);
+                       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_mkdir,
+                                                   nres, 0, 0, &tp);
                        if (error)
                                res_failed(error);
 
                        libxfs_trans_ijoin(tp, ip, 0);
-                       libxfs_trans_ihold(tp, ip);
 
-                       xfs_bmap_init(&flist, &first);
+                       libxfs_defer_init(&dfops, &first);
 
-                       error = libxfs_dir_createname(tp, ip, &xfs_name_dot,
-                                       ip->i_ino, &first, &flist, nres);
+                       error = -libxfs_dir_createname(tp, ip, &xfs_name_dot,
+                                       ip->i_ino, &first, nres);
                        if (error)
                                do_error(
        _("can't make \".\" entry in dir ino %" PRIu64 ", createname error %d\n"),
@@ -2766,13 +3104,13 @@ process_dir_inode(
 
                        libxfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
 
-                       error = libxfs_bmap_finish(&tp, &flist, &committed);
+                       libxfs_defer_ijoin(&dfops, ip);
+                       error = -libxfs_defer_finish(&tp, &dfops);
                        ASSERT(error == 0);
-                       libxfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES
-                                       |XFS_TRANS_SYNC);
+                       libxfs_trans_commit(tp);
                }
        }
-       libxfs_iput(ip, 0);
+       IRELE(ip);
 }
 
 /*
@@ -2788,8 +3126,6 @@ mark_standalone_inodes(xfs_mount_t *mp)
        irec = find_inode_rec(mp, XFS_INO_TO_AGNO(mp, mp->m_sb.sb_rbmino),
                        XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rbmino));
 
-       ASSERT(irec != NULL);
-
        offset = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rbmino) -
                        irec->ino_startnum;
 
@@ -2801,8 +3137,6 @@ mark_standalone_inodes(xfs_mount_t *mp)
        offset = XFS_INO_TO_AGINO(mp, mp->m_sb.sb_rsumino) -
                        irec->ino_startnum;
 
-       ASSERT(irec != NULL);
-
        add_inode_reached(irec, offset);
 
        if (fs_quotas)  {
@@ -2880,7 +3214,7 @@ check_for_orphaned_inodes(
 
 static void
 traverse_function(
-       work_queue_t            *wq,
+       struct workqueue        *wq,
        xfs_agnumber_t          agno,
        void                    *arg)
 {
@@ -2897,12 +3231,19 @@ traverse_function(
                if (irec->ino_isa_dir == 0)
                        continue;
 
-               if (pf_args)
+               if (pf_args) {
                        sem_post(&pf_args->ra_count);
+#ifdef XR_PF_TRACE
+                       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->mp, agno, irec, i);
+                               process_dir_inode(wq->wq_ctx, agno, irec, i);
                }
        }
        cleanup_inode_prefetch(pf_args);
@@ -2919,9 +3260,10 @@ update_missing_dotdot_entries(
         * set dotdot_update flag so processing routines do not count links
         */
        dotdot_update = 1;
-       while (dotdot_update_list) {
-               dir = dotdot_update_list;
-               dotdot_update_list = dir->next;
+       while (!list_empty(&dotdot_update_list)) {
+               dir = list_entry(dotdot_update_list.prev, struct dotdot_update,
+                                list);
+               list_del(&dir->list);
                process_dir_inode(mp, dir->agno, dir->irec, dir->ino_offset);
                free(dir);
        }
@@ -2929,23 +3271,9 @@ update_missing_dotdot_entries(
 
 static void
 traverse_ags(
-       xfs_mount_t             *mp)
+       struct xfs_mount        *mp)
 {
-       int                     i;
-       work_queue_t            queue;
-       prefetch_args_t         *pf_args[2];
-
-       /*
-        * we always do prefetch for phase 6 as it will fill in the gaps
-        * not read during phase 3 prefetch.
-        */
-       queue.mp = mp;
-       pf_args[0] = start_inode_prefetch(0, 1, NULL);
-       for (i = 0; i < glob_agcount; i++) {
-               pf_args[(~i) & 1] = start_inode_prefetch(i + 1, 1,
-                               pf_args[i & 1]);
-               traverse_function(&queue, i, pf_args[i & 1]);
-       }
+       do_inode_prefetch(mp, 0, traverse_function, false, true);
 }
 
 void