From 0519f662c86a7a3396fc2eba079f180ac46cbf5e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 1 Jun 2015 11:17:09 +1000 Subject: [PATCH] xfs_repair: better checking of v5 metadata fields Check the UUID, owner, and block number fields during repair, looking for blocks that fail either the checksum or the data structure verifiers. For directories we can simply rebuild corrupt/broken index data, though for anything else we have to toss out the broken object. Signed-off-by: Darrick J. Wong Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- repair/dir2.c | 7 +++ repair/phase6.c | 118 +++++++++++++++++++++++++++++++++++++++++++++--- repair/scan.c | 19 +++++++- 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/repair/dir2.c b/repair/dir2.c index c6d618dcd..644c21402 100644 --- a/repair/dir2.c +++ b/repair/dir2.c @@ -198,6 +198,13 @@ _("bad dir magic number 0x%x in inode %" PRIu64 " bno = %u\n"), da_cursor->ino, bno); goto error_out; } + /* corrupt node; rebuild the dir. */ + if (bp->b_error == EFSBADCRC || bp->b_error == EFSCORRUPTED) { + do_warn( +_("corrupt tree block %u for directory inode %" PRIu64 "\n"), + bno, da_cursor->ino); + goto error_out; + } btree = xfs_da3_node_tree_p(node); if (nodehdr.count > mp->m_dir_node_ents) { libxfs_putbuf(bp); diff --git a/repair/phase6.c b/repair/phase6.c index c09b394b9..7a984bf5b 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -1925,6 +1925,66 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 " freetab->ents[db].s = 0; } +/* check v5 metadata */ +static int +__check_dir3_header( + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_ino_t ino, + __be64 owner, + __be64 blkno, + uuid_t *uuid) +{ + + /* verify owner */ + if (be64_to_cpu(owner) != ino) { + do_warn( +_("expected owner inode %" PRIu64 ", got %llu, directory block %" PRIu64 "\n"), + ino, be64_to_cpu(owner), bp->b_bn); + return 1; + } + /* verify block number */ + if (be64_to_cpu(blkno) != bp->b_bn) { + do_warn( +_("expected block %" PRIu64 ", got %llu, directory inode %" PRIu64 "\n"), + bp->b_bn, be64_to_cpu(blkno), ino); + return 1; + } + /* verify uuid */ + if (platform_uuid_compare(uuid, &mp->m_sb.sb_uuid) != 0) { + do_warn( +_("wrong FS UUID, directory inode %" PRIu64 " block %" PRIu64 "\n"), + ino, bp->b_bn); + return 1; + } + + return 0; +} + +static int +check_da3_header( + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_ino_t ino) +{ + struct xfs_da3_blkinfo *info = bp->b_addr; + + return __check_dir3_header(mp, bp, ino, info->owner, info->blkno, + &info->uuid); +} + +static int +check_dir3_header( + struct xfs_mount *mp, + struct xfs_buf *bp, + xfs_ino_t ino) +{ + struct xfs_dir3_blk_hdr *info = bp->b_addr; + + return __check_dir3_header(mp, bp, ino, info->owner, info->blkno, + &info->uuid); +} + /* * Check contents of leaf-form block. */ @@ -1981,6 +2041,15 @@ longform_dir2_check_leaf( libxfs_putbuf(bp); return 1; } + + if (leafhdr.magic == XFS_DIR3_LEAF1_MAGIC) { + error = check_da3_header(mp, bp, ip->i_ino); + if (error) { + libxfs_putbuf(bp); + return error; + } + } + seeval = dir_hash_see_all(hashtab, ents, leafhdr.count, leafhdr.stale); if (dir_hash_check(hashtab, ip, seeval)) { libxfs_putbuf(bp); @@ -2055,12 +2124,9 @@ longform_dir2_check_node( xfs_dir3_leaf_hdr_from_disk(&leafhdr, leaf); ents = xfs_dir3_leaf_ents_p(leaf); if (!(leafhdr.magic == XFS_DIR2_LEAFN_MAGIC || - leafhdr.magic == XFS_DIR3_LEAFN_MAGIC)) { - if (leafhdr.magic == XFS_DA_NODE_MAGIC || - leafhdr.magic == XFS_DA3_NODE_MAGIC) { - libxfs_putbuf(bp); - continue; - } + leafhdr.magic == XFS_DIR3_LEAFN_MAGIC || + leafhdr.magic == XFS_DA_NODE_MAGIC || + leafhdr.magic == XFS_DA3_NODE_MAGIC)) { do_warn( _("unknown magic number %#x for block %u in directory inode %" PRIu64 "\n"), leafhdr.magic, da_bno, ip->i_ino); @@ -2068,6 +2134,23 @@ longform_dir2_check_node( return 1; } + /* check v5 metadata */ + if (leafhdr.magic == XFS_DIR3_LEAFN_MAGIC || + leafhdr.magic == XFS_DA3_NODE_MAGIC) { + error = check_da3_header(mp, bp, ip->i_ino); + if (error) { + libxfs_putbuf(bp); + return error; + } + } + + /* ignore nodes */ + if (leafhdr.magic == XFS_DA_NODE_MAGIC || + leafhdr.magic == XFS_DA3_NODE_MAGIC) { + libxfs_putbuf(bp); + continue; + } + /* * If there's a validator error, we need to ensure that we got * the right ops on the buffer for when we write it back out. @@ -2121,6 +2204,14 @@ longform_dir2_check_node( libxfs_putbuf(bp); return 1; } + + if (freehdr.magic == XFS_DIR3_FREE_MAGIC) { + error = check_dir3_header(mp, bp, ip->i_ino); + if (error) { + libxfs_putbuf(bp); + return error; + } + } for (i = used = 0; i < freehdr.nvalid; i++) { if (i + freehdr.firstdb >= freetab->nents || freetab->ents[i + freehdr.firstdb].v != @@ -2212,6 +2303,7 @@ longform_dir2_entry_check(xfs_mount_t *mp, da_bno = (xfs_dablk_t)next_da_bno) { const struct xfs_buf_ops *ops; int error; + struct xfs_dir2_data_hdr *d; next_da_bno = da_bno + mp->m_dirblkfsbs - 1; if (bmap_next_offset(NULL, ip, &next_da_bno, XFS_DATA_FORK)) { @@ -2260,6 +2352,20 @@ longform_dir2_entry_check(xfs_mount_t *mp, } continue; } + + /* check v5 metadata */ + d = bplist[db]->b_addr; + if (be32_to_cpu(d->magic) == XFS_DIR3_BLOCK_MAGIC || + be32_to_cpu(d->magic) == XFS_DIR3_DATA_MAGIC) { + struct xfs_buf *bp = bplist[db]; + + error = check_dir3_header(mp, bp, ino); + if (error) { + fixit++; + continue; + } + } + longform_dir2_entry_check_data(mp, ip, num_illegal, need_dot, irec, ino_offset, &bplist[db], hashtab, &freetab, da_bno, isblock); diff --git a/repair/scan.c b/repair/scan.c index 12aa782e1..6508a47a5 100644 --- a/repair/scan.c +++ b/repair/scan.c @@ -225,7 +225,24 @@ _("expected level %d got %d in inode %" PRIu64 ", (%s fork) bmbt block %" PRIu64 do_warn( _("expected owner inode %" PRIu64 ", got %llu, bmbt block %" PRIu64 "\n"), ino, be64_to_cpu(block->bb_u.l.bb_owner), bno); - return(1); + return 1; + } + /* verify block number */ + if (be64_to_cpu(block->bb_u.l.bb_blkno) != + XFS_FSB_TO_DADDR(mp, bno)) { + do_warn( +_("expected block %" PRIu64 ", got %llu, bmbt block %" PRIu64 "\n"), + XFS_FSB_TO_DADDR(mp, bno), + be64_to_cpu(block->bb_u.l.bb_blkno), bno); + return 1; + } + /* verify uuid */ + if (platform_uuid_compare(&block->bb_u.l.bb_uuid, + &mp->m_sb.sb_uuid) != 0) { + do_warn( +_("wrong FS UUID, bmbt block %" PRIu64 "\n"), + bno); + return 1; } } -- 2.50.1