OSDN Git Service

Merge tag 'for-linus-v3.10-rc1-2' of git://oss.sgi.com/xfs/xfs
[android-x86/kernel.git] / fs / xfs / xfs_dir2_node.c
index 5980f9b..5246de4 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2000-2005 Silicon Graphics, Inc.
+ * Copyright (c) 2013 Red Hat, Inc.
  * All Rights Reserved.
  *
  * This program is free software; you can redistribute it and/or
 #include "xfs_dir2_priv.h"
 #include "xfs_error.h"
 #include "xfs_trace.h"
+#include "xfs_buf_item.h"
+#include "xfs_cksum.h"
 
 /*
  * Function declarations.
  */
 static int xfs_dir2_leafn_add(struct xfs_buf *bp, xfs_da_args_t *args,
                              int index);
-#ifdef DEBUG
-static void xfs_dir2_leafn_check(struct xfs_inode *dp, struct xfs_buf *bp);
-#else
-#define        xfs_dir2_leafn_check(dp, bp)
-#endif
-static void xfs_dir2_leafn_moveents(xfs_da_args_t *args, struct xfs_buf *bp_s,
-                                   int start_s, struct xfs_buf *bp_d,
-                                   int start_d, int count);
 static void xfs_dir2_leafn_rebalance(xfs_da_state_t *state,
                                     xfs_da_state_blk_t *blk1,
                                     xfs_da_state_blk_t *blk2);
@@ -55,52 +50,126 @@ static int xfs_dir2_leafn_remove(xfs_da_args_t *args, struct xfs_buf *bp,
 static int xfs_dir2_node_addname_int(xfs_da_args_t *args,
                                     xfs_da_state_blk_t *fblk);
 
-static void
-xfs_dir2_free_verify(
+/*
+ * Check internal consistency of a leafn block.
+ */
+#ifdef DEBUG
+#define        xfs_dir3_leaf_check(mp, bp) \
+do { \
+       if (!xfs_dir3_leafn_check((mp), (bp))) \
+               ASSERT(0); \
+} while (0);
+
+static bool
+xfs_dir3_leafn_check(
+       struct xfs_mount        *mp,
+       struct xfs_buf          *bp)
+{
+       struct xfs_dir2_leaf    *leaf = bp->b_addr;
+       struct xfs_dir3_icleaf_hdr leafhdr;
+
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+
+       if (leafhdr.magic == XFS_DIR3_LEAFN_MAGIC) {
+               struct xfs_dir3_leaf_hdr *leaf3 = bp->b_addr;
+               if (be64_to_cpu(leaf3->info.blkno) != bp->b_bn)
+                       return false;
+       } else if (leafhdr.magic != XFS_DIR2_LEAFN_MAGIC)
+               return false;
+
+       return xfs_dir3_leaf_check_int(mp, &leafhdr, leaf);
+}
+#else
+#define        xfs_dir3_leaf_check(mp, bp)
+#endif
+
+static bool
+xfs_dir3_free_verify(
        struct xfs_buf          *bp)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
        struct xfs_dir2_free_hdr *hdr = bp->b_addr;
-       int                     block_ok = 0;
 
-       block_ok = hdr->magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC);
-       if (!block_ok) {
-               XFS_CORRUPTION_ERROR("xfs_dir2_free_verify magic",
-                                    XFS_ERRLEVEL_LOW, mp, hdr);
-               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+
+               if (hdr3->magic != cpu_to_be32(XFS_DIR3_FREE_MAGIC))
+                       return false;
+               if (!uuid_equal(&hdr3->uuid, &mp->m_sb.sb_uuid))
+                       return false;
+               if (be64_to_cpu(hdr3->blkno) != bp->b_bn)
+                       return false;
+       } else {
+               if (hdr->magic != cpu_to_be32(XFS_DIR2_FREE_MAGIC))
+                       return false;
        }
+
+       /* XXX: should bounds check the xfs_dir3_icfree_hdr here */
+
+       return true;
 }
 
 static void
-xfs_dir2_free_read_verify(
+xfs_dir3_free_read_verify(
        struct xfs_buf  *bp)
 {
-       xfs_dir2_free_verify(bp);
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+
+       if ((xfs_sb_version_hascrc(&mp->m_sb) &&
+            !xfs_verify_cksum(bp->b_addr, BBTOB(bp->b_length),
+                                         XFS_DIR3_FREE_CRC_OFF)) ||
+           !xfs_dir3_free_verify(bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+       }
 }
 
 static void
-xfs_dir2_free_write_verify(
+xfs_dir3_free_write_verify(
        struct xfs_buf  *bp)
 {
-       xfs_dir2_free_verify(bp);
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_buf_log_item *bip = bp->b_fspriv;
+       struct xfs_dir3_blk_hdr *hdr3 = bp->b_addr;
+
+       if (!xfs_dir3_free_verify(bp)) {
+               XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, bp->b_addr);
+               xfs_buf_ioerror(bp, EFSCORRUPTED);
+               return;
+       }
+
+       if (!xfs_sb_version_hascrc(&mp->m_sb))
+               return;
+
+       if (bip)
+               hdr3->lsn = cpu_to_be64(bip->bli_item.li_lsn);
+
+       xfs_update_cksum(bp->b_addr, BBTOB(bp->b_length), XFS_DIR3_FREE_CRC_OFF);
 }
 
-static const struct xfs_buf_ops xfs_dir2_free_buf_ops = {
-       .verify_read = xfs_dir2_free_read_verify,
-       .verify_write = xfs_dir2_free_write_verify,
+const struct xfs_buf_ops xfs_dir3_free_buf_ops = {
+       .verify_read = xfs_dir3_free_read_verify,
+       .verify_write = xfs_dir3_free_write_verify,
 };
 
 
 static int
-__xfs_dir2_free_read(
+__xfs_dir3_free_read(
        struct xfs_trans        *tp,
        struct xfs_inode        *dp,
        xfs_dablk_t             fbno,
        xfs_daddr_t             mappedbno,
        struct xfs_buf          **bpp)
 {
-       return xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
-                               XFS_DATA_FORK, &xfs_dir2_free_buf_ops);
+       int                     err;
+
+       err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
+                               XFS_DATA_FORK, &xfs_dir3_free_buf_ops);
+
+       /* try read returns without an error or *bpp if it lands in a hole */
+       if (!err && tp && *bpp)
+               xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_FREE_BUF);
+       return err;
 }
 
 int
@@ -110,7 +179,7 @@ xfs_dir2_free_read(
        xfs_dablk_t             fbno,
        struct xfs_buf          **bpp)
 {
-       return __xfs_dir2_free_read(tp, dp, fbno, -1, bpp);
+       return __xfs_dir3_free_read(tp, dp, fbno, -1, bpp);
 }
 
 static int
@@ -120,7 +189,95 @@ xfs_dir2_free_try_read(
        xfs_dablk_t             fbno,
        struct xfs_buf          **bpp)
 {
-       return __xfs_dir2_free_read(tp, dp, fbno, -2, bpp);
+       return __xfs_dir3_free_read(tp, dp, fbno, -2, bpp);
+}
+
+
+void
+xfs_dir3_free_hdr_from_disk(
+       struct xfs_dir3_icfree_hdr      *to,
+       struct xfs_dir2_free            *from)
+{
+       if (from->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC)) {
+               to->magic = be32_to_cpu(from->hdr.magic);
+               to->firstdb = be32_to_cpu(from->hdr.firstdb);
+               to->nvalid = be32_to_cpu(from->hdr.nvalid);
+               to->nused = be32_to_cpu(from->hdr.nused);
+       } else {
+               struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)from;
+
+               to->magic = be32_to_cpu(hdr3->hdr.magic);
+               to->firstdb = be32_to_cpu(hdr3->firstdb);
+               to->nvalid = be32_to_cpu(hdr3->nvalid);
+               to->nused = be32_to_cpu(hdr3->nused);
+       }
+
+       ASSERT(to->magic == XFS_DIR2_FREE_MAGIC ||
+              to->magic == XFS_DIR3_FREE_MAGIC);
+}
+
+static void
+xfs_dir3_free_hdr_to_disk(
+       struct xfs_dir2_free            *to,
+       struct xfs_dir3_icfree_hdr      *from)
+{
+       ASSERT(from->magic == XFS_DIR2_FREE_MAGIC ||
+              from->magic == XFS_DIR3_FREE_MAGIC);
+
+       if (from->magic == XFS_DIR2_FREE_MAGIC) {
+               to->hdr.magic = cpu_to_be32(from->magic);
+               to->hdr.firstdb = cpu_to_be32(from->firstdb);
+               to->hdr.nvalid = cpu_to_be32(from->nvalid);
+               to->hdr.nused = cpu_to_be32(from->nused);
+       } else {
+               struct xfs_dir3_free_hdr *hdr3 = (struct xfs_dir3_free_hdr *)to;
+
+               hdr3->hdr.magic = cpu_to_be32(from->magic);
+               hdr3->firstdb = cpu_to_be32(from->firstdb);
+               hdr3->nvalid = cpu_to_be32(from->nvalid);
+               hdr3->nused = cpu_to_be32(from->nused);
+       }
+}
+
+static int
+xfs_dir3_free_get_buf(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *dp,
+       xfs_dir2_db_t           fbno,
+       struct xfs_buf          **bpp)
+{
+       struct xfs_mount        *mp = dp->i_mount;
+       struct xfs_buf          *bp;
+       int                     error;
+       struct xfs_dir3_icfree_hdr hdr;
+
+       error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, fbno),
+                                  -1, &bp, XFS_DATA_FORK);
+       if (error)
+               return error;
+
+       xfs_trans_buf_set_type(tp, bp, XFS_BLFT_DIR_FREE_BUF);
+       bp->b_ops = &xfs_dir3_free_buf_ops;
+
+       /*
+        * Initialize the new block to be empty, and remember
+        * its first slot as our empty slot.
+        */
+       hdr.magic = XFS_DIR2_FREE_MAGIC;
+       hdr.firstdb = 0;
+       hdr.nused = 0;
+       hdr.nvalid = 0;
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               struct xfs_dir3_free_hdr *hdr3 = bp->b_addr;
+
+               hdr.magic = XFS_DIR3_FREE_MAGIC;
+               hdr3->hdr.blkno = cpu_to_be64(bp->b_bn);
+               hdr3->hdr.owner = cpu_to_be64(dp->i_ino);
+               uuid_copy(&hdr3->hdr.uuid, &mp->m_sb.sb_uuid);
+       }
+       xfs_dir3_free_hdr_to_disk(bp->b_addr, &hdr);
+       *bpp = bp;
+       return 0;
 }
 
 /*
@@ -134,13 +291,16 @@ xfs_dir2_free_log_bests(
        int                     last)           /* last entry to log */
 {
        xfs_dir2_free_t         *free;          /* freespace structure */
+       __be16                  *bests;
 
        free = bp->b_addr;
-       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
+       bests = xfs_dir3_free_bests_p(tp->t_mountp, free);
+       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
+              free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
        xfs_trans_log_buf(tp, bp,
-               (uint)((char *)&free->bests[first] - (char *)free),
-               (uint)((char *)&free->bests[last] - (char *)free +
-                      sizeof(free->bests[0]) - 1));
+               (uint)((char *)&bests[first] - (char *)free),
+               (uint)((char *)&bests[last] - (char *)free +
+                      sizeof(bests[0]) - 1));
 }
 
 /*
@@ -154,9 +314,9 @@ xfs_dir2_free_log_header(
        xfs_dir2_free_t         *free;          /* freespace structure */
 
        free = bp->b_addr;
