[PATCH v2 01/16] xfs_repair: fix missing dir buffer corruption checks

Darrick J. Wong
2020-05-12T17:29:44Z

From: Darrick J. Wong <darrick.wong@oracle.com>

The da_read_buf() function operates in "salvage" mode, which means that
if the verifiers fail, it will return a buffer with b_error set. The
callers of da_read_buf, however, do not adequately check for verifier
errors, which means that repair can fail to flag a corrupt filesystem.

Fix the callers to do this properly. The dabtree block walker and the
dabtree path checker functions to complain any time the da node / leafn
verifiers fail. Fix the directory block walking functions to complain
about EFSCORRUPTED, since they already dealt with EFSBADCRC.

Found by running xfs/496 against lhdr.stale = middlebit.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: check for corruption before checking magics; expand corruption
checks to other da_read_buf callers
---
repair/da_util.c | 25 ++++++++++++++++---------
repair/dir2.c | 21 +++++++++++++++++++++
2 files changed, 37 insertions(+), 9 deletions(-)

diff --git a/repair/da_util.c b/repair/da_util.c
index 5061880f..7239c2e2 100644
--- a/repair/da_util.c
+++ b/repair/da_util.c
@@ -134,6 +134,15 @@ _("can't read %s block %u for inode %" PRIu64 "\n"),
goto error_out;
}

+ /* corrupt leafn/node; rebuild the dir. */
+ if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) {
+ do_warn(
+_("corrupt %s tree block %u for inode %" PRIu64 "\n"),
+ FORKNAME(whichfork), bno, da_cursor->ino);
+ libxfs_buf_relse(bp);
+ goto error_out;
+ }
+
node = bp->b_addr;
libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);

@@ -160,15 +169,6 @@ _("bad %s magic number 0x%x in inode %" PRIu64 " bno = %u\n"),
goto error_out;
}

- /* corrupt node; rebuild the dir. */
- if (bp->b_error == -EFSBADCRC || bp->b_error == -EFSCORRUPTED) {
- libxfs_buf_relse(bp);
- do_warn(
-_("corrupt %s tree block %u for inode %" PRIu64 "\n"),
- FORKNAME(whichfork), bno, da_cursor->ino);
- goto error_out;
- }
-
if (nodehdr.count > geo->node_ents) {
do_warn(
_("bad %s record count in inode %" PRIu64 ", count = %d, max = %d\n"),
@@ -562,6 +562,13 @@ _("can't read %s block %u for inode %" PRIu64 "\n"),
FORKNAME(whichfork), dabno, cursor->ino);
return 1;
}
+ if (bp->b_error == -EFSCORRUPTED || bp->b_error == -EFSBADCRC) {
+ do_warn(
+_("corrupt %s tree block %u for inode %" PRIu64 "\n"),
+ FORKNAME(whichfork), dabno, cursor->ino);
+ libxfs_buf_relse(bp);
+ return 1;
+ }

newnode = bp->b_addr;
libxfs_da3_node_hdr_from_disk(mp, &nodehdr, newnode);
diff --git a/repair/dir2.c b/repair/dir2.c
index cbbce601..b374bc7b 100644
--- a/repair/dir2.c
+++ b/repair/dir2.c
@@ -983,6 +983,13 @@ _("can't read block %u for directory inode %" PRIu64 "\n"),
mp->m_dir_geo->datablk, ino);
return 1;
}
+ if (bp->b_error == -EFSCORRUPTED) {
+ do_warn(
+_("corrupt directory block %u for inode %" PRIu64 "\n"),
+ mp->m_dir_geo->datablk, ino);
+ libxfs_buf_relse(bp);
+ return 1;
+ }
/*
* Verify the block
*/
@@ -1122,6 +1129,13 @@ _("can't read file block %u for directory inode %" PRIu64 "\n"),
da_bno, ino);
goto error_out;
}
+ if (bp->b_error == -EFSCORRUPTED) {
+ do_warn(
+_("corrupt directory leafn block %u for inode %" PRIu64 "\n"),
+ da_bno, ino);
+ libxfs_buf_relse(bp);
+ goto error_out;
+ }
leaf = bp->b_addr;
libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf);
/*
@@ -1324,6 +1338,13 @@ _("can't read block %" PRIu64 " for directory inode %" PRIu64 "\n"),
dbno, ino);
continue;
}
+ if (bp->b_error == -EFSCORRUPTED) {
+ do_warn(
+_("corrupt directory data block %lu for inode %" PRIu64 "\n"),
+ dbno, ino);
+ libxfs_buf_relse(bp);
+ continue;
+ }
data = bp->b_addr;
if (!(be32_to_cpu(data->magic) == XFS_DIR2_DATA_MAGIC ||
be32_to_cpu(data->magic) == XFS_DIR3_DATA_MAGIC))