From: Darrick J. Wong Date: Wed, 22 Jan 2020 16:29:37 +0000 (-0500) Subject: xfs: always log corruption errors X-Git-Tag: v5.5.0-rc0~83 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a0264b735a2eb7aa9ea744d917b6d66cdfe94bcc;p=thirdparty%2Fxfsprogs-dev.git xfs: always log corruption errors Source kernel commit: a5155b870d687de1a5f07e774b49b1e8ef0f6f50 Make sure we log something to dmesg whenever we return -EFSCORRUPTED up the call stack. Signed-off-by: Darrick J. Wong Reviewed-by: Carlos Maiolino Reviewed-by: Christoph Hellwig Signed-off-by: Eric Sandeen --- diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h index 3a0282f17..90b41f078 100644 --- a/libxfs/libxfs_priv.h +++ b/libxfs/libxfs_priv.h @@ -539,6 +539,8 @@ void xfs_inode_verifier_error(struct xfs_inode *ip, int error, #define xfs_buf_verifier_error(bp,e,n,bu,bus,fa) \ xfs_verifier_error(bp, e, fa) +void +xfs_buf_corruption_error(struct xfs_buf *bp); /* XXX: this is clearly a bug - a shared header needs to export this */ /* xfs_rtalloc.c */ diff --git a/libxfs/util.c b/libxfs/util.c index 885dd42bf..69110076d 100644 --- a/libxfs/util.c +++ b/libxfs/util.c @@ -636,6 +636,19 @@ xfs_inode_verifier_error( ip->i_ino, name); } +/* + * Complain about the kinds of metadata corruption that we can't detect from a + * verifier, such as incorrect inter-block relationship data. Does not set + * bp->b_error. + */ +void +xfs_buf_corruption_error( + struct xfs_buf *bp) +{ + xfs_alert(NULL, "Metadata corruption detected at %p, %s block 0x%llx", + __return_address, bp->b_ops->name, bp->b_bn); +} + /* * This is called from I/O verifiers on v5 superblock filesystems. In the * kernel, it validates the metadata LSN parameter against the current LSN of diff --git a/libxfs/xfs_alloc.c b/libxfs/xfs_alloc.c index 7cb5159e3..36cd9238f 100644 --- a/libxfs/xfs_alloc.c +++ b/libxfs/xfs_alloc.c @@ -697,8 +697,10 @@ xfs_alloc_update_counters( xfs_trans_agblocks_delta(tp, len); if (unlikely(be32_to_cpu(agf->agf_freeblks) > - be32_to_cpu(agf->agf_length))) + be32_to_cpu(agf->agf_length))) { + xfs_buf_corruption_error(agbp); return -EFSCORRUPTED; + } xfs_alloc_log_agf(tp, agbp, XFS_AGF_FREEBLKS); return 0; @@ -1043,6 +1045,7 @@ xfs_alloc_ag_vextent_small( bp = xfs_btree_get_bufs(args->mp, args->tp, args->agno, fbno); if (!bp) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, args->mp); error = -EFSCORRUPTED; goto error; } @@ -2210,8 +2213,10 @@ xfs_free_agfl_block( return error; bp = xfs_btree_get_bufs(tp->t_mountp, tp, agno, agbno); - if (!bp) + if (!bp) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, tp->t_mountp); return -EFSCORRUPTED; + } xfs_trans_binval(tp, bp); return 0; diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c index fab37c103..92758f1ed 100644 --- a/libxfs/xfs_attr_leaf.c +++ b/libxfs/xfs_attr_leaf.c @@ -2342,8 +2342,10 @@ xfs_attr3_leaf_lookup_int( leaf = bp->b_addr; xfs_attr3_leaf_hdr_from_disk(args->geo, &ichdr, leaf); entries = xfs_attr3_leaf_entryp(leaf); - if (ichdr.count >= args->geo->blksize / 8) + if (ichdr.count >= args->geo->blksize / 8) { + xfs_buf_corruption_error(bp); return -EFSCORRUPTED; + } /* * Binary search. (note: small blocks will skip this loop) @@ -2359,10 +2361,14 @@ xfs_attr3_leaf_lookup_int( else break; } - if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count))) + if (!(probe >= 0 && (!ichdr.count || probe < ichdr.count))) { + xfs_buf_corruption_error(bp); return -EFSCORRUPTED; - if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval)) + } + if (!(span <= 4 || be32_to_cpu(entry->hashval) == hashval)) { + xfs_buf_corruption_error(bp); return -EFSCORRUPTED; + } /* * Since we may have duplicate hashval's, find the first matching diff --git a/libxfs/xfs_bmap.c b/libxfs/xfs_bmap.c index d43f18afb..bd117a30a 100644 --- a/libxfs/xfs_bmap.c +++ b/libxfs/xfs_bmap.c @@ -722,6 +722,7 @@ xfs_bmap_extents_to_btree( xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT, 1L); abp = xfs_btree_get_bufl(mp, tp, args.fsbno); if (!abp) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); error = -EFSCORRUPTED; goto out_unreserve_dquot; } @@ -1077,6 +1078,7 @@ xfs_bmap_add_attrfork( if (XFS_IFORK_Q(ip)) goto trans_cancel; if (ip->i_d.di_anextents != 0) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); error = -EFSCORRUPTED; goto trans_cancel; } @@ -1330,6 +1332,7 @@ xfs_bmap_last_before( case XFS_DINODE_FMT_EXTENTS: break; default: + ASSERT(0); return -EFSCORRUPTED; } @@ -1430,8 +1433,10 @@ xfs_bmap_last_offset( return 0; if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE && - XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS) + XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS) { + ASSERT(0); return -EFSCORRUPTED; + } error = xfs_bmap_last_extent(NULL, ip, whichfork, &rec, &is_empty); if (error || is_empty) @@ -5822,6 +5827,7 @@ xfs_bmap_insert_extents( del_cursor); if (stop_fsb >= got.br_startoff + got.br_blockcount) { + ASSERT(0); error = -EFSCORRUPTED; goto del_cursor; } diff --git a/libxfs/xfs_btree.c b/libxfs/xfs_btree.c index 5955ae364..5a04d3bd3 100644 --- a/libxfs/xfs_btree.c +++ b/libxfs/xfs_btree.c @@ -1815,6 +1815,7 @@ xfs_btree_lookup_get_block( out_bad: *blkp = NULL; + xfs_buf_corruption_error(bp); xfs_trans_brelse(cur->bc_tp, bp); return -EFSCORRUPTED; } @@ -1862,8 +1863,10 @@ xfs_btree_lookup( XFS_BTREE_STATS_INC(cur, lookup); /* No such thing as a zero-level tree. */ - if (cur->bc_nlevels == 0) + if (cur->bc_nlevels == 0) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, cur->bc_mp); return -EFSCORRUPTED; + } block = NULL; keyno = 0; diff --git a/libxfs/xfs_da_btree.c b/libxfs/xfs_da_btree.c index e7af4464c..93a500da6 100644 --- a/libxfs/xfs_da_btree.c +++ b/libxfs/xfs_da_btree.c @@ -501,6 +501,7 @@ xfs_da3_split( node = oldblk->bp->b_addr; if (node->hdr.info.forw) { if (be32_to_cpu(node->hdr.info.forw) != addblk->blkno) { + xfs_buf_corruption_error(oldblk->bp); error = -EFSCORRUPTED; goto out; } @@ -513,6 +514,7 @@ xfs_da3_split( node = oldblk->bp->b_addr; if (node->hdr.info.back) { if (be32_to_cpu(node->hdr.info.back) != addblk->blkno) { + xfs_buf_corruption_error(oldblk->bp); error = -EFSCORRUPTED; goto out; } @@ -1538,8 +1540,10 @@ xfs_da3_node_lookup_int( break; } - if (magic != XFS_DA_NODE_MAGIC && magic != XFS_DA3_NODE_MAGIC) + if (magic != XFS_DA_NODE_MAGIC && magic != XFS_DA3_NODE_MAGIC) { + xfs_buf_corruption_error(blk->bp); return -EFSCORRUPTED; + } blk->magic = XFS_DA_NODE_MAGIC; @@ -1551,15 +1555,18 @@ xfs_da3_node_lookup_int( btree = dp->d_ops->node_tree_p(node); /* Tree taller than we can handle; bail out! */ - if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) + if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) { + xfs_buf_corruption_error(blk->bp); return -EFSCORRUPTED; + } /* Check the level from the root. */ if (blkno == args->geo->leafblk) expected_level = nodehdr.level - 1; - else if (expected_level != nodehdr.level) + else if (expected_level != nodehdr.level) { + xfs_buf_corruption_error(blk->bp); return -EFSCORRUPTED; - else + } else expected_level--; max = nodehdr.count; @@ -1609,12 +1616,17 @@ xfs_da3_node_lookup_int( } /* We can't point back to the root. */ - if (blkno == args->geo->leafblk) + if (blkno == args->geo->leafblk) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, + dp->i_mount); return -EFSCORRUPTED; + } } - if (expected_level != 0) + if (expected_level != 0) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, dp->i_mount); return -EFSCORRUPTED; + } /* * A leaf block that ends in the hashval that we are interested in diff --git a/libxfs/xfs_dir2.c b/libxfs/xfs_dir2.c index b3fbc4d59..f34a55de8 100644 --- a/libxfs/xfs_dir2.c +++ b/libxfs/xfs_dir2.c @@ -598,8 +598,10 @@ xfs_dir2_isblock( if ((rval = xfs_bmap_last_offset(args->dp, &last, XFS_DATA_FORK))) return rval; rval = XFS_FSB_TO_B(args->dp->i_mount, last) == args->geo->blksize; - if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize) + if (rval != 0 && args->dp->i_d.di_size != args->geo->blksize) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, args->dp->i_mount); return -EFSCORRUPTED; + } *vp = rval; return 0; } diff --git a/libxfs/xfs_dir2_leaf.c b/libxfs/xfs_dir2_leaf.c index ff4a04ef0..7a1e42840 100644 --- a/libxfs/xfs_dir2_leaf.c +++ b/libxfs/xfs_dir2_leaf.c @@ -1341,8 +1341,10 @@ xfs_dir2_leaf_removename( oldbest = be16_to_cpu(bf[0].length); ltp = xfs_dir2_leaf_tail_p(args->geo, leaf); bestsp = xfs_dir2_leaf_bests_p(ltp); - if (be16_to_cpu(bestsp[db]) != oldbest) + if (be16_to_cpu(bestsp[db]) != oldbest) { + xfs_buf_corruption_error(lbp); return -EFSCORRUPTED; + } /* * Mark the former data entry unused. */ diff --git a/libxfs/xfs_dir2_node.c b/libxfs/xfs_dir2_node.c index d3dea3f1c..783492a29 100644 --- a/libxfs/xfs_dir2_node.c +++ b/libxfs/xfs_dir2_node.c @@ -370,8 +370,10 @@ xfs_dir2_leaf_to_node( leaf = lbp->b_addr; ltp = xfs_dir2_leaf_tail_p(args->geo, leaf); if (be32_to_cpu(ltp->bestcount) > - (uint)dp->i_d.di_size / args->geo->blksize) + (uint)dp->i_d.di_size / args->geo->blksize) { + xfs_buf_corruption_error(lbp); return -EFSCORRUPTED; + } /* * Copy freespace entries from the leaf block to the new block. @@ -442,8 +444,10 @@ xfs_dir2_leafn_add( * Quick check just to make sure we are not going to index * into other peoples memory */ - if (index < 0) + if (index < 0) { + xfs_buf_corruption_error(bp); return -EFSCORRUPTED; + } /* * If there are already the maximum number of leaf entries in @@ -736,8 +740,10 @@ xfs_dir2_leafn_lookup_for_entry( ents = dp->d_ops->leaf_ents_p(leaf); xfs_dir3_leaf_check(dp, bp); - if (leafhdr.count <= 0) + if (leafhdr.count <= 0) { + xfs_buf_corruption_error(bp); return -EFSCORRUPTED; + } /* * Look up the hash value in the leaf entries. diff --git a/libxfs/xfs_inode_fork.c b/libxfs/xfs_inode_fork.c index 58ba5a56e..1d726650f 100644 --- a/libxfs/xfs_inode_fork.c +++ b/libxfs/xfs_inode_fork.c @@ -73,11 +73,15 @@ xfs_iformat_fork( error = xfs_iformat_btree(ip, dip, XFS_DATA_FORK); break; default: + xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, + dip, sizeof(*dip), __this_address); return -EFSCORRUPTED; } break; default: + xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, + sizeof(*dip), __this_address); return -EFSCORRUPTED; } if (error) @@ -108,6 +112,8 @@ xfs_iformat_fork( error = xfs_iformat_btree(ip, dip, XFS_ATTR_FORK); break; default: + xfs_inode_verifier_error(ip, error, __func__, dip, + sizeof(*dip), __this_address); error = -EFSCORRUPTED; break; } diff --git a/libxfs/xfs_refcount.c b/libxfs/xfs_refcount.c index 8b810a30e..c68a018ad 100644 --- a/libxfs/xfs_refcount.c +++ b/libxfs/xfs_refcount.c @@ -1589,8 +1589,10 @@ xfs_refcount_recover_extent( struct list_head *debris = priv; struct xfs_refcount_recovery *rr; - if (be32_to_cpu(rec->refc.rc_refcount) != 1) + if (be32_to_cpu(rec->refc.rc_refcount) != 1) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, cur->bc_mp); return -EFSCORRUPTED; + } rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), 0); xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec); diff --git a/libxfs/xfs_rtbitmap.c b/libxfs/xfs_rtbitmap.c index ef6f8679f..84879c5ce 100644 --- a/libxfs/xfs_rtbitmap.c +++ b/libxfs/xfs_rtbitmap.c @@ -15,7 +15,6 @@ #include "xfs_bmap.h" #include "xfs_trans.h" - /* * Realtime allocator bitmap functions shared with userspace. */ @@ -69,8 +68,10 @@ xfs_rtbuf_get( if (error) return error; - if (nmap == 0 || !xfs_bmap_is_real_extent(&map)) + if (nmap == 0 || !xfs_bmap_is_real_extent(&map)) { + XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp); return -EFSCORRUPTED; + } ASSERT(map.br_startblock != NULLFSBLOCK); error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp,