]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/dinode.c
xfs: remove suport for filesystems without unwritten extent flag
[thirdparty/xfsprogs-dev.git] / repair / dinode.c
index ae2feb23051a00aa2389d0f5a5b4a8ab613d209a..f670bf87c50d86c9003640950492d9d4e549a02b 100644 (file)
@@ -1,29 +1,16 @@
+// 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 "avl.h"
 #include "globals.h"
 #include "agheader.h"
 #include "incore.h"
 #include "protos.h"
 #include "err_protos.h"
-#include "dir.h"
 #include "dir2.h"
 #include "dinode.h"
 #include "scan.h"
 #include "attr_repair.h"
 #include "bmap.h"
 #include "threads.h"
+#include "slab.h"
+#include "rmap.h"
 
 /*
- * inode clearing routines
+ * gettext lookups for translations of strings use mutexes internally to
+ * the library. Hence when we come through here doing parallel scans in
+ * multiple AGs, then all do concurrent text conversions and serialise
+ * on the translation string lookups. Let's avoid doing repeated lookups
+ * by making them static variables and only assigning the translation
+ * once.
  */
+static char    *forkname_data;
+static char    *forkname_attr;
+static char    *ftype_real_time;
+static char    *ftype_regular;
 
-/*
- * return the offset into the inode where the attribute fork starts
- */
-/* ARGSUSED */
-int
-calc_attr_offset(xfs_mount_t *mp, xfs_dinode_t *dino)
+void
+dinode_bmbt_translation_init(void)
 {
-       xfs_dinode_core_t       *dinoc = &dino->di_core;
-       int                     offset = ((__psint_t) &dino->di_u)
-                                               - (__psint_t)dino;
+       forkname_data = _("data");
+       forkname_attr = _("attr");
+       ftype_real_time = _("real-time");
+       ftype_regular = _("regular");
+}
 
-       /*
-        * don't worry about alignment when calculating offset
-        * because the data fork is already 8-byte aligned
-        */
-       switch (dinoc->di_format)  {
-       case XFS_DINODE_FMT_DEV:
-               offset += sizeof(xfs_dev_t);
-               break;
-       case XFS_DINODE_FMT_LOCAL:
-               offset += be64_to_cpu(dinoc->di_size);
-               break;
-       case XFS_DINODE_FMT_EXTENTS:
-               offset += be32_to_cpu(dinoc->di_nextents) * 
-                                               sizeof(xfs_bmbt_rec_t);
-               break;
-       case XFS_DINODE_FMT_BTREE:
-               offset += be16_to_cpu(dino->di_u.di_bmbt.bb_numrecs) * 
-                                               sizeof(xfs_bmbt_rec_t);
-               break;
-       default:
-               do_error(_("Unknown inode format.\n"));
-               abort();
-               break;
-       }
+char *
+get_forkname(int whichfork)
+{
 
-       return(offset);
+       if (whichfork == XFS_DATA_FORK)
+               return forkname_data;
+       return forkname_attr;
 }
 
-/* ARGSUSED */
-int
+/*
+ * inode clearing routines
+ */
+
+static int
 clear_dinode_attr(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num)
 {
-       xfs_dinode_core_t *dinoc = &dino->di_core;
-
-       ASSERT(dinoc->di_forkoff != 0);
+       ASSERT(dino->di_forkoff != 0);
 
        if (!no_modify)
-               fprintf(stderr, _("clearing inode %llu attributes\n"),
-                       (unsigned long long)ino_num);
+               fprintf(stderr,
+_("clearing inode %" PRIu64 " attributes\n"), ino_num);
        else
-               fprintf(stderr, _("would have cleared inode %llu attributes\n"),
-                       (unsigned long long)ino_num);
+               fprintf(stderr,
+_("would have cleared inode %" PRIu64 " attributes\n"), ino_num);
 
-       if (be16_to_cpu(dinoc->di_anextents) != 0)  {
+       if (be16_to_cpu(dino->di_anextents) != 0)  {
                if (no_modify)
                        return(1);
-               dinoc->di_anextents = cpu_to_be16(0);
+               dino->di_anextents = cpu_to_be16(0);
        }
 
-       if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS)  {
+       if (dino->di_aformat != XFS_DINODE_FMT_EXTENTS)  {
                if (no_modify)
                        return(1);
-               dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
+               dino->di_aformat = XFS_DINODE_FMT_EXTENTS;
        }
 
        /* get rid of the fork by clearing forkoff */
@@ -118,7 +96,7 @@ clear_dinode_attr(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num)
                                XFS_DFORK_APTR(dino);
                asf->hdr.totsize = cpu_to_be16(sizeof(xfs_attr_sf_hdr_t));
                asf->hdr.count = 0;
-               dinoc->di_forkoff = 0;  /* got to do this after asf is set */
+               dino->di_forkoff = 0;  /* got to do this after asf is set */
        }
 
        /*
@@ -127,177 +105,46 @@ clear_dinode_attr(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num)
        return(1);
 }
 
-/* ARGSUSED */
-int
-clear_dinode_core(xfs_dinode_core_t *dinoc, xfs_ino_t ino_num)
+static void
+clear_dinode_core(struct xfs_mount *mp, xfs_dinode_t *dinoc, xfs_ino_t ino_num)
 {
-       int dirty = 0;
-
-       if (be16_to_cpu(dinoc->di_magic) != XFS_DINODE_MAGIC)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
-       }
-
-       if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) ||
-           (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1))  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_version = (fs_inode_nlink) ? XFS_DINODE_VERSION_2
-                                               : XFS_DINODE_VERSION_1;
-       }
-
-       if (be16_to_cpu(dinoc->di_mode) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_mode = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_flags) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_flags = 0;
-       }
-
-       if (be32_to_cpu(dinoc->di_dmevmask) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_dmevmask = 0;
-       }
-
-       if (dinoc->di_forkoff != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_forkoff = 0;
-       }
-
-       if (dinoc->di_format != XFS_DINODE_FMT_EXTENTS)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_format = XFS_DINODE_FMT_EXTENTS;
-       }
-
-       if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
-       }
-
-       if (be64_to_cpu(dinoc->di_size) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_size = 0;
-       }
-
-       if (be64_to_cpu(dinoc->di_nblocks) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_nblocks = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_onlink) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_onlink = 0;
-       }
-
-       if (be32_to_cpu(dinoc->di_nextents) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_nextents = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_anextents) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_anextents = 0;
-       }
-
-       if (dinoc->di_version > XFS_DINODE_VERSION_1 &&
-                       be32_to_cpu(dinoc->di_nlink) != 0)  {
-               dirty = 1;
-
-               if (no_modify)
-                       return(1);
-
-               dinoc->di_nlink = 0;
-       }
-
-       return(dirty);
+       memset(dinoc, 0, sizeof(*dinoc));
+       dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
+       if (xfs_sb_version_hascrc(&mp->m_sb))
+               dinoc->di_version = 3;
+       else
+               dinoc->di_version = 2;
+       dinoc->di_gen = cpu_to_be32(random());
+       dinoc->di_format = XFS_DINODE_FMT_EXTENTS;
+       dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
+       /* we are done for version 1/2 inodes */
+       if (dinoc->di_version < 3)
+               return;
+       dinoc->di_ino = cpu_to_be64(ino_num);
+       platform_uuid_copy(&dinoc->di_uuid, &mp->m_sb.sb_meta_uuid);
+       return;
 }
 
-/* ARGSUSED */
-int
+static void
 clear_dinode_unlinked(xfs_mount_t *mp, xfs_dinode_t *dino)
 {
 
-       if (be32_to_cpu(dino->di_next_unlinked) != NULLAGINO)  {
-               if (!no_modify)
-                       dino->di_next_unlinked = cpu_to_be32(NULLAGINO);
-               return(1);
-       }
-
-       return(0);
+       dino->di_next_unlinked = cpu_to_be32(NULLAGINO);
 }
 
 /*
  * this clears the unlinked list too so it should not be called
  * until after the agi unlinked lists are walked in phase 3.
- * returns > zero if the inode has been altered while being cleared
  */
-int
+static void
 clear_dinode(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num)
 {
-       int dirty;
-
-       dirty = clear_dinode_core(&dino->di_core, ino_num);
-       dirty += clear_dinode_unlinked(mp, dino);
+       clear_dinode_core(mp, dino, ino_num);
+       clear_dinode_unlinked(mp, dino);
 
        /* and clear the forks */
-
-       if (dirty && !no_modify)
-               memset(&dino->di_u, 0, XFS_LITINO(mp));
-
-       return(dirty);
+       memset(XFS_DFORK_DPTR(dino), 0, XFS_LITINO(mp, dino->di_version));
+       return;
 }
 
 
@@ -315,11 +162,12 @@ verify_ag_bno(xfs_sb_t *sbp,
                xfs_agnumber_t agno,
                xfs_agblock_t agbno)
 {
-       if (agno < (sbp->sb_agcount - 1)) 
+       if (agno < (sbp->sb_agcount - 1))
                return (agbno >= sbp->sb_agblocks);
-       if (agno == (sbp->sb_agcount - 1)) 
+       if (agno == (sbp->sb_agcount - 1))
                return (agbno >= (sbp->sb_dblocks -
-                               ((sbp->sb_agcount - 1) * sbp->sb_agblocks)));
+                               ((xfs_rfsblock_t)(sbp->sb_agcount - 1) *
+                                sbp->sb_agblocks)));
        return 1;
 }
 
