xref: /openbmc/linux/fs/xfs/scrub/btree.c (revision 38384569)
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
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
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
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
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
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
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 
122*38384569SDarrick J. Wong void
123*38384569SDarrick J. Wong xchk_btree_set_preen(
124*38384569SDarrick J. Wong 	struct xfs_scrub	*sc,
125*38384569SDarrick J. Wong 	struct xfs_btree_cur	*cur,
126*38384569SDarrick J. Wong 	int			level)
127*38384569SDarrick J. Wong {
128*38384569SDarrick J. Wong 	__xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_PREEN,
129*38384569SDarrick J. Wong 			__return_address);
130*38384569SDarrick J. Wong }
131*38384569SDarrick 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
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 
15437f3fa7fSDarrick J. Wong 	/* If this isn't the first record, are they in order? */
1556ca444cfSDarrick J. Wong 	if (cur->bc_levels[0].ptr > 1 &&
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);
15937f3fa7fSDarrick J. Wong 
16037f3fa7fSDarrick J. Wong 	if (cur->bc_nlevels == 1)
16137f3fa7fSDarrick J. Wong 		return;
16237f3fa7fSDarrick J. Wong 
16337f3fa7fSDarrick J. Wong 	/* Is this at least as large as the parent low key? */
16437f3fa7fSDarrick J. Wong 	cur->bc_ops->init_key_from_rec(&key, rec);
16537f3fa7fSDarrick J. Wong 	keyblock = xfs_btree_get_block(cur, 1, &bp);
1666ca444cfSDarrick J. Wong 	keyp = xfs_btree_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
16737f3fa7fSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, &key, keyp) < 0)
168c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
16937f3fa7fSDarrick J. Wong 
17037f3fa7fSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
17137f3fa7fSDarrick J. Wong 		return;
17237f3fa7fSDarrick J. Wong 
17337f3fa7fSDarrick J. Wong 	/* Is this no larger than the parent high key? */
17437f3fa7fSDarrick J. Wong 	cur->bc_ops->init_high_key_from_rec(&hkey, rec);
1756ca444cfSDarrick J. Wong 	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
17637f3fa7fSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, keyp, &hkey) < 0)
177c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
17837f3fa7fSDarrick J. Wong }
17937f3fa7fSDarrick J. Wong 
18037f3fa7fSDarrick J. Wong /*
18137f3fa7fSDarrick J. Wong  * Make sure this key is in order and doesn't stray outside of the parent
18237f3fa7fSDarrick J. Wong  * keys.
18337f3fa7fSDarrick J. Wong  */
18437f3fa7fSDarrick J. Wong STATIC void
185c517b3aaSDarrick J. Wong xchk_btree_key(
186c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
18737f3fa7fSDarrick J. Wong 	int			level)
18837f3fa7fSDarrick J. Wong {
18937f3fa7fSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
19037f3fa7fSDarrick J. Wong 	union xfs_btree_key	*key;
19137f3fa7fSDarrick J. Wong 	union xfs_btree_key	*keyp;
19237f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*block;
19337f3fa7fSDarrick J. Wong 	struct xfs_btree_block	*keyblock;
19437f3fa7fSDarrick J. Wong 	struct xfs_buf		*bp;
19537f3fa7fSDarrick J. Wong 
19637f3fa7fSDarrick J. Wong 	block = xfs_btree_get_block(cur, level, &bp);
1976ca444cfSDarrick J. Wong 	key = xfs_btree_key_addr(cur, cur->bc_levels[level].ptr, block);
19837f3fa7fSDarrick J. Wong 
199c517b3aaSDarrick J. Wong 	trace_xchk_btree_key(bs->sc, cur, level);
20037f3fa7fSDarrick J. Wong 
20137f3fa7fSDarrick J. Wong 	/* If this isn't the first key, are they in order? */
2026ca444cfSDarrick J. Wong 	if (cur->bc_levels[level].ptr > 1 &&
203eae5db47SDarrick J. Wong 	    !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1], key))
204c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
205eae5db47SDarrick J. Wong 	memcpy(&bs->lastkey[level - 1], key, cur->bc_ops->key_len);
20637f3fa7fSDarrick J. Wong 
20737f3fa7fSDarrick J. Wong 	if (level + 1 >= cur->bc_nlevels)
20837f3fa7fSDarrick J. Wong 		return;
20937f3fa7fSDarrick J. Wong 
21037f3fa7fSDarrick J. Wong 	/* Is this at least as large as the parent low key? */
21137f3fa7fSDarrick J. Wong 	keyblock = xfs_btree_get_block(cur, level + 1, &bp);
2126ca444cfSDarrick J. Wong 	keyp = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr, keyblock);
21337f3fa7fSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, key, keyp) < 0)
214c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
21537f3fa7fSDarrick J. Wong 
21637f3fa7fSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
21737f3fa7fSDarrick J. Wong 		return;
21837f3fa7fSDarrick J. Wong 
21937f3fa7fSDarrick J. Wong 	/* Is this no larger than the parent high key? */
2206ca444cfSDarrick J. Wong 	key = xfs_btree_high_key_addr(cur, cur->bc_levels[level].ptr, block);
2216ca444cfSDarrick J. Wong 	keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
2226ca444cfSDarrick J. Wong 			keyblock);
22337f3fa7fSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, keyp, key) < 0)
224c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
22537f3fa7fSDarrick J. Wong }
22637f3fa7fSDarrick J. Wong 
22737f3fa7fSDarrick J. Wong /*
228cc3e0948SDarrick J. Wong  * Check a btree pointer.  Returns true if it's ok to use this pointer.
229cc3e0948SDarrick J. Wong  * Callers do not need to set the corrupt flag.
230cc3e0948SDarrick J. Wong  */
231cc3e0948SDarrick J. Wong static bool
232c517b3aaSDarrick J. Wong xchk_btree_ptr_ok(
233c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
234cc3e0948SDarrick J. Wong 	int			level,
235cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*ptr)
236cc3e0948SDarrick J. Wong {
237cc3e0948SDarrick J. Wong 	bool			res;
238cc3e0948SDarrick J. Wong 
239cc3e0948SDarrick J. Wong 	/* A btree rooted in an inode has no block pointer to the root. */
240cc3e0948SDarrick J. Wong 	if ((bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
241cc3e0948SDarrick J. Wong 	    level == bs->cur->bc_nlevels)
242cc3e0948SDarrick J. Wong 		return true;
243cc3e0948SDarrick J. Wong 
244cc3e0948SDarrick J. Wong 	/* Otherwise, check the pointers. */
245cc3e0948SDarrick J. Wong 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
246cc3e0948SDarrick J. Wong 		res = xfs_btree_check_lptr(bs->cur, be64_to_cpu(ptr->l), level);
247cc3e0948SDarrick J. Wong 	else
248cc3e0948SDarrick J. Wong 		res = xfs_btree_check_sptr(bs->cur, be32_to_cpu(ptr->s), level);
249cc3e0948SDarrick J. Wong 	if (!res)
250c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
251cc3e0948SDarrick J. Wong 
252cc3e0948SDarrick J. Wong 	return res;
253cc3e0948SDarrick J. Wong }
254cc3e0948SDarrick J. Wong 
255cc3e0948SDarrick J. Wong /* Check that a btree block's sibling matches what we expect it. */
256cc3e0948SDarrick J. Wong STATIC int
257c517b3aaSDarrick J. Wong xchk_btree_block_check_sibling(
258c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
259cc3e0948SDarrick J. Wong 	int			level,
260cc3e0948SDarrick J. Wong 	int			direction,
261cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*sibling)
262cc3e0948SDarrick J. Wong {
263cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
264cc3e0948SDarrick J. Wong 	struct xfs_btree_block	*pblock;
265cc3e0948SDarrick J. Wong 	struct xfs_buf		*pbp;
266cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*ncur = NULL;
267cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*pp;
268cc3e0948SDarrick J. Wong 	int			success;
269cc3e0948SDarrick J. Wong 	int			error;
270cc3e0948SDarrick J. Wong 
271cc3e0948SDarrick J. Wong 	error = xfs_btree_dup_cursor(cur, &ncur);
272c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error) ||
273cc3e0948SDarrick J. Wong 	    !ncur)
274cc3e0948SDarrick J. Wong 		return error;
275cc3e0948SDarrick J. Wong 
276cc3e0948SDarrick J. Wong 	/*
277cc3e0948SDarrick J. Wong 	 * If the pointer is null, we shouldn't be able to move the upper
278cc3e0948SDarrick J. Wong 	 * level pointer anywhere.
279cc3e0948SDarrick J. Wong 	 */
280cc3e0948SDarrick J. Wong 	if (xfs_btree_ptr_is_null(cur, sibling)) {
281cc3e0948SDarrick J. Wong 		if (direction > 0)
282cc3e0948SDarrick J. Wong 			error = xfs_btree_increment(ncur, level + 1, &success);
283cc3e0948SDarrick J. Wong 		else
284cc3e0948SDarrick J. Wong 			error = xfs_btree_decrement(ncur, level + 1, &success);
285cc3e0948SDarrick J. Wong 		if (error == 0 && success)
286c517b3aaSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
287cc3e0948SDarrick J. Wong 		error = 0;
288cc3e0948SDarrick J. Wong 		goto out;
289cc3e0948SDarrick J. Wong 	}
290cc3e0948SDarrick J. Wong 
291cc3e0948SDarrick J. Wong 	/* Increment upper level pointer. */
292cc3e0948SDarrick J. Wong 	if (direction > 0)
293cc3e0948SDarrick J. Wong 		error = xfs_btree_increment(ncur, level + 1, &success);
294cc3e0948SDarrick J. Wong 	else
295cc3e0948SDarrick J. Wong 		error = xfs_btree_decrement(ncur, level + 1, &success);
296c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error))
297cc3e0948SDarrick J. Wong 		goto out;
298cc3e0948SDarrick J. Wong 	if (!success) {
299c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level + 1);
300cc3e0948SDarrick J. Wong 		goto out;
301cc3e0948SDarrick J. Wong 	}
302cc3e0948SDarrick J. Wong 
303cc3e0948SDarrick J. Wong 	/* Compare upper level pointer to sibling pointer. */
304cc3e0948SDarrick J. Wong 	pblock = xfs_btree_get_block(ncur, level + 1, &pbp);
3056ca444cfSDarrick J. Wong 	pp = xfs_btree_ptr_addr(ncur, ncur->bc_levels[level + 1].ptr, pblock);
306c517b3aaSDarrick J. Wong 	if (!xchk_btree_ptr_ok(bs, level + 1, pp))
307cc3e0948SDarrick J. Wong 		goto out;
308cf1b0b8bSDarrick J. Wong 	if (pbp)
309c517b3aaSDarrick J. Wong 		xchk_buffer_recheck(bs->sc, pbp);
310cc3e0948SDarrick J. Wong 
311cc3e0948SDarrick J. Wong 	if (xfs_btree_diff_two_ptrs(cur, pp, sibling))
312c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
313cc3e0948SDarrick J. Wong out:
314cc3e0948SDarrick J. Wong 	xfs_btree_del_cursor(ncur, XFS_BTREE_ERROR);
315cc3e0948SDarrick J. Wong 	return error;
316cc3e0948SDarrick J. Wong }
317cc3e0948SDarrick J. Wong 
318cc3e0948SDarrick J. Wong /* Check the siblings of a btree block. */
319cc3e0948SDarrick J. Wong STATIC int
320c517b3aaSDarrick J. Wong xchk_btree_block_check_siblings(
321c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
322cc3e0948SDarrick J. Wong 	struct xfs_btree_block	*block)
323cc3e0948SDarrick J. Wong {
324cc3e0948SDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
325cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	leftsib;
326cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	rightsib;
327cc3e0948SDarrick J. Wong 	int			level;
328cc3e0948SDarrick J. Wong 	int			error = 0;
329cc3e0948SDarrick J. Wong 
330cc3e0948SDarrick J. Wong 	xfs_btree_get_sibling(cur, block, &leftsib, XFS_BB_LEFTSIB);
331cc3e0948SDarrick J. Wong 	xfs_btree_get_sibling(cur, block, &rightsib, XFS_BB_RIGHTSIB);
332cc3e0948SDarrick J. Wong 	level = xfs_btree_get_level(block);
333cc3e0948SDarrick J. Wong 
334cc3e0948SDarrick J. Wong 	/* Root block should never have siblings. */
335cc3e0948SDarrick J. Wong 	if (level == cur->bc_nlevels - 1) {
336cc3e0948SDarrick J. Wong 		if (!xfs_btree_ptr_is_null(cur, &leftsib) ||
337cc3e0948SDarrick J. Wong 		    !xfs_btree_ptr_is_null(cur, &rightsib))
338c517b3aaSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
339cc3e0948SDarrick J. Wong 		goto out;
340cc3e0948SDarrick J. Wong 	}
341cc3e0948SDarrick J. Wong 
342cc3e0948SDarrick J. Wong 	/*
343cc3e0948SDarrick J. Wong 	 * Does the left & right sibling pointers match the adjacent
344cc3e0948SDarrick J. Wong 	 * parent level pointers?
345cc3e0948SDarrick J. Wong 	 * (These function absorbs error codes for us.)
346cc3e0948SDarrick J. Wong 	 */
347c517b3aaSDarrick J. Wong 	error = xchk_btree_block_check_sibling(bs, level, -1, &leftsib);
348cc3e0948SDarrick J. Wong 	if (error)
349cc3e0948SDarrick J. Wong 		return error;
350c517b3aaSDarrick J. Wong 	error = xchk_btree_block_check_sibling(bs, level, 1, &rightsib);
351cc3e0948SDarrick J. Wong 	if (error)
352cc3e0948SDarrick J. Wong 		return error;
353cc3e0948SDarrick J. Wong out:
354cc3e0948SDarrick J. Wong 	return error;
355cc3e0948SDarrick J. Wong }
356cc3e0948SDarrick J. Wong 
357858333dcSDarrick J. Wong struct check_owner {
358858333dcSDarrick J. Wong 	struct list_head	list;
359858333dcSDarrick J. Wong 	xfs_daddr_t		daddr;
360858333dcSDarrick J. Wong 	int			level;
361858333dcSDarrick J. Wong };
362858333dcSDarrick J. Wong 
363858333dcSDarrick J. Wong /*
364858333dcSDarrick J. Wong  * Make sure this btree block isn't in the free list and that there's
365858333dcSDarrick J. Wong  * an rmap record for it.
366858333dcSDarrick J. Wong  */
367858333dcSDarrick J. Wong STATIC int
368c517b3aaSDarrick J. Wong xchk_btree_check_block_owner(
369c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
370858333dcSDarrick J. Wong 	int			level,
371858333dcSDarrick J. Wong 	xfs_daddr_t		daddr)
372858333dcSDarrick J. Wong {
373858333dcSDarrick J. Wong 	xfs_agnumber_t		agno;
37452dc4b44SDarrick J. Wong 	xfs_agblock_t		agbno;
37552dc4b44SDarrick J. Wong 	xfs_btnum_t		btnum;
376858333dcSDarrick J. Wong 	bool			init_sa;
377858333dcSDarrick J. Wong 	int			error = 0;
378858333dcSDarrick J. Wong 
379858333dcSDarrick J. Wong 	if (!bs->cur)
380858333dcSDarrick J. Wong 		return 0;
381858333dcSDarrick J. Wong 
38252dc4b44SDarrick J. Wong 	btnum = bs->cur->bc_btnum;
383858333dcSDarrick J. Wong 	agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
38452dc4b44SDarrick J. Wong 	agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
385858333dcSDarrick J. Wong 
386858333dcSDarrick J. Wong 	init_sa = bs->cur->bc_flags & XFS_BTREE_LONG_PTRS;
387858333dcSDarrick J. Wong 	if (init_sa) {
38848c6615cSDarrick J. Wong 		error = xchk_ag_init_existing(bs->sc, agno, &bs->sc->sa);
389c517b3aaSDarrick J. Wong 		if (!xchk_btree_xref_process_error(bs->sc, bs->cur,
390858333dcSDarrick J. Wong 				level, &error))
39161e0d0ccSDarrick J. Wong 			goto out_free;
392858333dcSDarrick J. Wong 	}
393858333dcSDarrick J. Wong 
394c517b3aaSDarrick J. Wong 	xchk_xref_is_used_space(bs->sc, agbno, 1);
39552dc4b44SDarrick J. Wong 	/*
39652dc4b44SDarrick J. Wong 	 * The bnobt scrubber aliases bs->cur to bs->sc->sa.bno_cur, so we
39752dc4b44SDarrick J. Wong 	 * have to nullify it (to shut down further block owner checks) if
39852dc4b44SDarrick J. Wong 	 * self-xref encounters problems.
39952dc4b44SDarrick J. Wong 	 */
40052dc4b44SDarrick J. Wong 	if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
40152dc4b44SDarrick J. Wong 		bs->cur = NULL;
40252dc4b44SDarrick J. Wong 
403c517b3aaSDarrick J. Wong 	xchk_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo);
404d852657cSDarrick J. Wong 	if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
405d852657cSDarrick J. Wong 		bs->cur = NULL;
406d852657cSDarrick J. Wong 
40761e0d0ccSDarrick J. Wong out_free:
408858333dcSDarrick J. Wong 	if (init_sa)
409c517b3aaSDarrick J. Wong 		xchk_ag_free(bs->sc, &bs->sc->sa);
410858333dcSDarrick J. Wong 
411858333dcSDarrick J. Wong 	return error;
412858333dcSDarrick J. Wong }
413858333dcSDarrick J. Wong 
414858333dcSDarrick J. Wong /* Check the owner of a btree block. */
415858333dcSDarrick J. Wong STATIC int
416c517b3aaSDarrick J. Wong xchk_btree_check_owner(
417c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
418858333dcSDarrick J. Wong 	int			level,
419858333dcSDarrick J. Wong 	struct xfs_buf		*bp)
420858333dcSDarrick J. Wong {
421858333dcSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
422858333dcSDarrick J. Wong 
423a72e9d8dSDarrick J. Wong 	/*
424a72e9d8dSDarrick J. Wong 	 * In theory, xfs_btree_get_block should only give us a null buffer
425a72e9d8dSDarrick J. Wong 	 * pointer for the root of a root-in-inode btree type, but we need
426a72e9d8dSDarrick J. Wong 	 * to check defensively here in case the cursor state is also screwed
427a72e9d8dSDarrick J. Wong 	 * up.
428a72e9d8dSDarrick J. Wong 	 */
429a72e9d8dSDarrick J. Wong 	if (bp == NULL) {
430a72e9d8dSDarrick J. Wong 		if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE))
431a72e9d8dSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, bs->cur, level);
432858333dcSDarrick J. Wong 		return 0;
433a72e9d8dSDarrick J. Wong 	}
434858333dcSDarrick J. Wong 
435858333dcSDarrick J. Wong 	/*
436858333dcSDarrick J. Wong 	 * We want to cross-reference each btree block with the bnobt
437858333dcSDarrick J. Wong 	 * and the rmapbt.  We cannot cross-reference the bnobt or
438858333dcSDarrick J. Wong 	 * rmapbt while scanning the bnobt or rmapbt, respectively,
439858333dcSDarrick J. Wong 	 * because we cannot alter the cursor and we'd prefer not to
440858333dcSDarrick J. Wong 	 * duplicate cursors.  Therefore, save the buffer daddr for
441858333dcSDarrick J. Wong 	 * later scanning.
442858333dcSDarrick J. Wong 	 */
443858333dcSDarrick J. Wong 	if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
444fcd2a434SDarrick J. Wong 		struct check_owner	*co;
445fcd2a434SDarrick J. Wong 
446306195f3SDarrick J. Wong 		co = kmalloc(sizeof(struct check_owner), XCHK_GFP_FLAGS);
447858333dcSDarrick J. Wong 		if (!co)
448858333dcSDarrick J. Wong 			return -ENOMEM;
449fcd2a434SDarrick J. Wong 
450fcd2a434SDarrick J. Wong 		INIT_LIST_HEAD(&co->list);
451858333dcSDarrick J. Wong 		co->level = level;
45204fcad80SDave Chinner 		co->daddr = xfs_buf_daddr(bp);
453858333dcSDarrick J. Wong 		list_add_tail(&co->list, &bs->to_check);
454858333dcSDarrick J. Wong 		return 0;
455858333dcSDarrick J. Wong 	}
456858333dcSDarrick J. Wong 
45704fcad80SDave Chinner 	return xchk_btree_check_block_owner(bs, level, xfs_buf_daddr(bp));
458858333dcSDarrick J. Wong }
459858333dcSDarrick J. Wong 
460ae7bae68SChandan Babu R /* Decide if we want to check minrecs of a btree block in the inode root. */
461ae7bae68SChandan Babu R static inline bool
462ae7bae68SChandan Babu R xchk_btree_check_iroot_minrecs(
463ae7bae68SChandan Babu R 	struct xchk_btree	*bs)
464ae7bae68SChandan Babu R {
465ae7bae68SChandan Babu R 	/*
466ae7bae68SChandan Babu R 	 * xfs_bmap_add_attrfork_btree had an implementation bug wherein it
467ae7bae68SChandan Babu R 	 * would miscalculate the space required for the data fork bmbt root
468ae7bae68SChandan Babu R 	 * when adding an attr fork, and promote the iroot contents to an
469ae7bae68SChandan Babu R 	 * external block unnecessarily.  This went unnoticed for many years
470ae7bae68SChandan Babu R 	 * until scrub found filesystems in this state.  Inode rooted btrees are
471ae7bae68SChandan Babu R 	 * not supposed to have immediate child blocks that are small enough
472ae7bae68SChandan Babu R 	 * that the contents could fit in the inode root, but we can't fail
473ae7bae68SChandan Babu R 	 * existing filesystems, so instead we disable the check for data fork
474ae7bae68SChandan Babu R 	 * bmap btrees when there's an attr fork.
475ae7bae68SChandan Babu R 	 */
476ae7bae68SChandan Babu R 	if (bs->cur->bc_btnum == XFS_BTNUM_BMAP &&
477ae7bae68SChandan Babu R 	    bs->cur->bc_ino.whichfork == XFS_DATA_FORK &&
478932b42c6SDarrick J. Wong 	    xfs_inode_has_attr_fork(bs->sc->ip))
479ae7bae68SChandan Babu R 		return false;
480ae7bae68SChandan Babu R 
481ae7bae68SChandan Babu R 	return true;
482ae7bae68SChandan Babu R }
483ae7bae68SChandan Babu R 
484cc3e0948SDarrick J. Wong /*
48508a3a692SDarrick J. Wong  * Check that this btree block has at least minrecs records or is one of the
48608a3a692SDarrick J. Wong  * special blocks that don't require that.
48708a3a692SDarrick J. Wong  */
48808a3a692SDarrick J. Wong STATIC void
489c517b3aaSDarrick J. Wong xchk_btree_check_minrecs(
490c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
49108a3a692SDarrick J. Wong 	int			level,
49208a3a692SDarrick J. Wong 	struct xfs_btree_block	*block)
49308a3a692SDarrick J. Wong {
494e95b6c3eSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
495e95b6c3eSDarrick J. Wong 	unsigned int		root_level = cur->bc_nlevels - 1;
496e95b6c3eSDarrick J. Wong 	unsigned int		numrecs = be16_to_cpu(block->bb_numrecs);
49708a3a692SDarrick J. Wong 
49808a3a692SDarrick J. Wong 	/* More records than minrecs means the block is ok. */
499e95b6c3eSDarrick J. Wong 	if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
50008a3a692SDarrick J. Wong 		return;
50108a3a692SDarrick J. Wong 
50208a3a692SDarrick J. Wong 	/*
503e95b6c3eSDarrick J. Wong 	 * For btrees rooted in the inode, it's possible that the root block
504e95b6c3eSDarrick J. Wong 	 * contents spilled into a regular ondisk block because there wasn't
505e95b6c3eSDarrick J. Wong 	 * enough space in the inode root.  The number of records in that
506e95b6c3eSDarrick J. Wong 	 * child block might be less than the standard minrecs, but that's ok
507e95b6c3eSDarrick J. Wong 	 * provided that there's only one direct child of the root.
50808a3a692SDarrick J. Wong 	 */
509e95b6c3eSDarrick J. Wong 	if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
510e95b6c3eSDarrick J. Wong 	    level == cur->bc_nlevels - 2) {
511e95b6c3eSDarrick J. Wong 		struct xfs_btree_block	*root_block;
512e95b6c3eSDarrick J. Wong 		struct xfs_buf		*root_bp;
513e95b6c3eSDarrick J. Wong 		int			root_maxrecs;
51408a3a692SDarrick J. Wong 
515e95b6c3eSDarrick J. Wong 		root_block = xfs_btree_get_block(cur, root_level, &root_bp);
516e95b6c3eSDarrick J. Wong 		root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
517ae7bae68SChandan Babu R 		if (xchk_btree_check_iroot_minrecs(bs) &&
518ae7bae68SChandan Babu R 		    (be16_to_cpu(root_block->bb_numrecs) != 1 ||
519ae7bae68SChandan Babu R 		     numrecs <= root_maxrecs))
520e95b6c3eSDarrick J. Wong 			xchk_btree_set_corrupt(bs->sc, cur, level);
521e95b6c3eSDarrick J. Wong 		return;
522e95b6c3eSDarrick J. Wong 	}
523e95b6c3eSDarrick J. Wong 
524e95b6c3eSDarrick J. Wong 	/*
525e95b6c3eSDarrick J. Wong 	 * Otherwise, only the root level is allowed to have fewer than minrecs
526e95b6c3eSDarrick J. Wong 	 * records or keyptrs.
527e95b6c3eSDarrick J. Wong 	 */
528e95b6c3eSDarrick J. Wong 	if (level < root_level)
529e95b6c3eSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, level);
53008a3a692SDarrick J. Wong }
53108a3a692SDarrick J. Wong 
53208a3a692SDarrick J. Wong /*
533cc3e0948SDarrick J. Wong  * Grab and scrub a btree block given a btree pointer.  Returns block
534cc3e0948SDarrick J. Wong  * and buffer pointers (if applicable) if they're ok to use.
535cc3e0948SDarrick J. Wong  */
536cc3e0948SDarrick J. Wong STATIC int
537c517b3aaSDarrick J. Wong xchk_btree_get_block(
538c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
539cc3e0948SDarrick J. Wong 	int			level,
540cc3e0948SDarrick J. Wong 	union xfs_btree_ptr	*pp,
541cc3e0948SDarrick J. Wong 	struct xfs_btree_block	**pblock,
542cc3e0948SDarrick J. Wong 	struct xfs_buf		**pbp)
543cc3e0948SDarrick J. Wong {
544032d91f9SDarrick J. Wong 	xfs_failaddr_t		failed_at;
545cc3e0948SDarrick J. Wong 	int			error;
546cc3e0948SDarrick J. Wong 
547cc3e0948SDarrick J. Wong 	*pblock = NULL;
548cc3e0948SDarrick J. Wong 	*pbp = NULL;
549cc3e0948SDarrick J. Wong 
550cc3e0948SDarrick J. Wong 	error = xfs_btree_lookup_get_block(bs->cur, level, pp, pblock);
551c517b3aaSDarrick J. Wong 	if (!xchk_btree_process_error(bs->sc, bs->cur, level, &error) ||
552a605e869SDarrick J. Wong 	    !*pblock)
553cc3e0948SDarrick J. Wong 		return error;
554cc3e0948SDarrick J. Wong 
555cc3e0948SDarrick J. Wong 	xfs_btree_get_block(bs->cur, level, pbp);
556cc3e0948SDarrick J. Wong 	if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
557cc3e0948SDarrick J. Wong 		failed_at = __xfs_btree_check_lblock(bs->cur, *pblock,
558cc3e0948SDarrick J. Wong 				level, *pbp);
559cc3e0948SDarrick J. Wong 	else
560cc3e0948SDarrick J. Wong 		failed_at = __xfs_btree_check_sblock(bs->cur, *pblock,
561cc3e0948SDarrick J. Wong 				 level, *pbp);
562cc3e0948SDarrick J. Wong 	if (failed_at) {
563c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, bs->cur, level);
564cc3e0948SDarrick J. Wong 		return 0;
565cc3e0948SDarrick J. Wong 	}
566cf1b0b8bSDarrick J. Wong 	if (*pbp)
567c517b3aaSDarrick J. Wong 		xchk_buffer_recheck(bs->sc, *pbp);
568cc3e0948SDarrick J. Wong 
569c517b3aaSDarrick J. Wong 	xchk_btree_check_minrecs(bs, level, *pblock);
57008a3a692SDarrick J. Wong 
571cc3e0948SDarrick J. Wong 	/*
572858333dcSDarrick J. Wong 	 * Check the block's owner; this function absorbs error codes
573858333dcSDarrick J. Wong 	 * for us.
574858333dcSDarrick J. Wong 	 */
575c517b3aaSDarrick J. Wong 	error = xchk_btree_check_owner(bs, level, *pbp);
576858333dcSDarrick J. Wong 	if (error)
577858333dcSDarrick J. Wong 		return error;
578858333dcSDarrick J. Wong 
579858333dcSDarrick J. Wong 	/*
580cc3e0948SDarrick J. Wong 	 * Check the block's siblings; this function absorbs error codes
581cc3e0948SDarrick J. Wong 	 * for us.
582cc3e0948SDarrick J. Wong 	 */
583c517b3aaSDarrick J. Wong 	return xchk_btree_block_check_siblings(bs, *pblock);
584cc3e0948SDarrick J. Wong }
585cc3e0948SDarrick J. Wong 
586cc3e0948SDarrick J. Wong /*
5872fdbec5cSDarrick J. Wong  * Check that the low and high keys of this block match the keys stored
5882fdbec5cSDarrick J. Wong  * in the parent block.
5892fdbec5cSDarrick J. Wong  */
5902fdbec5cSDarrick J. Wong STATIC void
591c517b3aaSDarrick J. Wong xchk_btree_block_keys(
592c517b3aaSDarrick J. Wong 	struct xchk_btree	*bs,
5932fdbec5cSDarrick J. Wong 	int			level,
5942fdbec5cSDarrick J. Wong 	struct xfs_btree_block	*block)
5952fdbec5cSDarrick J. Wong {
5962fdbec5cSDarrick J. Wong 	union xfs_btree_key	block_keys;
5972fdbec5cSDarrick J. Wong 	struct xfs_btree_cur	*cur = bs->cur;
5982fdbec5cSDarrick J. Wong 	union xfs_btree_key	*high_bk;
5992fdbec5cSDarrick J. Wong 	union xfs_btree_key	*parent_keys;
6002fdbec5cSDarrick J. Wong 	union xfs_btree_key	*high_pk;
6012fdbec5cSDarrick J. Wong 	struct xfs_btree_block	*parent_block;
6022fdbec5cSDarrick J. Wong 	struct xfs_buf		*bp;
6032fdbec5cSDarrick J. Wong 
6042fdbec5cSDarrick J. Wong 	if (level >= cur->bc_nlevels - 1)
6052fdbec5cSDarrick J. Wong 		return;
6062fdbec5cSDarrick J. Wong 
6072fdbec5cSDarrick J. Wong 	/* Calculate the keys for this block. */
6082fdbec5cSDarrick J. Wong 	xfs_btree_get_keys(cur, block, &block_keys);
6092fdbec5cSDarrick J. Wong 
6102fdbec5cSDarrick J. Wong 	/* Obtain the parent's copy of the keys for this block. */
6112fdbec5cSDarrick J. Wong 	parent_block = xfs_btree_get_block(cur, level + 1, &bp);
6126ca444cfSDarrick J. Wong 	parent_keys = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
6132fdbec5cSDarrick J. Wong 			parent_block);
6142fdbec5cSDarrick J. Wong 
6152fdbec5cSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, &block_keys, parent_keys) != 0)
616c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
6172fdbec5cSDarrick J. Wong 
6182fdbec5cSDarrick J. Wong 	if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
6192fdbec5cSDarrick J. Wong 		return;
6202fdbec5cSDarrick J. Wong 
6212fdbec5cSDarrick J. Wong 	/* Get high keys */
6222fdbec5cSDarrick J. Wong 	high_bk = xfs_btree_high_key_from_key(cur, &block_keys);
6236ca444cfSDarrick J. Wong 	high_pk = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
6242fdbec5cSDarrick J. Wong 			parent_block);
6252fdbec5cSDarrick J. Wong 
6262fdbec5cSDarrick J. Wong 	if (cur->bc_ops->diff_two_keys(cur, high_bk, high_pk) != 0)
627c517b3aaSDarrick J. Wong 		xchk_btree_set_corrupt(bs->sc, cur, 1);
6282fdbec5cSDarrick J. Wong }
6292fdbec5cSDarrick J. Wong 
6302fdbec5cSDarrick J. Wong /*
631537964bcSDarrick J. Wong  * Visit all nodes and leaves of a btree.  Check that all pointers and
632537964bcSDarrick J. Wong  * records are in order, that the keys reflect the records, and use a callback
633cc3e0948SDarrick J. Wong  * so that the caller can verify individual records.
634537964bcSDarrick J. Wong  */
635537964bcSDarrick J. Wong int
636c517b3aaSDarrick J. Wong xchk_btree(
6371d8a748aSDarrick J. Wong 	struct xfs_scrub		*sc,
638537964bcSDarrick J. Wong 	struct xfs_btree_cur		*cur,
639c517b3aaSDarrick J. Wong 	xchk_btree_rec_fn		scrub_fn,
64066e3237eSDarrick J. Wong 	const struct xfs_owner_info	*oinfo,
641537964bcSDarrick J. Wong 	void				*private)
642537964bcSDarrick J. Wong {
643cc3e0948SDarrick J. Wong 	union xfs_btree_ptr		ptr;
644510a28e1SDarrick J. Wong 	struct xchk_btree		*bs;
645cc3e0948SDarrick J. Wong 	union xfs_btree_ptr		*pp;
64637f3fa7fSDarrick J. Wong 	union xfs_btree_rec		*recp;
647cc3e0948SDarrick J. Wong 	struct xfs_btree_block		*block;
648cc3e0948SDarrick J. Wong 	struct xfs_buf			*bp;
649858333dcSDarrick J. Wong 	struct check_owner		*co;
650858333dcSDarrick J. Wong 	struct check_owner		*n;
651eae5db47SDarrick J. Wong 	size_t				cur_sz;
652eae5db47SDarrick J. Wong 	int				level;
653cc3e0948SDarrick J. Wong 	int				error = 0;
654537964bcSDarrick J. Wong 
655510a28e1SDarrick J. Wong 	/*
656510a28e1SDarrick J. Wong 	 * Allocate the btree scrub context from the heap, because this
657eae5db47SDarrick J. Wong 	 * structure can get rather large.  Don't let a caller feed us a
658eae5db47SDarrick J. Wong 	 * totally absurd size.
659510a28e1SDarrick J. Wong 	 */
660eae5db47SDarrick J. Wong 	cur_sz = xchk_btree_sizeof(cur->bc_nlevels);
661eae5db47SDarrick J. Wong 	if (cur_sz > PAGE_SIZE) {
662eae5db47SDarrick J. Wong 		xchk_btree_set_corrupt(sc, cur, 0);
663eae5db47SDarrick J. Wong 		return 0;
664eae5db47SDarrick J. Wong 	}
665306195f3SDarrick J. Wong 	bs = kzalloc(cur_sz, XCHK_GFP_FLAGS);
666510a28e1SDarrick J. Wong 	if (!bs)
667510a28e1SDarrick J. Wong 		return -ENOMEM;
668510a28e1SDarrick J. Wong 	bs->cur = cur;
669510a28e1SDarrick J. Wong 	bs->scrub_rec = scrub_fn;
670510a28e1SDarrick J. Wong 	bs->oinfo = oinfo;
671510a28e1SDarrick J. Wong 	bs->private = private;
672510a28e1SDarrick J. Wong 	bs->sc = sc;
673510a28e1SDarrick J. Wong 
674cc3e0948SDarrick J. Wong 	/* Initialize scrub state */
675510a28e1SDarrick J. Wong 	INIT_LIST_HEAD(&bs->to_check);
676cc3e0948SDarrick J. Wong 
677cc3e0948SDarrick J. Wong 	/*
678cc3e0948SDarrick J. Wong 	 * Load the root of the btree.  The helper function absorbs
679cc3e0948SDarrick J. Wong 	 * error codes for us.
680cc3e0948SDarrick J. Wong 	 */
681cc3e0948SDarrick J. Wong 	level = cur->bc_nlevels - 1;
682cc3e0948SDarrick J. Wong 	cur->bc_ops->init_ptr_from_cur(cur, &ptr);
683510a28e1SDarrick J. Wong 	if (!xchk_btree_ptr_ok(bs, cur->bc_nlevels, &ptr))
684cc3e0948SDarrick J. Wong 		goto out;
685510a28e1SDarrick J. Wong 	error = xchk_btree_get_block(bs, level, &ptr, &block, &bp);
686cc3e0948SDarrick J. Wong 	if (error || !block)
687cc3e0948SDarrick J. Wong 		goto out;
688cc3e0948SDarrick J. Wong 
6896ca444cfSDarrick J. Wong 	cur->bc_levels[level].ptr = 1;
690cc3e0948SDarrick J. Wong 
691cc3e0948SDarrick J. Wong 	while (level < cur->bc_nlevels) {
692cc3e0948SDarrick J. Wong 		block = xfs_btree_get_block(cur, level, &bp);
693cc3e0948SDarrick J. Wong 
694cc3e0948SDarrick J. Wong 		if (level == 0) {
695cc3e0948SDarrick J. Wong 			/* End of leaf, pop back towards the root. */
6966ca444cfSDarrick J. Wong 			if (cur->bc_levels[level].ptr >
697cc3e0948SDarrick J. Wong 			    be16_to_cpu(block->bb_numrecs)) {
698510a28e1SDarrick J. Wong 				xchk_btree_block_keys(bs, level, block);
699cc3e0948SDarrick J. Wong 				if (level < cur->bc_nlevels - 1)
7006ca444cfSDarrick J. Wong 					cur->bc_levels[level + 1].ptr++;
701cc3e0948SDarrick J. Wong 				level++;
702cc3e0948SDarrick J. Wong 				continue;
703cc3e0948SDarrick J. Wong 			}
704cc3e0948SDarrick J. Wong 
70537f3fa7fSDarrick J. Wong 			/* Records in order for scrub? */
706510a28e1SDarrick J. Wong 			xchk_btree_rec(bs);
70737f3fa7fSDarrick J. Wong 
70837f3fa7fSDarrick J. Wong 			/* Call out to the record checker. */
7096ca444cfSDarrick J. Wong 			recp = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr,
7106ca444cfSDarrick J. Wong 					block);
711510a28e1SDarrick J. Wong 			error = bs->scrub_rec(bs, recp);
71237f3fa7fSDarrick J. Wong 			if (error)
71337f3fa7fSDarrick J. Wong 				break;
714c517b3aaSDarrick J. Wong 			if (xchk_should_terminate(sc, &error) ||
71537f3fa7fSDarrick J. Wong 			    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
716cc3e0948SDarrick J. Wong 				break;
717cc3e0948SDarrick J. Wong 
7186ca444cfSDarrick J. Wong 			cur->bc_levels[level].ptr++;
719cc3e0948SDarrick J. Wong 			continue;
720cc3e0948SDarrick J. Wong 		}
721cc3e0948SDarrick J. Wong 
722cc3e0948SDarrick J. Wong 		/* End of node, pop back towards the root. */
7236ca444cfSDarrick J. Wong 		if (cur->bc_levels[level].ptr >
7246ca444cfSDarrick J. Wong 					be16_to_cpu(block->bb_numrecs)) {
725510a28e1SDarrick J. Wong 			xchk_btree_block_keys(bs, level, block);
726cc3e0948SDarrick J. Wong 			if (level < cur->bc_nlevels - 1)
7276ca444cfSDarrick J. Wong 				cur->bc_levels[level + 1].ptr++;
728cc3e0948SDarrick J. Wong 			level++;
729cc3e0948SDarrick J. Wong 			continue;
730cc3e0948SDarrick J. Wong 		}
731cc3e0948SDarrick J. Wong 
73237f3fa7fSDarrick J. Wong 		/* Keys in order for scrub? */
733510a28e1SDarrick J. Wong 		xchk_btree_key(bs, level);
73437f3fa7fSDarrick J. Wong 
735cc3e0948SDarrick J. Wong 		/* Drill another level deeper. */
7366ca444cfSDarrick J. Wong 		pp = xfs_btree_ptr_addr(cur, cur->bc_levels[level].ptr, block);
737510a28e1SDarrick J. Wong 		if (!xchk_btree_ptr_ok(bs, level, pp)) {
7386ca444cfSDarrick J. Wong 			cur->bc_levels[level].ptr++;
739cc3e0948SDarrick J. Wong 			continue;
740cc3e0948SDarrick J. Wong 		}
741cc3e0948SDarrick J. Wong 		level--;
742510a28e1SDarrick J. Wong 		error = xchk_btree_get_block(bs, level, pp, &block, &bp);
743cc3e0948SDarrick J. Wong 		if (error || !block)
744cc3e0948SDarrick J. Wong 			goto out;
745cc3e0948SDarrick J. Wong 
7466ca444cfSDarrick J. Wong 		cur->bc_levels[level].ptr = 1;
747cc3e0948SDarrick J. Wong 	}
748cc3e0948SDarrick J. Wong 
749cc3e0948SDarrick J. Wong out:
750858333dcSDarrick J. Wong 	/* Process deferred owner checks on btree blocks. */
751510a28e1SDarrick J. Wong 	list_for_each_entry_safe(co, n, &bs->to_check, list) {
752510a28e1SDarrick J. Wong 		if (!error && bs->cur)
753510a28e1SDarrick J. Wong 			error = xchk_btree_check_block_owner(bs, co->level,
754510a28e1SDarrick J. Wong 					co->daddr);
755858333dcSDarrick J. Wong 		list_del(&co->list);
756306195f3SDarrick J. Wong 		kfree(co);
757858333dcSDarrick J. Wong 	}
758306195f3SDarrick J. Wong 	kfree(bs);
759858333dcSDarrick J. Wong 
760537964bcSDarrick J. Wong 	return error;
761537964bcSDarrick J. Wong }
762