]> 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 d6c41d09d09e12481c98c6f279c4260a9ab7be37..f670bf87c50d86c9003640950492d9d4e549a02b 100644 (file)
@@ -1,22 +1,10 @@
+// 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"
@@ -30,6 +18,8 @@
 #include "attr_repair.h"
 #include "bmap.h"
 #include "threads.h"
+#include "slab.h"
+#include "rmap.h"
 
 /*
  * gettext lookups for translations of strings use mutexes internally to
@@ -115,162 +105,46 @@ _("would have cleared inode %" PRIu64 " attributes\n"), ino_num);
        return(1);
 }
 
-static int
+static void
 clear_dinode_core(struct xfs_mount *mp, xfs_dinode_t *dinoc, xfs_ino_t ino_num)
 {
-       int dirty = 0;
-       int i;
-
-#define __dirty_no_modify_ret(dirty) \
-       ({ (dirty) = 1; if (no_modify) return 1; })
-
-       if (be16_to_cpu(dinoc->di_magic) != XFS_DINODE_MAGIC)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_magic = cpu_to_be16(XFS_DINODE_MAGIC);
-       }
-
-       if (!XFS_DINODE_GOOD_VERSION(dinoc->di_version)) {
-               __dirty_no_modify_ret(dirty);
-               if (xfs_sb_version_hascrc(&mp->m_sb))
-                       dinoc->di_version = 3;
-               else
-                       dinoc->di_version = 2;
-       }
-
-       if (be16_to_cpu(dinoc->di_mode) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_mode = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_flags) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_flags = 0;
-       }
-
-       if (be32_to_cpu(dinoc->di_dmevmask) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_dmevmask = 0;
-       }
-
-       if (dinoc->di_forkoff != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_forkoff = 0;
-       }
-
-       if (dinoc->di_format != XFS_DINODE_FMT_EXTENTS)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_format = XFS_DINODE_FMT_EXTENTS;
-       }
-
-       if (dinoc->di_aformat != XFS_DINODE_FMT_EXTENTS)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_aformat = XFS_DINODE_FMT_EXTENTS;
-       }
-
-       if (be64_to_cpu(dinoc->di_size) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_size = 0;
-       }
-
-       if (be64_to_cpu(dinoc->di_nblocks) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_nblocks = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_onlink) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_onlink = 0;
-       }
-
-       if (be32_to_cpu(dinoc->di_nextents) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_nextents = 0;
-       }
-
-       if (be16_to_cpu(dinoc->di_anextents) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_anextents = 0;
-       }
-
-       if (dinoc->di_version > 1 &&
-                       be32_to_cpu(dinoc->di_nlink) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_nlink = 0;
-       }
-
+       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 dirty;
-
-       if (be64_to_cpu(dinoc->di_ino) != ino_num) {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_ino = cpu_to_be64(ino_num);
-       }
-
-       if (platform_uuid_compare(&dinoc->di_uuid, &mp->m_sb.sb_uuid)) {
-               __dirty_no_modify_ret(dirty);
-               platform_uuid_copy(&dinoc->di_uuid, &mp->m_sb.sb_uuid);
-       }
-
-       for (i = 0; i < sizeof(dinoc->di_pad2)/sizeof(dinoc->di_pad2[0]); i++) {
-               if (dinoc->di_pad2[i] != 0) {
-                       __dirty_no_modify_ret(dirty);
-                       memset(dinoc->di_pad2, 0, sizeof(dinoc->di_pad2));
-                       break;
-               }
-       }
-
-       if (be64_to_cpu(dinoc->di_flags2) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_flags2 = 0;
-       }
-
-       if (be64_to_cpu(dinoc->di_lsn) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_lsn = 0;
-       }
-
-       if (be64_to_cpu(dinoc->di_changecount) != 0)  {
-               __dirty_no_modify_ret(dirty);
-               dinoc->di_changecount = 0;
-       }
-
-       return dirty;
+               return;
+       dinoc->di_ino = cpu_to_be64(ino_num);
+       platform_uuid_copy(&dinoc->di_uuid, &mp->m_sb.sb_meta_uuid);
+       return;
 }
 
-static 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
  */