-       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
-       xfs_trans_log_buf(tp, bp, (uint)((char *)&free->hdr - (char *)free),
-               (uint)(sizeof(xfs_dir2_free_hdr_t) - 1));
+       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
+              free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
+       xfs_trans_log_buf(tp, bp, 0, xfs_dir3_free_hdr_size(tp->t_mountp) - 1);
 }
 
 /*
@@ -183,6 +343,7 @@ xfs_dir2_leaf_to_node(
        xfs_dir2_data_off_t     off;            /* freespace entry value */
        __be16                  *to;            /* pointer to freespace entry */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir3_icfree_hdr freehdr;
 
        trace_xfs_dir2_leaf_to_node(args);
 
@@ -199,44 +360,53 @@ xfs_dir2_leaf_to_node(
        /*
         * Get the buffer for the new freespace block.
         */
-       error = xfs_da_get_buf(tp, dp, xfs_dir2_db_to_da(mp, fdb), -1, &fbp,
-                               XFS_DATA_FORK);
+       error = xfs_dir3_free_get_buf(tp, dp, fdb, &fbp);
        if (error)
                return error;
-       fbp->b_ops = &xfs_dir2_free_buf_ops;
 
        free = fbp->b_addr;
+       xfs_dir3_free_hdr_from_disk(&freehdr, free);
        leaf = lbp->b_addr;
        ltp = xfs_dir2_leaf_tail_p(mp, leaf);
-       /*
-        * Initialize the freespace block header.
-        */
-       free->hdr.magic = cpu_to_be32(XFS_DIR2_FREE_MAGIC);
-       free->hdr.firstdb = 0;
-       ASSERT(be32_to_cpu(ltp->bestcount) <= (uint)dp->i_d.di_size / mp->m_dirblksize);
-       free->hdr.nvalid = ltp->bestcount;
+       ASSERT(be32_to_cpu(ltp->bestcount) <=
+                               (uint)dp->i_d.di_size / mp->m_dirblksize);
+
        /*
         * Copy freespace entries from the leaf block to the new block.
         * Count active entries.
         */
-       for (i = n = 0, from = xfs_dir2_leaf_bests_p(ltp), to = free->bests;
-            i < be32_to_cpu(ltp->bestcount); i++, from++, to++) {
+       from = xfs_dir2_leaf_bests_p(ltp);
+       to = xfs_dir3_free_bests_p(mp, free);
+       for (i = n = 0; i < be32_to_cpu(ltp->bestcount); i++, from++, to++) {
                if ((off = be16_to_cpu(*from)) != NULLDATAOFF)
                        n++;
                *to = cpu_to_be16(off);
        }
-       free->hdr.nused = cpu_to_be32(n);
-
-       lbp->b_ops = &xfs_dir2_leafn_buf_ops;
-       leaf->hdr.info.magic = cpu_to_be16(XFS_DIR2_LEAFN_MAGIC);
 
        /*
-        * Log everything.
+        * Now initialize the freespace block header.
         */
-       xfs_dir2_leaf_log_header(tp, lbp);
+       freehdr.nused = n;
+       freehdr.nvalid = be32_to_cpu(ltp->bestcount);
+
+       xfs_dir3_free_hdr_to_disk(fbp->b_addr, &freehdr);
+       xfs_dir2_free_log_bests(tp, fbp, 0, freehdr.nvalid - 1);
        xfs_dir2_free_log_header(tp, fbp);
-       xfs_dir2_free_log_bests(tp, fbp, 0, be32_to_cpu(free->hdr.nvalid) - 1);
-       xfs_dir2_leafn_check(dp, lbp);
+
+       /*
+        * Converting the leaf to a leafnode is just a matter of changing the
+        * magic number and the ops. Do the change directly to the buffer as
+        * it's less work (and less code) than decoding the header to host
+        * format and back again.
+        */
+       if (leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAF1_MAGIC))
+               leaf->hdr.info.magic = cpu_to_be16(XFS_DIR2_LEAFN_MAGIC);
+       else
+               leaf->hdr.info.magic = cpu_to_be16(XFS_DIR3_LEAFN_MAGIC);
+       lbp->b_ops = &xfs_dir3_leafn_buf_ops;
+       xfs_trans_buf_set_type(tp, lbp, XFS_BLFT_DIR_LEAFN_BUF);
+       xfs_dir3_leaf_log_header(tp, lbp);
+       xfs_dir3_leaf_check(mp, lbp);
        return 0;
 }
 
