Commit 35e3b9a1 authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: standardize ondisk to incore conversion for free space btrees



Create a xfs_alloc_btrec_to_irec function to convert an ondisk record to
an incore record, and a xfs_alloc_check_irec function to detect
corruption.  Replace all the open-coded logic with calls to the new
helpers and bubble up corruption reports.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
parent 88accf17
Loading
Loading
Loading
Loading
+43 −13
Original line number Diff line number Diff line
@@ -233,6 +233,34 @@ xfs_alloc_update(
	return xfs_btree_update(cur, &rec);
}

/* Convert the ondisk btree record to its incore representation. */
void
xfs_alloc_btrec_to_irec(
	const union xfs_btree_rec	*rec,
	struct xfs_alloc_rec_incore	*irec)
{
	irec->ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
	irec->ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
}

/* Simple checks for free space records. */
xfs_failaddr_t
xfs_alloc_check_irec(
	struct xfs_btree_cur		*cur,
	const struct xfs_alloc_rec_incore *irec)
{
	struct xfs_perag		*pag = cur->bc_ag.pag;

	if (irec->ar_blockcount == 0)
		return __this_address;

	/* check for valid extent range, including overflow */
	if (!xfs_verify_agbext(pag, irec->ar_startblock, irec->ar_blockcount))
		return __this_address;

	return NULL;
}

/*
 * Get the data from the pointed-to record.
 */
@@ -243,34 +271,34 @@ xfs_alloc_get_rec(
	xfs_extlen_t		*len,	/* output: length of extent */
	int			*stat)	/* output: success/failure */
{
	struct xfs_alloc_rec_incore irec;
	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);
	if (error || !(*stat))
		return error;

	*bno = be32_to_cpu(rec->alloc.ar_startblock);
	*len = be32_to_cpu(rec->alloc.ar_blockcount);

	if (*len == 0)
		goto out_bad_rec;

	/* check for valid extent range, including overflow */
	if (!xfs_verify_agbext(pag, *bno, *len))
	xfs_alloc_btrec_to_irec(rec, &irec);
	fa = xfs_alloc_check_irec(cur, &irec);
	if (fa)
		goto out_bad_rec;

	*bno = irec.ar_startblock;
	*len = irec.ar_blockcount;
	return 0;

out_bad_rec:
	xfs_warn(mp,
		"%s Freespace BTree record corruption in AG %d detected!",
		"%s Freespace BTree record corruption in AG %d detected at %pS!",
		cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size",
		pag->pag_agno);
		pag->pag_agno, fa);
	xfs_warn(mp,
		"start block 0x%x block count 0x%x", *bno, *len);
		"start block 0x%x block count 0x%x", irec.ar_startblock,
		irec.ar_blockcount);
	return -EFSCORRUPTED;
}

@@ -3665,8 +3693,10 @@ xfs_alloc_query_range_helper(
	struct xfs_alloc_query_range_info	*query = priv;
	struct xfs_alloc_rec_incore		irec;

	irec.ar_startblock = be32_to_cpu(rec->alloc.ar_startblock);
	irec.ar_blockcount = be32_to_cpu(rec->alloc.ar_blockcount);
	xfs_alloc_btrec_to_irec(rec, &irec);
	if (xfs_alloc_check_irec(cur, &irec) != NULL)
		return -EFSCORRUPTED;

	return query->fn(cur, &irec, query->priv);
}

+6 −0
Original line number Diff line number Diff line
@@ -181,6 +181,12 @@ xfs_alloc_get_rec(
	xfs_extlen_t		*len,	/* output: length of extent */
	int			*stat);	/* output: success/failure */

union xfs_btree_rec;
void xfs_alloc_btrec_to_irec(const union xfs_btree_rec *rec,
		struct xfs_alloc_rec_incore *irec);
xfs_failaddr_t xfs_alloc_check_irec(struct xfs_btree_cur *cur,
		const struct xfs_alloc_rec_incore *irec);

int xfs_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
		struct xfs_buf **agfbpp);
int xfs_alloc_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags,
+12 −12
Original line number Diff line number Diff line
@@ -78,9 +78,11 @@ xchk_allocbt_xref_other(
STATIC void
xchk_allocbt_xref(
	struct xfs_scrub	*sc,
	xfs_agblock_t		agbno,
	xfs_extlen_t		len)
	const struct xfs_alloc_rec_incore *irec)
{
	xfs_agblock_t		agbno = irec->ar_startblock;
	xfs_extlen_t		len = irec->ar_blockcount;

	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
		return;

@@ -96,17 +98,15 @@ xchk_allocbt_rec(
	struct xchk_btree		*bs,
	const union xfs_btree_rec	*rec)
{
	struct xfs_perag	*pag = bs->cur->bc_ag.pag;
	xfs_agblock_t		bno;
	xfs_extlen_t		len;
	struct xfs_alloc_rec_incore	irec;

	bno = be32_to_cpu(rec->alloc.ar_startblock);
	len = be32_to_cpu(rec->alloc.ar_blockcount);

	if (!xfs_verify_agbext(pag, bno, len))
	xfs_alloc_btrec_to_irec(rec, &irec);
	if (xfs_alloc_check_irec(bs->cur, &irec) != NULL) {
		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
		return 0;
	}

	xchk_allocbt_xref(bs->sc, bno, len);
	xchk_allocbt_xref(bs->sc, &irec);

	return 0;
}