xref: /openbmc/linux/fs/xfs/scrub/btree.c (revision 20bccdb0)
1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later
2537964bcSDarrick J. Wong /*
3ecc73f8aSDarrick J. Wong  * Copyright (C) 2017-2023 Oracle.  All Rights Reserved.
4739a2fe0SDarrick J. Wong  * Author: Darrick J. Wong <djwong@kernel.org>
5537964bcSDarrick J. Wong  */
6537964bcSDarrick J. Wong #include "xfs.h"
7537964bcSDarrick J. Wong #include "xfs_fs.h"
8537964bcSDarrick J. Wong #include "xfs_shared.h"
9537964bcSDarrick J. Wong #include "xfs_format.h"
10537964bcSDarrick J. Wong #include "xfs_trans_resv.h"
11537964bcSDarrick J. Wong #include "xfs_mount.h"
12ae7bae68SChandan Babu R #include "xfs_inode.h"
13537964bcSDarrick J. Wong #include "xfs_btree.h"
14537964bcSDarrick J. Wong #include "scrub/scrub.h"
15537964bcSDarrick J. Wong #include "scrub/common.h"
16537964bcSDarrick J. Wong #include "scrub/btree.h"
17537964bcSDarrick J. Wong #include "scrub/trace.h"
18537964bcSDarrick J. Wong 
19537964bcSDarrick J. Wong /* btree scrubbing */
20537964bcSDarrick J. Wong 
21537964bcSDarrick J. Wong /*
22537964bcSDarrick J. Wong  * Check for btree operation errors.  See the section about handling
23537964bcSDarrick J. Wong  * operational errors in common.c.
24537964bcSDarrick J. Wong  */
2564b12563SDarrick J. Wong static bool
__xchk_btree_process_error(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level,int * error,__u32 errflag,void * ret_ip)26c517b3aaSDarrick J. Wong __xchk_btree_process_error(
271d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
28537964bcSDarrick J. Wong 	struct xfs_btree_cur	*cur,
29537964bcSDarrick J. Wong 	int			level,
3064b12563SDarrick J. Wong 	int			*error,
3164b12563SDarrick J. Wong 	__u32			errflag,
3264b12563SDarrick J. Wong 	void			*ret_ip)
33537964bcSDarrick J. Wong {
34537964bcSDarrick J. Wong 	if (*error == 0)
35537964bcSDarrick J. Wong 		return true;
36537964bcSDarrick J. Wong 
37537964bcSDarrick J. Wong 	switch (*error) {
38537964bcSDarrick J. Wong 	case -EDEADLOCK:
3988accf17SDarrick J. Wong 	case -ECHRNG:
40537964bcSDarrick J. Wong 		/* Used to restart an op with deadlock avoidance. */
41c517b3aaSDarrick J. Wong 		trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
42537964bcSDarrick J. Wong 		break;
43537964bcSDarrick J. Wong 	case -EFSBADCRC:
44537964bcSDarrick J. Wong 	case -EFSCORRUPTED:
45537964bcSDarrick J. Wong 		/* Note the badness but don't abort. */
4664b12563SDarrick J. Wong 		sc->sm->sm_flags |= errflag;
47537964bcSDarrick J. Wong 		*error = 0;
4853004ee7SGustavo A. R. Silva 		fallthrough;
49537964bcSDarrick J. Wong 	default:
50537964bcSDarrick J. Wong 		if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
51c517b3aaSDarrick J. Wong 			trace_xchk_ifork_btree_op_error(sc, cur, level,
5264b12563SDarrick J. Wong 					*error, ret_ip);
53537964bcSDarrick J. Wong 		else
54c517b3aaSDarrick J. Wong 			trace_xchk_btree_op_error(sc, cur, level,
5564b12563SDarrick J. Wong 					*error, ret_ip);
56537964bcSDarrick J. Wong 		break;
57537964bcSDarrick J. Wong 	}
58537964bcSDarrick J. Wong 	return false;
59537964bcSDarrick J. Wong }
60537964bcSDarrick J. Wong 
6164b12563SDarrick J. Wong bool
xchk_btree_process_error(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level,int * error)62c517b3aaSDarrick J. Wong xchk_btree_process_error(
631d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
6464b12563SDarrick J. Wong 	struct xfs_btree_cur	*cur,
6564b12563SDarrick J. Wong 	int			level,
6664b12563SDarrick J. Wong 	int			*error)
6764b12563SDarrick J. Wong {
68c517b3aaSDarrick J. Wong 	return __xchk_btree_process_error(sc, cur, level, error,
6964b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_CORRUPT, __return_address);
7064b12563SDarrick J. Wong }
7164b12563SDarrick J. Wong 
7264b12563SDarrick J. Wong bool
xchk_btree_xref_process_error(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level,int * error)73c517b3aaSDarrick J. Wong xchk_btree_xref_process_error(
741d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
7564b12563SDarrick J. Wong 	struct xfs_btree_cur	*cur,
7664b12563SDarrick J. Wong 	int			level,
7764b12563SDarrick J. Wong 	int			*error)
7864b12563SDarrick J. Wong {
79c517b3aaSDarrick J. Wong 	return __xchk_btree_process_error(sc, cur, level, error,
8064b12563SDarrick J. Wong 			XFS_SCRUB_OFLAG_XFAIL, __return_address);
8164b12563SDarrick J. Wong }
8264b12563SDarrick J. Wong 
83537964bcSDarrick J. Wong /* Record btree block corruption. */
8464b12563SDarrick J. Wong static void
__xchk_btree_set_corrupt(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level,__u32 errflag,void * ret_ip)85c517b3aaSDarrick J. Wong __xchk_btree_set_corrupt(
861d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
8764b12563SDarrick J. Wong 	struct xfs_btree_cur	*cur,
8864b12563SDarrick J. Wong 	int			level,
8964b12563SDarrick J. Wong 	__u32			errflag,
9064b12563SDarrick J. Wong 	void			*ret_ip)
9164b12563SDarrick J. Wong {
9264b12563SDarrick J. Wong 	sc->sm->sm_flags |= errflag;
9364b12563SDarrick J. Wong 
9464b12563SDarrick J. Wong 	if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
95c517b3aaSDarrick J. Wong 		trace_xchk_ifork_btree_error(sc, cur, level,
9664b12563SDarrick J. Wong 				ret_ip);
9764b12563SDarrick J. Wong 	else
98c517b3aaSDarrick J. Wong 		trace_xchk_btree_error(sc, cur, level,
9964b12563SDarrick J. Wong 				ret_ip);
10064b12563SDarrick J. Wong }
10164b12563SDarrick J. Wong 
102537964bcSDarrick J. Wong void
xchk_btree_set_corrupt(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level)103c517b3aaSDarrick J. Wong xchk_btree_set_corrupt(
1041d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
105537964bcSDarrick J. Wong 	struct xfs_btree_cur	*cur,
106537964bcSDarrick J. Wong 	int			level)
107537964bcSDarrick J. Wong {
108c517b3aaSDarrick J. Wong 	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_CORRUPT,
109537964bcSDarrick J. Wong 			__return_address);
11064b12563SDarrick J. Wong }
11164b12563SDarrick J. Wong 
11264b12563SDarrick J. Wong void
xchk_btree_xref_set_corrupt(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level)113c517b3aaSDarrick J. Wong xchk_btree_xref_set_corrupt(
1141d8a748aSDarrick J. Wong 	struct xfs_scrub	*sc,
11564b12563SDarrick J. Wong 	struct xfs_btree_cur	*cur,
11664b12563SDarrick J. Wong 	int			level)
11764b12563SDarrick J. Wong {
118c517b3aaSDarrick J. Wong 	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_XCORRUPT,
119537964bcSDarrick J. Wong 			__return_address);
120537964bcSDarrick J. Wong }
121537964bcSDarrick J. Wong 
12238384569SDarrick J. Wong void
xchk_btree_set_preen(struct xfs_scrub * sc,struct xfs_btree_cur * cur,int level)12338384569SDarrick J. Wong xchk_btree_set_preen(
12438384569SDarrick J. Wong 	struct xfs_scrub	*sc,
12538384569SDarrick J. Wong 	struct xfs_btree_cur	*cur,
12638384569SDarrick J. Wong 	int			level)
12738384569SDarrick J. Wong {
12838384569SDarrick J. Wong 	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_PREEN,
12938384569SDarrick J. Wong 			__return_address);
13038384569SDarrick J. Wong }
13138384569SDarrick J. Wong 
132537964bcSDarrick J. Wong /*
13337f3fa7fSDarrick J. Wong  * Make sure this record is in order and doesn't stray outside of the parent
13437f3fa7fSDarrick J. Wong  * keys.
13537f3fa7fSDarrick J. Wong  */
13637f3fa7fSDarrick J. Wong STATIC void
xchk_btree_rec(struct xchk_btree * bs)137c517b3aaSDarrick J. Wong xchk_btree_rec(
138c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs)
13937f3fa7fSDarrick J. Wong {
14037f3fa7fSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
14137f3fa7fSDarrick J. Wong 	union xfs_btree_rec	*rec;
14237f3fa7fSDarrick J. Wong 	union xfs_btree_key	key;
14337f3fa7fSDarrick J. Wong 	union xfs_btree_key	hkey;
14437f3fa7fSDarrick J. Wong 	union xfs_btree_key	*keyp;
14537f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*block;
14637f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*keyblock;
14737f3fa7fSDarrick J. Wong 	struct xfs_buf		*bp;
14837f3fa7fSDarrick J. Wong 
14937f3fa7fSDarrick J. Wong 	block = xfs_btree_get_block(cur, 0, &bp);
1506ca444cfSDarrick J. Wong 	rec = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr, block);
15137f3fa7fSDarrick J. Wong 
152c517b3aaSDarrick J. Wong 	trace_xchk_btree_rec(bs->sc, cur, 0);
15337f3fa7fSDarrick J. Wong 
1542bea8df0SDarrick J. Wong 	/* Are all records across all record blocks in order? */
1552bea8df0SDarrick J. Wong 	if (bs->lastrec_valid &&
156d47fef93SDarrick J. Wong 	    !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
157c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 0);
15837f3fa7fSDarrick J. Wong 	memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
1592bea8df0SDarrick J. Wong 	bs->lastrec_valid = true;
16037f3fa7fSDarrick J. Wong 
16137f3fa7fSDarrick J. Wong 	if (cur->bc_nlevels == 1)
16237f3fa7fSDarrick J. Wong 		return;
16337f3fa7fSDarrick J. Wong 
164bd7e7951SDarrick J. Wong 	/* Is low_key(rec) at least as large as the parent low key? */
16537f3fa7fSDarrick J. Wong 	cur->bc_ops->init_key_from_rec(&key, rec);
16637f3fa7fSDarrick J. Wong 	keyblock = xfs_btree_get_block(cur, 1, &bp);
1676ca444cfSDarrick J. Wong 	keyp = xfs_btree_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
168bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_lt(cur, &key, keyp))
169c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
17037f3fa7fSDarrick J. Wong 
17137f3fa7fSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
17237f3fa7fSDarrick J. Wong 		return;
17337f3fa7fSDarrick J. Wong 
174bd7e7951SDarrick J. Wong 	/* Is high_key(rec) no larger than the parent high key? */
17537f3fa7fSDarrick J. Wong 	cur->bc_ops->init_high_key_from_rec(&hkey, rec);
1766ca444cfSDarrick J. Wong 	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
177bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_lt(cur, keyp, &hkey))
178c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
17937f3fa7fSDarrick J. Wong }
18037f3fa7fSDarrick J. Wong 
18137f3fa7fSDarrick J. Wong /*
18237f3fa7fSDarrick J. Wong  * Make sure this key is in order and doesn't stray outside of the parent
18337f3fa7fSDarrick J. Wong  * keys.
18437f3fa7fSDarrick J. Wong  */
18537f3fa7fSDarrick J. Wong STATIC void
xchk_btree_key(struct xchk_btree * bs,int level)186c517b3aaSDarrick J. Wong xchk_btree_key(
187c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
18837f3fa7fSDarrick J. Wong 	int			level)
18937f3fa7fSDarrick J. Wong {
19037f3fa7fSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
19137f3fa7fSDarrick J. Wong 	union xfs_btree_key	*key;
19237f3fa7fSDarrick J. Wong 	union xfs_btree_key	*keyp;
19337f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*block;
19437f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*keyblock;
19537f3fa7fSDarrick J. Wong 	struct xfs_buf		*bp;
19637f3fa7fSDarrick J. Wong 
19737f3fa7fSDarrick J. Wong 	block = xfs_btree_get_block(cur, level, &bp);
1986ca444cfSDarrick J. Wong 	key = xfs_btree_key_addr(cur, cur->bc_levels[level].ptr, block);
19937f3fa7fSDarrick J. Wong 
200c517b3aaSDarrick J. Wong 	trace_xchk_btree_key(bs->sc, cur, level);
20137f3fa7fSDarrick J. Wong 
2022bea8df0SDarrick J. Wong 	/* Are all low keys across all node blocks in order? */
2032bea8df0SDarrick J. Wong 	if (bs->lastkey[level - 1].valid &&
2042bea8df0SDarrick J. Wong 	    !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1].key, key))
205c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
2062bea8df0SDarrick J. Wong 	memcpy(&bs->lastkey[level - 1].key, key, cur->bc_ops->key_len);
2072bea8df0SDarrick J. Wong 	bs->lastkey[level - 1].valid = true;
20837f3fa7fSDarrick J. Wong 
20937f3fa7fSDarrick J. Wong 	if (level + 1 >= cur->bc_nlevels)
21037f3fa7fSDarrick J. Wong 		return;
21137f3fa7fSDarrick J. Wong 
212bd7e7951SDarrick J. Wong 	/* Is this block's low key at least as large as the parent low key? */
21337f3fa7fSDarrick J. Wong 	keyblock = xfs_btree_get_block(cur, level + 1, &bp);
2146ca444cfSDarrick J. Wong 	keyp = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr, keyblock);
215bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_lt(cur, key, keyp))
216c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
21737f3fa7fSDarrick J. Wong 
21837f3fa7fSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
21937f3fa7fSDarrick J. Wong 		return;
22037f3fa7fSDarrick J. Wong 
221bd7e7951SDarrick J. Wong 	/* Is this block's high key no larger than the parent high key? */
2226ca444cfSDarrick J. Wong 	key = xfs_btree_high_key_addr(cur, cur->bc_levels[level].ptr, block);
2236ca444cfSDarrick J. Wong 	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
2246ca444cfSDarrick J. Wong 			keyblock);
225bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_lt(cur, keyp, key))
226c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
22737f3fa7fSDarrick J. Wong }
22837f3fa7fSDarrick J. Wong 
22937f3fa7fSDarrick J. Wong /*
230cc3e0948SDarrick J. Wong  * Check a btree pointer.  Returns true if it's ok to use this pointer.
231cc3e0948SDarrick J. Wong  * Callers do not need to set the corrupt flag.
232cc3e0948SDarrick J. Wong  */
233cc3e0948SDarrick J. Wong static bool
xchk_btree_ptr_ok(struct xchk_btree * bs,int level,union xfs_btree_ptr * ptr)234c517b3aaSDarrick J. Wong xchk_btree_ptr_ok(
235c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
236cc3e0948SDarrick J. Wong 	int			level,
237cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*ptr)
238cc3e0948SDarrick J. Wong {
239cc3e0948SDarrick J. Wong 	bool			res;
240cc3e0948SDarrick J. Wong 
241cc3e0948SDarrick J. Wong 	/* A btree rooted in an inode has no block pointer to the root. */
242cc3e0948SDarrick J. Wong 	if ((bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
243cc3e0948SDarrick J. Wong 	    level == bs->cur->bc_nlevels)
244cc3e0948SDarrick J. Wong 		return true;
245cc3e0948SDarrick J. Wong 
246cc3e0948SDarrick J. Wong 	/* Otherwise, check the pointers. */
247cc3e0948SDarrick J. Wong 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
248cc3e0948SDarrick J. Wong 		res = xfs_btree_check_lptr(bs->cur, be64_to_cpu(ptr->l), level);
249cc3e0948SDarrick J. Wong 	else
250cc3e0948SDarrick J. Wong 		res = xfs_btree_check_sptr(bs->cur, be32_to_cpu(ptr->s), level);
251cc3e0948SDarrick J. Wong 	if (!res)
252c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
253cc3e0948SDarrick J. Wong 
254cc3e0948SDarrick J. Wong 	return res;
255cc3e0948SDarrick J. Wong }
256cc3e0948SDarrick J. Wong 
257cc3e0948SDarrick J. Wong /* Check that a btree block's sibling matches what we expect it. */
258cc3e0948SDarrick J. Wong STATIC int
xchk_btree_block_check_sibling(struct xchk_btree * bs,int level,int direction,union xfs_btree_ptr * sibling)259c517b3aaSDarrick J. Wong xchk_btree_block_check_sibling(
260c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
261cc3e0948SDarrick J. Wong 	int			level,
262cc3e0948SDarrick J. Wong 	int			direction,
263cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*sibling)
264cc3e0948SDarrick J. Wong {
265cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
266cc3e0948SDarrick J. Wong 	struct xfs_btree_block	*pblock;
267cc3e0948SDarrick J. Wong 	struct xfs_buf		*pbp;
268cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*ncur = NULL;
269cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*pp;
270cc3e0948SDarrick J. Wong 	int			success;
271cc3e0948SDarrick J. Wong 	int			error;
272cc3e0948SDarrick J. Wong 
273cc3e0948SDarrick J. Wong 	error = xfs_btree_dup_cursor(cur, &ncur);
274c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error) ||
275cc3e0948SDarrick J. Wong 	    !ncur)
276cc3e0948SDarrick J. Wong 		return error;
277cc3e0948SDarrick J. Wong 
278cc3e0948SDarrick J. Wong 	/*
279cc3e0948SDarrick J. Wong 	 * If the pointer is null, we shouldn't be able to move the upper
280cc3e0948SDarrick J. Wong 	 * level pointer anywhere.
281cc3e0948SDarrick J. Wong 	 */
282cc3e0948SDarrick J. Wong 	if (xfs_btree_ptr_is_null(cur, sibling)) {
283cc3e0948SDarrick J. Wong 		if (direction > 0)
284cc3e0948SDarrick J. Wong 			error = xfs_btree_increment(ncur, level + 1, &success);
285cc3e0948SDarrick J. Wong 		else
286cc3e0948SDarrick J. Wong 			error = xfs_btree_decrement(ncur, level + 1, &success);
287cc3e0948SDarrick J. Wong 		if (error == 0 && success)
288c517b3aaSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
289cc3e0948SDarrick J. Wong 		error = 0;
290cc3e0948SDarrick J. Wong 		goto out;
291cc3e0948SDarrick J. Wong 	}
292cc3e0948SDarrick J. Wong 
293cc3e0948SDarrick J. Wong 	/* Increment upper level pointer. */
294cc3e0948SDarrick J. Wong 	if (direction > 0)
295cc3e0948SDarrick J. Wong 		error = xfs_btree_increment(ncur, level + 1, &success);
296cc3e0948SDarrick J. Wong 	else
297cc3e0948SDarrick J. Wong 		error = xfs_btree_decrement(ncur, level + 1, &success);
298c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error))
299cc3e0948SDarrick J. Wong 		goto out;
300cc3e0948SDarrick J. Wong 	if (!success) {
301c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level + 1);
302cc3e0948SDarrick J. Wong 		goto out;
303cc3e0948SDarrick J. Wong 	}
304cc3e0948SDarrick J. Wong 
305cc3e0948SDarrick J. Wong 	/* Compare upper level pointer to sibling pointer. */
306cc3e0948SDarrick J. Wong 	pblock = xfs_btree_get_block(ncur, level + 1, &pbp);
3076ca444cfSDarrick J. Wong 	pp = xfs_btree_ptr_addr(ncur, ncur->bc_levels[level + 1].ptr, pblock);
308c517b3aaSDarrick J. Wong 	if (!xchk_btree_ptr_ok(bs, level + 1, pp))
309cc3e0948SDarrick J. Wong 		goto out;
310cf1b0b8bSDarrick J. Wong 	if (pbp)
311c517b3aaSDarrick J. Wong 		xchk_buffer_recheck(bs->sc, pbp);
312cc3e0948SDarrick J. Wong 
313cc3e0948SDarrick J. Wong 	if (xfs_btree_diff_two_ptrs(cur, pp, sibling))
314c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
315cc3e0948SDarrick J. Wong out:
316cc3e0948SDarrick J. Wong 	xfs_btree_del_cursor(ncur, XFS_BTREE_ERROR);
317cc3e0948SDarrick J. Wong 	return error;
318cc3e0948SDarrick J. Wong }
319cc3e0948SDarrick J. Wong 
320cc3e0948SDarrick J. Wong /* Check the siblings of a btree block. */
321cc3e0948SDarrick J. Wong STATIC int
xchk_btree_block_check_siblings(struct xchk_btree * bs,struct xfs_btree_block * block)322c517b3aaSDarrick J. Wong xchk_btree_block_check_siblings(
323c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
324cc3e0948SDarrick J. Wong 	struct xfs_btree_block	*block)
325cc3e0948SDarrick J. Wong {
326cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
327cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	leftsib;
328cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	rightsib;
329cc3e0948SDarrick J. Wong 	int			level;
330cc3e0948SDarrick J. Wong 	int			error = 0;
331cc3e0948SDarrick J. Wong 
332cc3e0948SDarrick J. Wong 	xfs_btree_get_sibling(cur, block, &leftsib, XFS_BB_LEFTSIB);
333cc3e0948SDarrick J. Wong 	xfs_btree_get_sibling(cur, block, &rightsib, XFS_BB_RIGHTSIB);
334cc3e0948SDarrick J. Wong 	level = xfs_btree_get_level(block);
335cc3e0948SDarrick J. Wong 
336cc3e0948SDarrick J. Wong 	/* Root block should never have siblings. */
337cc3e0948SDarrick J. Wong 	if (level == cur->bc_nlevels - 1) {
338cc3e0948SDarrick J. Wong 		if (!xfs_btree_ptr_is_null(cur, &leftsib) ||
339cc3e0948SDarrick J. Wong 		    !xfs_btree_ptr_is_null(cur, &rightsib))
340c517b3aaSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
341cc3e0948SDarrick J. Wong 		goto out;
342cc3e0948SDarrick J. Wong 	}
343cc3e0948SDarrick J. Wong 
344cc3e0948SDarrick J. Wong 	/*
345cc3e0948SDarrick J. Wong 	 * Does the left & right sibling pointers match the adjacent
346cc3e0948SDarrick J. Wong 	 * parent level pointers?
347cc3e0948SDarrick J. Wong 	 * (These function absorbs error codes for us.)
348cc3e0948SDarrick J. Wong 	 */
349c517b3aaSDarrick J. Wong 	error = xchk_btree_block_check_sibling(bs, level, -1, &leftsib);
350cc3e0948SDarrick J. Wong 	if (error)
351cc3e0948SDarrick J. Wong 		return error;
352c517b3aaSDarrick J. Wong 	error = xchk_btree_block_check_sibling(bs, level, 1, &rightsib);
353cc3e0948SDarrick J. Wong 	if (error)
354cc3e0948SDarrick J. Wong 		return error;
355cc3e0948SDarrick J. Wong out:
356cc3e0948SDarrick J. Wong 	return error;
357cc3e0948SDarrick J. Wong }
358cc3e0948SDarrick J. Wong 
359858333dcSDarrick J. Wong struct check_owner {
360858333dcSDarrick J. Wong 	struct list_head	list;
361858333dcSDarrick J. Wong 	xfs_daddr_t		daddr;
362858333dcSDarrick J. Wong 	int			level;
363858333dcSDarrick J. Wong };
364858333dcSDarrick J. Wong 
365858333dcSDarrick J. Wong /*
366858333dcSDarrick J. Wong  * Make sure this btree block isn't in the free list and that there's
367858333dcSDarrick J. Wong  * an rmap record for it.
368858333dcSDarrick J. Wong  */
369858333dcSDarrick J. Wong STATIC int
xchk_btree_check_block_owner(struct xchk_btree * bs,int level,xfs_daddr_t daddr)370c517b3aaSDarrick J. Wong xchk_btree_check_block_owner(
371c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
372858333dcSDarrick J. Wong 	int			level,
373858333dcSDarrick J. Wong 	xfs_daddr_t		daddr)
374858333dcSDarrick J. Wong {
375858333dcSDarrick J. Wong 	xfs_agnumber_t		agno;
37652dc4b44SDarrick J. Wong 	xfs_agblock_t		agbno;
37752dc4b44SDarrick J. Wong 	xfs_btnum_t		btnum;
378858333dcSDarrick J. Wong 	bool			init_sa;
379858333dcSDarrick J. Wong 	int			error = 0;
380858333dcSDarrick J. Wong 
381858333dcSDarrick J. Wong 	if (!bs->cur)
382858333dcSDarrick J. Wong 		return 0;
383858333dcSDarrick J. Wong 
38452dc4b44SDarrick J. Wong 	btnum = bs->cur->bc_btnum;
385858333dcSDarrick J. Wong 	agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
38652dc4b44SDarrick J. Wong 	agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
387858333dcSDarrick J. Wong 
38820bccdb0SDarrick J. Wong 	/*
38920bccdb0SDarrick J. Wong 	 * If the btree being examined is not itself a per-AG btree, initialize
39020bccdb0SDarrick J. Wong 	 * sc->sa so that we can check for the presence of an ownership record
39120bccdb0SDarrick J. Wong 	 * in the rmap btree for the AG containing the block.
39220bccdb0SDarrick J. Wong 	 */
39320bccdb0SDarrick J. Wong 	init_sa = bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE;
394858333dcSDarrick J. Wong 	if (init_sa) {
39548c6615cSDarrick J. Wong 		error = xchk_ag_init_existing(bs->sc, agno, &bs->sc->sa);
396c517b3aaSDarrick J. Wong 		if (!xchk_btree_xref_process_error(bs->sc, bs->cur,
397858333dcSDarrick J. Wong 				level, &error))
39861e0d0ccSDarrick J. Wong 			goto out_free;
399858333dcSDarrick J. Wong 	}
400858333dcSDarrick J. Wong 
401c517b3aaSDarrick J. Wong 	xchk_xref_is_used_space(bs->sc, agbno, 1);
40252dc4b44SDarrick J. Wong 	/*
40352dc4b44SDarrick J. Wong 	 * The bnobt scrubber aliases bs->cur to bs->sc->sa.bno_cur, so we
40452dc4b44SDarrick J. Wong 	 * have to nullify it (to shut down further block owner checks) if
40552dc4b44SDarrick J. Wong 	 * self-xref encounters problems.
40652dc4b44SDarrick J. Wong 	 */
40752dc4b44SDarrick J. Wong 	if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
40852dc4b44SDarrick J. Wong 		bs->cur = NULL;
40952dc4b44SDarrick J. Wong 
41069115f77SDarrick J. Wong 	xchk_xref_is_only_owned_by(bs->sc, agbno, 1, bs->oinfo);
411d852657cSDarrick J. Wong 	if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
412d852657cSDarrick J. Wong 		bs->cur = NULL;
413d852657cSDarrick J. Wong 
41461e0d0ccSDarrick J. Wong out_free:
415858333dcSDarrick J. Wong 	if (init_sa)
416c517b3aaSDarrick J. Wong 		xchk_ag_free(bs->sc, &bs->sc->sa);
417858333dcSDarrick J. Wong 
418858333dcSDarrick J. Wong 	return error;
419858333dcSDarrick J. Wong }
420858333dcSDarrick J. Wong 
421858333dcSDarrick J. Wong /* Check the owner of a btree block. */
422858333dcSDarrick J. Wong STATIC int
xchk_btree_check_owner(struct xchk_btree * bs,int level,struct xfs_buf * bp)423c517b3aaSDarrick J. Wong xchk_btree_check_owner(
424c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
425858333dcSDarrick J. Wong 	int			level,
426858333dcSDarrick J. Wong 	struct xfs_buf		*bp)
427858333dcSDarrick J. Wong {
428858333dcSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
429858333dcSDarrick J. Wong 
430a72e9d8dSDarrick J. Wong 	/*
431a72e9d8dSDarrick J. Wong 	 * In theory, xfs_btree_get_block should only give us a null buffer
432a72e9d8dSDarrick J. Wong 	 * pointer for the root of a root-in-inode btree type, but we need
433a72e9d8dSDarrick J. Wong 	 * to check defensively here in case the cursor state is also screwed
434a72e9d8dSDarrick J. Wong 	 * up.
435a72e9d8dSDarrick J. Wong 	 */
436a72e9d8dSDarrick J. Wong 	if (bp == NULL) {
437a72e9d8dSDarrick J. Wong 		if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE))
438a72e9d8dSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, bs->cur, level);
439858333dcSDarrick J. Wong 		return 0;
440a72e9d8dSDarrick J. Wong 	}
441858333dcSDarrick J. Wong 
442858333dcSDarrick J. Wong 	/*
443858333dcSDarrick J. Wong 	 * We want to cross-reference each btree block with the bnobt
444858333dcSDarrick J. Wong 	 * and the rmapbt.  We cannot cross-reference the bnobt or
445858333dcSDarrick J. Wong 	 * rmapbt while scanning the bnobt or rmapbt, respectively,
446858333dcSDarrick J. Wong 	 * because we cannot alter the cursor and we'd prefer not to
447858333dcSDarrick J. Wong 	 * duplicate cursors.  Therefore, save the buffer daddr for
448858333dcSDarrick J. Wong 	 * later scanning.
449858333dcSDarrick J. Wong 	 */
450858333dcSDarrick J. Wong 	if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
451fcd2a434SDarrick J. Wong 		struct check_owner	*co;
452fcd2a434SDarrick J. Wong 
453306195f3SDarrick J. Wong 		co = kmalloc(sizeof(struct check_owner), XCHK_GFP_FLAGS);
454858333dcSDarrick J. Wong 		if (!co)
455858333dcSDarrick J. Wong 			return -ENOMEM;
456fcd2a434SDarrick J. Wong 
457fcd2a434SDarrick J. Wong 		INIT_LIST_HEAD(&co->list);
458858333dcSDarrick J. Wong 		co->level = level;
45904fcad80SDave Chinner 		co->daddr = xfs_buf_daddr(bp);
460858333dcSDarrick J. Wong 		list_add_tail(&co->list, &bs->to_check);
461858333dcSDarrick J. Wong 		return 0;
462858333dcSDarrick J. Wong 	}
463858333dcSDarrick J. Wong 
46404fcad80SDave Chinner 	return xchk_btree_check_block_owner(bs, level, xfs_buf_daddr(bp));
465858333dcSDarrick J. Wong }
466858333dcSDarrick J. Wong 
467ae7bae68SChandan Babu R /* Decide if we want to check minrecs of a btree block in the inode root. */
468ae7bae68SChandan Babu R static inline bool
xchk_btree_check_iroot_minrecs(struct xchk_btree * bs)469ae7bae68SChandan Babu R xchk_btree_check_iroot_minrecs(
470ae7bae68SChandan Babu R 	struct xchk_btree	*bs)
471ae7bae68SChandan Babu R {
472ae7bae68SChandan Babu R 	/*
473ae7bae68SChandan Babu R 	 * xfs_bmap_add_attrfork_btree had an implementation bug wherein it
474ae7bae68SChandan Babu R 	 * would miscalculate the space required for the data fork bmbt root
475ae7bae68SChandan Babu R 	 * when adding an attr fork, and promote the iroot contents to an
476ae7bae68SChandan Babu R 	 * external block unnecessarily.  This went unnoticed for many years
477ae7bae68SChandan Babu R 	 * until scrub found filesystems in this state.  Inode rooted btrees are
478ae7bae68SChandan Babu R 	 * not supposed to have immediate child blocks that are small enough
479ae7bae68SChandan Babu R 	 * that the contents could fit in the inode root, but we can't fail
480ae7bae68SChandan Babu R 	 * existing filesystems, so instead we disable the check for data fork
481ae7bae68SChandan Babu R 	 * bmap btrees when there's an attr fork.
482ae7bae68SChandan Babu R 	 */
483ae7bae68SChandan Babu R 	if (bs->cur->bc_btnum == XFS_BTNUM_BMAP &&
484ae7bae68SChandan Babu R 	    bs->cur->bc_ino.whichfork == XFS_DATA_FORK &&
485932b42c6SDarrick J. Wong 	    xfs_inode_has_attr_fork(bs->sc->ip))
486ae7bae68SChandan Babu R 		return false;
487ae7bae68SChandan Babu R 
488ae7bae68SChandan Babu R 	return true;
489ae7bae68SChandan Babu R }
490ae7bae68SChandan Babu R 
491cc3e0948SDarrick J. Wong /*
49208a3a692SDarrick J. Wong  * Check that this btree block has at least minrecs records or is one of the
49308a3a692SDarrick J. Wong  * special blocks that don't require that.
49408a3a692SDarrick J. Wong  */
49508a3a692SDarrick J. Wong STATIC void
xchk_btree_check_minrecs(struct xchk_btree * bs,int level,struct xfs_btree_block * block)496c517b3aaSDarrick J. Wong xchk_btree_check_minrecs(
497c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
49808a3a692SDarrick J. Wong 	int			level,
49908a3a692SDarrick J. Wong 	struct xfs_btree_block	*block)
50008a3a692SDarrick J. Wong {
501e95b6c3eSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
502e95b6c3eSDarrick J. Wong 	unsigned int		root_level = cur->bc_nlevels - 1;
503e95b6c3eSDarrick J. Wong 	unsigned int		numrecs = be16_to_cpu(block->bb_numrecs);
50408a3a692SDarrick J. Wong 
50508a3a692SDarrick J. Wong 	/* More records than minrecs means the block is ok. */
506e95b6c3eSDarrick J. Wong 	if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
50708a3a692SDarrick J. Wong 		return;
50808a3a692SDarrick J. Wong 
50908a3a692SDarrick J. Wong 	/*
510e95b6c3eSDarrick J. Wong 	 * For btrees rooted in the inode, it's possible that the root block
511e95b6c3eSDarrick J. Wong 	 * contents spilled into a regular ondisk block because there wasn't
512e95b6c3eSDarrick J. Wong 	 * enough space in the inode root.  The number of records in that
513e95b6c3eSDarrick J. Wong 	 * child block might be less than the standard minrecs, but that's ok
514e95b6c3eSDarrick J. Wong 	 * provided that there's only one direct child of the root.
51508a3a692SDarrick J. Wong 	 */
516e95b6c3eSDarrick J. Wong 	if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
517e95b6c3eSDarrick J. Wong 	    level == cur->bc_nlevels - 2) {
518e95b6c3eSDarrick J. Wong 		struct xfs_btree_block	*root_block;
519e95b6c3eSDarrick J. Wong 		struct xfs_buf		*root_bp;
520e95b6c3eSDarrick J. Wong 		int			root_maxrecs;
52108a3a692SDarrick J. Wong 
522e95b6c3eSDarrick J. Wong 		root_block = xfs_btree_get_block(cur, root_level, &root_bp);
523e95b6c3eSDarrick J. Wong 		root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
524ae7bae68SChandan Babu R 		if (xchk_btree_check_iroot_minrecs(bs) &&
525ae7bae68SChandan Babu R 		    (be16_to_cpu(root_block->bb_numrecs) != 1 ||
526ae7bae68SChandan Babu R 		     numrecs <= root_maxrecs))
527e95b6c3eSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
528e95b6c3eSDarrick J. Wong 		return;
529e95b6c3eSDarrick J. Wong 	}
530e95b6c3eSDarrick J. Wong 
531e95b6c3eSDarrick J. Wong 	/*
532e95b6c3eSDarrick J. Wong 	 * Otherwise, only the root level is allowed to have fewer than minrecs
533e95b6c3eSDarrick J. Wong 	 * records or keyptrs.
534e95b6c3eSDarrick J. Wong 	 */
535e95b6c3eSDarrick J. Wong 	if (level < root_level)
536e95b6c3eSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
53708a3a692SDarrick J. Wong }
53808a3a692SDarrick J. Wong 
53908a3a692SDarrick J. Wong /*
540c99f99faSDarrick J. Wong  * If this btree block has a parent, make sure that the parent's keys capture
541c99f99faSDarrick J. Wong  * the keyspace contained in this block.
542c99f99faSDarrick J. Wong  */
543c99f99faSDarrick J. Wong STATIC void
xchk_btree_block_check_keys(struct xchk_btree * bs,int level,struct xfs_btree_block * block)544c99f99faSDarrick J. Wong xchk_btree_block_check_keys(
545c99f99faSDarrick J. Wong 	struct xchk_btree	*bs,
546c99f99faSDarrick J. Wong 	int			level,
547c99f99faSDarrick J. Wong 	struct xfs_btree_block	*block)
548c99f99faSDarrick J. Wong {
549c99f99faSDarrick J. Wong 	union xfs_btree_key	block_key;
550c99f99faSDarrick J. Wong 	union xfs_btree_key	*block_high_key;
551c99f99faSDarrick J. Wong 	union xfs_btree_key	*parent_low_key, *parent_high_key;
552c99f99faSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
553c99f99faSDarrick J. Wong 	struct xfs_btree_block	*parent_block;
554c99f99faSDarrick J. Wong 	struct xfs_buf		*bp;
555c99f99faSDarrick J. Wong 
556c99f99faSDarrick J. Wong 	if (level == cur->bc_nlevels - 1)
557c99f99faSDarrick J. Wong 		return;
558c99f99faSDarrick J. Wong 
559c99f99faSDarrick J. Wong 	xfs_btree_get_keys(cur, block, &block_key);
560c99f99faSDarrick J. Wong 
561c99f99faSDarrick J. Wong 	/* Make sure the low key of this block matches the parent. */
562c99f99faSDarrick J. Wong 	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
563c99f99faSDarrick J. Wong 	parent_low_key = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
564c99f99faSDarrick J. Wong 			parent_block);
565bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_ne(cur, &block_key, parent_low_key)) {
566c99f99faSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
567c99f99faSDarrick J. Wong 		return;
568c99f99faSDarrick J. Wong 	}
569c99f99faSDarrick J. Wong 
570c99f99faSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
571c99f99faSDarrick J. Wong 		return;
572c99f99faSDarrick J. Wong 
573c99f99faSDarrick J. Wong 	/* Make sure the high key of this block matches the parent. */
574c99f99faSDarrick J. Wong 	parent_high_key = xfs_btree_high_key_addr(cur,
575c99f99faSDarrick J. Wong 			cur->bc_levels[level + 1].ptr, parent_block);
576c99f99faSDarrick J. Wong 	block_high_key = xfs_btree_high_key_from_key(cur, &block_key);
577bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_ne(cur, block_high_key, parent_high_key))
578c99f99faSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
579c99f99faSDarrick J. Wong }
580c99f99faSDarrick J. Wong 
581c99f99faSDarrick J. Wong /*
582cc3e0948SDarrick J. Wong  * Grab and scrub a btree block given a btree pointer.  Returns block
583cc3e0948SDarrick J. Wong  * and buffer pointers (if applicable) if they're ok to use.
584cc3e0948SDarrick J. Wong  */
585cc3e0948SDarrick J. Wong STATIC int
xchk_btree_get_block(struct xchk_btree * bs,int level,union xfs_btree_ptr * pp,struct xfs_btree_block ** pblock,struct xfs_buf ** pbp)586c517b3aaSDarrick J. Wong xchk_btree_get_block(
587c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
588cc3e0948SDarrick J. Wong 	int			level,
589cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*pp,
590cc3e0948SDarrick J. Wong 	struct xfs_btree_block	**pblock,
591cc3e0948SDarrick J. Wong 	struct xfs_buf		**pbp)
592cc3e0948SDarrick J. Wong {
593032d91f9SDarrick J. Wong 	xfs_failaddr_t		failed_at;
594cc3e0948SDarrick J. Wong 	int			error;
595cc3e0948SDarrick J. Wong 
596cc3e0948SDarrick J. Wong 	*pblock = NULL;
597cc3e0948SDarrick J. Wong 	*pbp = NULL;
598cc3e0948SDarrick J. Wong 
599cc3e0948SDarrick J. Wong 	error = xfs_btree_lookup_get_block(bs->cur, level, pp, pblock);
600c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, bs->cur, level, &error) ||
601a605e869SDarrick J. Wong 	    !*pblock)
602cc3e0948SDarrick J. Wong 		return error;
603cc3e0948SDarrick J. Wong 
604cc3e0948SDarrick J. Wong 	xfs_btree_get_block(bs->cur, level, pbp);
605cc3e0948SDarrick J. Wong 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
606cc3e0948SDarrick J. Wong 		failed_at = __xfs_btree_check_lblock(bs->cur, *pblock,
607cc3e0948SDarrick J. Wong 				level, *pbp);
608cc3e0948SDarrick J. Wong 	else
609cc3e0948SDarrick J. Wong 		failed_at = __xfs_btree_check_sblock(bs->cur, *pblock,
610cc3e0948SDarrick J. Wong 				 level, *pbp);
611cc3e0948SDarrick J. Wong 	if (failed_at) {
612c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
613cc3e0948SDarrick J. Wong 		return 0;
614cc3e0948SDarrick J. Wong 	}
615cf1b0b8bSDarrick J. Wong 	if (*pbp)
616c517b3aaSDarrick J. Wong 		xchk_buffer_recheck(bs->sc, *pbp);
617cc3e0948SDarrick J. Wong 
618c517b3aaSDarrick J. Wong 	xchk_btree_check_minrecs(bs, level, *pblock);
61908a3a692SDarrick J. Wong 
620cc3e0948SDarrick J. Wong 	/*
621858333dcSDarrick J. Wong 	 * Check the block's owner; this function absorbs error codes
622858333dcSDarrick J. Wong 	 * for us.
623858333dcSDarrick J. Wong 	 */
624c517b3aaSDarrick J. Wong 	error = xchk_btree_check_owner(bs, level, *pbp);
625858333dcSDarrick J. Wong 	if (error)
626858333dcSDarrick J. Wong 		return error;
627858333dcSDarrick J. Wong 
628858333dcSDarrick J. Wong 	/*
629cc3e0948SDarrick J. Wong 	 * Check the block's siblings; this function absorbs error codes
630cc3e0948SDarrick J. Wong 	 * for us.
631cc3e0948SDarrick J. Wong 	 */
632c99f99faSDarrick J. Wong 	error = xchk_btree_block_check_siblings(bs, *pblock);
633c99f99faSDarrick J. Wong 	if (error)
634c99f99faSDarrick J. Wong 		return error;
635c99f99faSDarrick J. Wong 
636c99f99faSDarrick J. Wong 	xchk_btree_block_check_keys(bs, level, *pblock);
637c99f99faSDarrick J. Wong 	return 0;
638cc3e0948SDarrick J. Wong }
639cc3e0948SDarrick J. Wong 
640cc3e0948SDarrick J. Wong /*
6412fdbec5cSDarrick J. Wong  * Check that the low and high keys of this block match the keys stored
6422fdbec5cSDarrick J. Wong  * in the parent block.
6432fdbec5cSDarrick J. Wong  */
6442fdbec5cSDarrick J. Wong STATIC void
xchk_btree_block_keys(struct xchk_btree * bs,int level,struct xfs_btree_block * block)645c517b3aaSDarrick J. Wong xchk_btree_block_keys(
646c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
6472fdbec5cSDarrick J. Wong 	int			level,
6482fdbec5cSDarrick J. Wong 	struct xfs_btree_block	*block)
6492fdbec5cSDarrick J. Wong {
6502fdbec5cSDarrick J. Wong 	union xfs_btree_key	block_keys;
6512fdbec5cSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
6522fdbec5cSDarrick J. Wong 	union xfs_btree_key	*high_bk;
6532fdbec5cSDarrick J. Wong 	union xfs_btree_key	*parent_keys;
6542fdbec5cSDarrick J. Wong 	union xfs_btree_key	*high_pk;
6552fdbec5cSDarrick J. Wong 	struct xfs_btree_block	*parent_block;
6562fdbec5cSDarrick J. Wong 	struct xfs_buf		*bp;
6572fdbec5cSDarrick J. Wong 
6582fdbec5cSDarrick J. Wong 	if (level >= cur->bc_nlevels - 1)
6592fdbec5cSDarrick J. Wong 		return;
6602fdbec5cSDarrick J. Wong 
6612fdbec5cSDarrick J. Wong 	/* Calculate the keys for this block. */
6622fdbec5cSDarrick J. Wong 	xfs_btree_get_keys(cur, block, &block_keys);
6632fdbec5cSDarrick J. Wong 
6642fdbec5cSDarrick J. Wong 	/* Obtain the parent's copy of the keys for this block. */
6652fdbec5cSDarrick J. Wong 	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
6666ca444cfSDarrick J. Wong 	parent_keys = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
6672fdbec5cSDarrick J. Wong 			parent_block);
6682fdbec5cSDarrick J. Wong 
669bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_ne(cur, &block_keys, parent_keys))
670c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
6712fdbec5cSDarrick J. Wong 
6722fdbec5cSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
6732fdbec5cSDarrick J. Wong 		return;
6742fdbec5cSDarrick J. Wong 
6752fdbec5cSDarrick J. Wong 	/* Get high keys */
6762fdbec5cSDarrick J. Wong 	high_bk = xfs_btree_high_key_from_key(cur, &block_keys);
6776ca444cfSDarrick J. Wong 	high_pk = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
6782fdbec5cSDarrick J. Wong 			parent_block);
6792fdbec5cSDarrick J. Wong 
680bd7e7951SDarrick J. Wong 	if (xfs_btree_keycmp_ne(cur, high_bk, high_pk))
681c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
6822fdbec5cSDarrick J. Wong }
6832fdbec5cSDarrick J. Wong 
6842fdbec5cSDarrick J. Wong /*
685537964bcSDarrick J. Wong  * Visit all nodes and leaves of a btree.  Check that all pointers and
686537964bcSDarrick J. Wong  * records are in order, that the keys reflect the records, and use a callback
687cc3e0948SDarrick J. Wong  * so that the caller can verify individual records.
688537964bcSDarrick J. Wong  */
689537964bcSDarrick J. Wong int
xchk_btree(struct xfs_scrub * sc,struct xfs_btree_cur * cur,xchk_btree_rec_fn scrub_fn,const struct xfs_owner_info * oinfo,void * private)690c517b3aaSDarrick J. Wong xchk_btree(
6911d8a748aSDarrick J. Wong 	struct xfs_scrub		*sc,
692537964bcSDarrick J. Wong 	struct xfs_btree_cur		*cur,
693c517b3aaSDarrick J. Wong 	xchk_btree_rec_fn		scrub_fn,
69466e3237eSDarrick J. Wong 	const struct xfs_owner_info	*oinfo,
695537964bcSDarrick J. Wong 	void				*private)
696537964bcSDarrick J. Wong {
697cc3e0948SDarrick J. Wong 	union xfs_btree_ptr		ptr;
698510a28e1SDarrick J. Wong 	struct xchk_btree		*bs;
699cc3e0948SDarrick J. Wong 	union xfs_btree_ptr		*pp;
70037f3fa7fSDarrick J. Wong 	union xfs_btree_rec		*recp;
701cc3e0948SDarrick J. Wong 	struct xfs_btree_block		*block;
702cc3e0948SDarrick J. Wong 	struct xfs_buf			*bp;
703858333dcSDarrick J. Wong 	struct check_owner		*co;
704858333dcSDarrick J. Wong 	struct check_owner		*n;
705eae5db47SDarrick J. Wong 	size_t				cur_sz;
706eae5db47SDarrick J. Wong 	int				level;
707cc3e0948SDarrick J. Wong 	int				error = 0;
708537964bcSDarrick J. Wong 
709510a28e1SDarrick J. Wong 	/*
710510a28e1SDarrick J. Wong 	 * Allocate the btree scrub context from the heap, because this
711eae5db47SDarrick J. Wong 	 * structure can get rather large.  Don't let a caller feed us a
712eae5db47SDarrick J. Wong 	 * totally absurd size.
713510a28e1SDarrick J. Wong 	 */
714eae5db47SDarrick J. Wong 	cur_sz = xchk_btree_sizeof(cur->bc_nlevels);
715eae5db47SDarrick J. Wong 	if (cur_sz > PAGE_SIZE) {
716eae5db47SDarrick J. Wong 		xchk_btree_set_corrupt(sc, cur, 0);
717eae5db47SDarrick J. Wong 		return 0;
718eae5db47SDarrick J. Wong 	}
719306195f3SDarrick J. Wong 	bs = kzalloc(cur_sz, XCHK_GFP_FLAGS);
720510a28e1SDarrick J. Wong 	if (!bs)
721510a28e1SDarrick J. Wong 		return -ENOMEM;
722510a28e1SDarrick J. Wong 	bs->cur = cur;
723510a28e1SDarrick J. Wong 	bs->scrub_rec = scrub_fn;
724510a28e1SDarrick J. Wong 	bs->oinfo = oinfo;
725510a28e1SDarrick J. Wong 	bs->private = private;
726510a28e1SDarrick J. Wong 	bs->sc = sc;
727510a28e1SDarrick J. Wong 
728cc3e0948SDarrick J. Wong 	/* Initialize scrub state */
729510a28e1SDarrick J. Wong 	INIT_LIST_HEAD(&bs->to_check);
730cc3e0948SDarrick J. Wong 
731cc3e0948SDarrick J. Wong 	/*
732cc3e0948SDarrick J. Wong 	 * Load the root of the btree.  The helper function absorbs
733cc3e0948SDarrick J. Wong 	 * error codes for us.
734cc3e0948SDarrick J. Wong 	 */
735cc3e0948SDarrick J. Wong 	level = cur->bc_nlevels - 1;
736cc3e0948SDarrick J. Wong 	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
737510a28e1SDarrick J. Wong 	if (!xchk_btree_ptr_ok(bs, cur->bc_nlevels, &ptr))
738cc3e0948SDarrick J. Wong 		goto out;
739510a28e1SDarrick J. Wong 	error = xchk_btree_get_block(bs, level, &ptr, &block, &bp);
740cc3e0948SDarrick J. Wong 	if (error || !block)
741cc3e0948SDarrick J. Wong 		goto out;
742cc3e0948SDarrick J. Wong 
7436ca444cfSDarrick J. Wong 	cur->bc_levels[level].ptr = 1;
744cc3e0948SDarrick J. Wong 
745cc3e0948SDarrick J. Wong 	while (level < cur->bc_nlevels) {
746cc3e0948SDarrick J. Wong 		block = xfs_btree_get_block(cur, level, &bp);
747cc3e0948SDarrick J. Wong 
748cc3e0948SDarrick J. Wong 		if (level == 0) {
749cc3e0948SDarrick J. Wong 			/* End of leaf, pop back towards the root. */
7506ca444cfSDarrick J. Wong 			if (cur->bc_levels[level].ptr >
751cc3e0948SDarrick J. Wong 			    be16_to_cpu(block->bb_numrecs)) {
752510a28e1SDarrick J. Wong 				xchk_btree_block_keys(bs, level, block);
753cc3e0948SDarrick J. Wong 				if (level < cur->bc_nlevels - 1)
7546ca444cfSDarrick J. Wong 					cur->bc_levels[level + 1].ptr++;
755cc3e0948SDarrick J. Wong 				level++;
756cc3e0948SDarrick J. Wong 				continue;
757cc3e0948SDarrick J. Wong 			}
758cc3e0948SDarrick J. Wong 
75937f3fa7fSDarrick J. Wong 			/* Records in order for scrub? */
760510a28e1SDarrick J. Wong 			xchk_btree_rec(bs);
76137f3fa7fSDarrick J. Wong 
76237f3fa7fSDarrick J. Wong 			/* Call out to the record checker. */
7636ca444cfSDarrick J. Wong 			recp = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr,
7646ca444cfSDarrick J. Wong 					block);
765510a28e1SDarrick J. Wong 			error = bs->scrub_rec(bs, recp);
76637f3fa7fSDarrick J. Wong 			if (error)
76737f3fa7fSDarrick J. Wong 				break;
768c517b3aaSDarrick J. Wong 			if (xchk_should_terminate(sc, &error) ||
76937f3fa7fSDarrick J. Wong 			    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
770cc3e0948SDarrick J. Wong 				break;
771cc3e0948SDarrick J. Wong 
7726ca444cfSDarrick J. Wong 			cur->bc_levels[level].ptr++;
773cc3e0948SDarrick J. Wong 			continue;
774cc3e0948SDarrick J. Wong 		}
775cc3e0948SDarrick J. Wong 
776cc3e0948SDarrick J. Wong 		/* End of node, pop back towards the root. */
7776ca444cfSDarrick J. Wong 		if (cur->bc_levels[level].ptr >
7786ca444cfSDarrick J. Wong 					be16_to_cpu(block->bb_numrecs)) {
779510a28e1SDarrick J. Wong 			xchk_btree_block_keys(bs, level, block);
780cc3e0948SDarrick J. Wong 			if (level < cur->bc_nlevels - 1)
7816ca444cfSDarrick J. Wong 				cur->bc_levels[level + 1].ptr++;
782cc3e0948SDarrick J. Wong 			level++;
783cc3e0948SDarrick J. Wong 			continue;
784cc3e0948SDarrick J. Wong 		}
785cc3e0948SDarrick J. Wong 
78637f3fa7fSDarrick J. Wong 		/* Keys in order for scrub? */
787510a28e1SDarrick J. Wong 		xchk_btree_key(bs, level);
78837f3fa7fSDarrick J. Wong 
789cc3e0948SDarrick J. Wong 		/* Drill another level deeper. */
7906ca444cfSDarrick J. Wong 		pp = xfs_btree_ptr_addr(cur, cur->bc_levels[level].ptr, block);
791510a28e1SDarrick J. Wong 		if (!xchk_btree_ptr_ok(bs, level, pp)) {
7926ca444cfSDarrick J. Wong 			cur->bc_levels[level].ptr++;
793cc3e0948SDarrick J. Wong 			continue;
794cc3e0948SDarrick J. Wong 		}
795cc3e0948SDarrick J. Wong 		level--;
796510a28e1SDarrick J. Wong 		error = xchk_btree_get_block(bs, level, pp, &block, &bp);
797cc3e0948SDarrick J. Wong 		if (error || !block)
798cc3e0948SDarrick J. Wong 			goto out;
799cc3e0948SDarrick J. Wong 
8006ca444cfSDarrick J. Wong 		cur->bc_levels[level].ptr = 1;
801cc3e0948SDarrick J. Wong 	}
802cc3e0948SDarrick J. Wong 
803cc3e0948SDarrick J. Wong out:
804858333dcSDarrick J. Wong 	/* Process deferred owner checks on btree blocks. */
805510a28e1SDarrick J. Wong 	list_for_each_entry_safe(co, n, &bs->to_check, list) {
806510a28e1SDarrick J. Wong 		if (!error && bs->cur)
807510a28e1SDarrick J. Wong 			error = xchk_btree_check_block_owner(bs, co->level,
808510a28e1SDarrick J. Wong 					co->daddr);
809858333dcSDarrick J. Wong 		list_del(&co->list);
810306195f3SDarrick J. Wong 		kfree(co);
811858333dcSDarrick J. Wong 	}
812306195f3SDarrick J. Wong 	kfree(bs);
813858333dcSDarrick J. Wong 
814537964bcSDarrick J. Wong 	return error;
815537964bcSDarrick J. Wong }
816