Commit 6858c887 authored by Dave Chinner's avatar Dave Chinner Committed by Dave Chinner
Browse files

Merge tag 'scrub-btree-key-enhancements-6.4_2023-04-11' of...

Merge tag 'scrub-btree-key-enhancements-6.4_2023-04-11' of git://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux

 into guilt/xfs-for-next

xfs: enhance btree key scrubbing [v24.5]

This series fixes the scrub btree block checker to ensure that the keys
in the parent block accurately represent the block, and check the
ordering of all interior key records.

Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parents 1ee75505 2bea8df0
Loading
Loading
Loading
Loading
+56 −7
Original line number Original line Diff line number Diff line
@@ -151,11 +151,12 @@ xchk_btree_rec(


	trace_xchk_btree_rec(bs->sc, cur, 0);
	trace_xchk_btree_rec(bs->sc, cur, 0);


	/* If this isn't the first record, are they in order? */
	/* Are all records across all record blocks in order? */
	if (cur->bc_levels[0].ptr > 1 &&
	if (bs->lastrec_valid &&
	    !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
	    !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
		xchk_btree_set_corrupt(bs->sc, cur, 0);
		xchk_btree_set_corrupt(bs->sc, cur, 0);
	memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
	memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
	bs->lastrec_valid = true;


	if (cur->bc_nlevels == 1)
	if (cur->bc_nlevels == 1)
		return;
		return;
@@ -198,11 +199,12 @@ xchk_btree_key(


	trace_xchk_btree_key(bs->sc, cur, level);
	trace_xchk_btree_key(bs->sc, cur, level);


	/* If this isn't the first key, are they in order? */
	/* Are all low keys across all node blocks in order? */
	if (cur->bc_levels[level].ptr > 1 &&
	if (bs->lastkey[level - 1].valid &&
	    !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1], key))
	    !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1].key, key))
		xchk_btree_set_corrupt(bs->sc, cur, level);
		xchk_btree_set_corrupt(bs->sc, cur, level);
	memcpy(&bs->lastkey[level - 1], key, cur->bc_ops->key_len);
	memcpy(&bs->lastkey[level - 1].key, key, cur->bc_ops->key_len);
	bs->lastkey[level - 1].valid = true;


	if (level + 1 >= cur->bc_nlevels)
	if (level + 1 >= cur->bc_nlevels)
		return;
		return;
@@ -529,6 +531,48 @@ xchk_btree_check_minrecs(
		xchk_btree_set_corrupt(bs->sc, cur, level);
		xchk_btree_set_corrupt(bs->sc, cur, level);
}
}


/*
 * If this btree block has a parent, make sure that the parent's keys capture
 * the keyspace contained in this block.
 */
STATIC void
xchk_btree_block_check_keys(
	struct xchk_btree	*bs,
	int			level,
	struct xfs_btree_block	*block)
{
	union xfs_btree_key	block_key;
	union xfs_btree_key	*block_high_key;
	union xfs_btree_key	*parent_low_key, *parent_high_key;
	struct xfs_btree_cur	*cur = bs->cur;
	struct xfs_btree_block	*parent_block;
	struct xfs_buf		*bp;

	if (level == cur->bc_nlevels - 1)
		return;

	xfs_btree_get_keys(cur, block, &block_key);

	/* Make sure the low key of this block matches the parent. */
	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
	parent_low_key = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
			parent_block);
	if (cur->bc_ops->diff_two_keys(cur, &block_key, parent_low_key)) {
		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
		return;
	}

	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
		return;

	/* Make sure the high key of this block matches the parent. */
	parent_high_key = xfs_btree_high_key_addr(cur,
			cur->bc_levels[level + 1].ptr, parent_block);
	block_high_key = xfs_btree_high_key_from_key(cur, &block_key);
	if (cur->bc_ops->diff_two_keys(cur, block_high_key, parent_high_key))
		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
}

/*
/*
 * Grab and scrub a btree block given a btree pointer.  Returns block
 * Grab and scrub a btree block given a btree pointer.  Returns block
 * and buffer pointers (if applicable) if they're ok to use.
 * and buffer pointers (if applicable) if they're ok to use.
@@ -580,7 +624,12 @@ xchk_btree_get_block(
	 * Check the block's siblings; this function absorbs error codes
	 * Check the block's siblings; this function absorbs error codes
	 * for us.
	 * for us.
	 */
	 */
	return xchk_btree_block_check_siblings(bs, *pblock);
	error = xchk_btree_block_check_siblings(bs, *pblock);
	if (error)
		return error;

	xchk_btree_block_check_keys(bs, level, *pblock);
	return 0;
}
}


/*
/*
+7 −1
Original line number Original line Diff line number Diff line
@@ -31,6 +31,11 @@ typedef int (*xchk_btree_rec_fn)(
	struct xchk_btree		*bs,
	struct xchk_btree		*bs,
	const union xfs_btree_rec	*rec);
	const union xfs_btree_rec	*rec);


struct xchk_btree_key {
	union xfs_btree_key		key;
	bool				valid;
};

struct xchk_btree {
struct xchk_btree {
	/* caller-provided scrub state */
	/* caller-provided scrub state */
	struct xfs_scrub		*sc;
	struct xfs_scrub		*sc;
@@ -40,11 +45,12 @@ struct xchk_btree {
	void				*private;
	void				*private;


	/* internal scrub state */
	/* internal scrub state */
	bool				lastrec_valid;
	union xfs_btree_rec		lastrec;
	union xfs_btree_rec		lastrec;
	struct list_head		to_check;
	struct list_head		to_check;


	/* this element must come last! */
	/* this element must come last! */
	union xfs_btree_key		lastkey[];
	struct xchk_btree_key		lastkey[];
};
};


/*
/*