]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/dinode.c
libxfs: refactor manage_zones()
[thirdparty/xfsprogs-dev.git] / repair / dinode.c
index 89163b1ed0637c3f370f71de4899f7bf8b5c6155..c0a56daa0d64b0c03a2687a6e2ad44b6775eeb5d 100644 (file)
@@ -1,19 +1,7 @@
+// 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"
@@ -117,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(mp, 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_meta_uuid)) {
-               __dirty_no_modify_ret(dirty);
-               platform_uuid_copy(&dinoc->di_uuid, &mp->m_sb.sb_meta_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;
 }
 
 
@@ -466,21 +338,6 @@ _("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
@@ -488,8 +345,7 @@ _("malformed rt inode extent [%" PRIu64 " %" PRIu64 "] (fs rtext size = %u)\n"),
        for (b = irec->br_startblock; b < irec->br_startblock +
                        irec->br_blockcount; b += mp->m_sb.sb_rextsize)  {
                ext = (xfs_rtblock_t) b / mp->m_sb.sb_rextsize;
-               pwe = xfs_sb_version_hasextflgbit(&mp->m_sb) &&
-                               irec->br_state == XFS_EXT_UNWRITTEN &&
+               pwe = irec->br_state == XFS_EXT_UNWRITTEN &&
                                (b % mp->m_sb.sb_rextsize != 0);
 
                if (check_dups == 1)  {
@@ -525,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(
@@ -722,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"),
@@ -744,11 +603,10 @@ _("%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:
@@ -762,6 +620,7 @@ _("%s fork in ino %" PRIu64 " claims free block %" PRIu64 "\n"),
                        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);
@@ -769,20 +628,56 @@ _("%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 = add_rmap(mp, ino, whichfork, &irec);
+                       error = rmap_add_rec(mp, ino, whichfork, &irec);
                        if (error)
                                do_error(
 _("couldn't add reverse mapping\n")
@@ -847,43 +742,58 @@ scan_bmbt_reclist(
 }
 
 /*
- * 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;
-
-       if ((irec = find_inode_rec(mp, agno, agino)) == NULL)
-               return(NULL);
+       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;
 
-       size = MAX(1, XFS_FSB_TO_BB(mp,
+       /*
+        * 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));
-       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);
+       cluster_daddr = XFS_AGB_TO_DADDR(mp, agno,
+                       XFS_AGINO_TO_AGBNO(mp, cluster_agino));
+
+#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;
 }
 
 /*
@@ -903,7 +813,7 @@ process_btinode(
        int                     type,
        int                     *dirty,
        xfs_rfsblock_t          *tot,
-       __uint64_t              *nex,
+       uint64_t                *nex,
        blkmap_t                **blkmapp,
        int                     whichfork,
        int                     check_dups)
@@ -919,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);
@@ -966,7 +876,7 @@ _("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 = NULLFILEOFF;
 
@@ -1078,7 +988,7 @@ process_exinode(
        int                     type,
        int                     *dirty,
        xfs_rfsblock_t          *tot,
-       __uint64_t              *nex,
+       uint64_t                *nex,
        blkmap_t                **blkmapp,
        int                     whichfork,
        int                     check_dups)
@@ -1180,14 +1090,14 @@ process_symlink_extlist(xfs_mount_t *mp, xfs_ino_t lino, xfs_dinode_t *dino)
                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;
        }
 
@@ -1239,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')
@@ -1249,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 = 0;
+       char                    *quota_string = NULL;
+       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,
@@ -1302,6 +1325,13 @@ _("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"
@@ -1310,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)) {
@@ -1351,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
@@ -1359,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
@@ -1429,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, "
@@ -1566,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;
@@ -1580,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;
@@ -1589,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;
@@ -1598,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;
@@ -1705,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
@@ -1718,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;
                }
@@ -1792,8 +1842,8 @@ static int
 process_inode_blocks_and_extents(
        xfs_dinode_t    *dino,
        xfs_rfsblock_t  nblocks,
-       __uint64_t      nextents,
-       __uint64_t      anextents,
+       uint64_t        nextents,
+       uint64_t        anextents,
        xfs_ino_t       lino,
        int             *dirty)
 {
@@ -1883,7 +1933,7 @@ process_inode_data_fork(
        int             type,
        int             *dirty,
        xfs_rfsblock_t  *totblocks,
-       __uint64_t      *nextents,
+       uint64_t        *nextents,
        blkmap_t        **dblkmap,
        int             check_dups)
 {
@@ -1936,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;
        }
@@ -1992,7 +2042,7 @@ process_inode_attr_fork(
        int             type,
        int             *dirty,
        xfs_rfsblock_t  *atotblocks,
-       __uint64_t      *anextents,
+       uint64_t        *anextents,
        int             check_dups,
        int             extra_attr_check,
        int             *retval)
@@ -2060,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"));
@@ -2078,7 +2123,7 @@ process_inode_attr_fork(
                blkmap_free(ablkmap);
                *retval = 1;
 
-               return delete_attr_ok ? 0 : 1;
+               return 0;
        }
 
        if (check_dups)  {
@@ -2172,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
@@ -2203,8 +2270,8 @@ process_dinode_int(xfs_mount_t *mp,
        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;
@@ -2240,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)
@@ -2271,7 +2338,7 @@ process_dinode_int(xfs_mount_t *mp,
                }
        }
 
-       if (!xfs_dinode_good_version(mp, dino->di_version)) {
+       if (!libxfs_dinode_good_version(mp, dino->di_version)) {
                retval = 1;
                if (!uncertain)
                        do_warn(_("bad version number 0x%x on inode %" PRIu64 "%c"),
@@ -2298,7 +2365,8 @@ 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;
@@ -2321,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;
@@ -2329,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) {
@@ -2338,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;
                }
@@ -2356,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"));
@@ -2460,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;
 
@@ -2471,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 */
 
@@ -2488,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;
@@ -2522,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:
         */
@@ -2554,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
         */
@@ -2604,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;
        }
@@ -2621,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;