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