-static int
+static void
 clear_dinode(xfs_mount_t *mp, xfs_dinode_t *dino, xfs_ino_t ino_num)
 {
-       int dirty;
-
-       dirty = clear_dinode_core(mp, dino, 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(XFS_DFORK_DPTR(dino), 0,
-                      XFS_LITINO(mp, dino->di_version));
-
-       return(dirty);
+       memset(XFS_DFORK_DPTR(dino), 0, XFS_LITINO(mp, dino->di_version));
+       return;
 }
 
 
@@ -288,11 +162,11 @@ 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 -
-                               ((xfs_drfsbno_t)(sbp->sb_agcount - 1) *
+                               ((xfs_rfsblock_t)(sbp->sb_agcount - 1) *
                                 sbp->sb_agblocks)));
        return 1;
 }
@@ -363,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;
@@ -384,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;
@@ -426,11 +300,11 @@ 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 */
 
@@ -464,30 +338,14 @@ _("inode %" PRIu64 " - bad rt extent overflows - start %" PRIu64 ", "
                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 [%" PRIu64 " %" PRIu64 "] (fs rtext size = %u)\n"),
-                       irec->br_startblock,
-                       irec->br_blockcount,
-                       mp->m_sb.sb_rextsize);
-               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)  {
@@ -523,6 +381,7 @@ _("data fork in rt inode %" PRIu64 " found metadata block %" PRIu64 " in rt bmap
                case XR_E_INUSE:
                        if (pwe)
                                break;
+                       /* fall through */
                case XR_E_MULT:
                        set_rtbmap(ext, XR_E_MULT);
                        do_warn(
@@ -559,18 +418,18 @@ process_bmbt_reclist_int(
        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 = get_forkname(whichfork);
        int                     i;
@@ -720,7 +579,9 @@ _("Fatal error: inode %" PRIu64 " - blkmap_set_ext(): %s\n"
                         * checking each entry without setting the
                         * block bitmap
                         */
-                       if (search_dup_extent(agno, agbno, ebno)) {
+                       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"),
@@ -742,18 +603,24 @@ _("%s fork in ino %" PRIu64 " claims dup extent, "
                        case XR_E_FREE1:
                                do_warn(
 _("%s fork in ino %" PRIu64 " claims free block %" PRIu64 "\n"),
-                                       forkname, ino, (__uint64_t) b);
+                                       forkname, ino, (uint64_t) b);
                                /* fall through ... */
+                       case XR_E_INUSE1:       /* seen by rmap */
                        case XR_E_UNKNOWN:
-                               set_bmap_ext(agno, agbno, blen, XR_E_INUSE);
                                break;
 
                        case XR_E_BAD_STATE:
                                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:
+                       case XR_E_REFC:
                                do_warn(
 _("%s fork in inode %" PRIu64 " claims metadata block %" PRIu64 "\n"),
                                        forkname, ino, b);
@@ -761,18 +628,61 @@ _("%s fork in inode %" PRIu64 " claims metadata block %" PRIu64 "\n"),
 
                        case XR_E_INUSE:
                        case XR_E_MULT:
-                               set_bmap_ext(agno, agbno, blen, XR_E_MULT);
+                               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 %" 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;
@@ -800,10 +710,10 @@ process_bmbt_reclist(
        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,
@@ -821,53 +731,69 @@ scan_bmbt_reclist(
        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)
+struct xfs_buf *
+get_agino_buf(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_agino_t             agino,
+       struct xfs_dinode       **dipp)
 {
-       ino_tree_node_t *irec;
-       xfs_buf_t *bp;
-       int size;
+       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;
 
-       if ((irec = find_inode_rec(mp, agno, agino)) == NULL)
-               return(NULL);
+       /*
+        * 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.
+        */
+       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));
 
-       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,
-               &xfs_inode_buf_ops);
+#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
+
+       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, irec->ino_startnum,
-                       XFS_AGB_TO_DADDR(mp, agno,
-                               XFS_AGINO_TO_AGBNO(mp, irec->ino_startnum)));
-               return(NULL);
+                       agno, cluster_agino, cluster_daddr);
+               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);
+       *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;
 }
 
 /*
@@ -886,15 +812,15 @@ 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;
@@ -903,7 +829,7 @@ process_btinode(
        int                     level;
        int                     numrecs;
        bmap_cursor_t           cursor;
-       __uint64_t              magic;
+       uint64_t                magic;
 
        dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
        lino = XFS_AGINO_TO_INO(mp, agno, ino);
@@ -950,9 +876,9 @@ _("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
        init_bm_cursor(&cursor, level + 1);
 
        pp = XFS_BMDR_PTR_ADDR(dib, 1,
-               xfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0));
+               libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0));
        pkey = XFS_BMDR_KEY_ADDR(dib, 1);
-       last_key = NULLDFILOFF;
+       last_key = NULLFILEOFF;
 
        for (i = 0; i < numrecs; i++)  {
                /*
@@ -960,15 +886,17 @@ _("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
                 * 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 %" PRIu64 "\n"),
-                              (unsigned long long) 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, scan_bmapbt, type,
-                               whichfork, lino, tot, nex, blkmapp, &cursor,
-                               1, check_dups, magic, &xfs_bmbt_buf_ops))
+               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
@@ -977,28 +905,27 @@ _("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
                 * 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 %" PRIu64") in inode "
-         "%" PRIu64" %s fork\n"),
-                                      (unsigned long long)
-                                              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 %" PRIu64 ") in inode "
-         "%" PRIu64 " %s fork\n"),
-                                      (unsigned long long)
-                                              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);
                        }
                }
                /*
@@ -1006,7 +933,7 @@ _("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
                 * 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 %" PRIu64 " in inode %" PRIu64 " %s fork\n"),
@@ -1035,9 +962,9 @@ _("bad numrecs 0 in inode %" PRIu64 " bmap btree root block\n"),
         * 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 %" PRIu64 " should be NULLDFSBNO)\n"),
+       _("bad fwd (right) sibling pointer (saw %" PRIu64 " should be NULLFSBLOCK)\n"),
                        cursor.level[0].right_fsbno);
                do_warn(
        _("\tin inode %" PRIu64 " (%s fork) bmap btree block %" PRIu64 "\n"),
@@ -1060,16 +987,16 @@ 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;
 
@@ -1150,7 +1077,7 @@ process_lclinode(
 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;
@@ -1158,19 +1085,19 @@ process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
        int                     max_blocks;
 
        if (be64_to_cpu(dino->di_size) <= XFS_DFORK_DSIZE(dino, mp)) {
-               if (dino->di_format == XFS_DINODE_FMT_LOCAL)  
+               if (dino->di_format == XFS_DINODE_FMT_LOCAL)
                        return 0;
                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);
+                       (int64_t)be64_to_cpu(dino->di_size), lino);
                return 1;
        }
        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);
+                       (int64_t)be64_to_cpu(dino->di_size), lino);
                return 1;
        }
 
@@ -1222,7 +1149,7 @@ 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')
@@ -1232,6 +1159,119 @@ 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,
@@ -1240,7 +1280,7 @@ process_symlink_remote(
        struct blkmap           *blkmap,
        char                    *dst)
 {
-       xfs_dfsbno_t            fsbno;
+       xfs_fsblock_t           fsbno;
        struct xfs_buf          *bp;
        char                    *src;
        int                     pathlen;
@@ -1257,7 +1297,7 @@ process_symlink_remote(
                int     badcrc = 0;
 
                fsbno = blkmap_get(blkmap, i);
-               if (fsbno == NULLDFSBNO) {
+               if (fsbno == NULLFSBLOCK) {
                        do_warn(
 _("cannot read inode %" PRIu64 ", file block %d, NULL disk block\n"),
                                lino, i);
@@ -1285,7 +1325,14 @@ _("cannot read inode %" PRIu64 ", file block %d, disk block %" PRIu64 "\n"),
                                lino, i, fsbno);
                        return 1;
                }
-               if (bp->b_error == EFSBADCRC) {
+               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);
@@ -1293,7 +1340,7 @@ _("Bad symlink buffer CRC, block %" PRIu64 ", inode %" PRIu64 ".\n"
                }
 
                byte_cnt = XFS_SYMLINK_BUF_SPACE(mp, byte_cnt);
-               byte_cnt = MIN(pathlen, byte_cnt);
+               byte_cnt = min(pathlen, byte_cnt);
 
                src = bp->b_addr;
                if (xfs_sb_version_hascrc(&mp->m_sb)) {
@@ -1334,7 +1381,7 @@ process_symlink(
        blkmap_t        *blkmap)
 {
        char                    *symlink;
-       char                    data[MAXPATHLEN];
+       char                    data[XFS_SYMLINK_MAXLEN];
 
        /*
         * check size against kernel symlink limits.  we know
@@ -1342,12 +1389,17 @@ process_symlink(
         * the inode is structurally ok so we don't have to check
         * for that
         */
-       if (be64_to_cpu(dino->di_size) >= MAXPATHLEN)  {
+       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
@@ -1358,7 +1410,7 @@ process_symlink(
                 * local symlink, just copy the symlink out of the
                 * inode into the data area
                 */
-               memmove(symlink, XFS_DFORK_DPTR(dino), 
+               memmove(symlink, XFS_DFORK_DPTR(dino),
                                                be64_to_cpu(dino->di_size));
        } else {
                int error;
@@ -1412,22 +1464,29 @@ _("inode %" PRIu64 " has bad inode type (IFMNT)\n"), lino);
                case XR_INO_CHRDEV:
                        do_warn(
 _("size of character device inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
-                               (__int64_t)be64_to_cpu(dino->di_size));
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_BLKDEV:
                        do_warn(
 _("size of block device inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
-                               (__int64_t)be64_to_cpu(dino->di_size));
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_SOCK:
                        do_warn(
 _("size of socket inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
-                               (__int64_t)be64_to_cpu(dino->di_size));
+                               (int64_t)be64_to_cpu(dino->di_size));
                        break;
                case XR_INO_FIFO:
                        do_warn(
 _("size of fifo inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
-                               (__int64_t)be64_to_cpu(dino->di_size));
+                               (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, "
@@ -1442,7 +1501,7 @@ _("size of fifo inode %" PRIu64 " != 0 (%" PRId64 " bytes)\n"), lino,
 }
 
 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
@@ -1549,7 +1608,7 @@ process_check_sb_inodes(
        int             *dirty)
 {
        if (lino == mp->m_sb.sb_rootino) {
-               if (*type != XR_INO_DIR)  {
+               if (*type != XR_INO_DIR)  {
                        do_warn(_("root inode %" PRIu64 " has bad type 0x%x\n"),
                                lino, dinode_fmt(dinoc));
                        *type = XR_INO_DIR;
@@ -1563,7 +1622,7 @@ process_check_sb_inodes(
                return 0;
        }
        if (lino == mp->m_sb.sb_uquotino)  {
-               if (*type != XR_INO_DATA)  {
+               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;
@@ -1572,7 +1631,7 @@ process_check_sb_inodes(
                return 0;
        }
        if (lino == mp->m_sb.sb_gquotino)  {
-               if (*type != XR_INO_DATA)  {
+               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;
@@ -1581,7 +1640,7 @@ process_check_sb_inodes(
                return 0;
        }
        if (lino == mp->m_sb.sb_pquotino)  {
-               if (*type != XR_INO_DATA)  {
+               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;
@@ -1688,6 +1747,14 @@ _("directory inode %" PRIu64 " has bad size %" PRId64 "\n"),
                        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
@@ -1701,12 +1768,12 @@ _("found inode %" PRIu64 " claiming to be a real-time file\n"), lino);
                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 %" PRIu64 " has bad size %" PRId64 " (should be %" PRIu64 ")\n"),
                                lino, size,
-                               (__int64_t) mp->m_sb.sb_rbmblocks *
+                               (int64_t) mp->m_sb.sb_rbmblocks *
                                        mp->m_sb.sb_blocksize);
                        return 1;
                }
@@ -1774,9 +1841,9 @@ _("bad attr fork offset %d in inode %" PRIu64 ", max=%d\n"),
 static int
 process_inode_blocks_and_extents(
        xfs_dinode_t    *dino,
-       xfs_drfsbno_t   nblocks,
-       __uint64_t      nextents,
-       __uint64_t      anextents,
+       xfs_rfsblock_t  nblocks,
+       uint64_t        nextents,
+       uint64_t        anextents,
        xfs_ino_t       lino,
        int             *dirty)
 {
@@ -1865,8 +1932,8 @@ 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)
 {
@@ -1919,8 +1986,8 @@ process_inode_data_fork(
        if (err)  {
                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;
        }
@@ -1933,7 +2000,7 @@ process_inode_data_fork(
                 */
                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:
@@ -1974,8 +2041,8 @@ 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)
@@ -2043,14 +2110,9 @@ process_inode_attr_fork(
                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);
-                               dino->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"));
@@ -2061,13 +2123,13 @@ process_inode_attr_fork(
                blkmap_free(ablkmap);
                *retval = 1;
 
-               return delete_attr_ok ? 0 : 1;
+               return 0;
        }
 
        if (check_dups)  {
                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:
@@ -2155,6 +2217,28 @@ _("would clear obsolete nlink field in version 2 inode %" PRIu64 ", currently %d
        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
@@ -2181,13 +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_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;
@@ -2223,7 +2307,7 @@ process_dinode_int(xfs_mount_t *mp,
         * rewritten, and the CRC is updated automagically.
         */
        if (xfs_sb_version_hascrc(&mp->m_sb) &&
-           !xfs_verify_cksum((char *)dino, mp->m_sb.sb_inodesize,
+           !libxfs_verify_cksum((char *)dino, mp->m_sb.sb_inodesize,
                                XFS_DINODE_CRC_OFF)) {
                retval = 1;
                if (!uncertain)
@@ -2254,8 +2338,7 @@ process_dinode_int(xfs_mount_t *mp,
                }
        }
 
-       if (!XFS_DINODE_GOOD_VERSION(dino->di_version) ||
-           (xfs_sb_version_hascrc(&mp->m_sb) && dino->di_version < 3) )  {
+       if (!libxfs_dinode_good_version(mp, dino->di_version)) {
                retval = 1;
                if (!uncertain)
                        do_warn(_("bad version number 0x%x on inode %" PRIu64 "%c"),
@@ -2282,12 +2365,14 @@ process_dinode_int(xfs_mount_t *mp,
                        if (!uncertain)
                                do_warn(
 _("inode identifier %llu mismatch on inode %" PRIu64 "\n"),
-                                       be64_to_cpu(dino->di_ino), lino);
+                                       (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_uuid)) {
+               if (platform_uuid_compare(&dino->di_uuid,
+                                         &mp->m_sb.sb_meta_uuid)) {
                        if (!uncertain)
                                do_warn(
                        _("UUID mismatch on inode %" PRIu64 "\n"), lino);
@@ -2304,7 +2389,7 @@ _("inode identifier %llu mismatch on inode %" PRIu64 "\n"),
                if (!uncertain)
                        do_warn(
 _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
-                               (__int64_t)be64_to_cpu(dino->di_size),
+                               (int64_t)be64_to_cpu(dino->di_size),
                                lino);
                if (verify_mode)
                        return 1;
@@ -2312,7 +2397,7 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
        }
 
        /*
-        * 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) {
@@ -2321,12 +2406,21 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
                 */
                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;
                }
@@ -2339,7 +2433,8 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
        _("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"));
@@ -2421,7 +2516,7 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
                                                XFS_DIFLAG_NOSYMLINKS);
                        }
                }
-               if (flags & (XFS_DIFLAG_REALTIME | XFS_XFLAG_EXTSIZE)) {
+               if (flags & (XFS_DIFLAG_REALTIME | FS_XFLAG_EXTSIZE)) {
                        /* must be a file */
                        if (di_mode && !S_ISREG(di_mode)) {
                                if (!uncertain) {
@@ -2430,7 +2525,7 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
                                                lino);
                                }
                                flags &= ~(XFS_DIFLAG_REALTIME |
-                                               XFS_XFLAG_EXTSIZE);
+                                               FS_XFLAG_EXTSIZE);
                        }
                }
                if (!verify_mode && flags != be16_to_cpu(dino->di_flags)) {
@@ -2443,6 +2538,109 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\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;
 
@@ -2454,8 +2652,17 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
         * 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 */
 
@@ -2471,6 +2678,12 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
                        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;
@@ -2505,26 +2718,52 @@ _("bad (negative) size %" PRId64 " on inode %" PRIu64 "\n"),
         * only regular files with REALTIME or EXTSIZE flags set can have
         * extsize set, or directories with EXTSZINHERIT.
         */
-       if (be32_to_cpu(dino->di_extsize) != 0) {
-               if ((type == XR_INO_RTDATA) ||
-                   (type == XR_INO_DIR && (be16_to_cpu(dino->di_flags) &
-                                       XFS_DIFLAG_EXTSZINHERIT)) ||
-                   (type == XR_INO_DATA && (be16_to_cpu(dino->di_flags) &
-                                XFS_DIFLAG_EXTSIZE)))  {
-                       /* s'okay */ ;
-               } else {
-                       do_warn(
-_("bad non-zero extent size %u for non-realtime/extsize inode %" PRIu64 ", "),
-                                       be32_to_cpu(dino->di_extsize), lino);
-                       if (!no_modify)  {
-                               do_warn(_("resetting to zero\n"));
-                               dino->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:
         */
@@ -2537,6 +2776,12 @@ _("bad non-zero extent size %u for non-realtime/extsize inode %" PRIu64 ", "),
        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
         */
@@ -2587,6 +2832,15 @@ _("bad non-zero extent size %u for non-realtime/extsize inode %" PRIu64 ", "),
                        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;
        }
@@ -2604,8 +2858,8 @@ _("bad non-zero extent size %u for non-realtime/extsize inode %" PRIu64 ", "),
 
 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;