@@ -389,7 +237,7 @@ verify_aginum(xfs_mount_t   *mp,
  */
 int
 verify_dfsbno(xfs_mount_t      *mp,
-               xfs_dfsbno_t    fsbno)
+               xfs_fsblock_t   fsbno)
 {
        xfs_agnumber_t  agno;
        xfs_agblock_t   agbno;
@@ -410,8 +258,8 @@ verify_dfsbno(xfs_mount_t   *mp,
 
 static __inline int
 verify_dfsbno_range(xfs_mount_t        *mp,
-               xfs_dfsbno_t    fsbno,
-               xfs_dfilblks_t  count)
+               xfs_fsblock_t   fsbno,
+               xfs_filblks_t   count)
 {
        xfs_agnumber_t  agno;
        xfs_agblock_t   agbno;
@@ -447,41 +295,16 @@ verify_agbno(xfs_mount_t  *mp,
        return verify_ag_bno(sbp, agno, agbno) == 0;
 }
 
-/*
- * return address of block fblock if it's within the range described
- * by the extent list.  Otherwise, returns a null address.
- */
-/* ARGSUSED */
-xfs_dfsbno_t
-get_bmbt_reclist(
-       xfs_mount_t             *mp,
-       xfs_bmbt_rec_t          *rp,
-       int                     numrecs,
-       xfs_dfiloff_t           fblock)
-{
-       int                     i;
-       xfs_bmbt_irec_t         irec;
-
-       for (i = 0; i < numrecs; i++) {
-               libxfs_bmbt_disk_get_all(rp + i, &irec);
-               if (irec.br_startoff >= fblock &&
-                               irec.br_startoff + irec.br_blockcount < fblock)
-                       return (irec.br_startblock + fblock - irec.br_startoff);
-       }
-       return(NULLDFSBNO);
-}
-
-
 static int
 process_rt_rec(
        xfs_mount_t             *mp,
        xfs_bmbt_irec_t         *irec,
        xfs_ino_t               ino,
-       xfs_drfsbno_t           *tot,
+       xfs_rfsblock_t          *tot,
        int                     check_dups)
 {
-       xfs_dfsbno_t            b;
-       xfs_drtbno_t            ext;
+       xfs_fsblock_t           b;
+       xfs_rtblock_t           ext;
        int                     state;
        int                     pwe;            /* partially-written extent */
 
@@ -489,95 +312,87 @@ process_rt_rec(
         * check numeric validity of the extent
         */
        if (irec->br_startblock >= mp->m_sb.sb_rblocks) {
-               do_warn(_("inode %llu - bad rt extent start block number "
-                               "%llu, offset %llu\n"), ino,
-                               irec->br_startblock, irec->br_startoff);
+               do_warn(
+_("inode %" PRIu64 " - bad rt extent start block number %" PRIu64 ", offset %" PRIu64 "\n"),
+                       ino,
+                       irec->br_startblock,
+                       irec->br_startoff);
                return 1;
        }
        if (irec->br_startblock + irec->br_blockcount - 1 >= mp->m_sb.sb_rblocks) {
-               do_warn(_("inode %llu - bad rt extent last block number %llu, "
-                               "offset %llu\n"), ino, irec->br_startblock +
-                               irec->br_blockcount - 1, irec->br_startoff);
+               do_warn(
+_("inode %" PRIu64 " - bad rt extent last block number %" PRIu64 ", offset %" PRIu64 "\n"),
+                       ino,
+                       irec->br_startblock + irec->br_blockcount - 1,
+                       irec->br_startoff);
                return 1;
        }
        if (irec->br_startblock + irec->br_blockcount - 1 < irec->br_startblock) {
-               do_warn(_("inode %llu - bad rt extent overflows - start %llu, "
-                               "end %llu, offset %llu\n"), ino,
-                               irec->br_startblock, irec->br_startblock +
-                               irec->br_blockcount - 1, irec->br_startoff);
-               return 1;
-       }
-
-       /*
-        * verify that the blocks listed in the record
-        * are multiples of an extent
-        */
-       if (xfs_sb_version_hasextflgbit(&mp->m_sb) == 0 &&
-                       (irec->br_startblock % mp->m_sb.sb_rextsize != 0 ||
-                        irec->br_blockcount % mp->m_sb.sb_rextsize != 0)) {
-               do_warn(_("malformed rt inode extent [%llu %llu] (fs rtext "
-                               "size = %u)\n"), irec->br_startblock,
-                               irec->br_blockcount, mp->m_sb.sb_rextsize);
+               do_warn(
+_("inode %" PRIu64 " - bad rt extent overflows - start %" PRIu64 ", "
+  "end %" PRIu64 ", offset %" PRIu64 "\n"),
+                       ino,
+                       irec->br_startblock,
+                       irec->br_startblock + irec->br_blockcount - 1,
+                       irec->br_startoff);
                return 1;
        }
 
        /*
         * set the appropriate number of extents
+        * this iterates block by block, this can be optimised using extents
         */
        for (b = irec->br_startblock; b < irec->br_startblock +
                        irec->br_blockcount; b += mp->m_sb.sb_rextsize)  {
-               ext = (xfs_drtbno_t) b / mp->m_sb.sb_rextsize;
-               pwe = xfs_sb_version_hasextflgbit(&mp->m_sb) &&
-                               irec->br_state == XFS_EXT_UNWRITTEN &&
+               ext = (xfs_rtblock_t) b / mp->m_sb.sb_rextsize;
+               pwe = irec->br_state == XFS_EXT_UNWRITTEN &&
                                (b % mp->m_sb.sb_rextsize != 0);
 
                if (check_dups == 1)  {
                        if (search_rt_dup_extent(mp, ext) && !pwe)  {
-                               do_warn(_("data fork in rt ino %llu claims "
-                                               "dup rt extent, off - %llu, "
-                                               "start - %llu, count %llu\n"),
-                                               ino, irec->br_startoff,
-                                               irec->br_startblock,
-                                               irec->br_blockcount);
+                               do_warn(
+_("data fork in rt ino %" PRIu64 " claims dup rt extent,"
+  "off - %" PRIu64 ", start - %" PRIu64 ", count %" PRIu64 "\n"),
+                                       ino,
+                                       irec->br_startoff,
+                                       irec->br_startblock,
+                                       irec->br_blockcount);
                                return 1;
                        }
                        continue;
                }
 
-               state = get_rtbno_state(mp, ext);
-
+               state = get_rtbmap(ext);
                switch (state)  {
-                       case XR_E_FREE:
-                       case XR_E_UNKNOWN:
-                               set_rtbno_state(mp, ext, XR_E_INUSE);
+               case XR_E_FREE:
+               case XR_E_UNKNOWN:
+                       set_rtbmap(ext, XR_E_INUSE);
+                       break;
+               case XR_E_BAD_STATE:
+                       do_error(
+_("bad state in rt block map %" PRIu64 "\n"),
+                               ext);
+               case XR_E_FS_MAP:
+               case XR_E_INO:
+               case XR_E_INUSE_FS:
+                       do_error(
+_("data fork in rt inode %" PRIu64 " found metadata block %" PRIu64 " in rt bmap\n"),
+                               ino, ext);
+               case XR_E_INUSE:
+                       if (pwe)
                                break;
-
-                       case XR_E_BAD_STATE:
-                               do_error(_("bad state in rt block map %llu\n"),
-                                               ext);
-
-                       case XR_E_FS_MAP:
-                       case XR_E_INO:
-                       case XR_E_INUSE_FS:
-                               do_error(_("data fork in rt inode %llu found "
-                                       "metadata block %llu in rt bmap\n"),
-                                       ino, ext);
-
-                       case XR_E_INUSE:
-                               if (pwe)
-                                       break;
-
-                       case XR_E_MULT:
-                               set_rtbno_state(mp, ext, XR_E_MULT);
-                               do_warn(_("data fork in rt inode %llu claims "
-                                               "used rt block %llu\n"),
-                                               ino, ext);
-                               return 1;
-
-                       case XR_E_FREE1:
-                       default:
-                               do_error(_("illegal state %d in rt block map "
-                                               "%llu\n"), state, b);
+                       /* fall through */
+               case XR_E_MULT:
+                       set_rtbmap(ext, XR_E_MULT);
+                       do_warn(
+_("data fork in rt inode %" PRIu64 " claims used rt block %" PRIu64 "\n"),
+                               ino, ext);
+                       return 1;
+               case XR_E_FREE1:
+               default:
+                       do_error(
+_("illegal state %d in rt block map %" PRIu64 "\n"),
+                               state, b);
                }
        }
 
@@ -596,55 +411,52 @@ process_rt_rec(
  * file overlaps with any duplicate extents (in the
  * duplicate extent list).
  */
-/* ARGSUSED */
-int
+static int
 process_bmbt_reclist_int(
        xfs_mount_t             *mp,
        xfs_bmbt_rec_t          *rp,
-       int                     numrecs,
+       int                     *numrecs,
        int                     type,
        xfs_ino_t               ino,
-       xfs_drfsbno_t           *tot,
+       xfs_rfsblock_t          *tot,
        blkmap_t                **blkmapp,
-       xfs_dfiloff_t           *first_key,
-       xfs_dfiloff_t           *last_key,
+       xfs_fileoff_t           *first_key,
+       xfs_fileoff_t           *last_key,
        int                     check_dups,
        int                     whichfork)
 {
        xfs_bmbt_irec_t         irec;
-       xfs_dfilblks_t          cp = 0;         /* prev count */
-       xfs_dfsbno_t            sp = 0;         /* prev start */
-       xfs_dfiloff_t           op = 0;         /* prev offset */
-       xfs_dfsbno_t            b;
+       xfs_filblks_t           cp = 0;         /* prev count */
+       xfs_fsblock_t           sp = 0;         /* prev start */
+       xfs_fileoff_t           op = 0;         /* prev offset */
+       xfs_fsblock_t           b;
        char                    *ftype;
-       char                    *forkname;
+       char                    *forkname = get_forkname(whichfork);
        int                     i;
        int                     state;
-       xfs_dfsbno_t            e;
        xfs_agnumber_t          agno;
        xfs_agblock_t           agbno;
+       xfs_agblock_t           ebno;
+       xfs_extlen_t            blen;
        xfs_agnumber_t          locked_agno = -1;
        int                     error = 1;
 
-       if (whichfork == XFS_DATA_FORK)
-               forkname = _("data");
-       else
-               forkname = _("attr");
-
        if (type == XR_INO_RTDATA)
-               ftype = _("real-time");
+               ftype = ftype_real_time;
        else
-               ftype = _("regular");
+               ftype = ftype_regular;
 
-       for (i = 0; i < numrecs; i++) {
-               libxfs_bmbt_disk_get_all(rp + i, &irec);
+       for (i = 0; i < *numrecs; i++) {
+               libxfs_bmbt_disk_get_all((rp +i), &irec);
                if (i == 0)
                        *last_key = *first_key = irec.br_startoff;
                else
                        *last_key = irec.br_startoff;
                if (i > 0 && op + cp > irec.br_startoff)  {
-                       do_warn(_("bmap rec out of order, inode %llu entry %d "
-                               "[o s c] [%llu %llu %llu], %d [%llu %llu %llu]\n"),
+                       do_warn(
+_("bmap rec out of order, inode %" PRIu64" entry %d "
+  "[o s c] [%" PRIu64 " %" PRIu64 " %" PRIu64 "], "
+  "%d [%" PRIu64 " %" PRIu64 " %" PRIu64 "]\n"),
                                ino, i, irec.br_startoff, irec.br_startblock,
                                irec.br_blockcount, i - 1, op, sp, cp);
                        goto done;
@@ -657,9 +469,11 @@ process_bmbt_reclist_int(
                 * check numeric validity of the extent
                 */
                if (irec.br_blockcount == 0)  {
-                       do_warn(_("zero length extent (off = %llu, fsbno = "
-                               "%llu) in ino %llu\n"), irec.br_startoff,
-                               irec.br_startblock, ino);
+                       do_warn(
+_("zero length extent (off = %" PRIu64 ", fsbno = %" PRIu64 ") in ino %" PRIu64 "\n"),
+                               irec.br_startoff,
+                               irec.br_startblock,
+                               ino);
                        goto done;
                }
 
@@ -686,49 +500,75 @@ process_bmbt_reclist_int(
                                break;
 
                        case XR_DFSBNORANGE_BADSTART:
-                               do_warn(_("inode %llu - bad extent starting "
-                                       "block number %llu, offset %llu\n"),
-                                       ino, irec.br_startblock,
+                               do_warn(
+_("inode %" PRIu64 " - bad extent starting block number %" PRIu64 ", offset %" PRIu64 "\n"),
+                                       ino,
+                                       irec.br_startblock,
                                        irec.br_startoff);
                                goto done;
 
                        case XR_DFSBNORANGE_BADEND:
-                               do_warn(_("inode %llu - bad extent last block "
-                                       "number %llu, offset %llu\n"), ino,
-                                       irec.br_startblock + irec.br_blockcount
-                                       - 1, irec.br_startoff);
+                               do_warn(
+_("inode %" PRIu64 " - bad extent last block number %" PRIu64 ", offset %" PRIu64 "\n"),
+                                       ino,
+                                       irec.br_startblock + irec.br_blockcount - 1,
+                                       irec.br_startoff);
                                goto done;
 
                        case XR_DFSBNORANGE_OVERFLOW:
-                               do_warn(_("inode %llu - bad extent overflows - "
-                                       "start %llu, end %llu, offset %llu\n"),
-                                       ino, irec.br_startblock,
-                                       irec.br_startblock + irec.br_blockcount
-                                       - 1, irec.br_startoff);
+                               do_warn(
+_("inode %" PRIu64 " - bad extent overflows - start %" PRIu64 ", "
+  "end %" PRIu64 ", offset %" PRIu64 "\n"),
+                                       ino,
+                                       irec.br_startblock,
+                                       irec.br_startblock + irec.br_blockcount - 1,
+                                       irec.br_startoff);
                                goto done;
                }
-               if (irec.br_startoff >= fs_max_file_offset)  {
-                       do_warn(_("inode %llu - extent offset too large - "
-                               "start %llu, count %llu, offset %llu\n"),
-                               ino, irec.br_startblock, irec.br_blockcount,
-                               irec.br_startoff);
+               /* Ensure this extent does not extend beyond the max offset */
+               if (irec.br_startoff + irec.br_blockcount - 1 >
+                                                       fs_max_file_offset) {
+                       do_warn(
+_("inode %" PRIu64 " - extent exceeds max offset - start %" PRIu64 ", "
+  "count %" PRIu64 ", physical block %" PRIu64 "\n"),
+                               ino, irec.br_startoff, irec.br_blockcount,
+                               irec.br_startblock);
                        goto done;
                }
 
-               if (blkmapp && *blkmapp)
-                       blkmap_set_ext(blkmapp, irec.br_startoff,
+               if (blkmapp && *blkmapp) {
+                       int     error2;
+                       error2 = blkmap_set_ext(blkmapp, irec.br_startoff,
                                        irec.br_startblock, irec.br_blockcount);
+                       if (error2) {
+                               /*
+                                * we don't want to clear the inode due to an
+                                * internal bmap tracking error, but if we've
+                                * run out of memory then we simply can't
+                                * validate that the filesystem is consistent.
+                                * Hence just abort at this point with an ENOMEM
+                                * error.
+                                */
+                               do_abort(
+_("Fatal error: inode %" PRIu64 " - blkmap_set_ext(): %s\n"
+  "\t%s fork, off - %" PRIu64 ", start - %" PRIu64 ", cnt %" PRIu64 "\n"),
+                                       ino, strerror(error2), forkname,
+                                       irec.br_startoff, irec.br_startblock,
+                                       irec.br_blockcount);
+                       }
+               }
+
                /*
                 * Profiling shows that the following loop takes the
                 * most time in all of xfs_repair.
                 */
                agno = XFS_FSB_TO_AGNO(mp, irec.br_startblock);
                agbno = XFS_FSB_TO_AGBNO(mp, irec.br_startblock);
-               e = irec.br_startblock + irec.br_blockcount;
+               ebno = agbno + irec.br_blockcount;
                if (agno != locked_agno) {
                        if (locked_agno != -1)
-                               pthread_mutex_unlock(&ag_locks[locked_agno]);
-                       pthread_mutex_lock(&ag_locks[agno]);
+                               pthread_mutex_unlock(&ag_locks[locked_agno].lock);
+                       pthread_mutex_lock(&ag_locks[agno].lock);
                        locked_agno = agno;
                }
 
@@ -739,79 +579,123 @@ process_bmbt_reclist_int(
                         * checking each entry without setting the
                         * block bitmap
                         */
-                       for (b = irec.br_startblock; b < e; b++, agbno++)  {
-                               if (search_dup_extent(mp, agno, agbno)) {
-                                       do_warn(_("%s fork in ino %llu claims "
-                                               "dup extent, off - %llu, "
-                                               "start - %llu, cnt %llu\n"),
-                                               forkname, ino, irec.br_startoff,
-                                               irec.br_startblock,
-                                               irec.br_blockcount);
-                                       goto done;
-                               }
+                       if (!(type == XR_INO_DATA &&
+                           xfs_sb_version_hasreflink(&mp->m_sb)) &&
+                           search_dup_extent(agno, agbno, ebno)) {
+                               do_warn(
+_("%s fork in ino %" PRIu64 " claims dup extent, "
+  "off - %" PRIu64 ", start - %" PRIu64 ", cnt %" PRIu64 "\n"),
+                                       forkname, ino, irec.br_startoff,
+                                       irec.br_startblock,
+                                       irec.br_blockcount);
+                               goto done;
                        }
                        *tot += irec.br_blockcount;
                        continue;
                }
 
-               for (b = irec.br_startblock; b < e; b++, agbno++)  {
-                       /*
-                        * Process in chunks of 16 (XR_BB_UNIT/XR_BB)
-                        * for common XR_E_UNKNOWN to XR_E_INUSE transition
-                        */
-                       if (((agbno & XR_BB_MASK) == 0) && ((irec.br_startblock + irec.br_blockcount - b) >= (XR_BB_UNIT/XR_BB))) {
-                               if (ba_bmap[agno][agbno>>XR_BB] == XR_E_UNKNOWN_LL) {
-                                       ba_bmap[agno][agbno>>XR_BB] = XR_E_INUSE_LL;
-                                       agbno += (XR_BB_UNIT/XR_BB) - 1;
-                                       b += (XR_BB_UNIT/XR_BB) - 1;
-                                       continue;
-                               }
-
-                       }
-
-                       state = get_agbno_state(mp, agno, agbno);
-
+               for (b = irec.br_startblock;
+                    agbno < ebno;
+                    b += blen, agbno += blen) {
+                       state = get_bmap_ext(agno, agbno, ebno, &blen);
                        switch (state)  {
                        case XR_E_FREE:
                        case XR_E_FREE1:
-                               do_warn(_("%s fork in ino %llu claims free "
-                                       "block %llu\n"),
-                                       forkname, ino, (__uint64_t) b);
+                               do_warn(
+_("%s fork in ino %" PRIu64 " claims free block %" PRIu64 "\n"),
+                                       forkname, ino, (uint64_t) b);
                                /* fall through ... */
+                       case XR_E_INUSE1:       /* seen by rmap */
                        case XR_E_UNKNOWN:
-                               set_agbno_state(mp, agno, agbno, XR_E_INUSE);
                                break;
 
                        case XR_E_BAD_STATE:
-                               do_error(_("bad state in block map %llu\n"), b);
+                               do_error(_("bad state in block map %" PRIu64 "\n"), b);
 
+                       case XR_E_FS_MAP1:
+                       case XR_E_INO1:
+                       case XR_E_INUSE_FS1:
+                               do_warn(_("rmap claims metadata use!\n"));
+                               /* fall through */
                        case XR_E_FS_MAP:
                        case XR_E_INO:
                        case XR_E_INUSE_FS:
-                               do_warn(_("%s fork in inode %llu claims "
-                                       "metadata block %llu\n"),
-                                       forkname, ino, (__uint64_t) b);
+                       case XR_E_REFC:
+                               do_warn(
+_("%s fork in inode %" PRIu64 " claims metadata block %" PRIu64 "\n"),
+                                       forkname, ino, b);
                                goto done;
 
                        case XR_E_INUSE:
                        case XR_E_MULT:
-                               set_agbno_state(mp, agno, agbno, XR_E_MULT);
-                               do_warn(_("%s fork in %s inode %llu claims "
-                                       "used block %llu\n"),
-                                       forkname, ftype, ino, (__uint64_t) b);
+                               if (type == XR_INO_DATA &&
+                                   xfs_sb_version_hasreflink(&mp->m_sb))
+                                       break;
+                               do_warn(
+_("%s fork in %s inode %" PRIu64 " claims used block %" PRIu64 "\n"),
+                                       forkname, ftype, ino, b);
+                               goto done;
+
+                       case XR_E_COW:
+                               do_warn(
+_("%s fork in %s inode %" PRIu64 " claims CoW block %" PRIu64 "\n"),
+                                       forkname, ftype, ino, b);
                                goto done;
 
                        default:
-                               do_error(_("illegal state %d in block map %llu\n"),
+                               do_error(
+_("illegal state %d in block map %" PRIu64 "\n"),
                                        state, b);
+                               goto done;
+                       }
+               }
+
+               /*
+                * Update the internal extent map only after we've checked
+                * every block in this extent.  The first time we reject this
+                * data fork we'll try to rebuild the bmbt from rmap data.
+                * After a successful rebuild we'll try this scan again.
+                * (If the rebuild fails we won't come back here.)
+                */
+               agbno = XFS_FSB_TO_AGBNO(mp, irec.br_startblock);
+               ebno = agbno + irec.br_blockcount;
+               for (; agbno < ebno; agbno += blen) {
+                       state = get_bmap_ext(agno, agbno, ebno, &blen);
+                       switch (state)  {
+                       case XR_E_FREE:
+                       case XR_E_FREE1:
+                       case XR_E_INUSE1:
+                       case XR_E_UNKNOWN:
+                               set_bmap_ext(agno, agbno, blen, XR_E_INUSE);
+                               break;
+                       case XR_E_INUSE:
+                       case XR_E_MULT:
+                               set_bmap_ext(agno, agbno, blen, XR_E_MULT);
+                               break;
+                       default:
+                               break;
                        }
                }
+               if (collect_rmaps) { /* && !check_dups */
+                       error = rmap_add_rec(mp, ino, whichfork, &irec);
+                       if (error)
+                               do_error(
+_("couldn't add reverse mapping\n")
+                                       );
+               }
                *tot += irec.br_blockcount;
        }
        error = 0;
 done:
        if (locked_agno != -1)
-               pthread_mutex_unlock(&ag_locks[locked_agno]);
+               pthread_mutex_unlock(&ag_locks[locked_agno].lock);
+
+       if (i != *numrecs) {
+               ASSERT(i < *numrecs);
+               do_warn(_("correcting nextents for inode %" PRIu64 "\n"), ino);
+               *numrecs = i;
+       }
+
        return error;
 }
 
@@ -823,13 +707,13 @@ int
 process_bmbt_reclist(
        xfs_mount_t             *mp,
        xfs_bmbt_rec_t          *rp,
-       int                     numrecs,
+       int                     *numrecs,
        int                     type,
        xfs_ino_t               ino,
-       xfs_drfsbno_t           *tot,
+       xfs_rfsblock_t          *tot,
        blkmap_t                **blkmapp,
-       xfs_dfiloff_t           *first_key,
-       xfs_dfiloff_t           *last_key,
+       xfs_fileoff_t           *first_key,
+       xfs_fileoff_t           *last_key,
        int                     whichfork)
 {
        return process_bmbt_reclist_int(mp, rp, numrecs, type, ino, tot,
@@ -844,293 +728,72 @@ int
 scan_bmbt_reclist(
        xfs_mount_t             *mp,
        xfs_bmbt_rec_t          *rp,
-       int                     numrecs,
+       int                     *numrecs,
        int                     type,
        xfs_ino_t               ino,
-       xfs_drfsbno_t           *tot,
+       xfs_rfsblock_t          *tot,
        int                     whichfork)
 {
-       xfs_dfiloff_t           first_key = 0;
-       xfs_dfiloff_t           last_key = 0;
+       xfs_fileoff_t           first_key = 0;
+       xfs_fileoff_t           last_key = 0;
 
        return process_bmbt_reclist_int(mp, rp, numrecs, type, ino, tot,
                                NULL, &first_key, &last_key, 1, whichfork);
 }
 
 /*
- * these two are meant for routines that read and work with inodes
- * one at a time where the inodes may be in any order (like walking
- * the unlinked lists to look for inodes).  the caller is responsible
- * for writing/releasing the buffer.
+ * Grab the buffer backing an inode.  This is meant for routines that
+ * work with inodes one at a time in any order (like walking the
+ * unlinked lists to look for inodes).  The caller is responsible for
+ * writing/releasing the buffer.
  */
-xfs_buf_t *
-get_agino_buf(xfs_mount_t       *mp,
-               xfs_agnumber_t  agno,
-               xfs_agino_t     agino,
-               xfs_dinode_t    **dipp)
-{
-       ino_tree_node_t *irec;
-       xfs_buf_t *bp;
-       int size;
-
-       if ((irec = find_inode_rec(agno, agino)) == NULL)
-               return(NULL);
-
-       size = XFS_FSB_TO_BB(mp, MAX(1, XFS_INODES_PER_CHUNK/inodes_per_block));
-       bp = libxfs_readbuf(mp->m_dev, XFS_AGB_TO_DADDR(mp, agno,
-               XFS_AGINO_TO_AGBNO(mp, irec->ino_startnum)), size, 0);
-       if (!bp) {
-               do_warn(_("cannot read inode (%u/%u), disk block %lld\n"),
-                       agno, irec->ino_startnum,
-                       XFS_AGB_TO_DADDR(mp, agno,
-                               XFS_AGINO_TO_AGBNO(mp, irec->ino_startnum)));
-               return(NULL);
-       }
-
-       *dipp = XFS_MAKE_IPTR(mp, bp, agino -
-               XFS_OFFBNO_TO_AGINO(mp, XFS_AGINO_TO_AGBNO(mp,
-                                               irec->ino_startnum),
-               0));
-
-       return(bp);
-}
-
-/*
- * these next routines return the filesystem blockno of the
- * block containing the block "bno" in the file whose bmap
- * tree (or extent list) is rooted by "rootblock".
- *
- * the next routines are utility routines for the third
- * routine, get_bmapi().
- *
- * NOTE: getfunc_extlist only used by dirv1 checking code
- */
-xfs_dfsbno_t
-getfunc_extlist(xfs_mount_t            *mp,
-               xfs_ino_t               ino,
-               xfs_dinode_t            *dip,
-               xfs_dfiloff_t           bno,
-               int                     whichfork)
-{
-       xfs_bmbt_irec_t         irec;
-       xfs_dfsbno_t            final_fsbno = NULLDFSBNO;
-       xfs_bmbt_rec_t          *rootblock = (xfs_bmbt_rec_t *)
-                                               XFS_DFORK_PTR(dip, whichfork);
-       xfs_extnum_t            nextents = XFS_DFORK_NEXTENTS(dip, whichfork);
-       int                     i;
-
-       for (i = 0; i < nextents; i++)  {
-               libxfs_bmbt_disk_get_all(rootblock + i, &irec);
-               if (irec.br_startoff <= bno &&
-                               bno < irec.br_startoff + irec.br_blockcount) {
-                       final_fsbno = bno - irec.br_startoff + irec.br_startblock;
-                       break;
-               }
-       }
-
-       return(final_fsbno);
-}
-
-/*
- * NOTE: getfunc_btree only used by dirv1 checking code... 
- */
-xfs_dfsbno_t
-getfunc_btree(xfs_mount_t              *mp,
-               xfs_ino_t               ino,
-               xfs_dinode_t            *dip,
-               xfs_dfiloff_t           bno,
-               int                     whichfork)
+struct xfs_buf *
+get_agino_buf(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_agino_t             agino,
+       struct xfs_dinode       **dipp)
 {
-       int                     i;
-#ifdef DEBUG
-       int                     prev_level;
-#endif
-       int                     found;
-       int                     numrecs;
-       xfs_bmbt_rec_t          *rec;
-       xfs_bmbt_irec_t         irec;
-       xfs_bmbt_ptr_t          *pp;
-       xfs_bmbt_key_t          *key;
-       xfs_bmdr_key_t          *rkey;
-       xfs_bmdr_ptr_t          *rp;
-       xfs_dfsbno_t            fsbno;
-       xfs_buf_t               *bp;
-       xfs_dfsbno_t            final_fsbno = NULLDFSBNO;
-       xfs_bmbt_block_t        *block;
-       xfs_bmdr_block_t        *rootblock = (xfs_bmdr_block_t *)
-                                               XFS_DFORK_PTR(dip, whichfork);
-
-       ASSERT(rootblock->bb_level != 0);
-       /*
-        * deal with root block, it's got a slightly different
-        * header structure than interior nodes.  We know that
-        * a btree should have at least 2 levels otherwise it
-        * would be an extent list.
-        */
-       rkey = XFS_BTREE_KEY_ADDR(xfs_bmdr, rootblock, 1);
-       rp = XFS_BTREE_PTR_ADDR(xfs_bmdr, rootblock, 1, XFS_BTREE_BLOCK_MAXRECS(
-                       XFS_DFORK_SIZE(dip, mp, whichfork), xfs_bmdr, 1));
-       found = -1;
-       for (i = 0; i < be16_to_cpu(rootblock->bb_numrecs) - 1; i++) {
-               if (be64_to_cpu(rkey[i].br_startoff) <= bno && 
-                               bno < be64_to_cpu(rkey[i + 1].br_startoff)) {
-                       found = i;
-                       break;
-               }
-       }
-       if (i == be16_to_cpu(rootblock->bb_numrecs) - 1 && 
-                               bno >= be64_to_cpu(rkey[i].br_startoff))
-               found = i;
-
-       ASSERT(found != -1);
-
-       fsbno = be64_to_cpu(rp[found]);
-
-       ASSERT(verify_dfsbno(mp, fsbno));
-
-       bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
-                               XFS_FSB_TO_BB(mp, 1), 0);
-       if (!bp) {
-               do_error(_("cannot read bmap block %llu\n"), fsbno);
-               return(NULLDFSBNO);
-       }
-       block = XFS_BUF_TO_BMBT_BLOCK(bp);
-       numrecs = be16_to_cpu(block->bb_numrecs);
+       struct xfs_buf          *bp;
+       int                     cluster_size;
+       int                     ino_per_cluster;
+       xfs_agino_t             cluster_agino;
+       xfs_daddr_t             cluster_daddr;
+       xfs_daddr_t             cluster_blks;
 
        /*
-        * ok, now traverse any interior btree nodes
+        * Inode buffers have been read into memory in inode_cluster_size
+        * chunks (or one FSB).  To find the correct buffer for an inode,
+        * we must find the buffer for its cluster, add the appropriate
+        * offset, and return that.
         */
-#ifdef DEBUG
-       prev_level = be16_to_cpu(block->bb_level);
-#endif
-
-       while (be16_to_cpu(block->bb_level) > 0)  {
-#ifdef DEBUG
-               ASSERT(be16_to_cpu(block->bb_level) < prev_level);
+       cluster_size = max(mp->m_inode_cluster_size, mp->m_sb.sb_blocksize);
+       ino_per_cluster = cluster_size / mp->m_sb.sb_inodesize;
+       cluster_agino = agino & ~(ino_per_cluster - 1);
+       cluster_blks = XFS_FSB_TO_DADDR(mp, max(1,
+                       mp->m_inode_cluster_size >> mp->m_sb.sb_blocklog));
+       cluster_daddr = XFS_AGB_TO_DADDR(mp, agno,
+                       XFS_AGINO_TO_AGBNO(mp, cluster_agino));
 
-               prev_level = be16_to_cpu(block->bb_level);
+#ifdef XR_INODE_TRACE
+       printf("cluster_size %d ipc %d clusagino %d daddr %lld sectors %lld\n",
+               cluster_size, ino_per_cluster, cluster_agino, cluster_daddr,
+               cluster_blks);
 #endif
-               if (numrecs > mp->m_bmap_dmxr[1]) {
-                       do_warn(_("# of bmap records in inode %llu exceeds max "
-                                 "(%u, max - %u)\n"),
-                               ino, numrecs,
-                               mp->m_bmap_dmxr[1]);
-                       libxfs_putbuf(bp);
-                       return(NULLDFSBNO);
-               }
-               if (verbose && numrecs < mp->m_bmap_dmnr[1]) {
-                       do_warn(_("- # of bmap records in inode %llu less than "
-                                 "minimum (%u, min - %u), proceeding ...\n"),
-                               ino, numrecs, mp->m_bmap_dmnr[1]);
-               }
-               key = XFS_BTREE_KEY_ADDR(xfs_bmbt, block, 1);
-               pp = XFS_BTREE_PTR_ADDR(xfs_bmbt, block, 1, mp->m_bmap_dmxr[1]);
-               for (found = -1, i = 0; i < numrecs - 1; i++) {
-                       if (be64_to_cpu(key[i].br_startoff) <= bno && bno < 
-                                       be64_to_cpu(key[i + 1].br_startoff)) {
-                               found = i;
-                               break;
-                       }
-               }
-               if (i == numrecs - 1 && bno >= be64_to_cpu(key[i].br_startoff))
-                       found = i;
-
-               ASSERT(found != -1);
-               fsbno = be64_to_cpu(pp[found]);
-
-               ASSERT(verify_dfsbno(mp, fsbno));
 
-               /*
-                * release current btree block and read in the
-                * next btree block to be traversed
-                */
-               libxfs_putbuf(bp);
-               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
-                                       XFS_FSB_TO_BB(mp, 1), 0);
-               if (!bp) {
-                       do_error(_("cannot read bmap block %llu\n"), fsbno);
-                       return(NULLDFSBNO);
-               }
-               block = XFS_BUF_TO_BMBT_BLOCK(bp);
-               numrecs = be16_to_cpu(block->bb_numrecs);
-       }
-
-       /*
-        * current block must be a leaf block
-        */
-       ASSERT(be16_to_cpu(block->bb_level) == 0);
-       if (numrecs > mp->m_bmap_dmxr[0]) {
-               do_warn(_("# of bmap records in inode %llu greater than "
-                         "maximum (%u, max - %u)\n"),
-                       ino, numrecs, mp->m_bmap_dmxr[0]);
-               libxfs_putbuf(bp);
-               return(NULLDFSBNO);
-       }
-       if (verbose && numrecs < mp->m_bmap_dmnr[0])
-               do_warn(_("- # of bmap records in inode %llu less than minimum "
-                         "(%u, min - %u), continuing...\n"),
-                       ino, numrecs, mp->m_bmap_dmnr[0]);
-
-       rec = XFS_BTREE_REC_ADDR(xfs_bmbt, block, 1);
-       for (i = 0; i < numrecs; i++)  {
-               libxfs_bmbt_disk_get_all(rec + i, &irec);
-               if (irec.br_startoff <= bno &&
-                               bno < irec.br_startoff + irec.br_blockcount) {
-                       final_fsbno = bno - irec.br_startoff +
-                                                       irec.br_startblock;
-                       break;
-               }
-       }
-       libxfs_putbuf(bp);
-
-       if (final_fsbno == NULLDFSBNO)
-               do_warn(_("could not map block %llu\n"), bno);
-
-       return(final_fsbno);
-}
-
-/*
- * this could be smarter.  maybe we should have an open inode
- * routine that would get the inode buffer and return back
- * an inode handle.  I'm betting for the moment that this
- * is used only by the directory and attribute checking code
- * and that the avl tree find and buffer cache search are
- * relatively cheap.  If they're too expensive, we'll just
- * have to fix this and add an inode handle to the da btree
- * cursor.
- *
- * caller is responsible for checking doubly referenced blocks
- * and references to holes
- *
- * NOTE: get_bmapi only used by dirv1 checking code
- */
-xfs_dfsbno_t
-get_bmapi(xfs_mount_t *mp, xfs_dinode_t *dino_p,
-               xfs_ino_t ino_num, xfs_dfiloff_t bno, int whichfork)
-{
-       xfs_dfsbno_t            fsbno;
-
-       switch (XFS_DFORK_FORMAT(dino_p, whichfork)) {
-       case XFS_DINODE_FMT_EXTENTS:
-               fsbno = getfunc_extlist(mp, ino_num, dino_p, bno, whichfork);
-               break;
-       case XFS_DINODE_FMT_BTREE:
-               fsbno = getfunc_btree(mp, ino_num, dino_p, bno, whichfork);
-               break;
-       case XFS_DINODE_FMT_LOCAL:
-               do_error(_("get_bmapi() called for local inode %llu\n"),
-                       ino_num);
-               fsbno = NULLDFSBNO;
-               break;
-       default:
-               /*
-                * shouldn't happen
-                */
-               do_error(_("bad inode format for inode %llu\n"), ino_num);
-               fsbno = NULLDFSBNO;
+       bp = libxfs_readbuf(mp->m_dev, cluster_daddr, cluster_blks,
+                       0, &xfs_inode_buf_ops);
+       if (!bp) {
+               do_warn(_("cannot read inode (%u/%u), disk block %" PRIu64 "\n"),
+                       agno, cluster_agino, cluster_daddr);
+               return NULL;
        }
 
-       return(fsbno);
+       *dipp = xfs_make_iptr(mp, bp, agino - cluster_agino);
+       ASSERT(!xfs_sb_version_hascrc(&mp->m_sb) ||
+                       XFS_AGINO_TO_INO(mp, agno, agino) ==
+                       be64_to_cpu((*dipp)->di_ino));
+       return bp;
 }
 
 /*
@@ -1141,8 +804,7 @@ get_bmapi(xfs_mount_t *mp, xfs_dinode_t *dino_p,
 /*
  * return 1 if inode should be cleared, 0 otherwise
  */
-/* ARGSUSED */
-int
+static int
 process_btinode(
        xfs_mount_t             *mp,
        xfs_agnumber_t          agno,
@@ -1150,33 +812,32 @@ process_btinode(
        xfs_dinode_t            *dip,
        int                     type,
        int                     *dirty,
-       xfs_drfsbno_t           *tot,
-       __uint64_t              *nex,
+       xfs_rfsblock_t          *tot,
+       uint64_t                *nex,
        blkmap_t                **blkmapp,
        int                     whichfork,
        int                     check_dups)
 {
        xfs_bmdr_block_t        *dib;
-       xfs_dfiloff_t           last_key;
-       xfs_dfiloff_t           first_key = 0;
+       xfs_fileoff_t           last_key;
+       xfs_fileoff_t           first_key = 0;
        xfs_ino_t               lino;
        xfs_bmbt_ptr_t          *pp;
        xfs_bmbt_key_t          *pkey;
-       char                    *forkname;
+       char                    *forkname = get_forkname(whichfork);
        int                     i;
        int                     level;
        int                     numrecs;
        bmap_cursor_t           cursor;
+       uint64_t                magic;
 
        dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
        *tot = 0;
        *nex = 0;
 
-       if (whichfork == XFS_DATA_FORK)
-               forkname = _("data");
-       else
-               forkname = _("attr");
+       magic = xfs_sb_version_hascrc(&mp->m_sb) ? XFS_BMAP_CRC_MAGIC
+                                                : XFS_BMAP_MAGIC;
 
        level = be16_to_cpu(dib->bb_level);
        numrecs = be16_to_cpu(dib->bb_numrecs);
@@ -1190,12 +851,14 @@ process_btinode(
                 * to by the pointers in the fork.  For now
                 * though, we just bail (and blow out the inode).
                 */
-               do_warn(_("bad level %d in inode %llu bmap btree root block\n"),
+               do_warn(
+_("bad level %d in inode %" PRIu64 " bmap btree root block\n"),
                        level, XFS_AGINO_TO_INO(mp, agno, ino));
                return(1);
        }
        if (numrecs == 0) {
-               do_warn(_("bad numrecs 0 in inode %llu bmap btree root block\n"),
+               do_warn(
+_("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
                        XFS_AGINO_TO_INO(mp, agno, ino));
                return(1);
        }
@@ -1205,17 +868,17 @@ process_btinode(
        if (XFS_BMDR_SPACE_CALC(numrecs) > XFS_DFORK_SIZE(dip, mp, whichfork)) {
                do_warn(
        _("indicated size of %s btree root (%d bytes) greater than space in "
-         "inode %llu %s fork\n"),
+         "inode %" PRIu64 " %s fork\n"),
                        forkname, XFS_BMDR_SPACE_CALC(numrecs), lino, forkname);
                return(1);
        }
 
        init_bm_cursor(&cursor, level + 1);
 
-       pp = XFS_BTREE_PTR_ADDR(xfs_bmdr, dib, 1, XFS_BTREE_BLOCK_MAXRECS(
-                       XFS_DFORK_SIZE(dip, mp, whichfork), xfs_bmdr, 0));
-       pkey = XFS_BTREE_KEY_ADDR(xfs_bmdr, dib, 1);
-       last_key = NULLDFILOFF;
+       pp = XFS_BMDR_PTR_ADDR(dib, 1,
+               libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0));
+       pkey = XFS_BMDR_KEY_ADDR(dib, 1);
+       last_key = NULLFILEOFF;
 
        for (i = 0; i < numrecs; i++)  {
                /*
@@ -1223,15 +886,17 @@ process_btinode(
                 * btree, we'd do it right here.  For now, if there's a
                 * problem, we'll bail out and presumably clear the inode.
                 */
-               if (!verify_dfsbno(mp, be64_to_cpu(pp[i])))  {
-                       do_warn(_("bad bmap btree ptr 0x%llx in ino %llu\n"),
-                               be64_to_cpu(pp[i]), lino);
+               if (!verify_dfsbno(mp, get_unaligned_be64(&pp[i])))  {
+                       do_warn(
+_("bad bmap btree ptr 0x%" PRIx64 " in ino %" PRIu64 "\n"),
+                               get_unaligned_be64(&pp[i]), lino);
                        return(1);
                }
 
-               if (scan_lbtree(be64_to_cpu(pp[i]), level, scanfunc_bmap, type, 
-                               whichfork, lino, tot, nex, blkmapp, &cursor,
-                               1, check_dups))
+               if (scan_lbtree(get_unaligned_be64(&pp[i]), level, scan_bmapbt,
+                               type, whichfork, lino, tot, nex, blkmapp,
+                               &cursor, 1, check_dups, magic,
+                               &xfs_bmbt_buf_ops))
                        return(1);
                /*
                 * fix key (offset) mismatches between the keys in root
@@ -1240,26 +905,27 @@ process_btinode(
                 * blocks but the parent hasn't been updated
                 */
                if (!check_dups && cursor.level[level-1].first_key !=
-                                       be64_to_cpu(pkey[i].br_startoff))  {
+                                  get_unaligned_be64(&pkey[i].br_startoff)) {
                        if (!no_modify)  {
                                do_warn(
-       _("correcting key in bmbt root (was %llu, now %llu) in inode "
-         "%llu %s fork\n"),
-                                       be64_to_cpu(pkey[i].br_startoff),
-                                       cursor.level[level-1].first_key,
-                                       XFS_AGINO_TO_INO(mp, agno, ino),
-                                       forkname);
+_("correcting key in bmbt root (was %" PRIu64 ", now %" PRIu64") in inode "
+  "%" PRIu64" %s fork\n"),
+                                      get_unaligned_be64(&pkey[i].br_startoff),
+                                      cursor.level[level-1].first_key,
+                                      XFS_AGINO_TO_INO(mp, agno, ino),
+                                      forkname);
                                *dirty = 1;
-                               pkey[i].br_startoff = cpu_to_be64(
-                                       cursor.level[level-1].first_key);
+                               put_unaligned_be64(
+                                       cursor.level[level-1].first_key,
+                                       &pkey[i].br_startoff);
                        } else  {
                                do_warn(
-       _("bad key in bmbt root (is %llu, would reset to %llu) in inode "
-         "%llu %s fork\n"),
-                                       be64_to_cpu(pkey[i].br_startoff),
-                                       cursor.level[level-1].first_key,
-                                       XFS_AGINO_TO_INO(mp, agno, ino),
-                                       forkname);
+_("bad key in bmbt root (is %" PRIu64 ", would reset to %" PRIu64 ") in inode "
+  "%" PRIu64 " %s fork\n"),
+                                      get_unaligned_be64(&pkey[i].br_startoff),
+                                      cursor.level[level-1].first_key,
+                                      XFS_AGINO_TO_INO(mp, agno, ino),
+                                      forkname);
                        }
                }
                /*
@@ -1267,10 +933,10 @@ process_btinode(
                 * inode if the ordering doesn't hold
                 */
                if (check_dups == 0)  {
-                       if (last_key != NULLDFILOFF && last_key >=
+                       if (last_key != NULLFILEOFF && last_key >=
                            cursor.level[level-1].first_key)  {
                                do_warn(
-               _("out of order bmbt root key %llu in inode %llu %s fork\n"),
+       _("out of order bmbt root key %" PRIu64 " in inode %" PRIu64 " %s fork\n"),
                                        first_key,
                                        XFS_AGINO_TO_INO(mp, agno, ino),
                                        forkname);
@@ -1279,17 +945,29 @@ process_btinode(
                        last_key = cursor.level[level-1].first_key;
                }
        }
+       /*
+        * Ideally if all the extents are ok (perhaps after further
+        * checks below?) we'd just move this back into extents format.
+        * But for now clear it, as the kernel will choke on this
+        */
+       if (*nex <= XFS_DFORK_SIZE(dip, mp, whichfork) /
+                       sizeof(xfs_bmbt_rec_t)) {
+               do_warn(
+       _("extent count for ino %" PRIu64 " %s fork too low (%" PRIu64 ") for file format\n"),
+                               lino, forkname, *nex);
+               return(1);
+       }
        /*
         * Check that the last child block's forward sibling pointer
         * is NULL.
         */
        if (check_dups == 0 &&
-               cursor.level[0].right_fsbno != NULLDFSBNO)  {
+               cursor.level[0].right_fsbno != NULLFSBLOCK)  {
                do_warn(
-       _("bad fwd (right) sibling pointer (saw %llu should be NULLDFSBNO)\n"),
+       _("bad fwd (right) sibling pointer (saw %" PRIu64 " should be NULLFSBLOCK)\n"),
                        cursor.level[0].right_fsbno);
                do_warn(
-       _("\tin inode %u (%s fork) bmap btree block %llu\n"),
+       _("\tin inode %" PRIu64 " (%s fork) bmap btree block %" PRIu64 "\n"),
                        XFS_AGINO_TO_INO(mp, agno, ino), forkname,
                        cursor.level[0].fsbno);
                return(1);
@@ -1301,8 +979,7 @@ process_btinode(
 /*
  * return 1 if inode should be cleared, 0 otherwise
  */
-/* ARGSUSED */
-int
+static int
 process_exinode(
        xfs_mount_t             *mp,
        xfs_agnumber_t          agno,
@@ -1310,33 +987,48 @@ process_exinode(
        xfs_dinode_t            *dip,
        int                     type,
        int                     *dirty,
-       xfs_drfsbno_t           *tot,
-       __uint64_t              *nex,
+       xfs_rfsblock_t          *tot,
+       uint64_t                *nex,
        blkmap_t                **blkmapp,
        int                     whichfork,
        int                     check_dups)
 {
        xfs_ino_t               lino;
        xfs_bmbt_rec_t          *rp;
-       xfs_dfiloff_t           first_key;
-       xfs_dfiloff_t           last_key;
+       xfs_fileoff_t           first_key;
+       xfs_fileoff_t           last_key;
+       int32_t                 numrecs;
+       int                     ret;
 
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
        rp = (xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, whichfork);
        *tot = 0;
-       *nex = XFS_DFORK_NEXTENTS(dip, whichfork);
+       numrecs = XFS_DFORK_NEXTENTS(dip, whichfork);
+
+       /*
+        * We've already decided on the maximum number of extents on the inode,
+        * and numrecs may be corrupt. Hence make sure we only allow numrecs to
+        * be in the range of valid on-disk numbers, which is:
+        *      0 < numrecs < 2^31 - 1
+        */
+       if (numrecs < 0)
+               numrecs = *nex;
+
        /*
         * XXX - if we were going to fix up the btree record,
         * we'd do it right here.  For now, if there's a problem,
         * we'll bail out and presumably clear the inode.
         */
        if (check_dups == 0)
-               return(process_bmbt_reclist(mp, rp, *nex, type, lino,
+               ret = process_bmbt_reclist(mp, rp, &numrecs, type, lino,
                                        tot, blkmapp, &first_key, &last_key,
-                                       whichfork));
+                                       whichfork);
        else
-               return(scan_bmbt_reclist(mp, rp, *nex, type, lino, tot,
-                                       whichfork));
+               ret = scan_bmbt_reclist(mp, rp, &numrecs, type, lino, tot,
+                                       whichfork);
+
+       *nex = numrecs;
+       return ret;
 }
 
 /*
@@ -1354,25 +1046,25 @@ process_lclinode(
        xfs_ino_t               lino;
 
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
-       if (whichfork == XFS_DATA_FORK && be64_to_cpu(dip->di_core.di_size) >
+       if (whichfork == XFS_DATA_FORK && be64_to_cpu(dip->di_size) >
                                                XFS_DFORK_DSIZE(dip, mp)) {
                do_warn(
-       _("local inode %llu data fork is too large (size = %lld, max = %d)\n"),
-                       lino, be64_to_cpu(dip->di_core.di_size),
+       _("local inode %" PRIu64 " data fork is too large (size = %lld, max = %d)\n"),
+                      lino, (unsigned long long) be64_to_cpu(dip->di_size),
                        XFS_DFORK_DSIZE(dip, mp));
                return(1);
        } else if (whichfork == XFS_ATTR_FORK) {
                asf = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
                if (be16_to_cpu(asf->hdr.totsize) > XFS_DFORK_ASIZE(dip, mp)) {
                        do_warn(
-       _("local inode %llu attr fork too large (size %d, max = %d)\n"),
+       _("local inode %" PRIu64 " attr fork too large (size %d, max = %d)\n"),
                                lino, be16_to_cpu(asf->hdr.totsize),
                                XFS_DFORK_ASIZE(dip, mp));
                        return(1);
                }
                if (be16_to_cpu(asf->hdr.totsize) < sizeof(xfs_attr_sf_hdr_t)) {
                        do_warn(
-       _("local inode %llu attr too small (size = %d, min size = %d)\n"),
+       _("local inode %" PRIu64 " attr too small (size = %d, min size = %zd)\n"),
                                lino, be16_to_cpu(asf->hdr.totsize),
                                sizeof(xfs_attr_sf_hdr_t));
                        return(1);
@@ -1382,33 +1074,35 @@ process_lclinode(
        return(0);
 }
 
-int
+static int
 process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
 {
-       xfs_dfiloff_t           expected_offset;
+       xfs_fileoff_t           expected_offset;
        xfs_bmbt_rec_t          *rp;
        xfs_bmbt_irec_t         irec;
        int                     numrecs;
        int                     i;
        int                     max_blocks;
 
-       if (be64_to_cpu(dino->di_core.di_size) <= XFS_DFORK_DSIZE(dino, mp)) {
-               if (dino->di_core.di_format == XFS_DINODE_FMT_LOCAL)  
+       if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp)) {
+               if (dino->di_format == XFS_DINODE_FMT_LOCAL)
                        return 0;
-               do_warn(_("mismatch between format (%d) and size (%lld) in "
-                       "symlink ino %llu\n"), dino->di_core.di_format, 
-                       be64_to_cpu(dino->di_core.di_size), lino);
+               do_warn(
+_("mismatch between format (%d) and size (%" PRId64 ") in symlink ino %" PRIu64 "\n"),
+                       dino->di_format,
+                       (int64_t)be64_to_cpu(dino->di_size), lino);
                return 1;
        }
-       if (dino->di_core.di_format == XFS_DINODE_FMT_LOCAL) {
-               do_warn(_("mismatch between format (%d) and size (%lld) in "
-                       "symlink inode %llu\n"), dino->di_core.di_format,
-                       be64_to_cpu(dino->di_core.di_size), lino);
+       if (dino->di_format == XFS_DINODE_FMT_LOCAL) {
+               do_warn(
+_("mismatch between format (%d) and size (%" PRId64 ") in symlink inode %" PRIu64 "\n"),
+                       dino->di_format,
+                       (int64_t)be64_to_cpu(dino->di_size), lino);
                return 1;
        }
 
        rp = (xfs_bmbt_rec_t *)XFS_DFORK_DPTR(dino);
-       numrecs = be32_to_cpu(dino->di_core.di_nextents);
+       numrecs = be32_to_cpu(dino->di_nextents);
 
        /*
         * the max # of extents in a symlink inode is equal to the
@@ -1416,7 +1110,7 @@ process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
         */
        if (numrecs > max_symlink_blocks)  {
                do_warn(
-               _("bad number of extents (%d) in symlink %llu data fork\n"),
+_("bad number of extents (%d) in symlink %" PRIu64 " data fork\n"),
                        numrecs, lino);
                return(1);
        }
@@ -1425,17 +1119,16 @@ process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
        expected_offset = 0;
 
        for (i = 0; i < numrecs; i++)  {
-               libxfs_bmbt_disk_get_all(rp + i, &irec);
-
+               libxfs_bmbt_disk_get_all((rp +i), &irec);
                if (irec.br_startoff != expected_offset)  {
                        do_warn(
-               _("bad extent #%d offset (%llu) in symlink %llu data fork\n"),
+_("bad extent #%d offset (%" PRIu64 ") in symlink %" PRIu64 " data fork\n"),
                                i, irec.br_startoff, lino);
                        return(1);
                }
                if (irec.br_blockcount == 0 || irec.br_blockcount > max_blocks) {
                        do_warn(
-               _("bad extent #%d count (%llu) in symlink %llu data fork\n"),
+_("bad extent #%d count (%" PRIu64 ") in symlink %" PRIu64 " data fork\n"),
                                i, irec.br_blockcount, lino);
                        return(1);
                }
@@ -1451,12 +1144,12 @@ process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
  * takes a name and length and returns 1 if the name contains
  * a \0, returns 0 otherwise
  */
-int
+static int
 null_check(char *name, int length)
 {
        int i;
 
-       ASSERT(length < MAXPATHLEN);
+       ASSERT(length < XFS_SYMLINK_MAXLEN);
 
        for (i = 0; i < length; i++, name++)  {
                if (*name == '\0')
@@ -1466,23 +1159,229 @@ null_check(char *name, int length)
        return(0);
 }
 
+/*
+ * This does /not/ do quotacheck, it validates the basic quota
+ * inode metadata, checksums, etc.
+ */
+#define uuid_equal(s,d) (platform_uuid_compare((s),(d)) == 0)
+static int
+process_quota_inode(
+       struct xfs_mount        *mp,
+       xfs_ino_t               lino,
+       struct xfs_dinode       *dino,
+       uint                    ino_type,
+       struct blkmap           *blkmap)
+{
+       xfs_fsblock_t           fsbno;
+       struct xfs_buf          *bp;
+       xfs_filblks_t           dqchunklen;
+       uint                    dqperchunk;
+       int                     quota_type;
+       char                    *quota_string;
+       xfs_dqid_t              dqid;
+       xfs_fileoff_t           qbno;
+       int                     i;
+       int                     t = 0;
+
+       switch (ino_type) {
+               case XR_INO_UQUOTA:
+                       quota_type = XFS_DQ_USER;
+                       quota_string = _("User quota");
+                       break;
+               case XR_INO_GQUOTA:
+                       quota_type = XFS_DQ_GROUP;
+                       quota_string = _("Group quota");
+                       break;
+               case XR_INO_PQUOTA:
+                       quota_type = XFS_DQ_PROJ;
+                       quota_string = _("Project quota");
+                       break;
+               default:
+                       ASSERT(0);
+       }
+
+       dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB);
+       dqperchunk = libxfs_calc_dquots_per_chunk(dqchunklen);
+       dqid = 0;
+       qbno = NULLFILEOFF;
+
+       while ((qbno = blkmap_next_off(blkmap, qbno, &t)) != NULLFILEOFF) {
+               xfs_dqblk_t     *dqb;
+               int             writebuf = 0;
+
+               fsbno = blkmap_get(blkmap, qbno);
+               dqid = (xfs_dqid_t)qbno * dqperchunk;
+
+               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
+                                   dqchunklen, 0, &xfs_dquot_buf_ops);
+               if (!bp) {
+                       do_warn(
+_("cannot read inode %" PRIu64 ", file block %" PRIu64 ", disk block %" PRIu64 "\n"),
+                               lino, qbno, fsbno);
+                       return 1;
+               }
+
+               dqb = bp->b_addr;
+               for (i = 0; i < dqperchunk; i++, dqid++, dqb++) {
+                       int             bad_dqb = 0;
+
+                       /* We only print the first problem we find */
+                       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+                               if (!libxfs_verify_cksum((char *)dqb,
+                                                       sizeof(*dqb),
+                                                       XFS_DQUOT_CRC_OFF)) {
+                                       do_warn(_("%s: bad CRC for id %u. "),
+                                                       quota_string, dqid);
+                                       bad_dqb = 1;
+                                       goto bad;
+                               }
+
+                               if (!uuid_equal(&dqb->dd_uuid,
+                                               &mp->m_sb.sb_meta_uuid)) {
+                                       do_warn(_("%s: bad UUID for id %u. "),
+                                                       quota_string, dqid);
+                                       bad_dqb = 1;
+                                       goto bad;
+                               }
+                       }
+                       if (libxfs_dquot_verify(mp, &dqb->dd_diskdq, dqid,
+                                               quota_type) != NULL) {
+                               do_warn(_("%s: Corrupt quota for id %u. "),
+                                               quota_string, dqid);
+                               bad_dqb = 1;
+                       }
+
+bad:
+                       if (bad_dqb) {
+                               if (no_modify)
+                                       do_warn(_("Would correct.\n"));
+                               else {
+                                       do_warn(_("Corrected.\n"));
+                                       libxfs_dqblk_repair(mp, dqb,
+                                                           dqid, quota_type);
+                                       writebuf = 1;
+                               }
+                       }
+               }
+
+               if (writebuf && !no_modify)
+                       libxfs_writebuf(bp, 0);
+               else
+                       libxfs_putbuf(bp);
+       }
+       return 0;
+}
+
+static int
+process_symlink_remote(
+       struct xfs_mount        *mp,
+       xfs_ino_t               lino,
+       struct xfs_dinode       *dino,
+       struct blkmap           *blkmap,
+       char                    *dst)
+{
+       xfs_fsblock_t           fsbno;
+       struct xfs_buf          *bp;
+       char                    *src;
+       int                     pathlen;
+       int                     offset;
+       int                     i;
+
+       offset = 0;
+       pathlen = be64_to_cpu(dino->di_size);
+       i = 0;
+
+       while (pathlen > 0) {
+               int     blk_cnt = 1;
+               int     byte_cnt;
+               int     badcrc = 0;
+
+               fsbno = blkmap_get(blkmap, i);
+               if (fsbno == NULLFSBLOCK) {
+                       do_warn(
+_("cannot read inode %" PRIu64 ", file block %d, NULL disk block\n"),
+                               lino, i);
+                       return 1;
+               }
+
+               /*
+                * There's a symlink header for each contiguous extent. If
+                * there are contiguous blocks, read them in one go.
+                */
+               while (blk_cnt <= max_symlink_blocks) {
+                       if (blkmap_get(blkmap, i + 1) != fsbno + 1)
+                               break;
+                       blk_cnt++;
+                       i++;
+               }
+
+               byte_cnt = XFS_FSB_TO_B(mp, blk_cnt);
+
+               bp = libxfs_readbuf(mp->m_dev, XFS_FSB_TO_DADDR(mp, fsbno),
+                                   BTOBB(byte_cnt), 0, &xfs_symlink_buf_ops);
+               if (!bp) {
+                       do_warn(
+_("cannot read inode %" PRIu64 ", file block %d, disk block %" PRIu64 "\n"),
+                               lino, i, fsbno);
+                       return 1;
+               }
+               if (bp->b_error == -EFSCORRUPTED) {
+                       do_warn(
+_("Corrupt symlink remote block %" PRIu64 ", inode %" PRIu64 ".\n"),
+                               fsbno, lino);
+                       libxfs_putbuf(bp);
+                       return 1;
+               }
+               if (bp->b_error == -EFSBADCRC) {
+                       do_warn(
+_("Bad symlink buffer CRC, block %" PRIu64 ", inode %" PRIu64 ".\n"
+  "Correcting CRC, but symlink may be bad.\n"), fsbno, lino);
+                       badcrc = 1;
+               }
+
+               byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
+               byte_cnt = min(pathlen, byte_cnt);
+
+               src = bp->b_addr;
+               if (xfs_sb_version_hascrc(&mp->m_sb)) {
+                       if (!libxfs_symlink_hdr_ok(lino, offset,
+                                                  byte_cnt, bp)) {
+                               do_warn(
+_("bad symlink header ino %" PRIu64 ", file block %d, disk block %" PRIu64 "\n"),
+                                       lino, i, fsbno);
+                               libxfs_putbuf(bp);
+                               return 1;
+                       }
+                       src += sizeof(struct xfs_dsymlink_hdr);
+               }
+
+               memmove(dst + offset, src, byte_cnt);
+
+               pathlen -= byte_cnt;
+               offset += byte_cnt;
+               i++;
+
+               if (badcrc && !no_modify)
+                       libxfs_writebuf(bp, 0);
+               else
+                       libxfs_putbuf(bp);
+       }
+       return 0;
+}
+
 /*
  * like usual, returns 0 if everything's ok and 1 if something's
  * bogus
  */
-int
+static int
 process_symlink(
        xfs_mount_t     *mp,
        xfs_ino_t       lino,
        xfs_dinode_t    *dino,
        blkmap_t        *blkmap)
 {
-       xfs_dfsbno_t            fsbno;
-       xfs_dinode_core_t       *dinoc = &dino->di_core;
-       xfs_buf_t               *bp = NULL;
-       char                    *symlink, *cptr, *buf_data;
-       int                     i, size, amountdone;
-       char                    data[MAXPATHLEN];
+       char                    *symlink;
+       char                    data[XFS_SYMLINK_MAXLEN];
 
        /*
         * check size against kernel symlink limits.  we know
@@ -1490,92 +1389,49 @@ process_symlink(
         * the inode is structurally ok so we don't have to check
         * for that
         */
-       if (be64_to_cpu(dinoc->di_size) >= MAXPATHLEN)  {
-               do_warn(_("symlink in inode %llu too long (%lld chars)\n"),
-                       lino, be64_to_cpu(dinoc->di_size));
+       if (be64_to_cpu(dino->di_size) >= XFS_SYMLINK_MAXLEN)  {
+              do_warn(_("symlink in inode %" PRIu64 " too long (%llu chars)\n"),
+                      lino, (unsigned long long) be64_to_cpu(dino->di_size));
                return(1);
        }
 
+       if (be64_to_cpu(dino->di_size) == 0) {
+               do_warn(_("zero size symlink in inode %" PRIu64 "\n"), lino);
+               return 1;
+       }
+
        /*
         * have to check symlink component by component.
         * get symlink contents into data area
         */
        symlink = &data[0];
-       if (be64_to_cpu(dinoc->di_size) <= XFS_DFORK_DSIZE(dino, mp))  {
+       if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp))  {
                /*
                 * local symlink, just copy the symlink out of the
                 * inode into the data area
                 */
-               memmove(symlink, XFS_DFORK_DPTR(dino), 
-                                               be64_to_cpu(dinoc->di_size));
+               memmove(symlink, XFS_DFORK_DPTR(dino),
+                                               be64_to_cpu(dino->di_size));
        } else {
-               /*
-                * stored in a meta-data file, have to bmap one block
-                * at a time and copy the symlink into the data area
-                */
-               i = size = amountdone = 0;
-               cptr = symlink;
-
-               while (amountdone < be64_to_cpu(dinoc->di_size)) {
-                       fsbno = blkmap_get(blkmap, i);
-                       if (fsbno != NULLDFSBNO)
-                               bp = libxfs_readbuf(mp->m_dev,
-                                               XFS_FSB_TO_DADDR(mp, fsbno),
-                                               XFS_FSB_TO_BB(mp, 1), 0);
-                       if (!bp || fsbno == NULLDFSBNO) {
-                               do_warn(
-               _("cannot read inode %llu, file block %d, disk block %llu\n"),
-                                       lino, i, fsbno);
-                               return(1);
-                       }
+               int error;
 
-                       buf_data = (char *)XFS_BUF_PTR(bp);
-                       size = MIN(be64_to_cpu(dinoc->di_size) - amountdone, 
-                                               XFS_FSB_TO_BB(mp, 1) * BBSIZE);
-                       memmove(cptr, buf_data, size);
-                       cptr += size;
-                       amountdone += size;
-                       i++;
-                       libxfs_putbuf(bp);
-               }
+               error = process_symlink_remote(mp, lino, dino, blkmap, symlink);
+               if (error)
+                       return error;
        }
-       data[be64_to_cpu(dinoc->di_size)] = '\0';
+
+       data[be64_to_cpu(dino->di_size)] = '\0';
 
        /*
         * check for nulls
         */
-       if (null_check(symlink, be64_to_cpu(dinoc->di_size)))  {
+       if (null_check(symlink, be64_to_cpu(dino->di_size)))  {
                do_warn(
-               _("found illegal null character in symlink inode %llu\n"),
+_("found illegal null character in symlink inode %" PRIu64 "\n"),
                        lino);
                return(1);
        }
 
-       /*
-        * check for any component being too long
-        */
-       if (be64_to_cpu(dinoc->di_size) >= MAXNAMELEN)  {
-               cptr = strchr(symlink, '/');
-
-               while (cptr != NULL)  {
-                       if (cptr - symlink >= MAXNAMELEN)  {
-                               do_warn(
-                       _("component of symlink in inode %llu too long\n"),
-                                       lino);
-                               return(1);
-                       }
-                       symlink = cptr + 1;
-                       cptr = strchr(symlink, '/');
-               }
-
-               if (strlen(symlink) >= MAXNAMELEN)  {
-                       do_warn(
-                       _("component of symlink in inode %llu too long\n"),
-                               lino);
-                       return(1);
-               }
-       }
-
        return(0);
 }
 
@@ -1595,34 +1451,42 @@ process_misc_ino_types(xfs_mount_t      *mp,
         * probably require a superblock version rev, sigh).
         */
        if (type == XR_INO_MOUNTPOINT)  {
-               do_warn(_("inode %llu has bad inode type (IFMNT)\n"), lino);
+               do_warn(
+_("inode %" PRIu64 " has bad inode type (IFMNT)\n"), lino);
                return(1);
        }
 
        /*
         * must also have a zero size
         */
-       if (be64_to_cpu(dino->di_core.di_size) != 0)  {
+       if (be64_to_cpu(dino->di_size) != 0)  {
                switch (type)  {
                case XR_INO_CHRDEV:
-                       do_warn(_("size of character device inode %llu != 0 "
-                                 "(%lld bytes)\n"), lino,
-                               be64_to_cpu(dino->di_core.di_size));
+                       do_warn(
+_("size of character device inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_BLKDEV:
-                       do_warn(_("size of block device inode %llu != 0 "
-                                 "(%lld bytes)\n"), lino,
-                               be64_to_cpu(dino->di_core.di_size));
+                       do_warn(
+_("size of block device inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_SOCK:
-                       do_warn(_("size of socket inode %llu != 0 "
-                                 "(%lld bytes)\n"), lino,
-                               be64_to_cpu(dino->di_core.di_size));
+                       do_warn(
+_("size of socket inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_FIFO:
-                       do_warn(_("size of fifo inode %llu != 0 "
-                                 "(%lld bytes)\n"), lino,
-                               be64_to_cpu(dino->di_core.di_size));
+                       do_warn(
+_("size of fifo inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
+                               (int64_t)be64_to_cpu(dino->di_size));
+                       break;
+               case XR_INO_UQUOTA:
+               case XR_INO_GQUOTA:
+               case XR_INO_PQUOTA:
+                       do_warn(
+_("size of quota inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                default:
                        do_warn(_("Internal error - process_misc_ino_types, "
@@ -1637,11 +1501,11 @@ process_misc_ino_types(xfs_mount_t      *mp,
 }
 
 static int
-process_misc_ino_types_blocks(xfs_drfsbno_t totblocks, xfs_ino_t lino, int type)
+process_misc_ino_types_blocks(xfs_rfsblock_t totblocks, xfs_ino_t lino, int type)
 {
        /*
         * you can not enforce all misc types have zero data fork blocks
-        * by checking dino->di_core.di_nblocks because atotblocks (attribute
+        * by checking dino->di_nblocks because atotblocks (attribute
         * blocks) are part of nblocks. We must check this later when atotblocks
         * has been calculated or by doing a simple check that anExtents == 0.
         * We must also guarantee that totblocks is 0. Thus nblocks checking
@@ -1652,22 +1516,22 @@ process_misc_ino_types_blocks(xfs_drfsbno_t totblocks, xfs_ino_t lino, int type)
                switch (type)  {
                case XR_INO_CHRDEV:
                        do_warn(
-               _("size of character device inode %llu != 0 (%llu blocks)\n"),
+_("size of character device inode %" PRIu64 " != 0 (%" PRIu64 " blocks)\n"),
                                lino, totblocks);
                        break;
                case XR_INO_BLKDEV:
                        do_warn(
-               _("size of block device inode %llu != 0 (%llu blocks)\n"),
+_("size of block device inode %" PRIu64 " != 0 (%" PRIu64 " blocks)\n"),
                                lino, totblocks);
                        break;
                case XR_INO_SOCK:
                        do_warn(
-               _("size of socket inode %llu != 0 (%llu blocks)\n"),
+_("size of socket inode %" PRIu64 " != 0 (%" PRIu64 " blocks)\n"),
                                lino, totblocks);
                        break;
                case XR_INO_FIFO:
                        do_warn(
-               _("size of fifo inode %llu != 0 (%llu blocks)\n"),
+_("size of fifo inode %" PRIu64 " != 0 (%" PRIu64 " blocks)\n"),
                                lino, totblocks);
                        break;
                default:
@@ -1680,28 +1544,28 @@ process_misc_ino_types_blocks(xfs_drfsbno_t totblocks, xfs_ino_t lino, int type)
 
 static inline int
 dinode_fmt(
-       xfs_dinode_core_t *dinoc)
+       xfs_dinode_t *dino)
 {
-       return be16_to_cpu(dinoc->di_mode) & S_IFMT;
+       return be16_to_cpu(dino->di_mode) & S_IFMT;
 }
 
 static inline void
 change_dinode_fmt(
-       xfs_dinode_core_t *dinoc,
+       xfs_dinode_t    *dino,
        int             new_fmt)
 {
-       int             mode = be16_to_cpu(dinoc->di_mode);
+       int             mode = be16_to_cpu(dino->di_mode);
 
        ASSERT((new_fmt & ~S_IFMT) == 0);
 
        mode &= ~S_IFMT;
        mode |= new_fmt;
-       dinoc->di_mode = cpu_to_be16(mode);
+       dino->di_mode = cpu_to_be16(mode);
 }
 
 static int
 check_dinode_mode_format(
-       xfs_dinode_core_t *dinoc)
+       xfs_dinode_t *dinoc)
 {
        if (dinoc->di_format >= XFS_DINODE_FMT_UUID)
                return -1;      /* FMT_UUID is not used */
@@ -1738,14 +1602,14 @@ check_dinode_mode_format(
 static int
 process_check_sb_inodes(
        xfs_mount_t     *mp,
-       xfs_dinode_core_t *dinoc,
+       xfs_dinode_t    *dinoc,
        xfs_ino_t       lino,
        int             *type,
        int             *dirty)
 {
        if (lino == mp->m_sb.sb_rootino) {
-               if (*type != XR_INO_DIR)  {
-                       do_warn(_("root inode %llu has bad type 0x%x\n"),
+               if (*type != XR_INO_DIR)  {
+                       do_warn(_("root inode %" PRIu64 " has bad type 0x%x\n"),
                                lino, dinode_fmt(dinoc));
                        *type = XR_INO_DIR;
                        if (!no_modify)  {
@@ -1758,8 +1622,8 @@ process_check_sb_inodes(
                return 0;
        }
        if (lino == mp->m_sb.sb_uquotino)  {
-               if (*type != XR_INO_DATA)  {
-                       do_warn(_("user quota inode %llu has bad type 0x%x\n"),
+               if (*type != XR_INO_UQUOTA)  {
+                       do_warn(_("user quota inode %" PRIu64 " has bad type 0x%x\n"),
                                lino, dinode_fmt(dinoc));
                        mp->m_sb.sb_uquotino = NULLFSINO;
                        return 1;
@@ -1767,17 +1631,27 @@ process_check_sb_inodes(
                return 0;
        }
        if (lino == mp->m_sb.sb_gquotino)  {
-               if (*type != XR_INO_DATA)  {
-                       do_warn(_("group quota inode %llu has bad type 0x%x\n"),
+               if (*type != XR_INO_GQUOTA)  {
+                       do_warn(_("group quota inode %" PRIu64 " has bad type 0x%x\n"),
                                lino, dinode_fmt(dinoc));
                        mp->m_sb.sb_gquotino = NULLFSINO;
                        return 1;
                }
                return 0;
        }
+       if (lino == mp->m_sb.sb_pquotino)  {
+               if (*type != XR_INO_PQUOTA)  {
+                       do_warn(_("project quota inode %" PRIu64 " has bad type 0x%x\n"),
+                               lino, dinode_fmt(dinoc));
+                       mp->m_sb.sb_pquotino = NULLFSINO;
+                       return 1;
+               }
+               return 0;
+       }
        if (lino == mp->m_sb.sb_rsumino) {
                if (*type != XR_INO_RTSUM) {
-                       do_warn(_("realtime summary inode %llu has bad type 0x%x, "),
+                       do_warn(
+_("realtime summary inode %" PRIu64 " has bad type 0x%x, "),
                                lino, dinode_fmt(dinoc));
                        if (!no_modify)  {
                                do_warn(_("resetting to regular file\n"));
@@ -1788,7 +1662,8 @@ process_check_sb_inodes(
                        }
                }
                if (mp->m_sb.sb_rblocks == 0 && dinoc->di_nextents != 0)  {
-                       do_warn(_("bad # of extents (%u) for realtime summary inode %llu\n"),
+                       do_warn(
+_("bad # of extents (%u) for realtime summary inode %" PRIu64 "\n"),
                                be32_to_cpu(dinoc->di_nextents), lino);
                        return 1;
                }
@@ -1796,7 +1671,8 @@ process_check_sb_inodes(
        }
        if (lino == mp->m_sb.sb_rbmino) {
                if (*type != XR_INO_RTBITMAP) {
-                       do_warn(_("realtime bitmap inode %llu has bad type 0x%x, "),
+                       do_warn(
+_("realtime bitmap inode %" PRIu64 " has bad type 0x%x, "),
                                lino, dinode_fmt(dinoc));
                        if (!no_modify)  {
                                do_warn(_("resetting to regular file\n"));
@@ -1807,7 +1683,8 @@ process_check_sb_inodes(
                        }
                }
                if (mp->m_sb.sb_rblocks == 0 && dinoc->di_nextents != 0)  {
-                       do_warn(_("bad # of extents (%u) for realtime bitmap inode %llu\n"),
+                       do_warn(
+_("bad # of extents (%u) for realtime bitmap inode %" PRIu64 "\n"),
                                be32_to_cpu(dinoc->di_nextents), lino);
                        return 1;
                }
@@ -1834,21 +1711,21 @@ process_check_inode_sizes(
        xfs_ino_t       lino,
        int             type)
 {
-       xfs_dinode_core_t *dinoc = &dino->di_core;
-       xfs_fsize_t     size = be64_to_cpu(dinoc->di_size);
+       xfs_fsize_t     size = be64_to_cpu(dino->di_size);
 
        switch (type)  {
 
        case XR_INO_DIR:
                if (size <= XFS_DFORK_DSIZE(dino, mp) &&
-                               dinoc->di_format != XFS_DINODE_FMT_LOCAL) {
-                       do_warn(_("mismatch between format (%d) and size "
-                               "(%lld) in directory ino %llu\n"),
-                               dinoc->di_format, size, lino);
+                               dino->di_format != XFS_DINODE_FMT_LOCAL) {
+                       do_warn(
+_("mismatch between format (%d) and size (%" PRId64 ") in directory ino %" PRIu64 "\n"),
+                               dino->di_format, size, lino);
                        return 1;
                }
                if (size > XFS_DIR2_LEAF_OFFSET) {
-                       do_warn(_("directory inode %llu has bad size %lld\n"),
+                       do_warn(
+_("directory inode %" PRIu64 " has bad size %" PRId64 "\n"),
                                lino, size);
                        return 1;
                }
@@ -1856,7 +1733,7 @@ process_check_inode_sizes(
 
        case XR_INO_SYMLINK:
                if (process_symlink_extlist(mp, lino, dino))  {
-                       do_warn(_("bad data fork in symlink %llu\n"), lino);
+                       do_warn(_("bad data fork in symlink %" PRIu64 "\n"), lino);
                        return 1;
                }
                break;
@@ -1870,24 +1747,33 @@ process_check_inode_sizes(
                        return 1;
                break;
 
+       case XR_INO_UQUOTA:
+       case XR_INO_GQUOTA:
+       case XR_INO_PQUOTA:
+               /* Quota inodes have same restrictions as above types */
+               if (process_misc_ino_types(mp, dino, lino, type))
+                       return 1;
+               break;
+
        case XR_INO_RTDATA:
                /*
                 * if we have no realtime blocks, any inode claiming
                 * to be a real-time file is bogus
                 */
                if (mp->m_sb.sb_rblocks == 0)  {
-                       do_warn(_("found inode %llu claiming to be a "
-                               "real-time file\n"), lino);
+                       do_warn(
+_("found inode %" PRIu64 " claiming to be a real-time file\n"), lino);
                        return 1;
                }
                break;
 
        case XR_INO_RTBITMAP:
-               if (size != (__int64_t)mp->m_sb.sb_rbmblocks *
+               if (size != (int64_t)mp->m_sb.sb_rbmblocks *
                                        mp->m_sb.sb_blocksize) {
-                       do_warn(_("realtime bitmap inode %llu has bad size "
-                               "%lld (should be %lld)\n"),
-                               lino, size, (__int64_t) mp->m_sb.sb_rbmblocks *
+                       do_warn(
+_("realtime bitmap inode %" PRIu64 " has bad size %" PRId64 " (should be %" PRIu64 ")\n"),
+                               lino, size,
+                               (int64_t) mp->m_sb.sb_rbmblocks *
                                        mp->m_sb.sb_blocksize);
                        return 1;
                }
@@ -1895,8 +1781,8 @@ process_check_inode_sizes(
 
        case XR_INO_RTSUM:
                if (size != mp->m_rsumsize)  {
-                       do_warn(_("realtime summary inode %llu has bad size "
-                               "%lld (should be %d)\n"),
+                       do_warn(
+_("realtime summary inode %" PRIu64 " has bad size %" PRId64 " (should be %d)\n"),
                                lino, size, mp->m_rsumsize);
                        return 1;
                }
@@ -1914,17 +1800,18 @@ process_check_inode_sizes(
 static int
 process_check_inode_forkoff(
        xfs_mount_t     *mp,
-       xfs_dinode_core_t *dinoc,
+       xfs_dinode_t    *dino,
        xfs_ino_t       lino)
 {
-       if (dinoc->di_forkoff == 0)
+       if (dino->di_forkoff == 0)
                return 0;
 
-       switch (dinoc->di_format)  {
+       switch (dino->di_format)  {
        case XFS_DINODE_FMT_DEV:
-               if (dinoc->di_forkoff != (roundup(sizeof(xfs_dev_t), 8) >> 3)) {
-                       do_warn(_("bad attr fork offset %d in dev inode %llu, "
-                               "should be %d\n"), dinoc->di_forkoff, lino,
+               if (dino->di_forkoff != (roundup(sizeof(xfs_dev_t), 8) >> 3)) {
+                       do_warn(
+_("bad attr fork offset %d in dev inode %" PRIu64 ", should be %d\n"),
+                               dino->di_forkoff, lino,
                                (int)(roundup(sizeof(xfs_dev_t), 8) >> 3));
                        return 1;
                }
@@ -1932,15 +1819,17 @@ process_check_inode_forkoff(
        case XFS_DINODE_FMT_LOCAL:      /* fall through ... */
        case XFS_DINODE_FMT_EXTENTS:    /* fall through ... */
        case XFS_DINODE_FMT_BTREE:
-               if (dinoc->di_forkoff >= (XFS_LITINO(mp) >> 3)) {
-                       do_warn(_("bad attr fork offset %d in inode %llu, "
-                               "max=%d\n"), dinoc->di_forkoff, lino,
-                               XFS_LITINO(mp) >> 3);
+               if (dino->di_forkoff >=
+                               (XFS_LITINO(mp, dino->di_version) >> 3)) {
+                       do_warn(
+_("bad attr fork offset %d in inode %" PRIu64 ", max=%d\n"),
+                               dino->di_forkoff, lino,
+                               XFS_LITINO(mp, dino->di_version) >> 3);
                        return 1;
                }
                break;
        default:
-               do_error(_("unexpected inode format %d\n"), dinoc->di_format);
+               do_error(_("unexpected inode format %d\n"), dino->di_format);
                break;
        }
        return 0;
@@ -1951,64 +1840,84 @@ process_check_inode_forkoff(
  */
 static int
 process_inode_blocks_and_extents(
-       xfs_dinode_core_t *dinoc,
-       xfs_drfsbno_t   nblocks,
-       __uint64_t      nextents,
-       __uint64_t      anextents,
+       xfs_dinode_t    *dino,
+       xfs_rfsblock_t  nblocks,
+       uint64_t        nextents,
+       uint64_t        anextents,
        xfs_ino_t       lino,
        int             *dirty)
 {
-       if (nblocks != be64_to_cpu(dinoc->di_nblocks))  {
+       if (nblocks != be64_to_cpu(dino->di_nblocks))  {
                if (!no_modify)  {
-                       do_warn(_("correcting nblocks for inode %llu, "
-                               "was %llu - counted %llu\n"), lino,
-                               be64_to_cpu(dinoc->di_nblocks), nblocks);
-                       dinoc->di_nblocks = cpu_to_be64(nblocks);
+                       do_warn(
+_("correcting nblocks for inode %" PRIu64 ", was %llu - counted %" PRIu64 "\n"), lino,
+                              (unsigned long long) be64_to_cpu(dino->di_nblocks),
+                              nblocks);
+                       dino->di_nblocks = cpu_to_be64(nblocks);
                        *dirty = 1;
                } else  {
-                       do_warn(_("bad nblocks %llu for inode %llu, "
-                               "would reset to %llu\n"),
-                               be64_to_cpu(dinoc->di_nblocks), lino, nblocks);
+                       do_warn(
+_("bad nblocks %llu for inode %" PRIu64 ", would reset to %" PRIu64 "\n"),
+                              (unsigned long long) be64_to_cpu(dino->di_nblocks),
+                              lino, nblocks);
                }
        }
 
        if (nextents > MAXEXTNUM)  {
-               do_warn(_("too many data fork extents (%llu) in inode %llu\n"),
+               do_warn(
+_("too many data fork extents (%" PRIu64 ") in inode %" PRIu64 "\n"),
                        nextents, lino);
                return 1;
        }
-       if (nextents != be32_to_cpu(dinoc->di_nextents))  {
+       if (nextents != be32_to_cpu(dino->di_nextents))  {
                if (!no_modify)  {
-                       do_warn(_("correcting nextents for inode %llu, "
-                               "was %d - counted %llu\n"), lino,
-                               be32_to_cpu(dinoc->di_nextents), nextents);
-                       dinoc->di_nextents = cpu_to_be32(nextents);
+                       do_warn(
+_("correcting nextents for inode %" PRIu64 ", was %d - counted %" PRIu64 "\n"),
+                               lino,
+                               be32_to_cpu(dino->di_nextents),
+                               nextents);
+                       dino->di_nextents = cpu_to_be32(nextents);
                        *dirty = 1;
                } else  {
-                       do_warn(_("bad nextents %d for inode %llu, would reset "
-                               "to %llu\n"), be32_to_cpu(dinoc->di_nextents),
+                       do_warn(
+_("bad nextents %d for inode %" PRIu64 ", would reset to %" PRIu64 "\n"),
+                               be32_to_cpu(dino->di_nextents),
                                lino, nextents);
                }
        }
 
        if (anextents > MAXAEXTNUM)  {
-               do_warn(_("too many attr fork extents (%llu) in inode %llu\n"),
+               do_warn(
+_("too many attr fork extents (%" PRIu64 ") in inode %" PRIu64 "\n"),
                        anextents, lino);
                return 1;
        }
-       if (anextents != be16_to_cpu(dinoc->di_anextents))  {
+       if (anextents != be16_to_cpu(dino->di_anextents))  {
                if (!no_modify)  {
-                       do_warn(_("correcting anextents for inode %llu, "
-                               "was %d - counted %llu\n"), lino,
-                               be16_to_cpu(dinoc->di_anextents), anextents);
-                       dinoc->di_anextents = cpu_to_be16(anextents);
+                       do_warn(
+_("correcting anextents for inode %" PRIu64 ", was %d - counted %" PRIu64 "\n"),
+                               lino,
+                               be16_to_cpu(dino->di_anextents), anextents);
+                       dino->di_anextents = cpu_to_be16(anextents);
                        *dirty = 1;
                } else  {
-                       do_warn(_("bad anextents %d for inode %llu, would reset"
-                               " to %llu\n"), be16_to_cpu(dinoc->di_anextents),
+                       do_warn(
+_("bad anextents %d for inode %" PRIu64 ", would reset to %" PRIu64 "\n"),
+                               be16_to_cpu(dino->di_anextents),
                                lino, anextents);
                }
        }
+
+       /*
+        * We are comparing different units here, but that's fine given that
+        * an extent has to have at least a block in it.
+        */
+       if (nblocks < nextents + anextents) {
+               do_warn(
+_("nblocks (%" PRIu64 ") smaller than nextents for inode %" PRIu64 "\n"), nblocks, lino);
+               return 1;
+       }
+
        return 0;
 }
 
@@ -2023,24 +1932,35 @@ process_inode_data_fork(
        xfs_dinode_t    *dino,
        int             type,
        int             *dirty,
-       xfs_drfsbno_t   *totblocks,
-       __uint64_t      *nextents,
+       xfs_rfsblock_t  *totblocks,
+       uint64_t        *nextents,
        blkmap_t        **dblkmap,
        int             check_dups)
 {
-       xfs_dinode_core_t *dinoc = &dino->di_core;
        xfs_ino_t       lino = XFS_AGINO_TO_INO(mp, agno, ino);
        int             err = 0;
+       int             nex;
+
+       /*
+        * extent count on disk is only valid for positive values. The kernel
+        * uses negative values in memory. hence if we see negative numbers
+        * here, trash it!
+        */
+       nex = be32_to_cpu(dino->di_nextents);
+       if (nex < 0)
+               *nextents = 1;
+       else
+               *nextents = nex;
 
-       *nextents = be32_to_cpu(dinoc->di_nextents);
-       if (*nextents > be64_to_cpu(dinoc->di_nblocks))
+       if (*nextents > be64_to_cpu(dino->di_nblocks))
                *nextents = 1;
 
-       if (dinoc->di_format != XFS_DINODE_FMT_LOCAL && type != XR_INO_RTDATA)
-               *dblkmap = blkmap_alloc(*nextents);
+
+       if (dino->di_format != XFS_DINODE_FMT_LOCAL && type != XR_INO_RTDATA)
+               *dblkmap = blkmap_alloc(*nextents, XFS_DATA_FORK);
        *nextents = 0;
 
-       switch (dinoc->di_format) {
+       switch (dino->di_format) {
        case XFS_DINODE_FMT_LOCAL:
                err = process_lclinode(mp, agno, ino, dino, XFS_DATA_FORK);
                *totblocks = 0;
@@ -2059,15 +1979,15 @@ process_inode_data_fork(
                err = 0;
                break;
        default:
-               do_error(_("unknown format %d, ino %llu (mode = %d)\n"),
-                       dinoc->di_format, lino, be16_to_cpu(dinoc->di_mode));
+               do_error(_("unknown format %d, ino %" PRIu64 " (mode = %d)\n"),
+                       dino->di_format, lino, be16_to_cpu(dino->di_mode));
        }
 
        if (err)  {
-               do_warn(_("bad data fork in inode %llu\n"), lino);
+               do_warn(_("bad data fork in inode %" PRIu64 "\n"), lino);
                if (!no_modify)  {
-                       *dirty += clear_dinode(mp, dino, lino);
-                       ASSERT(*dirty > 0);
+                       clear_dinode(mp, dino, lino);
+                       *dirty += 1;
                }
                return 1;
        }
@@ -2078,9 +1998,9 @@ process_inode_data_fork(
                 * re-process data fork to set bitmap since the
                 * bitmap wasn't set the first time through
                 */
-               switch (dinoc->di_format) {
+               switch (dino->di_format) {
                case XFS_DINODE_FMT_LOCAL:
-                       err = process_lclinode(mp, agno, ino, dino, 
+                       err = process_lclinode(mp, agno, ino, dino,
                                                XFS_DATA_FORK);
                        break;
                case XFS_DINODE_FMT_EXTENTS:
@@ -2097,9 +2017,9 @@ process_inode_data_fork(
                        err = 0;
                        break;
                default:
-                       do_error(_("unknown format %d, ino %llu (mode = %d)\n"),
-                               dinoc->di_format, lino,
-                               be16_to_cpu(dinoc->di_mode));
+                       do_error(_("unknown format %d, ino %" PRIu64 " (mode = %d)\n"),
+                               dino->di_format, lino,
+                               be16_to_cpu(dino->di_mode));
                }
 
                if (no_modify && err != 0)
@@ -2121,13 +2041,12 @@ process_inode_attr_fork(
        xfs_dinode_t    *dino,
        int             type,
        int             *dirty,
-       xfs_drfsbno_t   *atotblocks,
-       __uint64_t      *anextents,
+       xfs_rfsblock_t  *atotblocks,
+       uint64_t        *anextents,
        int             check_dups,
        int             extra_attr_check,
        int             *retval)
 {
-       xfs_dinode_core_t *dinoc = &dino->di_core;
        xfs_ino_t       lino = XFS_AGINO_TO_INO(mp, agno, ino);
        blkmap_t        *ablkmap = NULL;
        int             repair = 0;
@@ -2135,12 +2054,12 @@ process_inode_attr_fork(
 
        if (!XFS_DFORK_Q(dino)) {
                *anextents = 0;
-               if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS) {
-                       do_warn(_("bad attribute format %d in inode %llu, "),
-                               dinoc->di_aformat, lino);
+               if (dino->di_aformat != XFS_DINODE_FMT_EXTENTS) {
+                       do_warn(_("bad attribute format %d in inode %" PRIu64 ", "),
+                               dino->di_aformat, lino);
                        if (!no_modify) {
                                do_warn(_("resetting value\n"));
-                               dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
+                               dino->di_aformat = XFS_DINODE_FMT_EXTENTS;
                                *dirty = 1;
                        } else
                                do_warn(_("would reset value\n"));
@@ -2148,33 +2067,33 @@ process_inode_attr_fork(
                return 0;
        }
 
-       *anextents = be16_to_cpu(dinoc->di_anextents);
-       if (*anextents > be64_to_cpu(dinoc->di_nblocks))
+       *anextents = be16_to_cpu(dino->di_anextents);
+       if (*anextents > be64_to_cpu(dino->di_nblocks))
                *anextents = 1;
 
-       switch (dinoc->di_aformat) {
+       switch (dino->di_aformat) {
        case XFS_DINODE_FMT_LOCAL:
                *anextents = 0;
                *atotblocks = 0;
                err = process_lclinode(mp, agno, ino, dino, XFS_ATTR_FORK);
                break;
        case XFS_DINODE_FMT_EXTENTS:
-               ablkmap = blkmap_alloc(*anextents);
+               ablkmap = blkmap_alloc(*anextents, XFS_ATTR_FORK);
                *anextents = 0;
                err = process_exinode(mp, agno, ino, dino, type, dirty,
                                atotblocks, anextents, &ablkmap,
                                XFS_ATTR_FORK, check_dups);
                break;
        case XFS_DINODE_FMT_BTREE:
-               ablkmap = blkmap_alloc(*anextents);
+               ablkmap = blkmap_alloc(*anextents, XFS_ATTR_FORK);
                *anextents = 0;
                err = process_btinode(mp, agno, ino, dino, type, dirty,
                                atotblocks, anextents, &ablkmap,
                                XFS_ATTR_FORK, check_dups);
                break;
        default:
-               do_warn(_("illegal attribute format %d, ino %llu\n"),
-                               dinoc->di_aformat, lino);
+               do_warn(_("illegal attribute format %d, ino %" PRIu64 "\n"),
+                               dino->di_aformat, lino);
                err = 1;
                break;
        }
@@ -2188,17 +2107,12 @@ process_inode_attr_fork(
                 * XXX - put the inode onto the "move it" list and
                 *      log the the attribute scrubbing
                 */
-               do_warn(_("bad attribute fork in inode %llu"), lino);
+               do_warn(_("bad attribute fork in inode %" PRIu64), lino);
 
                if (!no_modify)  {
-                       if (delete_attr_ok)  {
-                               do_warn(_(", clearing attr fork\n"));
-                               *dirty += clear_dinode_attr(mp, dino, lino);
-                               dinoc->di_aformat = XFS_DINODE_FMT_LOCAL;
-                       } else  {
-                               do_warn("\n");
-                               *dirty += clear_dinode(mp, dino, lino);
-                       }
+                       do_warn(_(", clearing attr fork\n"));
+                       *dirty += clear_dinode_attr(mp, dino, lino);
+                       dino->di_aformat = XFS_DINODE_FMT_LOCAL;
                        ASSERT(*dirty > 0);
                } else  {
                        do_warn(_(", would clear attr fork\n"));
@@ -2209,13 +2123,13 @@ process_inode_attr_fork(
                blkmap_free(ablkmap);
                *retval = 1;
 
-               return delete_attr_ok ? 0 : 1;
+               return 0;
        }
 
        if (check_dups)  {
-               switch (dinoc->di_aformat) {
+               switch (dino->di_aformat) {
                case XFS_DINODE_FMT_LOCAL:
-                       err = process_lclinode(mp, agno, ino, dino, 
+                       err = process_lclinode(mp, agno, ino, dino,
                                                XFS_ATTR_FORK);
                        break;
                case XFS_DINODE_FMT_EXTENTS:
@@ -2229,8 +2143,8 @@ process_inode_attr_fork(
                                &ablkmap, XFS_ATTR_FORK, 0);
                        break;
                default:
-                       do_error(_("illegal attribute fmt %d, ino %llu\n"),
-                               dinoc->di_aformat, lino);
+                       do_error(_("illegal attribute fmt %d, ino %" PRIu64 "\n"),
+                               dino->di_aformat, lino);
                }
 
                if (no_modify && err != 0) {
@@ -2248,13 +2162,14 @@ process_inode_attr_fork(
        /* get this only in phase 3, not in both phase 3 and 4 */
        if (extra_attr_check &&
                        process_attributes(mp, lino, dino, ablkmap, &repair)) {
-               do_warn(_("problem with attribute contents in inode %llu\n"),
+               do_warn(
+       _("problem with attribute contents in inode %" PRIu64 "\n"),
                        lino);
                if (!repair) {
                        /* clear attributes if not done already */
                        if (!no_modify)  {
                                *dirty += clear_dinode_attr(mp, dino, lino);
-                               dinoc->di_aformat = XFS_DINODE_FMT_LOCAL;
+                               dino->di_aformat = XFS_DINODE_FMT_LOCAL;
                        } else  {
                                do_warn(_("would clear attr fork\n"));
                        }
@@ -2277,105 +2192,53 @@ process_inode_attr_fork(
 
 static int
 process_check_inode_nlink_version(
-       xfs_dinode_core_t *dinoc,
+       xfs_dinode_t    *dino,
        xfs_ino_t       lino)
 {
        int             dirty = 0;
 
-       if (dinoc->di_version > XFS_DINODE_VERSION_1 && !fs_inode_nlink)  {
-               /*
-                * do we have a fs/inode version mismatch with a valid
-                * version 2 inode here that has to stay version 2 or
-                * lose links?
-                */
-               if (be32_to_cpu(dinoc->di_nlink) > XFS_MAXLINK_1)  {
-                       /*
-                        * yes.  are nlink inodes allowed?
-                        */
-                       if (fs_inode_nlink_allowed)  {
-                               /*
-                                * yes, update status variable which will
-                                * cause sb to be updated later.
-                                */
-                               fs_inode_nlink = 1;
-                               do_warn(_("version 2 inode %llu claims > %u links, "),
-                                       lino, XFS_MAXLINK_1);
-                               if (!no_modify)  {
-                                       do_warn(_("updating superblock "
-                                               "version number\n"));
-                               } else  {
-                                       do_warn(_("would update superblock "
-                                               "version number\n"));
-                               }
-                       } else  {
-                               /*
-                                * no, have to convert back to onlinks
-                                * even if we lose some links
-                                */
-                               do_warn(_("WARNING:  version 2 inode %llu "
-                                       "claims > %u links, "),
-                                       lino, XFS_MAXLINK_1);
-                               if (!no_modify)  {
-                                       do_warn(_("converting back to version 1,\n"
-                                               "this may destroy %d links\n"),
-                                               be32_to_cpu(dinoc->di_nlink) -
-                                                       XFS_MAXLINK_1);
-
-                                       dinoc->di_version = XFS_DINODE_VERSION_1;
-                                       dinoc->di_nlink = cpu_to_be32(XFS_MAXLINK_1);
-                                       dinoc->di_onlink = cpu_to_be16(XFS_MAXLINK_1);
-                                       dirty = 1;
-                               } else  {
-                                       do_warn(_("would convert back to version 1,\n"
-                                               "\tthis might destroy %d links\n"),
-                                               be32_to_cpu(dinoc->di_nlink) -
-                                                       XFS_MAXLINK_1);
-                               }
-                       }
-               } else  {
-                       /*
-                        * do we have a v2 inode that we could convert back
-                        * to v1 without losing any links?  if we do and
-                        * we have a mismatch between superblock bits and the
-                        * version bit, alter the version bit in this case.
-                        *
-                        * the case where we lost links was handled above.
-                        */
-                       do_warn(_("found version 2 inode %llu, "), lino);
-                       if (!no_modify)  {
-                               do_warn(_("converting back to version 1\n"));
-                               dinoc->di_version = XFS_DINODE_VERSION_1;
-                               dinoc->di_onlink = cpu_to_be16(
-                                       be32_to_cpu(dinoc->di_nlink));
-                               dirty = 1;
-                       } else  {
-                               do_warn(_("would convert back to version 1\n"));
-                       }
-               }
-       }
-
        /*
-        * ok, if it's still a version 2 inode, it's going
-        * to stay a version 2 inode.  it should have a zero
+        * if it's a version 2 inode, it should have a zero
         * onlink field, so clear it.
         */
-       if (dinoc->di_version > XFS_DINODE_VERSION_1 &&
-                       dinoc->di_onlink != 0 && fs_inode_nlink > 0) {
+       if (dino->di_version > 1 && dino->di_onlink != 0) {
                if (!no_modify) {
-                       do_warn(_("clearing obsolete nlink field in "
-                               "version 2 inode %llu, was %d, now 0\n"),
-                               lino, be16_to_cpu(dinoc->di_onlink));
-                       dinoc->di_onlink = 0;
+                       do_warn(
+_("clearing obsolete nlink field in version 2 inode %" PRIu64 ", was %d, now 0\n"),
+                               lino, be16_to_cpu(dino->di_onlink));
+                       dino->di_onlink = 0;
                        dirty = 1;
                } else  {
-                       do_warn(_("would clear obsolete nlink field in "
-                               "version 2 inode %llu, currently %d\n"),
-                               lino, be16_to_cpu(dinoc->di_onlink));
+                       do_warn(
+_("would clear obsolete nlink field in version 2 inode %" PRIu64 ", currently %d\n"),
+                               lino, be16_to_cpu(dino->di_onlink));
                }
        }
        return dirty;
 }
 
+/* Check nanoseconds of a timestamp don't exceed 1 second. */
+static void
+check_nsec(
+       const char              *name,
+       xfs_ino_t               lino,
+       struct xfs_timestamp    *t,
+       int                     *dirty)
+{
+       if (be32_to_cpu(t->t_nsec) < 1000000000)
+               return;
+
+       do_warn(
+_("Bad %s nsec %u on inode %" PRIu64 ", "), name, be32_to_cpu(t->t_nsec), lino);
+       if (no_modify) {
+               do_warn(_("would reset to zero\n"));
+       } else {
+               do_warn(_("resetting to zero\n"));
+               t->t_nsec = 0;
+               *dirty = 1;
+       }
+}
+
 /*
  * returns 0 if the inode is ok, 1 if the inode is corrupt
  * check_dups can be set to 1 *only* when called by the
@@ -2385,8 +2248,7 @@ process_check_inode_nlink_version(
  *
  * for detailed, info, look at process_dinode() comments.
  */
-/* ARGSUSED */
-int
+static int
 process_dinode_int(xfs_mount_t *mp,
                xfs_dinode_t *dino,
                xfs_agnumber_t agno,
@@ -2403,14 +2265,13 @@ process_dinode_int(xfs_mount_t *mp,
                int *isa_dir,           /* out == 1 if inode is a directory */
                xfs_ino_t *parent)      /* out -- parent if ino is a dir */
 {
-       xfs_drfsbno_t           totblocks = 0;
-       xfs_drfsbno_t           atotblocks = 0;
-       xfs_dinode_core_t       *dinoc;
+       xfs_rfsblock_t          totblocks = 0;
+       xfs_rfsblock_t          atotblocks = 0;
        int                     di_mode;
        int                     type;
        int                     retval = 0;
-       __uint64_t              nextents;
-       __uint64_t              anextents;
+       uint64_t                nextents;
+       uint64_t                anextents;
        xfs_ino_t               lino;
        const int               is_free = 0;
        const int               is_used = 1;
@@ -2420,9 +2281,8 @@ process_dinode_int(xfs_mount_t *mp,
        *used = is_used;
        type = XR_INO_UNKNOWN;
 
-       dinoc = &dino->di_core;
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
-       di_mode = be16_to_cpu(dinoc->di_mode);
+       di_mode = be16_to_cpu(dino->di_mode);
 
        /*
         * if in verify mode, don't modify the inode.
@@ -2438,55 +2298,106 @@ process_dinode_int(xfs_mount_t *mp,
         */
        ASSERT(uncertain == 0 || verify_mode != 0);
 
-       if (be16_to_cpu(dinoc->di_magic) != XFS_DINODE_MAGIC)  {
+       /*
+        * This is the only valid point to check the CRC; after this we may have
+        * made changes which invalidate it, and the CRC is only updated again
+        * when it gets written out.
+        *
+        * Of course if we make any modifications after this, the inode gets
+        * rewritten, and the CRC is updated automagically.
+        */
+       if (xfs_sb_version_hascrc(&mp->m_sb) &&
+           !libxfs_verify_cksum((char *)dino, mp->m_sb.sb_inodesize,
+                               XFS_DINODE_CRC_OFF)) {
+               retval = 1;
+               if (!uncertain)
+                       do_warn(_("bad CRC for inode %" PRIu64 "%c"),
+                               lino, verify_mode ? '\n' : ',');
+               if (!verify_mode) {
+                       if (!no_modify) {
+                               do_warn(_(" will rewrite\n"));
+                               *dirty = 1;
+                       } else
+                               do_warn(_(" would rewrite\n"));
+               }
+       }
+
+       if (be16_to_cpu(dino->di_magic) != XFS_DINODE_MAGIC)  {
                retval = 1;
                if (!uncertain)
-                       do_warn(_("bad magic number 0x%x on inode %llu%c"),
-                               be16_to_cpu(dinoc->di_magic), lino,
+                       do_warn(_("bad magic number 0x%x on inode %" PRIu64 "%c"),
+                               be16_to_cpu(dino->di_magic), lino,
                                verify_mode ? '\n' : ',');
                if (!verify_mode) {
                        if (!no_modify)  {
                                do_warn(_(" resetting magic number\n"));
-                               dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
+                               dino->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
                                *dirty = 1;
                        } else
                                do_warn(_(" would reset magic number\n"));
                }
        }
 
-       if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version) ||
-           (!fs_inode_nlink && dinoc->di_version > XFS_DINODE_VERSION_1))  {
+       if (!libxfs_dinode_good_version(mp, dino->di_version)) {
                retval = 1;
                if (!uncertain)
-                       do_warn(_("bad version number 0x%x on inode %llu%c"),
-                               (__s8)dinoc->di_version, lino,
+                       do_warn(_("bad version number 0x%x on inode %" PRIu64 "%c"),
+                               (__s8)dino->di_version, lino,
                                verify_mode ? '\n' : ',');
                if (!verify_mode) {
                        if (!no_modify) {
                                do_warn(_(" resetting version number\n"));
-                               dinoc->di_version = (fs_inode_nlink) ?
-                                       XFS_DINODE_VERSION_2 :
-                                       XFS_DINODE_VERSION_1;
+                               dino->di_version =
+                                       xfs_sb_version_hascrc(&mp->m_sb) ? 3 : 2;
                                *dirty = 1;
                        } else
                                do_warn(_(" would reset version number\n"));
                }
        }
 
+       /*
+        * We don't bother checking the CRC here - we cannot guarantee that when
+        * we are called here that the inode has not already been modified in
+        * memory and hence invalidated the CRC.
+        */
+       if (xfs_sb_version_hascrc(&mp->m_sb)) {
+               if (be64_to_cpu(dino->di_ino) != lino) {
+                       if (!uncertain)
+                               do_warn(
+_("inode identifier %llu mismatch on inode %" PRIu64 "\n"),
+                                       (unsigned long long)be64_to_cpu(dino->di_ino),
+                                       lino);
+                       if (verify_mode)
+                               return 1;
+                       goto clear_bad_out;
+               }
+               if (platform_uuid_compare(&dino->di_uuid,
+                                         &mp->m_sb.sb_meta_uuid)) {
+                       if (!uncertain)
+                               do_warn(
+                       _("UUID mismatch on inode %" PRIu64 "\n"), lino);
+                       if (verify_mode)
+                               return 1;
+                       goto clear_bad_out;
+               }
+       }
+
        /*
         * blow out of here if the inode size is < 0
         */
-       if ((xfs_fsize_t)be64_to_cpu(dinoc->di_size) < 0)  {
+       if ((xfs_fsize_t)be64_to_cpu(dino->di_size) < 0)  {
                if (!uncertain)
-                       do_warn(_("bad (negative) size %lld on inode %llu\n"),
-                               be64_to_cpu(dinoc->di_size), lino);
+                       do_warn(
+_("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
+                               (int64_t)be64_to_cpu(dino->di_size),
+                               lino);
                if (verify_mode)
                        return 1;
                goto clear_bad_out;
        }
 
        /*
-        * if not in verify mode, check to sii if the inode and imap
+        * if not in verify mode, check to see if the inode and imap
         * agree that the inode is free
         */
        if (!verify_mode && di_mode == 0) {
@@ -2495,12 +2406,21 @@ process_dinode_int(xfs_mount_t *mp,
                 */
                if (was_free) {
                        /*
-                        * easy case, inode free -- inode and map agree, clear
+                        * easy case, inode free -- inode and map agree, check
                         * it just in case to ensure that format, etc. are
                         * set correctly
                         */
-                       if (!no_modify)
-                               *dirty += clear_dinode(mp, dino, lino);
+                       if (libxfs_dinode_verify(mp, lino, dino) != NULL) {
+                               do_warn(
+ _("free inode %" PRIu64 " contains errors, "), lino);
+                               if (!no_modify) {
+                                       clear_dinode(mp, dino, lino);
+                                       do_warn(_("corrected\n"));
+                                       *dirty += 1;
+                               } else {
+                                       do_warn(_("would correct\n"));
+                               }
+                       }
                        *used = is_free;
                        return 0;
                }
@@ -2509,10 +2429,12 @@ process_dinode_int(xfs_mount_t *mp,
                 * clear the inode just to be safe and mark the inode
                 * free.
                 */
-               do_warn(_("imap claims a free inode %llu is in use, "), lino);
+               do_warn(
+       _("imap claims a free inode %" PRIu64 " is in use, "), lino);
                if (!no_modify)  {
                        do_warn(_("correcting imap and clearing inode\n"));
-                       *dirty += clear_dinode(mp, dino, lino);
+                       clear_dinode(mp, dino, lino);
+                       *dirty += 1;
                        retval = 1;
                } else
                        do_warn(_("would correct imap and clear inode\n"));
@@ -2529,14 +2451,196 @@ process_dinode_int(xfs_mount_t *mp,
         * free inodes since technically any format is legal
         * as we reset the inode when we re-use it.
         */
-       if (di_mode != 0 && check_dinode_mode_format(dinoc) != 0) {
+       if (di_mode != 0 && check_dinode_mode_format(dino) != 0) {
                if (!uncertain)
-                       do_warn(_("bad inode format in inode %llu\n"), lino);
+                       do_warn(
+       _("bad inode format in inode %" PRIu64 "\n"), lino);
                if (verify_mode)
                        return 1;
                goto clear_bad_out;
        }
 
+       /*
+        * check that we only have valid flags set, and those that are set make
+        * sense.
+        */
+       if (dino->di_flags) {
+               uint16_t flags = be16_to_cpu(dino->di_flags);
+
+               if (flags & ~XFS_DIFLAG_ANY) {
+                       if (!uncertain) {
+                               do_warn(
+       _("Bad flags set in inode %" PRIu64 "\n"),
+                                       lino);
+                       }
+                       flags &= XFS_DIFLAG_ANY;
+               }
+
+               if (flags & (XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT)) {
+                       /* need an rt-dev! */
+                       if (!rt_name) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("inode %" PRIu64 " has RT flag set but there is no RT device\n"),
+                                               lino);
+                               }
+                               flags &= ~(XFS_DIFLAG_REALTIME |
+                                               XFS_DIFLAG_RTINHERIT);
+                       }
+               }
+               if (flags & XFS_DIFLAG_NEWRTBM) {
+                       /* must be a rt bitmap inode */
+                       if (lino != mp->m_sb.sb_rbmino) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("inode %" PRIu64 " not rt bitmap\n"),
+                                               lino);
+                               }
+                               flags &= ~XFS_DIFLAG_NEWRTBM;
+                       }
+               }
+               if (flags & (XFS_DIFLAG_RTINHERIT |
+                            XFS_DIFLAG_EXTSZINHERIT |
+                            XFS_DIFLAG_PROJINHERIT |
+                            XFS_DIFLAG_NOSYMLINKS)) {
+                       /* must be a directory */
+                       if (di_mode && !S_ISDIR(di_mode)) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("directory flags set on non-directory inode %" PRIu64 "\n" ),
+                                               lino);
+                               }
+                               flags &= ~(XFS_DIFLAG_RTINHERIT |
+                                               XFS_DIFLAG_EXTSZINHERIT |
+                                               XFS_DIFLAG_PROJINHERIT |
+                                               XFS_DIFLAG_NOSYMLINKS);
+                       }
+               }
+               if (flags & (XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE)) {
+                       /* must be a file */
+                       if (di_mode && !S_ISREG(di_mode)) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("file flags set on non-file inode %" PRIu64 "\n"),
+                                               lino);
+                               }
+                               flags &= ~(XFS_DIFLAG_REALTIME |
+                                               FS_XFLAG_EXTSIZE);
+                       }
+               }
+               if (!verify_mode && flags != be16_to_cpu(dino->di_flags)) {
+                       if (!no_modify) {
+                               do_warn(_("fixing bad flags.\n"));
+                               dino->di_flags = cpu_to_be16(flags);
+                               *dirty = 1;
+                       } else
+                               do_warn(_("would fix bad flags.\n"));
+               }
+       }
+
+       /*
+        * check that we only have valid flags2 set, and those that are set make
+        * sense.
+        */
+       if (dino->di_version >= 3) {
+               uint16_t flags = be16_to_cpu(dino->di_flags);
+               uint64_t flags2 = be64_to_cpu(dino->di_flags2);
+
+               if (flags2 & ~XFS_DIFLAG2_ANY) {
+                       if (!uncertain) {
+                               do_warn(
+       _("Bad flags2 set in inode %" PRIu64 "\n"),
+                                       lino);
+                       }
+                       flags2 &= XFS_DIFLAG2_ANY;
+               }
+
+               if (flags2 & XFS_DIFLAG2_DAX) {
+                       /* must be a file or dir */
+                       if (di_mode && !(S_ISREG(di_mode) || S_ISDIR(di_mode))) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("DAX flag set on special inode %" PRIu64 "\n"),
+                                               lino);
+                               }
+                               flags2 &= ~XFS_DIFLAG2_DAX;
+                       }
+               }
+
+               if ((flags2 & XFS_DIFLAG2_REFLINK) &&
+                   !xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       if (!uncertain) {
+                               do_warn(
+       _("inode %" PRIu64 " is marked reflinked but file system does not support reflink\n"),
+                                       lino);
+                       }
+                       goto clear_bad_out;
+               }
+
+               if (flags2 & XFS_DIFLAG2_REFLINK) {
+                       /* must be a file */
+                       if (di_mode && !S_ISREG(di_mode)) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("reflink flag set on non-file inode %" PRIu64 "\n"),
+                                               lino);
+                               }
+                               goto clear_bad_out;
+                       }
+               }
+
+               if ((flags2 & XFS_DIFLAG2_REFLINK) &&
+                   (flags & (XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT))) {
+                       if (!uncertain) {
+                               do_warn(
+       _("Cannot have a reflinked realtime inode %" PRIu64 "\n"),
+                                       lino);
+                       }
+                       goto clear_bad_out;
+               }
+
+               if ((flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
+                   !xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       if (!uncertain) {
+                               do_warn(
+       _("inode %" PRIu64 " has CoW extent size hint but file system does not support reflink\n"),
+                                       lino);
+                       }
+                       flags2 &= ~XFS_DIFLAG2_COWEXTSIZE;
+               }
+
+               if (flags2 & XFS_DIFLAG2_COWEXTSIZE) {
+                       /* must be a directory or file */
+                       if (di_mode && !S_ISDIR(di_mode) && !S_ISREG(di_mode)) {
+                               if (!uncertain) {
+                                       do_warn(
+       _("CoW extent size flag set on non-file, non-directory inode %" PRIu64 "\n" ),
+                                               lino);
+                               }
+                               flags2 &= ~XFS_DIFLAG2_COWEXTSIZE;
+                       }
+               }
+
+               if ((flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
+                   (flags & (XFS_DIFLAG_REALTIME | XFS_DIFLAG_RTINHERIT))) {
+                       if (!uncertain) {
+                               do_warn(
+       _("Cannot have CoW extent size hint on a realtime inode %" PRIu64 "\n"),
+                                       lino);
+                       }
+                       flags2 &= ~XFS_DIFLAG2_COWEXTSIZE;
+               }
+
+               if (!verify_mode && flags2 != be64_to_cpu(dino->di_flags2)) {
+                       if (!no_modify) {
+                               do_warn(_("fixing bad flags2.\n"));
+                               dino->di_flags2 = cpu_to_be64(flags2);
+                               *dirty = 1;
+                       } else
+                               do_warn(_("would fix bad flags2.\n"));
+               }
+       }
+
        if (verify_mode)
                return retval;
 
@@ -2548,8 +2652,17 @@ process_dinode_int(xfs_mount_t *mp,
         * we're going to find.  check_dups is set to 1 only during
         * phase 4.  Ugly.
         */
-       if (check_dups && !no_modify)
-               *dirty += clear_dinode_unlinked(mp, dino);
+       if (check_dups && be32_to_cpu(dino->di_next_unlinked) != NULLAGINO) {
+               if (no_modify) {
+                       do_warn(
+       _("Would clear next_unlinked in inode %" PRIu64 "\n"), lino);
+               } else  {
+                       clear_dinode_unlinked(mp, dino);
+                       do_warn(
+       _("Cleared next_unlinked in inode %" PRIu64 "\n"), lino);
+                       *dirty += 1;
+               }
+       }
 
        /* set type and map type info */
 
@@ -2559,12 +2672,18 @@ process_dinode_int(xfs_mount_t *mp,
                *isa_dir = 1;
                break;
        case S_IFREG:
-               if (be16_to_cpu(dinoc->di_flags) & XFS_DIFLAG_REALTIME)
+               if (be16_to_cpu(dino->di_flags) & XFS_DIFLAG_REALTIME)
                        type = XR_INO_RTDATA;
                else if (lino == mp->m_sb.sb_rbmino)
                        type = XR_INO_RTBITMAP;
                else if (lino == mp->m_sb.sb_rsumino)
                        type = XR_INO_RTSUM;
+               else if (lino == mp->m_sb.sb_uquotino)
+                       type = XR_INO_UQUOTA;
+               else if (lino == mp->m_sb.sb_gquotino)
+                       type = XR_INO_GQUOTA;
+               else if (lino == mp->m_sb.sb_pquotino)
+                       type = XR_INO_PQUOTA;
                else
                        type = XR_INO_DATA;
                break;
@@ -2584,7 +2703,7 @@ process_dinode_int(xfs_mount_t *mp,
                type = XR_INO_FIFO;
                break;
        default:
-               do_warn(_("bad inode type %#o inode %llu\n"),
+               do_warn(_("bad inode type %#o inode %" PRIu64 "\n"),
                                di_mode & S_IFMT, lino);
                goto clear_bad_out;
        }
@@ -2592,33 +2711,59 @@ process_dinode_int(xfs_mount_t *mp,
        /*
         * type checks for superblock inodes
         */
-       if (process_check_sb_inodes(mp, dinoc, lino, &type, dirty) != 0)
+       if (process_check_sb_inodes(mp, dino, lino, &type, dirty) != 0)
                goto clear_bad_out;
 
        /*
         * only regular files with REALTIME or EXTSIZE flags set can have
         * extsize set, or directories with EXTSZINHERIT.
         */
-       if (be32_to_cpu(dinoc->di_extsize) != 0) {
-               if ((type == XR_INO_RTDATA) ||
-                   (type == XR_INO_DIR && (be16_to_cpu(dinoc->di_flags) &
-                                       XFS_DIFLAG_EXTSZINHERIT)) ||
-                   (type == XR_INO_DATA && (be16_to_cpu(dinoc->di_flags) &
-                                XFS_DIFLAG_EXTSIZE)))  {
-                       /* s'okay */ ;
-               } else {
-                       do_warn(_("bad non-zero extent size %u for "
-                                       "non-realtime/extsize inode %llu, "),
-                                       be32_to_cpu(dinoc->di_extsize), lino);
-                       if (!no_modify)  {
-                               do_warn(_("resetting to zero\n"));
-                               dinoc->di_extsize = 0;
-                               *dirty = 1;
-                       } else
-                               do_warn(_("would reset to zero\n"));
-               }
+       if (libxfs_inode_validate_extsize(mp,
+                       be32_to_cpu(dino->di_extsize),
+                       be16_to_cpu(dino->di_mode),
+                       be16_to_cpu(dino->di_flags)) != NULL) {
+               do_warn(
+_("Bad extent size %u on inode %" PRIu64 ", "),
+                               be32_to_cpu(dino->di_extsize), lino);
+               if (!no_modify)  {
+                       do_warn(_("resetting to zero\n"));
+                       dino->di_extsize = 0;
+                       dino->di_flags &= ~cpu_to_be16(XFS_DIFLAG_EXTSIZE |
+                                                      XFS_DIFLAG_EXTSZINHERIT);
+                       *dirty = 1;
+               } else
+                       do_warn(_("would reset to zero\n"));
+       }
+
+       /*
+        * Only (regular files and directories) with COWEXTSIZE flags
+        * set can have extsize set.
+        */
+       if (dino->di_version >= 3 &&
+           libxfs_inode_validate_cowextsize(mp,
+                       be32_to_cpu(dino->di_cowextsize),
+                       be16_to_cpu(dino->di_mode),
+                       be16_to_cpu(dino->di_flags),
+                       be64_to_cpu(dino->di_flags2)) != NULL) {
+               do_warn(
+_("Bad CoW extent size %u on inode %" PRIu64 ", "),
+                               be32_to_cpu(dino->di_cowextsize), lino);
+               if (!no_modify)  {
+                       do_warn(_("resetting to zero\n"));
+                       dino->di_flags2 &= ~cpu_to_be64(XFS_DIFLAG2_COWEXTSIZE);
+                       dino->di_cowextsize = 0;
+                       *dirty = 1;
+               } else
+                       do_warn(_("would reset to zero\n"));
        }
 
+       /* nsec fields cannot be larger than 1 billion */
+       check_nsec("atime", lino, &dino->di_atime, dirty);
+       check_nsec("mtime", lino, &dino->di_mtime, dirty);
+       check_nsec("ctime", lino, &dino->di_ctime, dirty);
+       if (dino->di_version >= 3)
+               check_nsec("crtime", lino, &dino->di_crtime, dirty);
+
        /*
         * general size/consistency checks:
         */
@@ -2628,9 +2773,15 @@ process_dinode_int(xfs_mount_t *mp,
        /*
         * check for illegal values of forkoff
         */
-       if (process_check_inode_forkoff(mp, dinoc, lino) != 0)
+       if (process_check_inode_forkoff(mp, dino, lino) != 0)
                goto clear_bad_out;
 
+       /*
+        * record the state of the reflink flag
+        */
+       if (collect_rmaps)
+               record_inode_reflink_flag(mp, dino, agno, ino, lino);
+
        /*
         * check data fork -- if it's bad, clear the inode
         */
@@ -2656,7 +2807,7 @@ process_dinode_int(xfs_mount_t *mp,
        /*
         * correct space counters if required
         */
-       if (process_inode_blocks_and_extents(dinoc, totblocks + atotblocks,
+       if (process_inode_blocks_and_extents(dino, totblocks + atotblocks,
                        nextents, anextents, lino, dirty) != 0)
                goto clear_bad_out;
 
@@ -2665,49 +2816,55 @@ process_dinode_int(xfs_mount_t *mp,
         */
        switch (type)  {
        case XR_INO_DIR:
-               if (xfs_sb_version_hasdirv2(&mp->m_sb) ?
-                               process_dir2(mp, lino, dino, ino_discovery,
-                                               dirty, "", parent, dblkmap) :
-                               process_dir(mp, lino, dino, ino_discovery,
+               if (process_dir2(mp, lino, dino, ino_discovery,
                                                dirty, "", parent, dblkmap)) {
-                       do_warn(_("problem with directory contents in "
-                               "inode %llu\n"), lino);
+                       do_warn(
+       _("problem with directory contents in inode %" PRIu64 "\n"),
+                               lino);
                        goto clear_bad_out;
                }
                break;
        case XR_INO_SYMLINK:
                if (process_symlink(mp, lino, dino, dblkmap) != 0) {
-                       do_warn(_("problem with symbolic link in inode %llu\n"),
+                       do_warn(
+       _("problem with symbolic link in inode %" PRIu64 "\n"),
                                lino);
                        goto clear_bad_out;
                }
                break;
+       case XR_INO_UQUOTA:
+       case XR_INO_GQUOTA:
+       case XR_INO_PQUOTA:
+               if (process_quota_inode(mp, lino, dino, type, dblkmap) != 0) {
+                       do_warn(
+       _("problem with quota inode %" PRIu64 "\n"), lino);
+                       goto clear_bad_out;
+               }
+               break;
        default:
                break;
        }
 
-       if (dblkmap)
-               blkmap_free(dblkmap);
+       blkmap_free(dblkmap);
 
        /*
         * check nlinks feature, if it's a version 1 inode,
         * just leave nlinks alone.  even if it's set wrong,
         * it'll be reset when read in.
         */
-       *dirty += process_check_inode_nlink_version(dinoc, lino);
+       *dirty += process_check_inode_nlink_version(dino, lino);
 
        return retval;
 
 clear_bad_out:
        if (!no_modify)  {
-               *dirty += clear_dinode(mp, dino, lino);
-               ASSERT(*dirty > 0);
+               clear_dinode(mp, dino, lino);
+               *dirty += 1;
        }
 bad_out:
        *used = is_free;
        *isa_dir = 0;
-       if (dblkmap)
-               blkmap_free(dblkmap);
+       blkmap_free(dblkmap);
        return 1;
 }
 
@@ -2774,7 +2931,7 @@ process_dinode(
        const int       uncertain = 0;
 
 #ifdef XR_INODE_TRACE
-       fprintf(stderr, "processing inode %d/%d\n", agno, ino);
+       fprintf(stderr, _("processing inode %d/%d\n"), agno, ino);
 #endif
        return process_dinode_int(mp, dino, agno, ino, was_free, dirty, used,
                                verify_mode, uncertain, ino_discovery,