162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
462306a36Sopenharmony_ci * Author: Darrick J. Wong <djwong@kernel.org>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1162306a36Sopenharmony_ci#include "xfs_mount.h"
1262306a36Sopenharmony_ci#include "xfs_inode.h"
1362306a36Sopenharmony_ci#include "xfs_btree.h"
1462306a36Sopenharmony_ci#include "scrub/scrub.h"
1562306a36Sopenharmony_ci#include "scrub/common.h"
1662306a36Sopenharmony_ci#include "scrub/btree.h"
1762306a36Sopenharmony_ci#include "scrub/trace.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* btree scrubbing */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * Check for btree operation errors.  See the section about handling
2362306a36Sopenharmony_ci * operational errors in common.c.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic bool
2662306a36Sopenharmony_ci__xchk_btree_process_error(
2762306a36Sopenharmony_ci	struct xfs_scrub	*sc,
2862306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
2962306a36Sopenharmony_ci	int			level,
3062306a36Sopenharmony_ci	int			*error,
3162306a36Sopenharmony_ci	__u32			errflag,
3262306a36Sopenharmony_ci	void			*ret_ip)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	if (*error == 0)
3562306a36Sopenharmony_ci		return true;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	switch (*error) {
3862306a36Sopenharmony_ci	case -EDEADLOCK:
3962306a36Sopenharmony_ci	case -ECHRNG:
4062306a36Sopenharmony_ci		/* Used to restart an op with deadlock avoidance. */
4162306a36Sopenharmony_ci		trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
4262306a36Sopenharmony_ci		break;
4362306a36Sopenharmony_ci	case -EFSBADCRC:
4462306a36Sopenharmony_ci	case -EFSCORRUPTED:
4562306a36Sopenharmony_ci		/* Note the badness but don't abort. */
4662306a36Sopenharmony_ci		sc->sm->sm_flags |= errflag;
4762306a36Sopenharmony_ci		*error = 0;
4862306a36Sopenharmony_ci		fallthrough;
4962306a36Sopenharmony_ci	default:
5062306a36Sopenharmony_ci		if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
5162306a36Sopenharmony_ci			trace_xchk_ifork_btree_op_error(sc, cur, level,
5262306a36Sopenharmony_ci					*error, ret_ip);
5362306a36Sopenharmony_ci		else
5462306a36Sopenharmony_ci			trace_xchk_btree_op_error(sc, cur, level,
5562306a36Sopenharmony_ci					*error, ret_ip);
5662306a36Sopenharmony_ci		break;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	return false;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cibool
6262306a36Sopenharmony_cixchk_btree_process_error(
6362306a36Sopenharmony_ci	struct xfs_scrub	*sc,
6462306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
6562306a36Sopenharmony_ci	int			level,
6662306a36Sopenharmony_ci	int			*error)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	return __xchk_btree_process_error(sc, cur, level, error,
6962306a36Sopenharmony_ci			XFS_SCRUB_OFLAG_CORRUPT, __return_address);
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cibool
7362306a36Sopenharmony_cixchk_btree_xref_process_error(
7462306a36Sopenharmony_ci	struct xfs_scrub	*sc,
7562306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
7662306a36Sopenharmony_ci	int			level,
7762306a36Sopenharmony_ci	int			*error)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	return __xchk_btree_process_error(sc, cur, level, error,
8062306a36Sopenharmony_ci			XFS_SCRUB_OFLAG_XFAIL, __return_address);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Record btree block corruption. */
8462306a36Sopenharmony_cistatic void
8562306a36Sopenharmony_ci__xchk_btree_set_corrupt(
8662306a36Sopenharmony_ci	struct xfs_scrub	*sc,
8762306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
8862306a36Sopenharmony_ci	int			level,
8962306a36Sopenharmony_ci	__u32			errflag,
9062306a36Sopenharmony_ci	void			*ret_ip)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	sc->sm->sm_flags |= errflag;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
9562306a36Sopenharmony_ci		trace_xchk_ifork_btree_error(sc, cur, level,
9662306a36Sopenharmony_ci				ret_ip);
9762306a36Sopenharmony_ci	else
9862306a36Sopenharmony_ci		trace_xchk_btree_error(sc, cur, level,
9962306a36Sopenharmony_ci				ret_ip);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid
10362306a36Sopenharmony_cixchk_btree_set_corrupt(
10462306a36Sopenharmony_ci	struct xfs_scrub	*sc,
10562306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
10662306a36Sopenharmony_ci	int			level)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_CORRUPT,
10962306a36Sopenharmony_ci			__return_address);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_civoid
11362306a36Sopenharmony_cixchk_btree_xref_set_corrupt(
11462306a36Sopenharmony_ci	struct xfs_scrub	*sc,
11562306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
11662306a36Sopenharmony_ci	int			level)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_XCORRUPT,
11962306a36Sopenharmony_ci			__return_address);
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid
12362306a36Sopenharmony_cixchk_btree_set_preen(
12462306a36Sopenharmony_ci	struct xfs_scrub	*sc,
12562306a36Sopenharmony_ci	struct xfs_btree_cur	*cur,
12662306a36Sopenharmony_ci	int			level)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_PREEN,
12962306a36Sopenharmony_ci			__return_address);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/*
13362306a36Sopenharmony_ci * Make sure this record is in order and doesn't stray outside of the parent
13462306a36Sopenharmony_ci * keys.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_ciSTATIC void
13762306a36Sopenharmony_cixchk_btree_rec(
13862306a36Sopenharmony_ci	struct xchk_btree	*bs)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
14162306a36Sopenharmony_ci	union xfs_btree_rec	*rec;
14262306a36Sopenharmony_ci	union xfs_btree_key	key;
14362306a36Sopenharmony_ci	union xfs_btree_key	hkey;
14462306a36Sopenharmony_ci	union xfs_btree_key	*keyp;
14562306a36Sopenharmony_ci	struct xfs_btree_block	*block;
14662306a36Sopenharmony_ci	struct xfs_btree_block	*keyblock;
14762306a36Sopenharmony_ci	struct xfs_buf		*bp;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	block = xfs_btree_get_block(cur, 0, &bp);
15062306a36Sopenharmony_ci	rec = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr, block);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	trace_xchk_btree_rec(bs->sc, cur, 0);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* Are all records across all record blocks in order? */
15562306a36Sopenharmony_ci	if (bs->lastrec_valid &&
15662306a36Sopenharmony_ci	    !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
15762306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, 0);
15862306a36Sopenharmony_ci	memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
15962306a36Sopenharmony_ci	bs->lastrec_valid = true;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (cur->bc_nlevels == 1)
16262306a36Sopenharmony_ci		return;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	/* Is low_key(rec) at least as large as the parent low key? */
16562306a36Sopenharmony_ci	cur->bc_ops->init_key_from_rec(&key, rec);
16662306a36Sopenharmony_ci	keyblock = xfs_btree_get_block(cur, 1, &bp);
16762306a36Sopenharmony_ci	keyp = xfs_btree_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
16862306a36Sopenharmony_ci	if (xfs_btree_keycmp_lt(cur, &key, keyp))
16962306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, 1);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
17262306a36Sopenharmony_ci		return;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/* Is high_key(rec) no larger than the parent high key? */
17562306a36Sopenharmony_ci	cur->bc_ops->init_high_key_from_rec(&hkey, rec);
17662306a36Sopenharmony_ci	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
17762306a36Sopenharmony_ci	if (xfs_btree_keycmp_lt(cur, keyp, &hkey))
17862306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, 1);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/*
18262306a36Sopenharmony_ci * Make sure this key is in order and doesn't stray outside of the parent
18362306a36Sopenharmony_ci * keys.
18462306a36Sopenharmony_ci */
18562306a36Sopenharmony_ciSTATIC void
18662306a36Sopenharmony_cixchk_btree_key(
18762306a36Sopenharmony_ci	struct xchk_btree	*bs,
18862306a36Sopenharmony_ci	int			level)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
19162306a36Sopenharmony_ci	union xfs_btree_key	*key;
19262306a36Sopenharmony_ci	union xfs_btree_key	*keyp;
19362306a36Sopenharmony_ci	struct xfs_btree_block	*block;
19462306a36Sopenharmony_ci	struct xfs_btree_block	*keyblock;
19562306a36Sopenharmony_ci	struct xfs_buf		*bp;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	block = xfs_btree_get_block(cur, level, &bp);
19862306a36Sopenharmony_ci	key = xfs_btree_key_addr(cur, cur->bc_levels[level].ptr, block);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	trace_xchk_btree_key(bs->sc, cur, level);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* Are all low keys across all node blocks in order? */
20362306a36Sopenharmony_ci	if (bs->lastkey[level - 1].valid &&
20462306a36Sopenharmony_ci	    !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1].key, key))
20562306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level);
20662306a36Sopenharmony_ci	memcpy(&bs->lastkey[level - 1].key, key, cur->bc_ops->key_len);
20762306a36Sopenharmony_ci	bs->lastkey[level - 1].valid = true;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	if (level + 1 >= cur->bc_nlevels)
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* Is this block's low key at least as large as the parent low key? */
21362306a36Sopenharmony_ci	keyblock = xfs_btree_get_block(cur, level + 1, &bp);
21462306a36Sopenharmony_ci	keyp = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr, keyblock);
21562306a36Sopenharmony_ci	if (xfs_btree_keycmp_lt(cur, key, keyp))
21662306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
21962306a36Sopenharmony_ci		return;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* Is this block's high key no larger than the parent high key? */
22262306a36Sopenharmony_ci	key = xfs_btree_high_key_addr(cur, cur->bc_levels[level].ptr, block);
22362306a36Sopenharmony_ci	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
22462306a36Sopenharmony_ci			keyblock);
22562306a36Sopenharmony_ci	if (xfs_btree_keycmp_lt(cur, keyp, key))
22662306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level);
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * Check a btree pointer.  Returns true if it's ok to use this pointer.
23162306a36Sopenharmony_ci * Callers do not need to set the corrupt flag.
23262306a36Sopenharmony_ci */
23362306a36Sopenharmony_cistatic bool
23462306a36Sopenharmony_cixchk_btree_ptr_ok(
23562306a36Sopenharmony_ci	struct xchk_btree	*bs,
23662306a36Sopenharmony_ci	int			level,
23762306a36Sopenharmony_ci	union xfs_btree_ptr	*ptr)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	bool			res;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* A btree rooted in an inode has no block pointer to the root. */
24262306a36Sopenharmony_ci	if ((bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
24362306a36Sopenharmony_ci	    level == bs->cur->bc_nlevels)
24462306a36Sopenharmony_ci		return true;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Otherwise, check the pointers. */
24762306a36Sopenharmony_ci	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
24862306a36Sopenharmony_ci		res = xfs_btree_check_lptr(bs->cur, be64_to_cpu(ptr->l), level);
24962306a36Sopenharmony_ci	else
25062306a36Sopenharmony_ci		res = xfs_btree_check_sptr(bs->cur, be32_to_cpu(ptr->s), level);
25162306a36Sopenharmony_ci	if (!res)
25262306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return res;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/* Check that a btree block's sibling matches what we expect it. */
25862306a36Sopenharmony_ciSTATIC int
25962306a36Sopenharmony_cixchk_btree_block_check_sibling(
26062306a36Sopenharmony_ci	struct xchk_btree	*bs,
26162306a36Sopenharmony_ci	int			level,
26262306a36Sopenharmony_ci	int			direction,
26362306a36Sopenharmony_ci	union xfs_btree_ptr	*sibling)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
26662306a36Sopenharmony_ci	struct xfs_btree_block	*pblock;
26762306a36Sopenharmony_ci	struct xfs_buf		*pbp;
26862306a36Sopenharmony_ci	struct xfs_btree_cur	*ncur = NULL;
26962306a36Sopenharmony_ci	union xfs_btree_ptr	*pp;
27062306a36Sopenharmony_ci	int			success;
27162306a36Sopenharmony_ci	int			error;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	error = xfs_btree_dup_cursor(cur, &ncur);
27462306a36Sopenharmony_ci	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error) ||
27562306a36Sopenharmony_ci	    !ncur)
27662306a36Sopenharmony_ci		return error;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/*
27962306a36Sopenharmony_ci	 * If the pointer is null, we shouldn't be able to move the upper
28062306a36Sopenharmony_ci	 * level pointer anywhere.
28162306a36Sopenharmony_ci	 */
28262306a36Sopenharmony_ci	if (xfs_btree_ptr_is_null(cur, sibling)) {
28362306a36Sopenharmony_ci		if (direction > 0)
28462306a36Sopenharmony_ci			error = xfs_btree_increment(ncur, level + 1, &success);
28562306a36Sopenharmony_ci		else
28662306a36Sopenharmony_ci			error = xfs_btree_decrement(ncur, level + 1, &success);
28762306a36Sopenharmony_ci		if (error == 0 && success)
28862306a36Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, cur, level);
28962306a36Sopenharmony_ci		error = 0;
29062306a36Sopenharmony_ci		goto out;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* Increment upper level pointer. */
29462306a36Sopenharmony_ci	if (direction > 0)
29562306a36Sopenharmony_ci		error = xfs_btree_increment(ncur, level + 1, &success);
29662306a36Sopenharmony_ci	else
29762306a36Sopenharmony_ci		error = xfs_btree_decrement(ncur, level + 1, &success);
29862306a36Sopenharmony_ci	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error))
29962306a36Sopenharmony_ci		goto out;
30062306a36Sopenharmony_ci	if (!success) {
30162306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level + 1);
30262306a36Sopenharmony_ci		goto out;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Compare upper level pointer to sibling pointer. */
30662306a36Sopenharmony_ci	pblock = xfs_btree_get_block(ncur, level + 1, &pbp);
30762306a36Sopenharmony_ci	pp = xfs_btree_ptr_addr(ncur, ncur->bc_levels[level + 1].ptr, pblock);
30862306a36Sopenharmony_ci	if (!xchk_btree_ptr_ok(bs, level + 1, pp))
30962306a36Sopenharmony_ci		goto out;
31062306a36Sopenharmony_ci	if (pbp)
31162306a36Sopenharmony_ci		xchk_buffer_recheck(bs->sc, pbp);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (xfs_btree_diff_two_ptrs(cur, pp, sibling))
31462306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level);
31562306a36Sopenharmony_ciout:
31662306a36Sopenharmony_ci	xfs_btree_del_cursor(ncur, XFS_BTREE_ERROR);
31762306a36Sopenharmony_ci	return error;
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/* Check the siblings of a btree block. */
32162306a36Sopenharmony_ciSTATIC int
32262306a36Sopenharmony_cixchk_btree_block_check_siblings(
32362306a36Sopenharmony_ci	struct xchk_btree	*bs,
32462306a36Sopenharmony_ci	struct xfs_btree_block	*block)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
32762306a36Sopenharmony_ci	union xfs_btree_ptr	leftsib;
32862306a36Sopenharmony_ci	union xfs_btree_ptr	rightsib;
32962306a36Sopenharmony_ci	int			level;
33062306a36Sopenharmony_ci	int			error = 0;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	xfs_btree_get_sibling(cur, block, &leftsib, XFS_BB_LEFTSIB);
33362306a36Sopenharmony_ci	xfs_btree_get_sibling(cur, block, &rightsib, XFS_BB_RIGHTSIB);
33462306a36Sopenharmony_ci	level = xfs_btree_get_level(block);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	/* Root block should never have siblings. */
33762306a36Sopenharmony_ci	if (level == cur->bc_nlevels - 1) {
33862306a36Sopenharmony_ci		if (!xfs_btree_ptr_is_null(cur, &leftsib) ||
33962306a36Sopenharmony_ci		    !xfs_btree_ptr_is_null(cur, &rightsib))
34062306a36Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, cur, level);
34162306a36Sopenharmony_ci		goto out;
34262306a36Sopenharmony_ci	}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/*
34562306a36Sopenharmony_ci	 * Does the left & right sibling pointers match the adjacent
34662306a36Sopenharmony_ci	 * parent level pointers?
34762306a36Sopenharmony_ci	 * (These function absorbs error codes for us.)
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	error = xchk_btree_block_check_sibling(bs, level, -1, &leftsib);
35062306a36Sopenharmony_ci	if (error)
35162306a36Sopenharmony_ci		return error;
35262306a36Sopenharmony_ci	error = xchk_btree_block_check_sibling(bs, level, 1, &rightsib);
35362306a36Sopenharmony_ci	if (error)
35462306a36Sopenharmony_ci		return error;
35562306a36Sopenharmony_ciout:
35662306a36Sopenharmony_ci	return error;
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cistruct check_owner {
36062306a36Sopenharmony_ci	struct list_head	list;
36162306a36Sopenharmony_ci	xfs_daddr_t		daddr;
36262306a36Sopenharmony_ci	int			level;
36362306a36Sopenharmony_ci};
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci/*
36662306a36Sopenharmony_ci * Make sure this btree block isn't in the free list and that there's
36762306a36Sopenharmony_ci * an rmap record for it.
36862306a36Sopenharmony_ci */
36962306a36Sopenharmony_ciSTATIC int
37062306a36Sopenharmony_cixchk_btree_check_block_owner(
37162306a36Sopenharmony_ci	struct xchk_btree	*bs,
37262306a36Sopenharmony_ci	int			level,
37362306a36Sopenharmony_ci	xfs_daddr_t		daddr)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	xfs_agnumber_t		agno;
37662306a36Sopenharmony_ci	xfs_agblock_t		agbno;
37762306a36Sopenharmony_ci	xfs_btnum_t		btnum;
37862306a36Sopenharmony_ci	bool			init_sa;
37962306a36Sopenharmony_ci	int			error = 0;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (!bs->cur)
38262306a36Sopenharmony_ci		return 0;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	btnum = bs->cur->bc_btnum;
38562306a36Sopenharmony_ci	agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
38662306a36Sopenharmony_ci	agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	init_sa = bs->cur->bc_flags & XFS_BTREE_LONG_PTRS;
38962306a36Sopenharmony_ci	if (init_sa) {
39062306a36Sopenharmony_ci		error = xchk_ag_init_existing(bs->sc, agno, &bs->sc->sa);
39162306a36Sopenharmony_ci		if (!xchk_btree_xref_process_error(bs->sc, bs->cur,
39262306a36Sopenharmony_ci				level, &error))
39362306a36Sopenharmony_ci			goto out_free;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	xchk_xref_is_used_space(bs->sc, agbno, 1);
39762306a36Sopenharmony_ci	/*
39862306a36Sopenharmony_ci	 * The bnobt scrubber aliases bs->cur to bs->sc->sa.bno_cur, so we
39962306a36Sopenharmony_ci	 * have to nullify it (to shut down further block owner checks) if
40062306a36Sopenharmony_ci	 * self-xref encounters problems.
40162306a36Sopenharmony_ci	 */
40262306a36Sopenharmony_ci	if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
40362306a36Sopenharmony_ci		bs->cur = NULL;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo);
40662306a36Sopenharmony_ci	if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
40762306a36Sopenharmony_ci		bs->cur = NULL;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciout_free:
41062306a36Sopenharmony_ci	if (init_sa)
41162306a36Sopenharmony_ci		xchk_ag_free(bs->sc, &bs->sc->sa);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return error;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci/* Check the owner of a btree block. */
41762306a36Sopenharmony_ciSTATIC int
41862306a36Sopenharmony_cixchk_btree_check_owner(
41962306a36Sopenharmony_ci	struct xchk_btree	*bs,
42062306a36Sopenharmony_ci	int			level,
42162306a36Sopenharmony_ci	struct xfs_buf		*bp)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/*
42662306a36Sopenharmony_ci	 * In theory, xfs_btree_get_block should only give us a null buffer
42762306a36Sopenharmony_ci	 * pointer for the root of a root-in-inode btree type, but we need
42862306a36Sopenharmony_ci	 * to check defensively here in case the cursor state is also screwed
42962306a36Sopenharmony_ci	 * up.
43062306a36Sopenharmony_ci	 */
43162306a36Sopenharmony_ci	if (bp == NULL) {
43262306a36Sopenharmony_ci		if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE))
43362306a36Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, bs->cur, level);
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/*
43862306a36Sopenharmony_ci	 * We want to cross-reference each btree block with the bnobt
43962306a36Sopenharmony_ci	 * and the rmapbt.  We cannot cross-reference the bnobt or
44062306a36Sopenharmony_ci	 * rmapbt while scanning the bnobt or rmapbt, respectively,
44162306a36Sopenharmony_ci	 * because we cannot alter the cursor and we'd prefer not to
44262306a36Sopenharmony_ci	 * duplicate cursors.  Therefore, save the buffer daddr for
44362306a36Sopenharmony_ci	 * later scanning.
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
44662306a36Sopenharmony_ci		struct check_owner	*co;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci		co = kmalloc(sizeof(struct check_owner), XCHK_GFP_FLAGS);
44962306a36Sopenharmony_ci		if (!co)
45062306a36Sopenharmony_ci			return -ENOMEM;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		INIT_LIST_HEAD(&co->list);
45362306a36Sopenharmony_ci		co->level = level;
45462306a36Sopenharmony_ci		co->daddr = xfs_buf_daddr(bp);
45562306a36Sopenharmony_ci		list_add_tail(&co->list, &bs->to_check);
45662306a36Sopenharmony_ci		return 0;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return xchk_btree_check_block_owner(bs, level, xfs_buf_daddr(bp));
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/* Decide if we want to check minrecs of a btree block in the inode root. */
46362306a36Sopenharmony_cistatic inline bool
46462306a36Sopenharmony_cixchk_btree_check_iroot_minrecs(
46562306a36Sopenharmony_ci	struct xchk_btree	*bs)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	/*
46862306a36Sopenharmony_ci	 * xfs_bmap_add_attrfork_btree had an implementation bug wherein it
46962306a36Sopenharmony_ci	 * would miscalculate the space required for the data fork bmbt root
47062306a36Sopenharmony_ci	 * when adding an attr fork, and promote the iroot contents to an
47162306a36Sopenharmony_ci	 * external block unnecessarily.  This went unnoticed for many years
47262306a36Sopenharmony_ci	 * until scrub found filesystems in this state.  Inode rooted btrees are
47362306a36Sopenharmony_ci	 * not supposed to have immediate child blocks that are small enough
47462306a36Sopenharmony_ci	 * that the contents could fit in the inode root, but we can't fail
47562306a36Sopenharmony_ci	 * existing filesystems, so instead we disable the check for data fork
47662306a36Sopenharmony_ci	 * bmap btrees when there's an attr fork.
47762306a36Sopenharmony_ci	 */
47862306a36Sopenharmony_ci	if (bs->cur->bc_btnum == XFS_BTNUM_BMAP &&
47962306a36Sopenharmony_ci	    bs->cur->bc_ino.whichfork == XFS_DATA_FORK &&
48062306a36Sopenharmony_ci	    xfs_inode_has_attr_fork(bs->sc->ip))
48162306a36Sopenharmony_ci		return false;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return true;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/*
48762306a36Sopenharmony_ci * Check that this btree block has at least minrecs records or is one of the
48862306a36Sopenharmony_ci * special blocks that don't require that.
48962306a36Sopenharmony_ci */
49062306a36Sopenharmony_ciSTATIC void
49162306a36Sopenharmony_cixchk_btree_check_minrecs(
49262306a36Sopenharmony_ci	struct xchk_btree	*bs,
49362306a36Sopenharmony_ci	int			level,
49462306a36Sopenharmony_ci	struct xfs_btree_block	*block)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
49762306a36Sopenharmony_ci	unsigned int		root_level = cur->bc_nlevels - 1;
49862306a36Sopenharmony_ci	unsigned int		numrecs = be16_to_cpu(block->bb_numrecs);
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/* More records than minrecs means the block is ok. */
50162306a36Sopenharmony_ci	if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
50262306a36Sopenharmony_ci		return;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/*
50562306a36Sopenharmony_ci	 * For btrees rooted in the inode, it's possible that the root block
50662306a36Sopenharmony_ci	 * contents spilled into a regular ondisk block because there wasn't
50762306a36Sopenharmony_ci	 * enough space in the inode root.  The number of records in that
50862306a36Sopenharmony_ci	 * child block might be less than the standard minrecs, but that's ok
50962306a36Sopenharmony_ci	 * provided that there's only one direct child of the root.
51062306a36Sopenharmony_ci	 */
51162306a36Sopenharmony_ci	if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
51262306a36Sopenharmony_ci	    level == cur->bc_nlevels - 2) {
51362306a36Sopenharmony_ci		struct xfs_btree_block	*root_block;
51462306a36Sopenharmony_ci		struct xfs_buf		*root_bp;
51562306a36Sopenharmony_ci		int			root_maxrecs;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		root_block = xfs_btree_get_block(cur, root_level, &root_bp);
51862306a36Sopenharmony_ci		root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
51962306a36Sopenharmony_ci		if (xchk_btree_check_iroot_minrecs(bs) &&
52062306a36Sopenharmony_ci		    (be16_to_cpu(root_block->bb_numrecs) != 1 ||
52162306a36Sopenharmony_ci		     numrecs <= root_maxrecs))
52262306a36Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, cur, level);
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	/*
52762306a36Sopenharmony_ci	 * Otherwise, only the root level is allowed to have fewer than minrecs
52862306a36Sopenharmony_ci	 * records or keyptrs.
52962306a36Sopenharmony_ci	 */
53062306a36Sopenharmony_ci	if (level < root_level)
53162306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, level);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci/*
53562306a36Sopenharmony_ci * If this btree block has a parent, make sure that the parent's keys capture
53662306a36Sopenharmony_ci * the keyspace contained in this block.
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_ciSTATIC void
53962306a36Sopenharmony_cixchk_btree_block_check_keys(
54062306a36Sopenharmony_ci	struct xchk_btree	*bs,
54162306a36Sopenharmony_ci	int			level,
54262306a36Sopenharmony_ci	struct xfs_btree_block	*block)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	union xfs_btree_key	block_key;
54562306a36Sopenharmony_ci	union xfs_btree_key	*block_high_key;
54662306a36Sopenharmony_ci	union xfs_btree_key	*parent_low_key, *parent_high_key;
54762306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
54862306a36Sopenharmony_ci	struct xfs_btree_block	*parent_block;
54962306a36Sopenharmony_ci	struct xfs_buf		*bp;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	if (level == cur->bc_nlevels - 1)
55262306a36Sopenharmony_ci		return;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	xfs_btree_get_keys(cur, block, &block_key);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* Make sure the low key of this block matches the parent. */
55762306a36Sopenharmony_ci	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
55862306a36Sopenharmony_ci	parent_low_key = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
55962306a36Sopenharmony_ci			parent_block);
56062306a36Sopenharmony_ci	if (xfs_btree_keycmp_ne(cur, &block_key, parent_low_key)) {
56162306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
56262306a36Sopenharmony_ci		return;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
56662306a36Sopenharmony_ci		return;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* Make sure the high key of this block matches the parent. */
56962306a36Sopenharmony_ci	parent_high_key = xfs_btree_high_key_addr(cur,
57062306a36Sopenharmony_ci			cur->bc_levels[level + 1].ptr, parent_block);
57162306a36Sopenharmony_ci	block_high_key = xfs_btree_high_key_from_key(cur, &block_key);
57262306a36Sopenharmony_ci	if (xfs_btree_keycmp_ne(cur, block_high_key, parent_high_key))
57362306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci/*
57762306a36Sopenharmony_ci * Grab and scrub a btree block given a btree pointer.  Returns block
57862306a36Sopenharmony_ci * and buffer pointers (if applicable) if they're ok to use.
57962306a36Sopenharmony_ci */
58062306a36Sopenharmony_ciSTATIC int
58162306a36Sopenharmony_cixchk_btree_get_block(
58262306a36Sopenharmony_ci	struct xchk_btree	*bs,
58362306a36Sopenharmony_ci	int			level,
58462306a36Sopenharmony_ci	union xfs_btree_ptr	*pp,
58562306a36Sopenharmony_ci	struct xfs_btree_block	**pblock,
58662306a36Sopenharmony_ci	struct xfs_buf		**pbp)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	xfs_failaddr_t		failed_at;
58962306a36Sopenharmony_ci	int			error;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	*pblock = NULL;
59262306a36Sopenharmony_ci	*pbp = NULL;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	error = xfs_btree_lookup_get_block(bs->cur, level, pp, pblock);
59562306a36Sopenharmony_ci	if (!xchk_btree_process_error(bs->sc, bs->cur, level, &error) ||
59662306a36Sopenharmony_ci	    !*pblock)
59762306a36Sopenharmony_ci		return error;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	xfs_btree_get_block(bs->cur, level, pbp);
60062306a36Sopenharmony_ci	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
60162306a36Sopenharmony_ci		failed_at = __xfs_btree_check_lblock(bs->cur, *pblock,
60262306a36Sopenharmony_ci				level, *pbp);
60362306a36Sopenharmony_ci	else
60462306a36Sopenharmony_ci		failed_at = __xfs_btree_check_sblock(bs->cur, *pblock,
60562306a36Sopenharmony_ci				 level, *pbp);
60662306a36Sopenharmony_ci	if (failed_at) {
60762306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
60862306a36Sopenharmony_ci		return 0;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	if (*pbp)
61162306a36Sopenharmony_ci		xchk_buffer_recheck(bs->sc, *pbp);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	xchk_btree_check_minrecs(bs, level, *pblock);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/*
61662306a36Sopenharmony_ci	 * Check the block's owner; this function absorbs error codes
61762306a36Sopenharmony_ci	 * for us.
61862306a36Sopenharmony_ci	 */
61962306a36Sopenharmony_ci	error = xchk_btree_check_owner(bs, level, *pbp);
62062306a36Sopenharmony_ci	if (error)
62162306a36Sopenharmony_ci		return error;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	/*
62462306a36Sopenharmony_ci	 * Check the block's siblings; this function absorbs error codes
62562306a36Sopenharmony_ci	 * for us.
62662306a36Sopenharmony_ci	 */
62762306a36Sopenharmony_ci	error = xchk_btree_block_check_siblings(bs, *pblock);
62862306a36Sopenharmony_ci	if (error)
62962306a36Sopenharmony_ci		return error;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	xchk_btree_block_check_keys(bs, level, *pblock);
63262306a36Sopenharmony_ci	return 0;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Check that the low and high keys of this block match the keys stored
63762306a36Sopenharmony_ci * in the parent block.
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_ciSTATIC void
64062306a36Sopenharmony_cixchk_btree_block_keys(
64162306a36Sopenharmony_ci	struct xchk_btree	*bs,
64262306a36Sopenharmony_ci	int			level,
64362306a36Sopenharmony_ci	struct xfs_btree_block	*block)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	union xfs_btree_key	block_keys;
64662306a36Sopenharmony_ci	struct xfs_btree_cur	*cur = bs->cur;
64762306a36Sopenharmony_ci	union xfs_btree_key	*high_bk;
64862306a36Sopenharmony_ci	union xfs_btree_key	*parent_keys;
64962306a36Sopenharmony_ci	union xfs_btree_key	*high_pk;
65062306a36Sopenharmony_ci	struct xfs_btree_block	*parent_block;
65162306a36Sopenharmony_ci	struct xfs_buf		*bp;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (level >= cur->bc_nlevels - 1)
65462306a36Sopenharmony_ci		return;
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* Calculate the keys for this block. */
65762306a36Sopenharmony_ci	xfs_btree_get_keys(cur, block, &block_keys);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Obtain the parent's copy of the keys for this block. */
66062306a36Sopenharmony_ci	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
66162306a36Sopenharmony_ci	parent_keys = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
66262306a36Sopenharmony_ci			parent_block);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (xfs_btree_keycmp_ne(cur, &block_keys, parent_keys))
66562306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, 1);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
66862306a36Sopenharmony_ci		return;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	/* Get high keys */
67162306a36Sopenharmony_ci	high_bk = xfs_btree_high_key_from_key(cur, &block_keys);
67262306a36Sopenharmony_ci	high_pk = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
67362306a36Sopenharmony_ci			parent_block);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (xfs_btree_keycmp_ne(cur, high_bk, high_pk))
67662306a36Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, cur, 1);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci/*
68062306a36Sopenharmony_ci * Visit all nodes and leaves of a btree.  Check that all pointers and
68162306a36Sopenharmony_ci * records are in order, that the keys reflect the records, and use a callback
68262306a36Sopenharmony_ci * so that the caller can verify individual records.
68362306a36Sopenharmony_ci */
68462306a36Sopenharmony_ciint
68562306a36Sopenharmony_cixchk_btree(
68662306a36Sopenharmony_ci	struct xfs_scrub		*sc,
68762306a36Sopenharmony_ci	struct xfs_btree_cur		*cur,
68862306a36Sopenharmony_ci	xchk_btree_rec_fn		scrub_fn,
68962306a36Sopenharmony_ci	const struct xfs_owner_info	*oinfo,
69062306a36Sopenharmony_ci	void				*private)
69162306a36Sopenharmony_ci{
69262306a36Sopenharmony_ci	union xfs_btree_ptr		ptr;
69362306a36Sopenharmony_ci	struct xchk_btree		*bs;
69462306a36Sopenharmony_ci	union xfs_btree_ptr		*pp;
69562306a36Sopenharmony_ci	union xfs_btree_rec		*recp;
69662306a36Sopenharmony_ci	struct xfs_btree_block		*block;
69762306a36Sopenharmony_ci	struct xfs_buf			*bp;
69862306a36Sopenharmony_ci	struct check_owner		*co;
69962306a36Sopenharmony_ci	struct check_owner		*n;
70062306a36Sopenharmony_ci	size_t				cur_sz;
70162306a36Sopenharmony_ci	int				level;
70262306a36Sopenharmony_ci	int				error = 0;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/*
70562306a36Sopenharmony_ci	 * Allocate the btree scrub context from the heap, because this
70662306a36Sopenharmony_ci	 * structure can get rather large.  Don't let a caller feed us a
70762306a36Sopenharmony_ci	 * totally absurd size.
70862306a36Sopenharmony_ci	 */
70962306a36Sopenharmony_ci	cur_sz = xchk_btree_sizeof(cur->bc_nlevels);
71062306a36Sopenharmony_ci	if (cur_sz > PAGE_SIZE) {
71162306a36Sopenharmony_ci		xchk_btree_set_corrupt(sc, cur, 0);
71262306a36Sopenharmony_ci		return 0;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci	bs = kzalloc(cur_sz, XCHK_GFP_FLAGS);
71562306a36Sopenharmony_ci	if (!bs)
71662306a36Sopenharmony_ci		return -ENOMEM;
71762306a36Sopenharmony_ci	bs->cur = cur;
71862306a36Sopenharmony_ci	bs->scrub_rec = scrub_fn;
71962306a36Sopenharmony_ci	bs->oinfo = oinfo;
72062306a36Sopenharmony_ci	bs->private = private;
72162306a36Sopenharmony_ci	bs->sc = sc;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	/* Initialize scrub state */
72462306a36Sopenharmony_ci	INIT_LIST_HEAD(&bs->to_check);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/*
72762306a36Sopenharmony_ci	 * Load the root of the btree.  The helper function absorbs
72862306a36Sopenharmony_ci	 * error codes for us.
72962306a36Sopenharmony_ci	 */
73062306a36Sopenharmony_ci	level = cur->bc_nlevels - 1;
73162306a36Sopenharmony_ci	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
73262306a36Sopenharmony_ci	if (!xchk_btree_ptr_ok(bs, cur->bc_nlevels, &ptr))
73362306a36Sopenharmony_ci		goto out;
73462306a36Sopenharmony_ci	error = xchk_btree_get_block(bs, level, &ptr, &block, &bp);
73562306a36Sopenharmony_ci	if (error || !block)
73662306a36Sopenharmony_ci		goto out;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	cur->bc_levels[level].ptr = 1;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	while (level < cur->bc_nlevels) {
74162306a36Sopenharmony_ci		block = xfs_btree_get_block(cur, level, &bp);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci		if (level == 0) {
74462306a36Sopenharmony_ci			/* End of leaf, pop back towards the root. */
74562306a36Sopenharmony_ci			if (cur->bc_levels[level].ptr >
74662306a36Sopenharmony_ci			    be16_to_cpu(block->bb_numrecs)) {
74762306a36Sopenharmony_ci				xchk_btree_block_keys(bs, level, block);
74862306a36Sopenharmony_ci				if (level < cur->bc_nlevels - 1)
74962306a36Sopenharmony_ci					cur->bc_levels[level + 1].ptr++;
75062306a36Sopenharmony_ci				level++;
75162306a36Sopenharmony_ci				continue;
75262306a36Sopenharmony_ci			}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci			/* Records in order for scrub? */
75562306a36Sopenharmony_ci			xchk_btree_rec(bs);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci			/* Call out to the record checker. */
75862306a36Sopenharmony_ci			recp = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr,
75962306a36Sopenharmony_ci					block);
76062306a36Sopenharmony_ci			error = bs->scrub_rec(bs, recp);
76162306a36Sopenharmony_ci			if (error)
76262306a36Sopenharmony_ci				break;
76362306a36Sopenharmony_ci			if (xchk_should_terminate(sc, &error) ||
76462306a36Sopenharmony_ci			    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
76562306a36Sopenharmony_ci				break;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci			cur->bc_levels[level].ptr++;
76862306a36Sopenharmony_ci			continue;
76962306a36Sopenharmony_ci		}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci		/* End of node, pop back towards the root. */
77262306a36Sopenharmony_ci		if (cur->bc_levels[level].ptr >
77362306a36Sopenharmony_ci					be16_to_cpu(block->bb_numrecs)) {
77462306a36Sopenharmony_ci			xchk_btree_block_keys(bs, level, block);
77562306a36Sopenharmony_ci			if (level < cur->bc_nlevels - 1)
77662306a36Sopenharmony_ci				cur->bc_levels[level + 1].ptr++;
77762306a36Sopenharmony_ci			level++;
77862306a36Sopenharmony_ci			continue;
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		/* Keys in order for scrub? */
78262306a36Sopenharmony_ci		xchk_btree_key(bs, level);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		/* Drill another level deeper. */
78562306a36Sopenharmony_ci		pp = xfs_btree_ptr_addr(cur, cur->bc_levels[level].ptr, block);
78662306a36Sopenharmony_ci		if (!xchk_btree_ptr_ok(bs, level, pp)) {
78762306a36Sopenharmony_ci			cur->bc_levels[level].ptr++;
78862306a36Sopenharmony_ci			continue;
78962306a36Sopenharmony_ci		}
79062306a36Sopenharmony_ci		level--;
79162306a36Sopenharmony_ci		error = xchk_btree_get_block(bs, level, pp, &block, &bp);
79262306a36Sopenharmony_ci		if (error || !block)
79362306a36Sopenharmony_ci			goto out;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		cur->bc_levels[level].ptr = 1;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ciout:
79962306a36Sopenharmony_ci	/* Process deferred owner checks on btree blocks. */
80062306a36Sopenharmony_ci	list_for_each_entry_safe(co, n, &bs->to_check, list) {
80162306a36Sopenharmony_ci		if (!error && bs->cur)
80262306a36Sopenharmony_ci			error = xchk_btree_check_block_owner(bs, co->level,
80362306a36Sopenharmony_ci					co->daddr);
80462306a36Sopenharmony_ci		list_del(&co->list);
80562306a36Sopenharmony_ci		kfree(co);
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	kfree(bs);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	return error;
81062306a36Sopenharmony_ci}
811