[PATCH 19/28] repair: scan sparse finobt records correctly

Brian Foster
2015-06-02T18:42:06Z

The finobt scan performs similar checks as to the inobt scan, including
internal record consistency checks, consistency with inobt records,
inode block state, etc. Various parts of this mechanism also assume
fully allocated inode records and thus lead to false errors with sparse
records.

Update the finobt scan to detect and handle sparse inode records
correctly. As for the inobt, do not assume that blocks associated with
sparse regions are allocated for inodes and do not account sparse inodes
against the freecount. Additionally, verify that sparse state is
consistent with the in-core record and set up any new in-core records
that might have been missing from the inobt correctly.

Signed-off-by: Brian Foster <bfoster@redhat.com>
---
repair/scan.c | 51 +++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 43 insertions(+), 8 deletions(-)

diff --git a/repair/scan.c b/repair/scan.c
index 5b67e15..52c05e2 100644
--- a/repair/scan.c
+++ b/repair/scan.c
@@ -950,7 +950,8 @@ scan_single_finobt_chunk(
int nfree;
int off;
int state;
- ino_tree_node_t *first_rec, *last_rec, *ino_rec;
+ ino_tree_node_t *ino_rec = NULL;
+ ino_tree_node_t *first_rec, *last_rec;
int freecount;

ino = be32_to_cpu(rp->ir_startino);
@@ -1014,8 +1015,19 @@ _("bad ending inode # (%" PRIu64 " (0x%x 0x%zx)) in finobt rec, skipping rec\n")
j < XFS_INODES_PER_CHUNK;
j += mp->m_sb.sb_inopblock) {
agbno = XFS_AGINO_TO_AGBNO(mp, ino + j);
-
state = get_bmap(agno, agbno);
+
+ /* sparse inodes should not refer to inode blocks */
+ if (ino_issparse(rp, j)) {
+ if (state == XR_E_INO) {
+ do_warn(
+_("sparse inode chunk claims inode block, finobt block - agno %d, bno %d, inopb %d\n"),
+ agno, agbno, mp->m_sb.sb_inopblock);
+ suspect++;
+ }
+ continue;
+ }
+
if (state == XR_E_INO) {
continue;
} else if ((state == XR_E_UNKNOWN) ||
@@ -1060,8 +1072,9 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
nfree = 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 (isfree)
+ if (isfree && !issparse)
nfree++;

/*
@@ -1071,6 +1084,10 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
if (!suspect &&
isfree != is_inode_free(first_rec, j))
suspect++;
+
+ if (!suspect &&
+ issparse != is_inode_sparse(first_rec, j))
+ suspect++;
}

goto check_freecount;
@@ -1088,16 +1105,13 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
* inodes previously inserted into the uncertain tree should be
* superceded by these when the uncertain tree is processed
*/
- nfree = 0;
if (XFS_INOBT_IS_FREE_DISK(rp, 0)) {
- nfree++;
ino_rec = set_inode_free_alloc(mp, agno, ino);
} else {
ino_rec = set_inode_used_alloc(mp, agno, ino);
}
for (j = 1; j < XFS_INODES_PER_CHUNK; j++) {
if (XFS_INOBT_IS_FREE_DISK(rp, j)) {
- nfree++;
set_inode_free(ino_rec, j);
} else {
set_inode_used(ino_rec, j);
@@ -1108,17 +1122,38 @@ _("finobt rec for ino %" PRIu64 " (%d/%u) does not match existing rec (%d/%d)\n"
* this should handle the case where the inobt scan may have
* already added uncertain inodes
*/
- nfree = 0;
for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
if (XFS_INOBT_IS_FREE_DISK(rp, j)) {
add_aginode_uncertain(mp, agno, ino + j, 1);
- nfree++;
} else {
add_aginode_uncertain(mp, agno, ino + j, 0);
}
}
}

+ /*
+ * 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;
+ for (j = 0; j < XFS_INODES_PER_CHUNK; j++) {
+ if (ino_issparse(rp, j)) {
+ if (!suspect && !XFS_INOBT_IS_FREE_DISK(rp, j)) {
+ do_warn(
+_("finobt ir_holemask/ir_free mismatch, inode chunk %d/%u, holemask 0x%x free 0x%llx\n"),
+ agno, ino,
+ be16_to_cpu(rp->ir_u.sp.ir_holemask),
+ be64_to_cpu(rp->ir_free));
+ suspect++;
+ }
+ 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++;
+ }
+ }
+
check_freecount:

/*