]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
repair: validate ir_count field for sparse format records
authorBrian Foster <bfoster@redhat.com>
Thu, 30 Jul 2015 23:18:22 +0000 (09:18 +1000)
committerDave Chinner <david@fromorbit.com>
Thu, 30 Jul 2015 23:18:22 +0000 (09:18 +1000)
Sparse format inobt records contain an additional count field that
records the number of physical inodes tracked by the record. Verify the
count is internally consistent according to the holemask, similar to how
freecount is validated against the free mask.

Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
repair/scan.c

index 16bf00ff5f9c0d4496488bfd359819e357684879..e3895c2035c758bdfc788033b317bd0185d00810 100644 (file)
@@ -775,6 +775,7 @@ scan_single_ino_chunk(
        xfs_agblock_t           agbno;
        int                     j;
        int                     nfree;
+       int                     ninodes;
        int                     off;
        int                     state;
        ino_tree_node_t         *ino_rec = NULL;
@@ -926,7 +927,7 @@ _("inode rec for ino %" PRIu64 " (%d/%d) overlaps existing rec (start %d/%d)\n")
         * Mark sparse inodes as such in the in-core tree. Verify that sparse
         * inodes are free and that freecount is consistent with the free mask.
         */
-       nfree = 0;
+       nfree = ninodes = 0;
        for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
                if (ino_issparse(rp, j)) {
                        if (!suspect && !XFS_INOBT_IS_FREE_DISK(rp, j)) {
@@ -939,9 +940,11 @@ _("ir_holemask/ir_free mismatch, inode chunk %d/%u, holemask 0x%x free 0x%llx\n"
                        }
                        if (!suspect && ino_rec)
                                set_inode_sparse(ino_rec, j);
-               } else if (XFS_INOBT_IS_FREE_DISK(rp, j)) {
-                       /* freecount only tracks non-sparse inos */
-                       nfree++;
+               } else {
+                       /* count fields track non-sparse inos */
+                       if (XFS_INOBT_IS_FREE_DISK(rp, j))
+                               nfree++;
+                       ninodes++;
                }
        }
 
@@ -951,6 +954,14 @@ _("ir_freecount/free mismatch, inode chunk %d/%u, freecount %d nfree %d\n"),
                        agno, ino, freecount, nfree);
        }
 
+       /* verify sparse record formats have a valid inode count */
+       if (xfs_sb_version_hassparseinodes(&mp->m_sb) &&
+           ninodes != rp->ir_u.sp.ir_count) {
+               do_warn(
+_("invalid inode count, inode chunk %d/%u, count %d ninodes %d\n"),
+                       agno, ino, rp->ir_u.sp.ir_count, ninodes);
+       }
+
        return suspect;
 }
 
@@ -965,6 +976,7 @@ scan_single_finobt_chunk(
        xfs_agblock_t           agbno;
        int                     j;
        int                     nfree;
+       int                     ninodes;
        int                     off;
        int                     state;
        ino_tree_node_t         *ino_rec = NULL;
@@ -1086,11 +1098,13 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
                        return ++suspect;
                }
 
-               nfree = 0;
+               nfree = ninodes = 0;
                for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
                        int isfree = XFS_INOBT_IS_FREE_DISK(rp, j);
                        int issparse = ino_issparse(rp, j);
 
+                       if (!issparse)
+                               ninodes++;
                        if (isfree && !issparse)
                                nfree++;
 
@@ -1152,7 +1166,7 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
         * Mark sparse inodes as such in the in-core tree. Verify that sparse
         * inodes are free and that freecount is consistent with the free mask.
         */
-       nfree = 0;
+       nfree = ninodes = 0;
        for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
                if (ino_issparse(rp, j)) {
                        if (!suspect && !XFS_INOBT_IS_FREE_DISK(rp, j)) {
@@ -1165,10 +1179,13 @@ _("finobt ir_holemask/ir_free mismatch, inode chunk %d/%u, holemask 0x%x free 0x
                        }
                        if (!suspect && ino_rec)
                                set_inode_sparse(ino_rec, j);
-               } else if (XFS_INOBT_IS_FREE_DISK(rp, j)) {
-                       /* freecount only tracks non-sparse inos */
-                       nfree++;
+               } else {
+                       /* count fields track non-sparse inos */
+                       if (XFS_INOBT_IS_FREE_DISK(rp, j))
+                               nfree++;
+                       ninodes++;
                }
+
        }
 
 check_freecount:
@@ -1195,6 +1212,14 @@ _("finobt ir_freecount/free mismatch, inode chunk %d/%u, freecount %d nfree %d\n
 _("finobt record with no free inodes, inode chunk %d/%u\n"), agno, ino);
        }
 
+       /* verify sparse record formats have a valid inode count */
+       if (xfs_sb_version_hassparseinodes(&mp->m_sb) &&
+           ninodes != rp->ir_u.sp.ir_count) {
+               do_warn(
+_("invalid inode count, inode chunk %d/%u, count %d ninodes %d\n"),
+                       agno, ino, rp->ir_u.sp.ir_count, ninodes);
+       }
+
        return suspect;
 }