xfs: standardize ondisk to incore conversion for refcount btrees
Create a xfs_refcount_check_irec function to detect corruption in btree records. Fix all xfs_refcount_btrec_to_irec callsites to call the new helper and bubble up corruption reports. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Dave Chinner <dchinner@redhat.com>
This commit is contained in:
parent
366a0b8d49
commit
2b30cc0bf0
|
@ -120,6 +120,30 @@ xfs_refcount_btrec_to_irec(
|
|||
irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
|
||||
}
|
||||
|
||||
/* Simple checks for refcount records. */
|
||||
xfs_failaddr_t
|
||||
xfs_refcount_check_irec(
|
||||
struct xfs_btree_cur *cur,
|
||||
const struct xfs_refcount_irec *irec)
|
||||
{
|
||||
struct xfs_perag *pag = cur->bc_ag.pag;
|
||||
|
||||
if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
|
||||
return __this_address;
|
||||
|
||||
if (!xfs_refcount_check_domain(irec))
|
||||
return __this_address;
|
||||
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount))
|
||||
return __this_address;
|
||||
|
||||
if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
|
||||
return __this_address;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the data from the pointed-to record.
|
||||
*/
|
||||
|
@ -132,6 +156,7 @@ xfs_refcount_get_rec(
|
|||
struct xfs_mount *mp = cur->bc_mp;
|
||||
struct xfs_perag *pag = cur->bc_ag.pag;
|
||||
union xfs_btree_rec *rec;
|
||||
xfs_failaddr_t fa;
|
||||
int error;
|
||||
|
||||
error = xfs_btree_get_rec(cur, &rec, stat);
|
||||
|
@ -139,17 +164,8 @@ xfs_refcount_get_rec(
|
|||
return error;
|
||||
|
||||
xfs_refcount_btrec_to_irec(rec, irec);
|
||||
if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN)
|
||||
goto out_bad_rec;
|
||||
|
||||
if (!xfs_refcount_check_domain(irec))
|
||||
goto out_bad_rec;
|
||||
|
||||
/* check for valid extent range, including overflow */
|
||||
if (!xfs_verify_agbext(pag, irec->rc_startblock, irec->rc_blockcount))
|
||||
goto out_bad_rec;
|
||||
|
||||
if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT)
|
||||
fa = xfs_refcount_check_irec(cur, irec);
|
||||
if (fa)
|
||||
goto out_bad_rec;
|
||||
|
||||
trace_xfs_refcount_get(cur->bc_mp, pag->pag_agno, irec);
|
||||
|
@ -157,8 +173,8 @@ xfs_refcount_get_rec(
|
|||
|
||||
out_bad_rec:
|
||||
xfs_warn(mp,
|
||||
"Refcount BTree record corruption in AG %d detected!",
|
||||
pag->pag_agno);
|
||||
"Refcount BTree record corruption in AG %d detected at %pS!",
|
||||
pag->pag_agno, fa);
|
||||
xfs_warn(mp,
|
||||
"Start block 0x%x, block count 0x%x, references 0x%x",
|
||||
irec->rc_startblock, irec->rc_blockcount, irec->rc_refcount);
|
||||
|
@ -1871,7 +1887,8 @@ xfs_refcount_recover_extent(
|
|||
INIT_LIST_HEAD(&rr->rr_list);
|
||||
xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
|
||||
|
||||
if (XFS_IS_CORRUPT(cur->bc_mp,
|
||||
if (xfs_refcount_check_irec(cur, &rr->rr_rrec) != NULL ||
|
||||
XFS_IS_CORRUPT(cur->bc_mp,
|
||||
rr->rr_rrec.rc_domain != XFS_REFC_DOMAIN_COW)) {
|
||||
kfree(rr);
|
||||
return -EFSCORRUPTED;
|
||||
|
|
|
@ -117,6 +117,8 @@ extern int xfs_refcount_has_record(struct xfs_btree_cur *cur,
|
|||
union xfs_btree_rec;
|
||||
extern void xfs_refcount_btrec_to_irec(const union xfs_btree_rec *rec,
|
||||
struct xfs_refcount_irec *irec);
|
||||
xfs_failaddr_t xfs_refcount_check_irec(struct xfs_btree_cur *cur,
|
||||
const struct xfs_refcount_irec *irec);
|
||||
extern int xfs_refcount_insert(struct xfs_btree_cur *cur,
|
||||
struct xfs_refcount_irec *irec, int *stat);
|
||||
|
||||
|
|
|
@ -340,24 +340,16 @@ xchk_refcountbt_rec(
|
|||
{
|
||||
struct xfs_refcount_irec irec;
|
||||
xfs_agblock_t *cow_blocks = bs->private;
|
||||
struct xfs_perag *pag = bs->cur->bc_ag.pag;
|
||||
|
||||
xfs_refcount_btrec_to_irec(rec, &irec);
|
||||
|
||||
/* Check the domain and refcount are not incompatible. */
|
||||
if (!xfs_refcount_check_domain(&irec))
|
||||
if (xfs_refcount_check_irec(bs->cur, &irec) != NULL) {
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (irec.rc_domain == XFS_REFC_DOMAIN_COW)
|
||||
(*cow_blocks) += irec.rc_blockcount;
|
||||
|
||||
/* Check the extent. */
|
||||
if (!xfs_verify_agbext(pag, irec.rc_startblock, irec.rc_blockcount))
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
|
||||
if (irec.rc_refcount == 0)
|
||||
xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
|
||||
|
||||
xchk_refcountbt_xref(bs->sc, &irec);
|
||||
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue