Commit 2b30cc0b authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

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: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 366a0b8d
Loading
Loading
Loading
Loading
+31 −14
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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);

+3 −11
Original line number Diff line number Diff line
@@ -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;