123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2017 Oracle. All Rights Reserved.
- * Author: Darrick J. Wong <[email protected]>
- */
- #include "xfs.h"
- #include "xfs_fs.h"
- #include "xfs_shared.h"
- #include "xfs_format.h"
- #include "xfs_trans_resv.h"
- #include "xfs_mount.h"
- #include "xfs_inode.h"
- #include "xfs_btree.h"
- #include "scrub/scrub.h"
- #include "scrub/common.h"
- #include "scrub/btree.h"
- #include "scrub/trace.h"
- /* btree scrubbing */
- /*
- * Check for btree operation errors. See the section about handling
- * operational errors in common.c.
- */
- static bool
- __xchk_btree_process_error(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level,
- int *error,
- __u32 errflag,
- void *ret_ip)
- {
- if (*error == 0)
- return true;
- switch (*error) {
- case -EDEADLOCK:
- /* Used to restart an op with deadlock avoidance. */
- trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
- break;
- case -EFSBADCRC:
- case -EFSCORRUPTED:
- /* Note the badness but don't abort. */
- sc->sm->sm_flags |= errflag;
- *error = 0;
- fallthrough;
- default:
- if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
- trace_xchk_ifork_btree_op_error(sc, cur, level,
- *error, ret_ip);
- else
- trace_xchk_btree_op_error(sc, cur, level,
- *error, ret_ip);
- break;
- }
- return false;
- }
- bool
- xchk_btree_process_error(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level,
- int *error)
- {
- return __xchk_btree_process_error(sc, cur, level, error,
- XFS_SCRUB_OFLAG_CORRUPT, __return_address);
- }
- bool
- xchk_btree_xref_process_error(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level,
- int *error)
- {
- return __xchk_btree_process_error(sc, cur, level, error,
- XFS_SCRUB_OFLAG_XFAIL, __return_address);
- }
- /* Record btree block corruption. */
- static void
- __xchk_btree_set_corrupt(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level,
- __u32 errflag,
- void *ret_ip)
- {
- sc->sm->sm_flags |= errflag;
- if (cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
- trace_xchk_ifork_btree_error(sc, cur, level,
- ret_ip);
- else
- trace_xchk_btree_error(sc, cur, level,
- ret_ip);
- }
- void
- xchk_btree_set_corrupt(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level)
- {
- __xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_CORRUPT,
- __return_address);
- }
- void
- xchk_btree_xref_set_corrupt(
- struct xfs_scrub *sc,
- struct xfs_btree_cur *cur,
- int level)
- {
- __xchk_btree_set_corrupt(sc, cur, level, XFS_SCRUB_OFLAG_XCORRUPT,
- __return_address);
- }
- /*
- * Make sure this record is in order and doesn't stray outside of the parent
- * keys.
- */
- STATIC void
- xchk_btree_rec(
- struct xchk_btree *bs)
- {
- struct xfs_btree_cur *cur = bs->cur;
- union xfs_btree_rec *rec;
- union xfs_btree_key key;
- union xfs_btree_key hkey;
- union xfs_btree_key *keyp;
- struct xfs_btree_block *block;
- struct xfs_btree_block *keyblock;
- struct xfs_buf *bp;
- block = xfs_btree_get_block(cur, 0, &bp);
- rec = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr, block);
- trace_xchk_btree_rec(bs->sc, cur, 0);
- /* If this isn't the first record, are they in order? */
- if (cur->bc_levels[0].ptr > 1 &&
- !cur->bc_ops->recs_inorder(cur, &bs->lastrec, rec))
- xchk_btree_set_corrupt(bs->sc, cur, 0);
- memcpy(&bs->lastrec, rec, cur->bc_ops->rec_len);
- if (cur->bc_nlevels == 1)
- return;
- /* Is this at least as large as the parent low key? */
- cur->bc_ops->init_key_from_rec(&key, rec);
- keyblock = xfs_btree_get_block(cur, 1, &bp);
- keyp = xfs_btree_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
- if (cur->bc_ops->diff_two_keys(cur, &key, keyp) < 0)
- xchk_btree_set_corrupt(bs->sc, cur, 1);
- if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
- return;
- /* Is this no larger than the parent high key? */
- cur->bc_ops->init_high_key_from_rec(&hkey, rec);
- keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[1].ptr, keyblock);
- if (cur->bc_ops->diff_two_keys(cur, keyp, &hkey) < 0)
- xchk_btree_set_corrupt(bs->sc, cur, 1);
- }
- /*
- * Make sure this key is in order and doesn't stray outside of the parent
- * keys.
- */
- STATIC void
- xchk_btree_key(
- struct xchk_btree *bs,
- int level)
- {
- struct xfs_btree_cur *cur = bs->cur;
- union xfs_btree_key *key;
- union xfs_btree_key *keyp;
- struct xfs_btree_block *block;
- struct xfs_btree_block *keyblock;
- struct xfs_buf *bp;
- block = xfs_btree_get_block(cur, level, &bp);
- key = xfs_btree_key_addr(cur, cur->bc_levels[level].ptr, block);
- trace_xchk_btree_key(bs->sc, cur, level);
- /* If this isn't the first key, are they in order? */
- if (cur->bc_levels[level].ptr > 1 &&
- !cur->bc_ops->keys_inorder(cur, &bs->lastkey[level - 1], key))
- xchk_btree_set_corrupt(bs->sc, cur, level);
- memcpy(&bs->lastkey[level - 1], key, cur->bc_ops->key_len);
- if (level + 1 >= cur->bc_nlevels)
- return;
- /* Is this at least as large as the parent low key? */
- keyblock = xfs_btree_get_block(cur, level + 1, &bp);
- keyp = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr, keyblock);
- if (cur->bc_ops->diff_two_keys(cur, key, keyp) < 0)
- xchk_btree_set_corrupt(bs->sc, cur, level);
- if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
- return;
- /* Is this no larger than the parent high key? */
- key = xfs_btree_high_key_addr(cur, cur->bc_levels[level].ptr, block);
- keyp = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
- keyblock);
- if (cur->bc_ops->diff_two_keys(cur, keyp, key) < 0)
- xchk_btree_set_corrupt(bs->sc, cur, level);
- }
- /*
- * Check a btree pointer. Returns true if it's ok to use this pointer.
- * Callers do not need to set the corrupt flag.
- */
- static bool
- xchk_btree_ptr_ok(
- struct xchk_btree *bs,
- int level,
- union xfs_btree_ptr *ptr)
- {
- bool res;
- /* A btree rooted in an inode has no block pointer to the root. */
- if ((bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
- level == bs->cur->bc_nlevels)
- return true;
- /* Otherwise, check the pointers. */
- if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
- res = xfs_btree_check_lptr(bs->cur, be64_to_cpu(ptr->l), level);
- else
- res = xfs_btree_check_sptr(bs->cur, be32_to_cpu(ptr->s), level);
- if (!res)
- xchk_btree_set_corrupt(bs->sc, bs->cur, level);
- return res;
- }
- /* Check that a btree block's sibling matches what we expect it. */
- STATIC int
- xchk_btree_block_check_sibling(
- struct xchk_btree *bs,
- int level,
- int direction,
- union xfs_btree_ptr *sibling)
- {
- struct xfs_btree_cur *cur = bs->cur;
- struct xfs_btree_block *pblock;
- struct xfs_buf *pbp;
- struct xfs_btree_cur *ncur = NULL;
- union xfs_btree_ptr *pp;
- int success;
- int error;
- error = xfs_btree_dup_cursor(cur, &ncur);
- if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error) ||
- !ncur)
- return error;
- /*
- * If the pointer is null, we shouldn't be able to move the upper
- * level pointer anywhere.
- */
- if (xfs_btree_ptr_is_null(cur, sibling)) {
- if (direction > 0)
- error = xfs_btree_increment(ncur, level + 1, &success);
- else
- error = xfs_btree_decrement(ncur, level + 1, &success);
- if (error == 0 && success)
- xchk_btree_set_corrupt(bs->sc, cur, level);
- error = 0;
- goto out;
- }
- /* Increment upper level pointer. */
- if (direction > 0)
- error = xfs_btree_increment(ncur, level + 1, &success);
- else
- error = xfs_btree_decrement(ncur, level + 1, &success);
- if (!xchk_btree_process_error(bs->sc, cur, level + 1, &error))
- goto out;
- if (!success) {
- xchk_btree_set_corrupt(bs->sc, cur, level + 1);
- goto out;
- }
- /* Compare upper level pointer to sibling pointer. */
- pblock = xfs_btree_get_block(ncur, level + 1, &pbp);
- pp = xfs_btree_ptr_addr(ncur, ncur->bc_levels[level + 1].ptr, pblock);
- if (!xchk_btree_ptr_ok(bs, level + 1, pp))
- goto out;
- if (pbp)
- xchk_buffer_recheck(bs->sc, pbp);
- if (xfs_btree_diff_two_ptrs(cur, pp, sibling))
- xchk_btree_set_corrupt(bs->sc, cur, level);
- out:
- xfs_btree_del_cursor(ncur, XFS_BTREE_ERROR);
- return error;
- }
- /* Check the siblings of a btree block. */
- STATIC int
- xchk_btree_block_check_siblings(
- struct xchk_btree *bs,
- struct xfs_btree_block *block)
- {
- struct xfs_btree_cur *cur = bs->cur;
- union xfs_btree_ptr leftsib;
- union xfs_btree_ptr rightsib;
- int level;
- int error = 0;
- xfs_btree_get_sibling(cur, block, &leftsib, XFS_BB_LEFTSIB);
- xfs_btree_get_sibling(cur, block, &rightsib, XFS_BB_RIGHTSIB);
- level = xfs_btree_get_level(block);
- /* Root block should never have siblings. */
- if (level == cur->bc_nlevels - 1) {
- if (!xfs_btree_ptr_is_null(cur, &leftsib) ||
- !xfs_btree_ptr_is_null(cur, &rightsib))
- xchk_btree_set_corrupt(bs->sc, cur, level);
- goto out;
- }
- /*
- * Does the left & right sibling pointers match the adjacent
- * parent level pointers?
- * (These function absorbs error codes for us.)
- */
- error = xchk_btree_block_check_sibling(bs, level, -1, &leftsib);
- if (error)
- return error;
- error = xchk_btree_block_check_sibling(bs, level, 1, &rightsib);
- if (error)
- return error;
- out:
- return error;
- }
- struct check_owner {
- struct list_head list;
- xfs_daddr_t daddr;
- int level;
- };
- /*
- * Make sure this btree block isn't in the free list and that there's
- * an rmap record for it.
- */
- STATIC int
- xchk_btree_check_block_owner(
- struct xchk_btree *bs,
- int level,
- xfs_daddr_t daddr)
- {
- xfs_agnumber_t agno;
- xfs_agblock_t agbno;
- xfs_btnum_t btnum;
- bool init_sa;
- int error = 0;
- if (!bs->cur)
- return 0;
- btnum = bs->cur->bc_btnum;
- agno = xfs_daddr_to_agno(bs->cur->bc_mp, daddr);
- agbno = xfs_daddr_to_agbno(bs->cur->bc_mp, daddr);
- init_sa = bs->cur->bc_flags & XFS_BTREE_LONG_PTRS;
- if (init_sa) {
- error = xchk_ag_init_existing(bs->sc, agno, &bs->sc->sa);
- if (!xchk_btree_xref_process_error(bs->sc, bs->cur,
- level, &error))
- goto out_free;
- }
- xchk_xref_is_used_space(bs->sc, agbno, 1);
- /*
- * The bnobt scrubber aliases bs->cur to bs->sc->sa.bno_cur, so we
- * have to nullify it (to shut down further block owner checks) if
- * self-xref encounters problems.
- */
- if (!bs->sc->sa.bno_cur && btnum == XFS_BTNUM_BNO)
- bs->cur = NULL;
- xchk_xref_is_owned_by(bs->sc, agbno, 1, bs->oinfo);
- if (!bs->sc->sa.rmap_cur && btnum == XFS_BTNUM_RMAP)
- bs->cur = NULL;
- out_free:
- if (init_sa)
- xchk_ag_free(bs->sc, &bs->sc->sa);
- return error;
- }
- /* Check the owner of a btree block. */
- STATIC int
- xchk_btree_check_owner(
- struct xchk_btree *bs,
- int level,
- struct xfs_buf *bp)
- {
- struct xfs_btree_cur *cur = bs->cur;
- struct check_owner *co;
- /*
- * In theory, xfs_btree_get_block should only give us a null buffer
- * pointer for the root of a root-in-inode btree type, but we need
- * to check defensively here in case the cursor state is also screwed
- * up.
- */
- if (bp == NULL) {
- if (!(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE))
- xchk_btree_set_corrupt(bs->sc, bs->cur, level);
- return 0;
- }
- /*
- * We want to cross-reference each btree block with the bnobt
- * and the rmapbt. We cannot cross-reference the bnobt or
- * rmapbt while scanning the bnobt or rmapbt, respectively,
- * because we cannot alter the cursor and we'd prefer not to
- * duplicate cursors. Therefore, save the buffer daddr for
- * later scanning.
- */
- if (cur->bc_btnum == XFS_BTNUM_BNO || cur->bc_btnum == XFS_BTNUM_RMAP) {
- co = kmem_alloc(sizeof(struct check_owner),
- KM_MAYFAIL);
- if (!co)
- return -ENOMEM;
- co->level = level;
- co->daddr = xfs_buf_daddr(bp);
- list_add_tail(&co->list, &bs->to_check);
- return 0;
- }
- return xchk_btree_check_block_owner(bs, level, xfs_buf_daddr(bp));
- }
- /* Decide if we want to check minrecs of a btree block in the inode root. */
- static inline bool
- xchk_btree_check_iroot_minrecs(
- struct xchk_btree *bs)
- {
- /*
- * xfs_bmap_add_attrfork_btree had an implementation bug wherein it
- * would miscalculate the space required for the data fork bmbt root
- * when adding an attr fork, and promote the iroot contents to an
- * external block unnecessarily. This went unnoticed for many years
- * until scrub found filesystems in this state. Inode rooted btrees are
- * not supposed to have immediate child blocks that are small enough
- * that the contents could fit in the inode root, but we can't fail
- * existing filesystems, so instead we disable the check for data fork
- * bmap btrees when there's an attr fork.
- */
- if (bs->cur->bc_btnum == XFS_BTNUM_BMAP &&
- bs->cur->bc_ino.whichfork == XFS_DATA_FORK &&
- xfs_inode_has_attr_fork(bs->sc->ip))
- return false;
- return true;
- }
- /*
- * Check that this btree block has at least minrecs records or is one of the
- * special blocks that don't require that.
- */
- STATIC void
- xchk_btree_check_minrecs(
- struct xchk_btree *bs,
- int level,
- struct xfs_btree_block *block)
- {
- struct xfs_btree_cur *cur = bs->cur;
- unsigned int root_level = cur->bc_nlevels - 1;
- unsigned int numrecs = be16_to_cpu(block->bb_numrecs);
- /* More records than minrecs means the block is ok. */
- if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
- return;
- /*
- * For btrees rooted in the inode, it's possible that the root block
- * contents spilled into a regular ondisk block because there wasn't
- * enough space in the inode root. The number of records in that
- * child block might be less than the standard minrecs, but that's ok
- * provided that there's only one direct child of the root.
- */
- if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
- level == cur->bc_nlevels - 2) {
- struct xfs_btree_block *root_block;
- struct xfs_buf *root_bp;
- int root_maxrecs;
- root_block = xfs_btree_get_block(cur, root_level, &root_bp);
- root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
- if (xchk_btree_check_iroot_minrecs(bs) &&
- (be16_to_cpu(root_block->bb_numrecs) != 1 ||
- numrecs <= root_maxrecs))
- xchk_btree_set_corrupt(bs->sc, cur, level);
- return;
- }
- /*
- * Otherwise, only the root level is allowed to have fewer than minrecs
- * records or keyptrs.
- */
- if (level < root_level)
- xchk_btree_set_corrupt(bs->sc, cur, level);
- }
- /*
- * Grab and scrub a btree block given a btree pointer. Returns block
- * and buffer pointers (if applicable) if they're ok to use.
- */
- 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)
- {
- xfs_failaddr_t failed_at;
- int error;
- *pblock = NULL;
- *pbp = NULL;
- error = xfs_btree_lookup_get_block(bs->cur, level, pp, pblock);
- if (!xchk_btree_process_error(bs->sc, bs->cur, level, &error) ||
- !*pblock)
- return error;
- xfs_btree_get_block(bs->cur, level, pbp);
- if (bs->cur->bc_flags & XFS_BTREE_LONG_PTRS)
- failed_at = __xfs_btree_check_lblock(bs->cur, *pblock,
- level, *pbp);
- else
- failed_at = __xfs_btree_check_sblock(bs->cur, *pblock,
- level, *pbp);
- if (failed_at) {
- xchk_btree_set_corrupt(bs->sc, bs->cur, level);
- return 0;
- }
- if (*pbp)
- xchk_buffer_recheck(bs->sc, *pbp);
- xchk_btree_check_minrecs(bs, level, *pblock);
- /*
- * Check the block's owner; this function absorbs error codes
- * for us.
- */
- error = xchk_btree_check_owner(bs, level, *pbp);
- if (error)
- return error;
- /*
- * Check the block's siblings; this function absorbs error codes
- * for us.
- */
- return xchk_btree_block_check_siblings(bs, *pblock);
- }
- /*
- * Check that the low and high keys of this block match the keys stored
- * in the parent block.
- */
- STATIC void
- xchk_btree_block_keys(
- struct xchk_btree *bs,
- int level,
- struct xfs_btree_block *block)
- {
- union xfs_btree_key block_keys;
- struct xfs_btree_cur *cur = bs->cur;
- union xfs_btree_key *high_bk;
- union xfs_btree_key *parent_keys;
- union xfs_btree_key *high_pk;
- struct xfs_btree_block *parent_block;
- struct xfs_buf *bp;
- if (level >= cur->bc_nlevels - 1)
- return;
- /* Calculate the keys for this block. */
- xfs_btree_get_keys(cur, block, &block_keys);
- /* Obtain the parent's copy of the keys for this block. */
- parent_block = xfs_btree_get_block(cur, level + 1, &bp);
- parent_keys = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
- parent_block);
- if (cur->bc_ops->diff_two_keys(cur, &block_keys, parent_keys) != 0)
- xchk_btree_set_corrupt(bs->sc, cur, 1);
- if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
- return;
- /* Get high keys */
- high_bk = xfs_btree_high_key_from_key(cur, &block_keys);
- high_pk = xfs_btree_high_key_addr(cur, cur->bc_levels[level + 1].ptr,
- parent_block);
- if (cur->bc_ops->diff_two_keys(cur, high_bk, high_pk) != 0)
- xchk_btree_set_corrupt(bs->sc, cur, 1);
- }
- /*
- * Visit all nodes and leaves of a btree. Check that all pointers and
- * records are in order, that the keys reflect the records, and use a callback
- * so that the caller can verify individual records.
- */
- 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)
- {
- union xfs_btree_ptr ptr;
- struct xchk_btree *bs;
- union xfs_btree_ptr *pp;
- union xfs_btree_rec *recp;
- struct xfs_btree_block *block;
- struct xfs_buf *bp;
- struct check_owner *co;
- struct check_owner *n;
- size_t cur_sz;
- int level;
- int error = 0;
- /*
- * Allocate the btree scrub context from the heap, because this
- * structure can get rather large. Don't let a caller feed us a
- * totally absurd size.
- */
- cur_sz = xchk_btree_sizeof(cur->bc_nlevels);
- if (cur_sz > PAGE_SIZE) {
- xchk_btree_set_corrupt(sc, cur, 0);
- return 0;
- }
- bs = kmem_zalloc(cur_sz, KM_NOFS | KM_MAYFAIL);
- if (!bs)
- return -ENOMEM;
- bs->cur = cur;
- bs->scrub_rec = scrub_fn;
- bs->oinfo = oinfo;
- bs->private = private;
- bs->sc = sc;
- /* Initialize scrub state */
- INIT_LIST_HEAD(&bs->to_check);
- /*
- * Load the root of the btree. The helper function absorbs
- * error codes for us.
- */
- level = cur->bc_nlevels - 1;
- cur->bc_ops->init_ptr_from_cur(cur, &ptr);
- if (!xchk_btree_ptr_ok(bs, cur->bc_nlevels, &ptr))
- goto out;
- error = xchk_btree_get_block(bs, level, &ptr, &block, &bp);
- if (error || !block)
- goto out;
- cur->bc_levels[level].ptr = 1;
- while (level < cur->bc_nlevels) {
- block = xfs_btree_get_block(cur, level, &bp);
- if (level == 0) {
- /* End of leaf, pop back towards the root. */
- if (cur->bc_levels[level].ptr >
- be16_to_cpu(block->bb_numrecs)) {
- xchk_btree_block_keys(bs, level, block);
- if (level < cur->bc_nlevels - 1)
- cur->bc_levels[level + 1].ptr++;
- level++;
- continue;
- }
- /* Records in order for scrub? */
- xchk_btree_rec(bs);
- /* Call out to the record checker. */
- recp = xfs_btree_rec_addr(cur, cur->bc_levels[0].ptr,
- block);
- error = bs->scrub_rec(bs, recp);
- if (error)
- break;
- if (xchk_should_terminate(sc, &error) ||
- (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
- break;
- cur->bc_levels[level].ptr++;
- continue;
- }
- /* End of node, pop back towards the root. */
- if (cur->bc_levels[level].ptr >
- be16_to_cpu(block->bb_numrecs)) {
- xchk_btree_block_keys(bs, level, block);
- if (level < cur->bc_nlevels - 1)
- cur->bc_levels[level + 1].ptr++;
- level++;
- continue;
- }
- /* Keys in order for scrub? */
- xchk_btree_key(bs, level);
- /* Drill another level deeper. */
- pp = xfs_btree_ptr_addr(cur, cur->bc_levels[level].ptr, block);
- if (!xchk_btree_ptr_ok(bs, level, pp)) {
- cur->bc_levels[level].ptr++;
- continue;
- }
- level--;
- error = xchk_btree_get_block(bs, level, pp, &block, &bp);
- if (error || !block)
- goto out;
- cur->bc_levels[level].ptr = 1;
- }
- out:
- /* Process deferred owner checks on btree blocks. */
- list_for_each_entry_safe(co, n, &bs->to_check, list) {
- if (!error && bs->cur)
- error = xchk_btree_check_block_owner(bs, co->level,
- co->daddr);
- list_del(&co->list);
- kmem_free(co);
- }
- kmem_free(bs);
- return error;
- }
|