@@ -260,6 +430,8 @@ xfs_dir2_leafn_add(
        int                     lowstale;       /* previous stale entry */
        xfs_mount_t             *mp;            /* filesystem mount point */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir3_icleaf_hdr leafhdr;
+       struct xfs_dir2_leaf_entry *ents;
 
        trace_xfs_dir2_leafn_add(args, index);
 
@@ -267,6 +439,8 @@ xfs_dir2_leafn_add(
        mp = dp->i_mount;
        tp = args->trans;
        leaf = bp->b_addr;
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
 
        /*
         * Quick check just to make sure we are not going to index
@@ -282,15 +456,15 @@ xfs_dir2_leafn_add(
         * a compact.
         */
 
-       if (be16_to_cpu(leaf->hdr.count) == xfs_dir2_max_leaf_ents(mp)) {
-               if (!leaf->hdr.stale)
+       if (leafhdr.count == xfs_dir3_max_leaf_ents(mp, leaf)) {
+               if (!leafhdr.stale)
                        return XFS_ERROR(ENOSPC);
-               compact = be16_to_cpu(leaf->hdr.stale) > 1;
+               compact = leafhdr.stale > 1;
        } else
                compact = 0;
-       ASSERT(index == 0 || be32_to_cpu(leaf->ents[index - 1].hashval) <= args->hashval);
-       ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
-              be32_to_cpu(leaf->ents[index].hashval) >= args->hashval);
+       ASSERT(index == 0 || be32_to_cpu(ents[index - 1].hashval) <= args->hashval);
+       ASSERT(index == leafhdr.count ||
+              be32_to_cpu(ents[index].hashval) >= args->hashval);
 
        if (args->op_flags & XFS_DA_OP_JUSTCHECK)
                return 0;
@@ -299,61 +473,51 @@ xfs_dir2_leafn_add(
         * Compact out all but one stale leaf entry.  Leaves behind
         * the entry closest to index.
         */
-       if (compact) {
-               xfs_dir2_leaf_compact_x1(bp, &index, &lowstale, &highstale,
-                       &lfloglow, &lfloghigh);
-       }
-       /*
-        * Set impossible logging indices for this case.
-        */
-       else if (leaf->hdr.stale) {
-               lfloglow = be16_to_cpu(leaf->hdr.count);
+       if (compact)
+               xfs_dir3_leaf_compact_x1(&leafhdr, ents, &index, &lowstale,
+                                        &highstale, &lfloglow, &lfloghigh);
+       else if (leafhdr.stale) {
+               /*
+                * Set impossible logging indices for this case.
+                */
+               lfloglow = leafhdr.count;
                lfloghigh = -1;
        }
 
        /*
         * Insert the new entry, log everything.
         */
-       lep = xfs_dir2_leaf_find_entry(leaf, index, compact, lowstale,
+       lep = xfs_dir3_leaf_find_entry(&leafhdr, ents, index, compact, lowstale,
                                       highstale, &lfloglow, &lfloghigh);
 
        lep->hashval = cpu_to_be32(args->hashval);
        lep->address = cpu_to_be32(xfs_dir2_db_off_to_dataptr(mp,
                                args->blkno, args->index));
-       xfs_dir2_leaf_log_header(tp, bp);
-       xfs_dir2_leaf_log_ents(tp, bp, lfloglow, lfloghigh);
-       xfs_dir2_leafn_check(dp, bp);
+
+       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, bp);
+       xfs_dir3_leaf_log_ents(tp, bp, lfloglow, lfloghigh);
+       xfs_dir3_leaf_check(mp, bp);
        return 0;
 }
 
 #ifdef DEBUG
-/*
- * Check internal consistency of a leafn block.
- */
-void
-xfs_dir2_leafn_check(
-       struct xfs_inode *dp,
-       struct xfs_buf  *bp)
+static void
+xfs_dir2_free_hdr_check(
+       struct xfs_mount *mp,
+       struct xfs_buf  *bp,
+       xfs_dir2_db_t   db)
 {
-       int             i;                      /* leaf index */
-       xfs_dir2_leaf_t *leaf;                  /* leaf structure */
-       xfs_mount_t     *mp;                    /* filesystem mount point */
-       int             stale;                  /* count of stale leaves */
+       struct xfs_dir3_icfree_hdr hdr;
 
-       leaf = bp->b_addr;
-       mp = dp->i_mount;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       ASSERT(be16_to_cpu(leaf->hdr.count) <= xfs_dir2_max_leaf_ents(mp));
-       for (i = stale = 0; i < be16_to_cpu(leaf->hdr.count); i++) {
-               if (i + 1 < be16_to_cpu(leaf->hdr.count)) {
-                       ASSERT(be32_to_cpu(leaf->ents[i].hashval) <=
-                              be32_to_cpu(leaf->ents[i + 1].hashval));
-               }
-               if (leaf->ents[i].address == cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
-                       stale++;
-       }
-       ASSERT(be16_to_cpu(leaf->hdr.stale) == stale);
+       xfs_dir3_free_hdr_from_disk(&hdr, bp->b_addr);
+
+       ASSERT((hdr.firstdb % xfs_dir3_free_max_bests(mp)) == 0);
+       ASSERT(hdr.firstdb <= db);
+       ASSERT(db < hdr.firstdb + hdr.nvalid);
 }
+#else
+#define xfs_dir2_free_hdr_check(mp, dp, db)
 #endif /* DEBUG */
 
 /*
@@ -365,15 +529,22 @@ xfs_dir2_leafn_lasthash(
        struct xfs_buf  *bp,                    /* leaf buffer */
        int             *count)                 /* count of entries in leaf */
 {
-       xfs_dir2_leaf_t *leaf;                  /* leaf structure */
+       struct xfs_dir2_leaf    *leaf = bp->b_addr;
+       struct xfs_dir2_leaf_entry *ents;
+       struct xfs_dir3_icleaf_hdr leafhdr;
+
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+
+       ASSERT(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC ||
+              leafhdr.magic == XFS_DIR3_LEAFN_MAGIC);
 
-       leaf = bp->b_addr;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
        if (count)
-               *count = be16_to_cpu(leaf->hdr.count);
-       if (!leaf->hdr.count)
+               *count = leafhdr.count;
+       if (!leafhdr.count)
                return 0;
-       return be32_to_cpu(leaf->ents[be16_to_cpu(leaf->hdr.count) - 1].hashval);
+
+       ents = xfs_dir3_leaf_ents_p(leaf);
+       return be32_to_cpu(ents[leafhdr.count - 1].hashval);
 }
 
 /*
@@ -402,16 +573,19 @@ xfs_dir2_leafn_lookup_for_addname(
        xfs_dir2_db_t           newdb;          /* new data block number */
        xfs_dir2_db_t           newfdb;         /* new free block number */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir2_leaf_entry *ents;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-#ifdef __KERNEL__
-       ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
-#endif
-       xfs_dir2_leafn_check(dp, bp);
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
+
+       xfs_dir3_leaf_check(mp, bp);
+       ASSERT(leafhdr.count > 0);
+
        /*
         * Look up the hash value in the leaf entries.
         */
@@ -424,15 +598,16 @@ xfs_dir2_leafn_lookup_for_addname(
                curbp = state->extrablk.bp;
                curfdb = state->extrablk.blkno;
                free = curbp->b_addr;
-               ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
+               ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC) ||
+                      free->hdr.magic == cpu_to_be32(XFS_DIR3_FREE_MAGIC));
        }
        length = xfs_dir2_data_entsize(args->namelen);
        /*
         * Loop over leaf entries with the right hash value.
         */
-       for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) &&
-                               be32_to_cpu(lep->hashval) == args->hashval;
-                               lep++, index++) {
+       for (lep = &ents[index];
+            index < leafhdr.count && be32_to_cpu(lep->hashval) == args->hashval;
+            lep++, index++) {
                /*
                 * Skip stale leaf entries.
                 */
@@ -451,6 +626,8 @@ xfs_dir2_leafn_lookup_for_addname(
                 * in hand, take a look at it.
                 */
                if (newdb != curdb) {
+                       __be16 *bests;
+
                        curdb = newdb;
                        /*
                         * Convert the data block to the free block
@@ -473,13 +650,8 @@ xfs_dir2_leafn_lookup_for_addname(
                                if (error)
                                        return error;
                                free = curbp->b_addr;
-                               ASSERT(be32_to_cpu(free->hdr.magic) ==
-                                       XFS_DIR2_FREE_MAGIC);
-                               ASSERT((be32_to_cpu(free->hdr.firstdb) %
-                                       xfs_dir2_free_max_bests(mp)) == 0);
-                               ASSERT(be32_to_cpu(free->hdr.firstdb) <= curdb);
-                               ASSERT(curdb < be32_to_cpu(free->hdr.firstdb) +
-                                       be32_to_cpu(free->hdr.nvalid));
+
+                               xfs_dir2_free_hdr_check(mp, curbp, curdb);
                        }
                        /*
                         * Get the index for our entry.
@@ -488,8 +660,8 @@ xfs_dir2_leafn_lookup_for_addname(
                        /*
                         * If it has room, return it.
                         */
-                       if (unlikely(free->bests[fi] ==
-                           cpu_to_be16(NULLDATAOFF))) {
+                       bests = xfs_dir3_free_bests_p(mp, free);
+                       if (unlikely(bests[fi] == cpu_to_be16(NULLDATAOFF))) {
                                XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
                                                        XFS_ERRLEVEL_LOW, mp);
                                if (curfdb != newfdb)
@@ -497,7 +669,7 @@ xfs_dir2_leafn_lookup_for_addname(
                                return XFS_ERROR(EFSCORRUPTED);
                        }
                        curfdb = newfdb;
-                       if (be16_to_cpu(free->bests[fi]) >= length)
+                       if (be16_to_cpu(bests[fi]) >= length)
                                goto out;
                }
        }
@@ -511,6 +683,12 @@ out:
                state->extrablk.bp = curbp;
                state->extrablk.index = fi;
                state->extrablk.blkno = curfdb;
+
+               /*
+                * Important: this magic number is not in the buffer - it's for
+                * buffer type information and therefore only the free/data type
+                * matters here, not whether CRCs are enabled or not.
+                */
                state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
        } else {
                state->extravalid = 0;
@@ -545,16 +723,19 @@ xfs_dir2_leafn_lookup_for_entry(
        xfs_dir2_db_t           newdb;          /* new data block number */
        xfs_trans_t             *tp;            /* transaction pointer */
        enum xfs_dacmp          cmp;            /* comparison result */
+       struct xfs_dir2_leaf_entry *ents;
+       struct xfs_dir3_icleaf_hdr leafhdr;
 
        dp = args->dp;
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-#ifdef __KERNEL__
-       ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
-#endif
-       xfs_dir2_leafn_check(dp, bp);
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
+
+       xfs_dir3_leaf_check(mp, bp);
+       ASSERT(leafhdr.count > 0);
+
        /*
         * Look up the hash value in the leaf entries.
         */
@@ -569,9 +750,9 @@ xfs_dir2_leafn_lookup_for_entry(
        /*
         * Loop over leaf entries with the right hash value.
         */
-       for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) &&
-                               be32_to_cpu(lep->hashval) == args->hashval;
-                               lep++, index++) {
+       for (lep = &ents[index];
+            index < leafhdr.count && be32_to_cpu(lep->hashval) == args->hashval;
+            lep++, index++) {
                /*
                 * Skip stale leaf entries.
                 */
@@ -604,13 +785,13 @@ xfs_dir2_leafn_lookup_for_entry(
                                ASSERT(state->extravalid);
                                curbp = state->extrablk.bp;
                        } else {
-                               error = xfs_dir2_data_read(tp, dp,
+                               error = xfs_dir3_data_read(tp, dp,
                                                xfs_dir2_db_to_da(mp, newdb),
                                                -1, &curbp);
                                if (error)
                                        return error;
                        }
-                       xfs_dir2_data_check(dp, curbp);
+                       xfs_dir3_data_check(dp, curbp);
                        curdb = newdb;
                }
                /*
@@ -638,13 +819,13 @@ xfs_dir2_leafn_lookup_for_entry(
                        state->extrablk.index = (int)((char *)dep -
                                                        (char *)curbp->b_addr);
                        state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
-                       curbp->b_ops = &xfs_dir2_data_buf_ops;
+                       curbp->b_ops = &xfs_dir3_data_buf_ops;
+                       xfs_trans_buf_set_type(tp, curbp, XFS_BLFT_DIR_DATA_BUF);
                        if (cmp == XFS_CMP_EXACT)
                                return XFS_ERROR(EEXIST);
                }
        }
-       ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
-                                       (args->op_flags & XFS_DA_OP_OKNOENT));
+       ASSERT(index == leafhdr.count || (args->op_flags & XFS_DA_OP_OKNOENT));
        if (curbp) {
                if (args->cmpresult == XFS_CMP_DIFFERENT) {
                        /* Giving back last used data block. */
@@ -653,7 +834,8 @@ xfs_dir2_leafn_lookup_for_entry(
                        state->extrablk.index = -1;
                        state->extrablk.blkno = curdb;
                        state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
-                       curbp->b_ops = &xfs_dir2_data_buf_ops;
+                       curbp->b_ops = &xfs_dir3_data_buf_ops;
+                       xfs_trans_buf_set_type(tp, curbp, XFS_BLFT_DIR_DATA_BUF);
                } else {
                        /* If the curbp is not the CI match block, drop it */
                        if (state->extrablk.bp != curbp)
@@ -689,52 +871,50 @@ xfs_dir2_leafn_lookup_int(
  * Log entries and headers.  Stale entries are preserved.
  */
 static void
-xfs_dir2_leafn_moveents(
-       xfs_da_args_t   *args,                  /* operation arguments */
-       struct xfs_buf  *bp_s,                  /* source leaf buffer */
-       int             start_s,                /* source leaf index */
-       struct xfs_buf  *bp_d,                  /* destination leaf buffer */
-       int             start_d,                /* destination leaf index */
-       int             count)                  /* count of leaves to copy */
+xfs_dir3_leafn_moveents(
+       xfs_da_args_t                   *args,  /* operation arguments */
+       struct xfs_buf                  *bp_s,  /* source */
+       struct xfs_dir3_icleaf_hdr      *shdr,
+       struct xfs_dir2_leaf_entry      *sents,
+       int                             start_s,/* source leaf index */
+       struct xfs_buf                  *bp_d,  /* destination */
+       struct xfs_dir3_icleaf_hdr      *dhdr,
+       struct xfs_dir2_leaf_entry      *dents,
+       int                             start_d,/* destination leaf index */
+       int                             count)  /* count of leaves to copy */
 {
-       xfs_dir2_leaf_t *leaf_d;                /* destination leaf structure */
-       xfs_dir2_leaf_t *leaf_s;                /* source leaf structure */
-       int             stale;                  /* count stale leaves copied */
-       xfs_trans_t     *tp;                    /* transaction pointer */
+       struct xfs_trans                *tp = args->trans;
+       int                             stale;  /* count stale leaves copied */
 
        trace_xfs_dir2_leafn_moveents(args, start_s, start_d, count);
 
        /*
         * Silently return if nothing to do.
         */
-       if (count == 0) {
+       if (count == 0)
                return;
-       }
-       tp = args->trans;
-       leaf_s = bp_s->b_addr;
-       leaf_d = bp_d->b_addr;
+
        /*
         * If the destination index is not the end of the current
         * destination leaf entries, open up a hole in the destination
         * to hold the new entries.
         */
-       if (start_d < be16_to_cpu(leaf_d->hdr.count)) {
-               memmove(&leaf_d->ents[start_d + count], &leaf_d->ents[start_d],
-                       (be16_to_cpu(leaf_d->hdr.count) - start_d) *
-                       sizeof(xfs_dir2_leaf_entry_t));
-               xfs_dir2_leaf_log_ents(tp, bp_d, start_d + count,
-                       count + be16_to_cpu(leaf_d->hdr.count) - 1);
+       if (start_d < dhdr->count) {
+               memmove(&dents[start_d + count], &dents[start_d],
+                       (dhdr->count - start_d) * sizeof(xfs_dir2_leaf_entry_t));
+               xfs_dir3_leaf_log_ents(tp, bp_d, start_d + count,
+                                      count + dhdr->count - 1);
        }
        /*
         * If the source has stale leaves, count the ones in the copy range
         * so we can update the header correctly.
         */
-       if (leaf_s->hdr.stale) {
+       if (shdr->stale) {
                int     i;                      /* temp leaf index */
 
                for (i = start_s, stale = 0; i < start_s + count; i++) {
-                       if (leaf_s->ents[i].address ==
-                           cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
+                       if (sents[i].address ==
+                                       cpu_to_be32(XFS_DIR2_NULL_DATAPTR))
                                stale++;
                }
        } else
@@ -742,29 +922,27 @@ xfs_dir2_leafn_moveents(
        /*
         * Copy the leaf entries from source to destination.
         */
-       memcpy(&leaf_d->ents[start_d], &leaf_s->ents[start_s],
+       memcpy(&dents[start_d], &sents[start_s],
                count * sizeof(xfs_dir2_leaf_entry_t));
-       xfs_dir2_leaf_log_ents(tp, bp_d, start_d, start_d + count - 1);
+       xfs_dir3_leaf_log_ents(tp, bp_d, start_d, start_d + count - 1);
+
        /*
         * If there are source entries after the ones we copied,
         * delete the ones we copied by sliding the next ones down.
         */
-       if (start_s + count < be16_to_cpu(leaf_s->hdr.count)) {
-               memmove(&leaf_s->ents[start_s], &leaf_s->ents[start_s + count],
+       if (start_s + count < shdr->count) {
+               memmove(&sents[start_s], &sents[start_s + count],
                        count * sizeof(xfs_dir2_leaf_entry_t));
-               xfs_dir2_leaf_log_ents(tp, bp_s, start_s, start_s + count - 1);
+               xfs_dir3_leaf_log_ents(tp, bp_s, start_s, start_s + count - 1);
        }
+
        /*
         * Update the headers and log them.
         */
-       be16_add_cpu(&leaf_s->hdr.count, -(count));
-       be16_add_cpu(&leaf_s->hdr.stale, -(stale));
-       be16_add_cpu(&leaf_d->hdr.count, count);
-       be16_add_cpu(&leaf_d->hdr.stale, stale);
-       xfs_dir2_leaf_log_header(tp, bp_s);
-       xfs_dir2_leaf_log_header(tp, bp_d);
-       xfs_dir2_leafn_check(args->dp, bp_s);
-       xfs_dir2_leafn_check(args->dp, bp_d);
+       shdr->count -= count;
+       shdr->stale -= stale;
+       dhdr->count += count;
+       dhdr->stale += stale;
 }
 
 /*
@@ -773,21 +951,25 @@ xfs_dir2_leafn_moveents(
  */
 int                                            /* sort order */
 xfs_dir2_leafn_order(
-       struct xfs_buf  *leaf1_bp,              /* leaf1 buffer */
-       struct xfs_buf  *leaf2_bp)              /* leaf2 buffer */
+       struct xfs_buf          *leaf1_bp,              /* leaf1 buffer */
+       struct xfs_buf          *leaf2_bp)              /* leaf2 buffer */
 {
-       xfs_dir2_leaf_t *leaf1;                 /* leaf1 structure */
-       xfs_dir2_leaf_t *leaf2;                 /* leaf2 structure */
-
-       leaf1 = leaf1_bp->b_addr;
-       leaf2 = leaf2_bp->b_addr;
-       ASSERT(leaf1->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       ASSERT(leaf2->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       if (be16_to_cpu(leaf1->hdr.count) > 0 &&
-           be16_to_cpu(leaf2->hdr.count) > 0 &&
-           (be32_to_cpu(leaf2->ents[0].hashval) < be32_to_cpu(leaf1->ents[0].hashval) ||
-            be32_to_cpu(leaf2->ents[be16_to_cpu(leaf2->hdr.count) - 1].hashval) <
-            be32_to_cpu(leaf1->ents[be16_to_cpu(leaf1->hdr.count) - 1].hashval)))
+       struct xfs_dir2_leaf    *leaf1 = leaf1_bp->b_addr;
+       struct xfs_dir2_leaf    *leaf2 = leaf2_bp->b_addr;
+       struct xfs_dir2_leaf_entry *ents1;
+       struct xfs_dir2_leaf_entry *ents2;
+       struct xfs_dir3_icleaf_hdr hdr1;
+       struct xfs_dir3_icleaf_hdr hdr2;
+
+       xfs_dir3_leaf_hdr_from_disk(&hdr1, leaf1);
+       xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf2);
+       ents1 = xfs_dir3_leaf_ents_p(leaf1);
+       ents2 = xfs_dir3_leaf_ents_p(leaf2);
+
+       if (hdr1.count > 0 && hdr2.count > 0 &&
+           (be32_to_cpu(ents2[0].hashval) < be32_to_cpu(ents1[0].hashval) ||
+            be32_to_cpu(ents2[hdr2.count - 1].hashval) <
+                               be32_to_cpu(ents1[hdr1.count - 1].hashval)))
                return 1;
        return 0;
 }
@@ -811,11 +993,15 @@ xfs_dir2_leafn_rebalance(
        xfs_dir2_leaf_t         *leaf1;         /* first leaf structure */
        xfs_dir2_leaf_t         *leaf2;         /* second leaf structure */
        int                     mid;            /* midpoint leaf index */
-#ifdef DEBUG
+#if defined(DEBUG) || defined(XFS_WARN)
        int                     oldstale;       /* old count of stale leaves */
 #endif
        int                     oldsum;         /* old total leaf count */
        int                     swap;           /* swapped leaf blocks */
+       struct xfs_dir2_leaf_entry *ents1;
+       struct xfs_dir2_leaf_entry *ents2;
+       struct xfs_dir3_icleaf_hdr hdr1;
+       struct xfs_dir3_icleaf_hdr hdr2;
 
        args = state->args;
        /*
@@ -830,11 +1016,17 @@ xfs_dir2_leafn_rebalance(
        }
        leaf1 = blk1->bp->b_addr;
        leaf2 = blk2->bp->b_addr;
-       oldsum = be16_to_cpu(leaf1->hdr.count) + be16_to_cpu(leaf2->hdr.count);
-#ifdef DEBUG
-       oldstale = be16_to_cpu(leaf1->hdr.stale) + be16_to_cpu(leaf2->hdr.stale);
+       xfs_dir3_leaf_hdr_from_disk(&hdr1, leaf1);
+       xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf2);
+       ents1 = xfs_dir3_leaf_ents_p(leaf1);
+       ents2 = xfs_dir3_leaf_ents_p(leaf2);
+
+       oldsum = hdr1.count + hdr2.count;
+#if defined(DEBUG) || defined(XFS_WARN)
+       oldstale = hdr1.stale + hdr2.stale;
 #endif
        mid = oldsum >> 1;
+
        /*
         * If the old leaf count was odd then the new one will be even,
         * so we need to divide the new count evenly.
@@ -842,10 +1034,10 @@ xfs_dir2_leafn_rebalance(
        if (oldsum & 1) {
                xfs_dahash_t    midhash;        /* middle entry hash value */
 
-               if (mid >= be16_to_cpu(leaf1->hdr.count))
-                       midhash = be32_to_cpu(leaf2->ents[mid - be16_to_cpu(leaf1->hdr.count)].hashval);
+               if (mid >= hdr1.count)
+                       midhash = be32_to_cpu(ents2[mid - hdr1.count].hashval);
                else
-                       midhash = be32_to_cpu(leaf1->ents[mid].hashval);
+                       midhash = be32_to_cpu(ents1[mid].hashval);
                isleft = args->hashval <= midhash;
        }
        /*
@@ -859,30 +1051,42 @@ xfs_dir2_leafn_rebalance(
         * Calculate moved entry count.  Positive means left-to-right,
         * negative means right-to-left.  Then move the entries.
         */
-       count = be16_to_cpu(leaf1->hdr.count) - mid + (isleft == 0);
+       count = hdr1.count - mid + (isleft == 0);
        if (count > 0)
-               xfs_dir2_leafn_moveents(args, blk1->bp,
-                       be16_to_cpu(leaf1->hdr.count) - count, blk2->bp, 0, count);
+               xfs_dir3_leafn_moveents(args, blk1->bp, &hdr1, ents1,
+                                       hdr1.count - count, blk2->bp,
+                                       &hdr2, ents2, 0, count);
        else if (count < 0)
-               xfs_dir2_leafn_moveents(args, blk2->bp, 0, blk1->bp,
-                       be16_to_cpu(leaf1->hdr.count), count);
-       ASSERT(be16_to_cpu(leaf1->hdr.count) + be16_to_cpu(leaf2->hdr.count) == oldsum);
-       ASSERT(be16_to_cpu(leaf1->hdr.stale) + be16_to_cpu(leaf2->hdr.stale) == oldstale);
+               xfs_dir3_leafn_moveents(args, blk2->bp, &hdr2, ents2, 0,
+                                       blk1->bp, &hdr1, ents1,
+                                       hdr1.count, count);
+
+       ASSERT(hdr1.count + hdr2.count == oldsum);
+       ASSERT(hdr1.stale + hdr2.stale == oldstale);
+
+       /* log the changes made when moving the entries */
+       xfs_dir3_leaf_hdr_to_disk(leaf1, &hdr1);
+       xfs_dir3_leaf_hdr_to_disk(leaf2, &hdr2);
+       xfs_dir3_leaf_log_header(args->trans, blk1->bp);
+       xfs_dir3_leaf_log_header(args->trans, blk2->bp);
+
+       xfs_dir3_leaf_check(args->dp->i_mount, blk1->bp);
+       xfs_dir3_leaf_check(args->dp->i_mount, blk2->bp);
+
        /*
         * Mark whether we're inserting into the old or new leaf.
         */
-       if (be16_to_cpu(leaf1->hdr.count) < be16_to_cpu(leaf2->hdr.count))
+       if (hdr1.count < hdr2.count)
                state->inleaf = swap;
-       else if (be16_to_cpu(leaf1->hdr.count) > be16_to_cpu(leaf2->hdr.count))
+       else if (hdr1.count > hdr2.count)
                state->inleaf = !swap;
        else
-               state->inleaf =
-                       swap ^ (blk1->index <= be16_to_cpu(leaf1->hdr.count));
+               state->inleaf = swap ^ (blk1->index <= hdr1.count);
        /*
         * Adjust the expected index for insertion.
         */
        if (!state->inleaf)
-               blk2->index = blk1->index - be16_to_cpu(leaf1->hdr.count);
+               blk2->index = blk1->index - hdr1.count;
 
        /*
         * Finally sanity check just to make sure we are not returning a
@@ -898,7 +1102,7 @@ xfs_dir2_leafn_rebalance(
 }
 
 static int
-xfs_dir2_data_block_free(
+xfs_dir3_data_block_free(
        xfs_da_args_t           *args,
        struct xfs_dir2_data_hdr *hdr,
        struct xfs_dir2_free    *free,
@@ -909,57 +1113,66 @@ xfs_dir2_data_block_free(
 {
        struct xfs_trans        *tp = args->trans;
        int                     logfree = 0;
+       __be16                  *bests;
+       struct xfs_dir3_icfree_hdr freehdr;
 
-       if (!hdr) {
-               /* One less used entry in the free table.  */
-               be32_add_cpu(&free->hdr.nused, -1);
-               xfs_dir2_free_log_header(tp, fbp);
+       xfs_dir3_free_hdr_from_disk(&freehdr, free);
 
+       bests = xfs_dir3_free_bests_p(tp->t_mountp, free);
+       if (hdr) {
                /*
-                * If this was the last entry in the table, we can trim the
-                * table size back.  There might be other entries at the end
-                * referring to non-existent data blocks, get those too.
+                * Data block is not empty, just set the free entry to the new
+                * value.
                 */
-               if (findex == be32_to_cpu(free->hdr.nvalid) - 1) {
-                       int     i;              /* free entry index */
+               bests[findex] = cpu_to_be16(longest);
+               xfs_dir2_free_log_bests(tp, fbp, findex, findex);
+               return 0;
+       }
 
-                       for (i = findex - 1; i >= 0; i--) {
-                               if (free->bests[i] != cpu_to_be16(NULLDATAOFF))
-                                       break;
-                       }
-                       free->hdr.nvalid = cpu_to_be32(i + 1);
-                       logfree = 0;
-               } else {
-                       /* Not the last entry, just punch it out.  */
-                       free->bests[findex] = cpu_to_be16(NULLDATAOFF);
-                       logfree = 1;
-               }
-               /*
-                * If there are no useful entries left in the block,
-                * get rid of the block if we can.
-                */
-               if (!free->hdr.nused) {
-                       int error;
+       /* One less used entry in the free table. */
+       freehdr.nused--;
 
-                       error = xfs_dir2_shrink_inode(args, fdb, fbp);
-                       if (error == 0) {
-                               fbp = NULL;
-                               logfree = 0;
-                       } else if (error != ENOSPC || args->total != 0)
-                               return error;
-                       /*
-                        * It's possible to get ENOSPC if there is no
-                        * space reservation.  In this case some one
-                        * else will eventually get rid of this block.
-                        */
+       /*
+        * If this was the last entry in the table, we can trim the table size
+        * back.  There might be other entries at the end referring to
+        * non-existent data blocks, get those too.
+        */
+       if (findex == freehdr.nvalid - 1) {
+               int     i;              /* free entry index */
+
+               for (i = findex - 1; i >= 0; i--) {
+                       if (bests[i] != cpu_to_be16(NULLDATAOFF))
+                               break;
                }
+               freehdr.nvalid = i + 1;
+               logfree = 0;
        } else {
+               /* Not the last entry, just punch it out.  */
+               bests[findex] = cpu_to_be16(NULLDATAOFF);
+               logfree = 1;
+       }
+
+       xfs_dir3_free_hdr_to_disk(free, &freehdr);
+       xfs_dir2_free_log_header(tp, fbp);
+
+       /*
+        * If there are no useful entries left in the block, get rid of the
+        * block if we can.
+        */
+       if (!freehdr.nused) {
+               int error;
+
+               error = xfs_dir2_shrink_inode(args, fdb, fbp);
+               if (error == 0) {
+                       fbp = NULL;
+                       logfree = 0;
+               } else if (error != ENOSPC || args->total != 0)
+                       return error;
                /*
-                * Data block is not empty, just set the free entry to the new
-                * value.
+                * It's possible to get ENOSPC if there is no
+                * space reservation.  In this case some one
+                * else will eventually get rid of this block.
                 */
-               free->bests[findex] = cpu_to_be16(longest);
-               logfree = 1;
        }
 
        /* Log the free entry that changed, unless we got rid of it.  */
@@ -994,6 +1207,9 @@ xfs_dir2_leafn_remove(
        int                     needlog;        /* need to log data header */
        int                     needscan;       /* need to rescan data frees */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir2_data_free *bf;          /* bestfree table */
+       struct xfs_dir3_icleaf_hdr leafhdr;
+       struct xfs_dir2_leaf_entry *ents;
 
        trace_xfs_dir2_leafn_remove(args, index);
 
@@ -1001,11 +1217,14 @@ xfs_dir2_leafn_remove(
        tp = args->trans;
        mp = dp->i_mount;
        leaf = bp->b_addr;
-       ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
+
        /*
         * Point to the entry we're removing.
         */
-       lep = &leaf->ents[index];
+       lep = &ents[index];
+
        /*
         * Extract the data block and offset from the entry.
         */
@@ -1013,14 +1232,18 @@ xfs_dir2_leafn_remove(
        ASSERT(dblk->blkno == db);
        off = xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address));
        ASSERT(dblk->index == off);
+
        /*
         * Kill the leaf entry by marking it stale.
         * Log the leaf block changes.
         */
-       be16_add_cpu(&leaf->hdr.stale, 1);
-       xfs_dir2_leaf_log_header(tp, bp);
+       leafhdr.stale++;
+       xfs_dir3_leaf_hdr_to_disk(leaf, &leafhdr);
+       xfs_dir3_leaf_log_header(tp, bp);
+
        lep->address = cpu_to_be32(XFS_DIR2_NULL_DATAPTR);
-       xfs_dir2_leaf_log_ents(tp, bp, index, index);
+       xfs_dir3_leaf_log_ents(tp, bp, index, index);
+
        /*
         * Make the data entry free.  Keep track of the longest freespace
         * in the data block in case it changes.
@@ -1028,7 +1251,8 @@ xfs_dir2_leafn_remove(
        dbp = dblk->bp;
        hdr = dbp->b_addr;
        dep = (xfs_dir2_data_entry_t *)((char *)hdr + off);
-       longest = be16_to_cpu(hdr->bestfree[0].length);
+       bf = xfs_dir3_data_bestfree_p(hdr);
+       longest = be16_to_cpu(bf[0].length);
        needlog = needscan = 0;
        xfs_dir2_data_make_free(tp, dbp, off,
                xfs_dir2_data_entsize(dep->namelen), &needlog, &needscan);
@@ -1040,12 +1264,12 @@ xfs_dir2_leafn_remove(
                xfs_dir2_data_freescan(mp, hdr, &needlog);
        if (needlog)
                xfs_dir2_data_log_header(tp, dbp);
-       xfs_dir2_data_check(dp, dbp);
+       xfs_dir3_data_check(dp, dbp);
        /*
         * If the longest data block freespace changes, need to update
         * the corresponding freeblock entry.
         */
-       if (longest < be16_to_cpu(hdr->bestfree[0].length)) {
+       if (longest < be16_to_cpu(bf[0].length)) {
                int             error;          /* error return value */
                struct xfs_buf  *fbp;           /* freeblock buffer */
                xfs_dir2_db_t   fdb;            /* freeblock block number */
@@ -1062,20 +1286,25 @@ xfs_dir2_leafn_remove(
                if (error)
                        return error;
                free = fbp->b_addr;
-               ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
-               ASSERT(be32_to_cpu(free->hdr.firstdb) ==
-                      xfs_dir2_free_max_bests(mp) *
-                      (fdb - XFS_DIR2_FREE_FIRSTDB(mp)));
+#ifdef DEBUG
+       {
+               struct xfs_dir3_icfree_hdr freehdr;
+               xfs_dir3_free_hdr_from_disk(&freehdr, free);
+               ASSERT(freehdr.firstdb == xfs_dir3_free_max_bests(mp) *
+                                         (fdb - XFS_DIR2_FREE_FIRSTDB(mp)));
+       }
+#endif
                /*
                 * Calculate which entry we need to fix.
                 */
                findex = xfs_dir2_db_to_fdindex(mp, db);
-               longest = be16_to_cpu(hdr->bestfree[0].length);
+               longest = be16_to_cpu(bf[0].length);
                /*
                 * If the data block is now empty we can get rid of it
                 * (usually).
                 */
-               if (longest == mp->m_dirblksize - (uint)sizeof(*hdr)) {
+               if (longest == mp->m_dirblksize -
+                              xfs_dir3_data_entry_offset(hdr)) {
                        /*
                         * Try to punch out the data block.
                         */
@@ -1096,21 +1325,19 @@ xfs_dir2_leafn_remove(
                 * If we got rid of the data block, we can eliminate that entry
                 * in the free block.
                 */
-               error = xfs_dir2_data_block_free(args, hdr, free,
+               error = xfs_dir3_data_block_free(args, hdr, free,
                                                 fdb, findex, fbp, longest);
                if (error)
                        return error;
        }
 
-       xfs_dir2_leafn_check(dp, bp);
+       xfs_dir3_leaf_check(mp, bp);
        /*
         * Return indication of whether this leaf block is empty enough
         * to justify trying to join it with a neighbor.
         */
-       *rval =
-               ((uint)sizeof(leaf->hdr) +
-                (uint)sizeof(leaf->ents[0]) *
-                (be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale))) <
+       *rval = (xfs_dir3_leaf_hdr_size(leaf) +
+                (uint)sizeof(ents[0]) * (leafhdr.count - leafhdr.stale)) <
                mp->m_dir_magicpct;
        return 0;
 }
@@ -1143,11 +1370,11 @@ xfs_dir2_leafn_split(
        /*
         * Initialize the new leaf block.
         */
-       error = xfs_dir2_leaf_init(args, xfs_dir2_da_to_db(mp, blkno),
-               &newblk->bp, XFS_DIR2_LEAFN_MAGIC);
-       if (error) {
+       error = xfs_dir3_leaf_get_buf(args, xfs_dir2_da_to_db(mp, blkno),
+                                     &newblk->bp, XFS_DIR2_LEAFN_MAGIC);
+       if (error)
                return error;
-       }
+
        newblk->blkno = blkno;
        newblk->magic = XFS_DIR2_LEAFN_MAGIC;
        /*
@@ -1155,7 +1382,7 @@ xfs_dir2_leafn_split(
         * block into the leaves.
         */
        xfs_dir2_leafn_rebalance(state, oldblk, newblk);
-       error = xfs_da_blk_link(state, oldblk, newblk);
+       error = xfs_da3_blk_link(state, oldblk, newblk);
        if (error) {
                return error;
        }
@@ -1171,8 +1398,8 @@ xfs_dir2_leafn_split(
         */
        oldblk->hashval = xfs_dir2_leafn_lasthash(oldblk->bp, NULL);
        newblk->hashval = xfs_dir2_leafn_lasthash(newblk->bp, NULL);
-       xfs_dir2_leafn_check(args->dp, oldblk->bp);
-       xfs_dir2_leafn_check(args->dp, newblk->bp);
+       xfs_dir3_leaf_check(mp, oldblk->bp);
+       xfs_dir3_leaf_check(mp, newblk->bp);
        return error;
 }
 
@@ -1198,9 +1425,10 @@ xfs_dir2_leafn_toosmall(
        int                     error;          /* error return value */
        int                     forward;        /* sibling block direction */
        int                     i;              /* sibling counter */
-       xfs_da_blkinfo_t        *info;          /* leaf block header */
        xfs_dir2_leaf_t         *leaf;          /* leaf structure */
        int                     rval;           /* result from path_shift */
+       struct xfs_dir3_icleaf_hdr leafhdr;
+       struct xfs_dir2_leaf_entry *ents;
 
        /*
         * Check for the degenerate case of the block being over 50% full.
@@ -1208,11 +1436,13 @@ xfs_dir2_leafn_toosmall(
         * to coalesce with a sibling.
         */
        blk = &state->path.blk[state->path.active - 1];
-       info = blk->bp->b_addr;
-       ASSERT(info->magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       leaf = (xfs_dir2_leaf_t *)info;
-       count = be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
-       bytes = (uint)sizeof(leaf->hdr) + count * (uint)sizeof(leaf->ents[0]);
+       leaf = blk->bp->b_addr;
+       xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf);
+       ents = xfs_dir3_leaf_ents_p(leaf);
+       xfs_dir3_leaf_check(state->args->dp->i_mount, blk->bp);
+
+       count = leafhdr.count - leafhdr.stale;
+       bytes = xfs_dir3_leaf_hdr_size(leaf) + count * sizeof(ents[0]);
        if (bytes > (state->blocksize >> 1)) {
                /*
                 * Blk over 50%, don't try to join.
@@ -1231,9 +1461,9 @@ xfs_dir2_leafn_toosmall(
                 * Make altpath point to the block we want to keep and
                 * path point to the block we want to drop (this one).
                 */
-               forward = (info->forw != 0);
+               forward = (leafhdr.forw != 0);
                memcpy(&state->altpath, &state->path, sizeof(state->path));
-               error = xfs_da_path_shift(state, &state->altpath, forward, 0,
+               error = xfs_da3_path_shift(state, &state->altpath, forward, 0,
                        &rval);
                if (error)
                        return error;
@@ -1247,15 +1477,17 @@ xfs_dir2_leafn_toosmall(
         * We prefer coalescing with the lower numbered sibling so as
         * to shrink a directory over time.
         */
-       forward = be32_to_cpu(info->forw) < be32_to_cpu(info->back);
+       forward = leafhdr.forw < leafhdr.back;
        for (i = 0, bp = NULL; i < 2; forward = !forward, i++) {
-               blkno = forward ? be32_to_cpu(info->forw) : be32_to_cpu(info->back);
+               struct xfs_dir3_icleaf_hdr hdr2;
+
+               blkno = forward ? leafhdr.forw : leafhdr.back;
                if (blkno == 0)
                        continue;
                /*
                 * Read the sibling leaf block.
                 */
-               error = xfs_dir2_leafn_read(state->args->trans, state->args->dp,
+               error = xfs_dir3_leafn_read(state->args->trans, state->args->dp,
                                            blkno, -1, &bp);
                if (error)
                        return error;
@@ -1263,13 +1495,15 @@ xfs_dir2_leafn_toosmall(
                /*
                 * Count bytes in the two blocks combined.
                 */
-               leaf = (xfs_dir2_leaf_t *)info;
-               count = be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
+               count = leafhdr.count - leafhdr.stale;
                bytes = state->blocksize - (state->blocksize >> 2);
+
                leaf = bp->b_addr;
-               ASSERT(leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-               count += be16_to_cpu(leaf->hdr.count) - be16_to_cpu(leaf->hdr.stale);
-               bytes -= count * (uint)sizeof(leaf->ents[0]);
+               xfs_dir3_leaf_hdr_from_disk(&hdr2, leaf);
+               ents = xfs_dir3_leaf_ents_p(leaf);
+               count += hdr2.count - hdr2.stale;
+               bytes -= count * sizeof(ents[0]);
+
                /*
                 * Fits with at least 25% to spare.
                 */
@@ -1291,10 +1525,10 @@ xfs_dir2_leafn_toosmall(
         */
        memcpy(&state->altpath, &state->path, sizeof(state->path));
        if (blkno < blk->blkno)
-               error = xfs_da_path_shift(state, &state->altpath, forward, 0,
+               error = xfs_da3_path_shift(state, &state->altpath, forward, 0,
                        &rval);
        else
-               error = xfs_da_path_shift(state, &state->path, forward, 0,
+               error = xfs_da3_path_shift(state, &state->path, forward, 0,
                        &rval);
        if (error) {
                return error;
@@ -1316,34 +1550,53 @@ xfs_dir2_leafn_unbalance(
        xfs_da_args_t           *args;          /* operation arguments */
        xfs_dir2_leaf_t         *drop_leaf;     /* dead leaf structure */
        xfs_dir2_leaf_t         *save_leaf;     /* surviving leaf structure */
+       struct xfs_dir3_icleaf_hdr savehdr;
+       struct xfs_dir3_icleaf_hdr drophdr;
+       struct xfs_dir2_leaf_entry *sents;
+       struct xfs_dir2_leaf_entry *dents;
 
        args = state->args;
        ASSERT(drop_blk->magic == XFS_DIR2_LEAFN_MAGIC);
        ASSERT(save_blk->magic == XFS_DIR2_LEAFN_MAGIC);
        drop_leaf = drop_blk->bp->b_addr;
        save_leaf = save_blk->bp->b_addr;
-       ASSERT(drop_leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
-       ASSERT(save_leaf->hdr.info.magic == cpu_to_be16(XFS_DIR2_LEAFN_MAGIC));
+
+       xfs_dir3_leaf_hdr_from_disk(&savehdr, save_leaf);
+       xfs_dir3_leaf_hdr_from_disk(&drophdr, drop_leaf);
+       sents = xfs_dir3_leaf_ents_p(save_leaf);
+       dents = xfs_dir3_leaf_ents_p(drop_leaf);
+
        /*
         * If there are any stale leaf entries, take this opportunity
         * to purge them.
         */
-       if (drop_leaf->hdr.stale)
-               xfs_dir2_leaf_compact(args, drop_blk->bp);
-       if (save_leaf->hdr.stale)
-               xfs_dir2_leaf_compact(args, save_blk->bp);
+       if (drophdr.stale)
+               xfs_dir3_leaf_compact(args, &drophdr, drop_blk->bp);
+       if (savehdr.stale)
+               xfs_dir3_leaf_compact(args, &savehdr, save_blk->bp);
+
        /*
         * Move the entries from drop to the appropriate end of save.
         */
-       drop_blk->hashval = be32_to_cpu(drop_leaf->ents[be16_to_cpu(drop_leaf->hdr.count) - 1].hashval);
+       drop_blk->hashval = be32_to_cpu(dents[drophdr.count - 1].hashval);
        if (xfs_dir2_leafn_order(save_blk->bp, drop_blk->bp))
-               xfs_dir2_leafn_moveents(args, drop_blk->bp, 0, save_blk->bp, 0,
-                       be16_to_cpu(drop_leaf->hdr.count));
+               xfs_dir3_leafn_moveents(args, drop_blk->bp, &drophdr, dents, 0,
+                                       save_blk->bp, &savehdr, sents, 0,
+                                       drophdr.count);
        else
-               xfs_dir2_leafn_moveents(args, drop_blk->bp, 0, save_blk->bp,
-                       be16_to_cpu(save_leaf->hdr.count), be16_to_cpu(drop_leaf->hdr.count));
-       save_blk->hashval = be32_to_cpu(save_leaf->ents[be16_to_cpu(save_leaf->hdr.count) - 1].hashval);
-       xfs_dir2_leafn_check(args->dp, save_blk->bp);
+               xfs_dir3_leafn_moveents(args, drop_blk->bp, &drophdr, dents, 0,
+                                       save_blk->bp, &savehdr, sents,
+                                       savehdr.count, drophdr.count);
+       save_blk->hashval = be32_to_cpu(sents[savehdr.count - 1].hashval);
+
+       /* log the changes made when moving the entries */
+       xfs_dir3_leaf_hdr_to_disk(save_leaf, &savehdr);
+       xfs_dir3_leaf_hdr_to_disk(drop_leaf, &drophdr);
+       xfs_dir3_leaf_log_header(args->trans, save_blk->bp);
+       xfs_dir3_leaf_log_header(args->trans, drop_blk->bp);
+
+       xfs_dir3_leaf_check(args->dp->i_mount, save_blk->bp);
+       xfs_dir3_leaf_check(args->dp->i_mount, drop_blk->bp);
 }
 
 /*
@@ -1372,7 +1625,7 @@ xfs_dir2_node_addname(
         * Look up the name.  We're not supposed to find it, but
         * this gives us the insertion point.
         */
-       error = xfs_da_node_lookup_int(state, &rval);
+       error = xfs_da3_node_lookup_int(state, &rval);
        if (error)
                rval = error;
        if (rval != ENOENT) {
@@ -1398,7 +1651,7 @@ xfs_dir2_node_addname(
                 * It worked, fix the hash values up the btree.
                 */
                if (!(args->op_flags & XFS_DA_OP_JUSTCHECK))
-                       xfs_da_fixhashpath(state, &state->path);
+                       xfs_da3_fixhashpath(state, &state->path);
        } else {
                /*
                 * It didn't work, we need to split the leaf block.
@@ -1410,7 +1663,7 @@ xfs_dir2_node_addname(
                /*
                 * Split the leaf block and insert the new entry.
                 */
-               rval = xfs_da_split(state);
+               rval = xfs_da3_split(state);
        }
 done:
        xfs_da_state_free(state);
@@ -1447,6 +1700,9 @@ xfs_dir2_node_addname_int(
        int                     needscan;       /* need to rescan data frees */
        __be16                  *tagp;          /* data entry tag pointer */
        xfs_trans_t             *tp;            /* transaction pointer */
+       __be16                  *bests;
+       struct xfs_dir3_icfree_hdr freehdr;
+       struct xfs_dir2_data_free *bf;
 
        dp = args->dp;
        mp = dp->i_mount;
@@ -1464,36 +1720,37 @@ xfs_dir2_node_addname_int(
                 */
                ifbno = fblk->blkno;
                free = fbp->b_addr;
-               ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                findex = fblk->index;
+               bests = xfs_dir3_free_bests_p(mp, free);
+               xfs_dir3_free_hdr_from_disk(&freehdr, free);
+
                /*
                 * This means the free entry showed that the data block had
                 * space for our entry, so we remembered it.
                 * Use that data block.
                 */
                if (findex >= 0) {
-                       ASSERT(findex < be32_to_cpu(free->hdr.nvalid));
-                       ASSERT(be16_to_cpu(free->bests[findex]) != NULLDATAOFF);
-                       ASSERT(be16_to_cpu(free->bests[findex]) >= length);
-                       dbno = be32_to_cpu(free->hdr.firstdb) + findex;
-               }
-               /*
-                * The data block looked at didn't have enough room.
-                * We'll start at the beginning of the freespace entries.
-                */
-               else {
+                       ASSERT(findex < freehdr.nvalid);
+                       ASSERT(be16_to_cpu(bests[findex]) != NULLDATAOFF);
+                       ASSERT(be16_to_cpu(bests[findex]) >= length);
+                       dbno = freehdr.firstdb + findex;
+               } else {
+                       /*
+                        * The data block looked at didn't have enough room.
+                        * We'll start at the beginning of the freespace entries.
+                        */
                        dbno = -1;
                        findex = 0;
                }
-       }
-       /*
-        * Didn't come in with a freespace block, so don't have a data block.
-        */
-       else {
+       } else {
+               /*
+                * Didn't come in with a freespace block, so no data block.
+                */
                ifbno = dbno = -1;
                fbp = NULL;
                findex = 0;
        }
+
        /*
         * If we don't have a data block yet, we're going to scan the
         * freespace blocks looking for one.  Figure out what the
@@ -1547,20 +1804,26 @@ xfs_dir2_node_addname_int(
                        if (!fbp)
                                continue;
                        free = fbp->b_addr;
-                       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
                        findex = 0;
                }
                /*
                 * Look at the current free entry.  Is it good enough?
+                *
+                * The bests initialisation should be where the bufer is read in
+                * the above branch. But gcc is too stupid to realise that bests
+                * and the freehdr are actually initialised if they are placed
+                * there, so we have to do it here to avoid warnings. Blech.
                 */
-               if (be16_to_cpu(free->bests[findex]) != NULLDATAOFF &&
-                   be16_to_cpu(free->bests[findex]) >= length)
-                       dbno = be32_to_cpu(free->hdr.firstdb) + findex;
+               bests = xfs_dir3_free_bests_p(mp, free);
+               xfs_dir3_free_hdr_from_disk(&freehdr, free);
+               if (be16_to_cpu(bests[findex]) != NULLDATAOFF &&
+                   be16_to_cpu(bests[findex]) >= length)
+                       dbno = freehdr.firstdb + findex;
                else {
                        /*
                         * Are we done with the freeblock?
                         */
-                       if (++findex == be32_to_cpu(free->hdr.nvalid)) {
+                       if (++findex == freehdr.nvalid) {
                                /*
                                 * Drop the block.
                                 */
@@ -1588,7 +1851,7 @@ xfs_dir2_node_addname_int(
                if (unlikely((error = xfs_dir2_grow_inode(args,
                                                         XFS_DIR2_DATA_SPACE,
                                                         &dbno)) ||
-                   (error = xfs_dir2_data_init(args, dbno, &dbp))))
+                   (error = xfs_dir3_data_init(args, dbno, &dbp))))
                        return error;
 
                /*
@@ -1614,11 +1877,11 @@ xfs_dir2_node_addname_int(
                 * If there wasn't a freespace block, the read will
                 * return a NULL fbp.  Allocate and initialize a new one.
                 */
-               if( fbp == NULL ) {
-                       if ((error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE,
-                                                       &fbno))) {
+               if (!fbp) {
+                       error = xfs_dir2_grow_inode(args, XFS_DIR2_FREE_SPACE,
+                                                   &fbno);
+                       if (error)
                                return error;
-                       }
 
                        if (unlikely(xfs_dir2_db_to_fdb(mp, dbno) != fbno)) {
                                xfs_alert(mp,
@@ -1646,27 +1909,24 @@ xfs_dir2_node_addname_int(
                        /*
                         * Get a buffer for the new block.
                         */
-                       error = xfs_da_get_buf(tp, dp,
-                                              xfs_dir2_db_to_da(mp, fbno),
-                                              -1, &fbp, XFS_DATA_FORK);
+                       error = xfs_dir3_free_get_buf(tp, dp, fbno, &fbp);
                        if (error)
                                return error;
-                       fbp->b_ops = &xfs_dir2_free_buf_ops;
+                       free = fbp->b_addr;
+                       bests = xfs_dir3_free_bests_p(mp, free);
+                       xfs_dir3_free_hdr_from_disk(&freehdr, free);
 
                        /*
-                        * Initialize the new block to be empty, and remember
-                        * its first slot as our empty slot.
+                        * Remember the first slot as our empty slot.
                         */
-                       free = fbp->b_addr;
-                       free->hdr.magic = cpu_to_be32(XFS_DIR2_FREE_MAGIC);
-                       free->hdr.firstdb = cpu_to_be32(
-                               (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) *
-                               xfs_dir2_free_max_bests(mp));
+                       freehdr.firstdb = (fbno - XFS_DIR2_FREE_FIRSTDB(mp)) *
+                                       xfs_dir3_free_max_bests(mp);
                        free->hdr.nvalid = 0;
                        free->hdr.nused = 0;
                } else {
                        free = fbp->b_addr;
-                       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
+                       bests = xfs_dir3_free_bests_p(mp, free);
+                       xfs_dir3_free_hdr_from_disk(&freehdr, free);
                }
 
                /*
@@ -1677,20 +1937,21 @@ xfs_dir2_node_addname_int(
                 * If it's after the end of the current entries in the
                 * freespace block, extend that table.
                 */
-               if (findex >= be32_to_cpu(free->hdr.nvalid)) {
-                       ASSERT(findex < xfs_dir2_free_max_bests(mp));
-                       free->hdr.nvalid = cpu_to_be32(findex + 1);
+               if (findex >= freehdr.nvalid) {
+                       ASSERT(findex < xfs_dir3_free_max_bests(mp));
+                       freehdr.nvalid = findex + 1;
                        /*
                         * Tag new entry so nused will go up.
                         */
-                       free->bests[findex] = cpu_to_be16(NULLDATAOFF);
+                       bests[findex] = cpu_to_be16(NULLDATAOFF);
                }
                /*
                 * If this entry was for an empty data block
                 * (this should always be true) then update the header.
                 */
-               if (free->bests[findex] == cpu_to_be16(NULLDATAOFF)) {
-                       be32_add_cpu(&free->hdr.nused, 1);
+               if (bests[findex] == cpu_to_be16(NULLDATAOFF)) {
+                       freehdr.nused++;
+                       xfs_dir3_free_hdr_to_disk(fbp->b_addr, &freehdr);
                        xfs_dir2_free_log_header(tp, fbp);
                }
                /*
@@ -1699,7 +1960,8 @@ xfs_dir2_node_addname_int(
                 * change again.
                 */
                hdr = dbp->b_addr;
-               free->bests[findex] = hdr->bestfree[0].length;
+               bf = xfs_dir3_data_bestfree_p(hdr);
+               bests[findex] = bf[0].length;
                logfree = 1;
        }
        /*
@@ -1715,19 +1977,20 @@ xfs_dir2_node_addname_int(
                /*
                 * Read the data block in.
                 */
-               error = xfs_dir2_data_read(tp, dp, xfs_dir2_db_to_da(mp, dbno),
+               error = xfs_dir3_data_read(tp, dp, xfs_dir2_db_to_da(mp, dbno),
                                           -1, &dbp);
                if (error)
                        return error;
                hdr = dbp->b_addr;
+               bf = xfs_dir3_data_bestfree_p(hdr);
                logfree = 0;
        }
-       ASSERT(be16_to_cpu(hdr->bestfree[0].length) >= length);
+       ASSERT(be16_to_cpu(bf[0].length) >= length);
        /*
         * Point to the existing unused space.
         */
        dup = (xfs_dir2_data_unused_t *)
-             ((char *)hdr + be16_to_cpu(hdr->bestfree[0].offset));
+             ((char *)hdr + be16_to_cpu(bf[0].offset));
        needscan = needlog = 0;
        /*
         * Mark the first part of the unused space, inuse for us.
@@ -1758,8 +2021,9 @@ xfs_dir2_node_addname_int(
        /*
         * If the freespace entry is now wrong, update it.
         */
-       if (be16_to_cpu(free->bests[findex]) != be16_to_cpu(hdr->bestfree[0].length)) {
-               free->bests[findex] = hdr->bestfree[0].length;
+       bests = xfs_dir3_free_bests_p(mp, free); /* gcc is so stupid */
+       if (be16_to_cpu(bests[findex]) != be16_to_cpu(bf[0].length)) {
+               bests[findex] = bf[0].length;
                logfree = 1;
        }
        /*
@@ -1777,7 +2041,7 @@ xfs_dir2_node_addname_int(
 
 /*
  * Lookup an entry in a node-format directory.
- * All the real work happens in xfs_da_node_lookup_int.
+ * All the real work happens in xfs_da3_node_lookup_int.
  * The only real output is the inode number of the entry.
  */
 int                                            /* error */
@@ -1802,7 +2066,7 @@ xfs_dir2_node_lookup(
        /*
         * Fill in the path to the entry in the cursor.
         */
-       error = xfs_da_node_lookup_int(state, &rval);
+       error = xfs_da3_node_lookup_int(state, &rval);
        if (error)
                rval = error;
        else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE) {
@@ -1857,7 +2121,7 @@ xfs_dir2_node_removename(
        /*
         * Look up the entry we're deleting, set up the cursor.
         */
-       error = xfs_da_node_lookup_int(state, &rval);
+       error = xfs_da3_node_lookup_int(state, &rval);
        if (error)
                rval = error;
        /*
@@ -1881,12 +2145,12 @@ xfs_dir2_node_removename(
        /*
         * Fix the hash values up the btree.
         */
-       xfs_da_fixhashpath(state, &state->path);
+       xfs_da3_fixhashpath(state, &state->path);
        /*
         * If we need to join leaf blocks, do it.
         */
        if (rval && state->path.active > 1)
-               error = xfs_da_join(state);
+               error = xfs_da3_join(state);
        /*
         * If no errors so far, try conversion to leaf format.
         */
@@ -1928,7 +2192,7 @@ xfs_dir2_node_replace(
        /*
         * Lookup the entry to change in the btree.
         */
-       error = xfs_da_node_lookup_int(state, &rval);
+       error = xfs_da3_node_lookup_int(state, &rval);
        if (error) {
                rval = error;
        }
@@ -1937,19 +2201,22 @@ xfs_dir2_node_replace(
         * and locked it.  But paranoia is good.
         */
        if (rval == EEXIST) {
+               struct xfs_dir2_leaf_entry *ents;
                /*
                 * Find the leaf entry.
                 */
                blk = &state->path.blk[state->path.active - 1];
                ASSERT(blk->magic == XFS_DIR2_LEAFN_MAGIC);
                leaf = blk->bp->b_addr;
-               lep = &leaf->ents[blk->index];
+               ents = xfs_dir3_leaf_ents_p(leaf);
+               lep = &ents[blk->index];
                ASSERT(state->extravalid);
                /*
                 * Point to the data entry.
                 */
                hdr = state->extrablk.bp->b_addr;
-               ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC));
+               ASSERT(hdr->magic == cpu_to_be32(XFS_DIR2_DATA_MAGIC) ||
+                      hdr->magic == cpu_to_be32(XFS_DIR3_DATA_MAGIC));
                dep = (xfs_dir2_data_entry_t *)
                      ((char *)hdr +
                       xfs_dir2_dataptr_to_off(state->mp, be32_to_cpu(lep->address)));
@@ -1995,6 +2262,7 @@ xfs_dir2_node_trim_free(
        xfs_dir2_free_t         *free;          /* freespace structure */
        xfs_mount_t             *mp;            /* filesystem mount point */
        xfs_trans_t             *tp;            /* transaction pointer */
+       struct xfs_dir3_icfree_hdr freehdr;
 
        dp = args->dp;
        mp = dp->i_mount;
@@ -2012,11 +2280,12 @@ xfs_dir2_node_trim_free(
        if (!bp)
                return 0;
        free = bp->b_addr;
-       ASSERT(free->hdr.magic == cpu_to_be32(XFS_DIR2_FREE_MAGIC));
+       xfs_dir3_free_hdr_from_disk(&freehdr, free);
+
        /*
         * If there are used entries, there's nothing to do.
         */
-       if (be32_to_cpu(free->hdr.nused) > 0) {
+       if (freehdr.nused > 0) {
                xfs_trans_brelse(tp, bp);
                *rvalp = 0;
                return 0;