Commit 366a0b8d authored by Darrick J. Wong's avatar Darrick J. Wong
Browse files

xfs: standardize ondisk to incore conversion for inode btrees



Create a xfs_inobt_check_irec function to detect corruption in btree
records.  Fix all xfs_inobt_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 35e3b9a1
Loading
Loading
Loading
Loading
+35 −18
Original line number Diff line number Diff line
@@ -95,6 +95,33 @@ xfs_inobt_btrec_to_irec(
	irec->ir_free = be64_to_cpu(rec->inobt.ir_free);
}

/* Simple checks for inode records. */
xfs_failaddr_t
xfs_inobt_check_irec(
	struct xfs_btree_cur			*cur,
	const struct xfs_inobt_rec_incore	*irec)
{
	uint64_t			realfree;

	if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
		return __this_address;
	if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
	    irec->ir_count > XFS_INODES_PER_CHUNK)
		return __this_address;
	if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
		return __this_address;

	/* if there are no holes, return the first available offset */
	if (!xfs_inobt_issparse(irec->ir_holemask))
		realfree = irec->ir_free;
	else
		realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
	if (hweight64(realfree) != irec->ir_freecount)
		return __this_address;

	return NULL;
}

/*
 * Get the data from the pointed-to record.
 */
@@ -106,38 +133,25 @@ xfs_inobt_get_rec(
{
	struct xfs_mount		*mp = cur->bc_mp;
	union xfs_btree_rec		*rec;
	xfs_failaddr_t			fa;
	int				error;
	uint64_t			realfree;

	error = xfs_btree_get_rec(cur, &rec, stat);
	if (error || *stat == 0)
		return error;

	xfs_inobt_btrec_to_irec(mp, rec, irec);

	if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino))
		goto out_bad_rec;
	if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT ||
	    irec->ir_count > XFS_INODES_PER_CHUNK)
		goto out_bad_rec;
	if (irec->ir_freecount > XFS_INODES_PER_CHUNK)
		goto out_bad_rec;

	/* if there are no holes, return the first available offset */
	if (!xfs_inobt_issparse(irec->ir_holemask))
		realfree = irec->ir_free;
	else
		realfree = irec->ir_free & xfs_inobt_irec_to_allocmask(irec);
	if (hweight64(realfree) != irec->ir_freecount)
	fa = xfs_inobt_check_irec(cur, irec);
	if (fa)
		goto out_bad_rec;

	return 0;

out_bad_rec:
	xfs_warn(mp,
		"%s Inode BTree record corruption in AG %d detected!",
		"%s Inode BTree record corruption in AG %d detected at %pS!",
		cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free",
		cur->bc_ag.pag->pag_agno);
		cur->bc_ag.pag->pag_agno, fa);
	xfs_warn(mp,
"start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x",
		irec->ir_startino, irec->ir_count, irec->ir_freecount,
@@ -2690,6 +2704,9 @@ xfs_ialloc_count_inodes_rec(
	struct xfs_ialloc_count_inodes	*ci = priv;

	xfs_inobt_btrec_to_irec(cur->bc_mp, rec, &irec);
	if (xfs_inobt_check_irec(cur, &irec) != NULL)
		return -EFSCORRUPTED;

	ci->count += irec.ir_count;
	ci->freecount += irec.ir_freecount;

+2 −0
Original line number Diff line number Diff line
@@ -93,6 +93,8 @@ union xfs_btree_rec;
void xfs_inobt_btrec_to_irec(struct xfs_mount *mp,
		const union xfs_btree_rec *rec,
		struct xfs_inobt_rec_incore *irec);
xfs_failaddr_t xfs_inobt_check_irec(struct xfs_btree_cur *cur,
		const struct xfs_inobt_rec_incore *irec);
int xfs_ialloc_has_inodes_at_extent(struct xfs_btree_cur *cur,
		xfs_agblock_t bno, xfs_extlen_t len, bool *exists);
int xfs_ialloc_has_inode_record(struct xfs_btree_cur *cur, xfs_agino_t low,
+1 −1
Original line number Diff line number Diff line
@@ -608,7 +608,7 @@ xfs_iallocbt_maxlevels_ondisk(void)
 */
uint64_t
xfs_inobt_irec_to_allocmask(
	struct xfs_inobt_rec_incore	*rec)
	const struct xfs_inobt_rec_incore	*rec)
{
	uint64_t			bitmap = 0;
	uint64_t			inodespbit;
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ struct xfs_btree_cur *xfs_inobt_stage_cursor(struct xfs_perag *pag,
extern int xfs_inobt_maxrecs(struct xfs_mount *, int, int);

/* ir_holemask to inode allocation bitmap conversion */
uint64_t xfs_inobt_irec_to_allocmask(struct xfs_inobt_rec_incore *);
uint64_t xfs_inobt_irec_to_allocmask(const struct xfs_inobt_rec_incore *irec);

#if defined(DEBUG) || defined(XFS_WARN)
int xfs_inobt_rec_check_count(struct xfs_mount *,
+4 −20
Original line number Diff line number Diff line
@@ -119,15 +119,6 @@ xchk_iallocbt_chunk(
	return true;
}

/* Count the number of free inodes. */
static unsigned int
xchk_iallocbt_freecount(
	xfs_inofree_t			freemask)
{
	BUILD_BUG_ON(sizeof(freemask) != sizeof(__u64));
	return hweight64(freemask);
}

/*
 * Check that an inode's allocation status matches ir_free in the inobt
 * record.  First we try querying the in-core inode state, and if the inode
@@ -431,24 +422,17 @@ xchk_iallocbt_rec(
	int				holecount;
	int				i;
	int				error = 0;
	unsigned int			real_freecount;
	uint16_t			holemask;

	xfs_inobt_btrec_to_irec(mp, rec, &irec);

	if (irec.ir_count > XFS_INODES_PER_CHUNK ||
	    irec.ir_freecount > XFS_INODES_PER_CHUNK)
		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);

	real_freecount = irec.ir_freecount +
			(XFS_INODES_PER_CHUNK - irec.ir_count);
	if (real_freecount != xchk_iallocbt_freecount(irec.ir_free))
	if (xfs_inobt_check_irec(bs->cur, &irec) != NULL) {
		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
		return 0;
	}

	agino = irec.ir_startino;
	/* Record has to be properly aligned within the AG. */
	if (!xfs_verify_agino(pag, agino) ||
	    !xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
	if (!xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) {
		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
		goto out;
	}