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_log_format.h"
1362306a36Sopenharmony_ci#include "xfs_trans.h"
1462306a36Sopenharmony_ci#include "xfs_inode.h"
1562306a36Sopenharmony_ci#include "xfs_dir2.h"
1662306a36Sopenharmony_ci#include "xfs_dir2_priv.h"
1762306a36Sopenharmony_ci#include "xfs_attr_leaf.h"
1862306a36Sopenharmony_ci#include "scrub/scrub.h"
1962306a36Sopenharmony_ci#include "scrub/common.h"
2062306a36Sopenharmony_ci#include "scrub/trace.h"
2162306a36Sopenharmony_ci#include "scrub/dabtree.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Directory/Attribute Btree */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * Check for da btree operation errors.  See the section about handling
2762306a36Sopenharmony_ci * operational errors in common.c.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cibool
3062306a36Sopenharmony_cixchk_da_process_error(
3162306a36Sopenharmony_ci	struct xchk_da_btree	*ds,
3262306a36Sopenharmony_ci	int			level,
3362306a36Sopenharmony_ci	int			*error)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	struct xfs_scrub	*sc = ds->sc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (*error == 0)
3862306a36Sopenharmony_ci		return true;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	switch (*error) {
4162306a36Sopenharmony_ci	case -EDEADLOCK:
4262306a36Sopenharmony_ci	case -ECHRNG:
4362306a36Sopenharmony_ci		/* Used to restart an op with deadlock avoidance. */
4462306a36Sopenharmony_ci		trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	case -EFSBADCRC:
4762306a36Sopenharmony_ci	case -EFSCORRUPTED:
4862306a36Sopenharmony_ci		/* Note the badness but don't abort. */
4962306a36Sopenharmony_ci		sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
5062306a36Sopenharmony_ci		*error = 0;
5162306a36Sopenharmony_ci		fallthrough;
5262306a36Sopenharmony_ci	default:
5362306a36Sopenharmony_ci		trace_xchk_file_op_error(sc, ds->dargs.whichfork,
5462306a36Sopenharmony_ci				xfs_dir2_da_to_db(ds->dargs.geo,
5562306a36Sopenharmony_ci					ds->state->path.blk[level].blkno),
5662306a36Sopenharmony_ci				*error, __return_address);
5762306a36Sopenharmony_ci		break;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci	return false;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Check for da btree corruption.  See the section about handling
6462306a36Sopenharmony_ci * operational errors in common.c.
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_civoid
6762306a36Sopenharmony_cixchk_da_set_corrupt(
6862306a36Sopenharmony_ci	struct xchk_da_btree	*ds,
6962306a36Sopenharmony_ci	int			level)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	struct xfs_scrub	*sc = ds->sc;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	trace_xchk_fblock_error(sc, ds->dargs.whichfork,
7662306a36Sopenharmony_ci			xfs_dir2_da_to_db(ds->dargs.geo,
7762306a36Sopenharmony_ci				ds->state->path.blk[level].blkno),
7862306a36Sopenharmony_ci			__return_address);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic struct xfs_da_node_entry *
8262306a36Sopenharmony_cixchk_da_btree_node_entry(
8362306a36Sopenharmony_ci	struct xchk_da_btree		*ds,
8462306a36Sopenharmony_ci	int				level)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct xfs_da_state_blk		*blk = &ds->state->path.blk[level];
8762306a36Sopenharmony_ci	struct xfs_da3_icnode_hdr	hdr;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ASSERT(blk->magic == XFS_DA_NODE_MAGIC);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	xfs_da3_node_hdr_from_disk(ds->sc->mp, &hdr, blk->bp->b_addr);
9262306a36Sopenharmony_ci	return hdr.btree + blk->index;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* Scrub a da btree hash (key). */
9662306a36Sopenharmony_ciint
9762306a36Sopenharmony_cixchk_da_btree_hash(
9862306a36Sopenharmony_ci	struct xchk_da_btree		*ds,
9962306a36Sopenharmony_ci	int				level,
10062306a36Sopenharmony_ci	__be32				*hashp)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct xfs_da_node_entry	*entry;
10362306a36Sopenharmony_ci	xfs_dahash_t			hash;
10462306a36Sopenharmony_ci	xfs_dahash_t			parent_hash;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Is this hash in order? */
10762306a36Sopenharmony_ci	hash = be32_to_cpu(*hashp);
10862306a36Sopenharmony_ci	if (hash < ds->hashes[level])
10962306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
11062306a36Sopenharmony_ci	ds->hashes[level] = hash;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (level == 0)
11362306a36Sopenharmony_ci		return 0;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Is this hash no larger than the parent hash? */
11662306a36Sopenharmony_ci	entry = xchk_da_btree_node_entry(ds, level - 1);
11762306a36Sopenharmony_ci	parent_hash = be32_to_cpu(entry->hashval);
11862306a36Sopenharmony_ci	if (parent_hash < hash)
11962306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * Check a da btree pointer.  Returns true if it's ok to use this
12662306a36Sopenharmony_ci * pointer.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_ciSTATIC bool
12962306a36Sopenharmony_cixchk_da_btree_ptr_ok(
13062306a36Sopenharmony_ci	struct xchk_da_btree	*ds,
13162306a36Sopenharmony_ci	int			level,
13262306a36Sopenharmony_ci	xfs_dablk_t		blkno)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	if (blkno < ds->lowest || (ds->highest != 0 && blkno >= ds->highest)) {
13562306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
13662306a36Sopenharmony_ci		return false;
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return true;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * The da btree scrubber can handle leaf1 blocks as a degenerate
14462306a36Sopenharmony_ci * form of leafn blocks.  Since the regular da code doesn't handle
14562306a36Sopenharmony_ci * leaf1, we must multiplex the verifiers.
14662306a36Sopenharmony_ci */
14762306a36Sopenharmony_cistatic void
14862306a36Sopenharmony_cixchk_da_btree_read_verify(
14962306a36Sopenharmony_ci	struct xfs_buf		*bp)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	struct xfs_da_blkinfo	*info = bp->b_addr;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	switch (be16_to_cpu(info->magic)) {
15462306a36Sopenharmony_ci	case XFS_DIR2_LEAF1_MAGIC:
15562306a36Sopenharmony_ci	case XFS_DIR3_LEAF1_MAGIC:
15662306a36Sopenharmony_ci		bp->b_ops = &xfs_dir3_leaf1_buf_ops;
15762306a36Sopenharmony_ci		bp->b_ops->verify_read(bp);
15862306a36Sopenharmony_ci		return;
15962306a36Sopenharmony_ci	default:
16062306a36Sopenharmony_ci		/*
16162306a36Sopenharmony_ci		 * xfs_da3_node_buf_ops already know how to handle
16262306a36Sopenharmony_ci		 * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks.
16362306a36Sopenharmony_ci		 */
16462306a36Sopenharmony_ci		bp->b_ops = &xfs_da3_node_buf_ops;
16562306a36Sopenharmony_ci		bp->b_ops->verify_read(bp);
16662306a36Sopenharmony_ci		return;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_cistatic void
17062306a36Sopenharmony_cixchk_da_btree_write_verify(
17162306a36Sopenharmony_ci	struct xfs_buf		*bp)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct xfs_da_blkinfo	*info = bp->b_addr;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	switch (be16_to_cpu(info->magic)) {
17662306a36Sopenharmony_ci	case XFS_DIR2_LEAF1_MAGIC:
17762306a36Sopenharmony_ci	case XFS_DIR3_LEAF1_MAGIC:
17862306a36Sopenharmony_ci		bp->b_ops = &xfs_dir3_leaf1_buf_ops;
17962306a36Sopenharmony_ci		bp->b_ops->verify_write(bp);
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci	default:
18262306a36Sopenharmony_ci		/*
18362306a36Sopenharmony_ci		 * xfs_da3_node_buf_ops already know how to handle
18462306a36Sopenharmony_ci		 * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks.
18562306a36Sopenharmony_ci		 */
18662306a36Sopenharmony_ci		bp->b_ops = &xfs_da3_node_buf_ops;
18762306a36Sopenharmony_ci		bp->b_ops->verify_write(bp);
18862306a36Sopenharmony_ci		return;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_cistatic void *
19262306a36Sopenharmony_cixchk_da_btree_verify(
19362306a36Sopenharmony_ci	struct xfs_buf		*bp)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct xfs_da_blkinfo	*info = bp->b_addr;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	switch (be16_to_cpu(info->magic)) {
19862306a36Sopenharmony_ci	case XFS_DIR2_LEAF1_MAGIC:
19962306a36Sopenharmony_ci	case XFS_DIR3_LEAF1_MAGIC:
20062306a36Sopenharmony_ci		bp->b_ops = &xfs_dir3_leaf1_buf_ops;
20162306a36Sopenharmony_ci		return bp->b_ops->verify_struct(bp);
20262306a36Sopenharmony_ci	default:
20362306a36Sopenharmony_ci		bp->b_ops = &xfs_da3_node_buf_ops;
20462306a36Sopenharmony_ci		return bp->b_ops->verify_struct(bp);
20562306a36Sopenharmony_ci	}
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic const struct xfs_buf_ops xchk_da_btree_buf_ops = {
20962306a36Sopenharmony_ci	.name = "xchk_da_btree",
21062306a36Sopenharmony_ci	.verify_read = xchk_da_btree_read_verify,
21162306a36Sopenharmony_ci	.verify_write = xchk_da_btree_write_verify,
21262306a36Sopenharmony_ci	.verify_struct = xchk_da_btree_verify,
21362306a36Sopenharmony_ci};
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci/* Check a block's sibling. */
21662306a36Sopenharmony_ciSTATIC int
21762306a36Sopenharmony_cixchk_da_btree_block_check_sibling(
21862306a36Sopenharmony_ci	struct xchk_da_btree	*ds,
21962306a36Sopenharmony_ci	int			level,
22062306a36Sopenharmony_ci	int			direction,
22162306a36Sopenharmony_ci	xfs_dablk_t		sibling)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct xfs_da_state_path *path = &ds->state->path;
22462306a36Sopenharmony_ci	struct xfs_da_state_path *altpath = &ds->state->altpath;
22562306a36Sopenharmony_ci	int			retval;
22662306a36Sopenharmony_ci	int			plevel;
22762306a36Sopenharmony_ci	int			error;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	memcpy(altpath, path, sizeof(ds->state->altpath));
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/*
23262306a36Sopenharmony_ci	 * If the pointer is null, we shouldn't be able to move the upper
23362306a36Sopenharmony_ci	 * level pointer anywhere.
23462306a36Sopenharmony_ci	 */
23562306a36Sopenharmony_ci	if (sibling == 0) {
23662306a36Sopenharmony_ci		error = xfs_da3_path_shift(ds->state, altpath, direction,
23762306a36Sopenharmony_ci				false, &retval);
23862306a36Sopenharmony_ci		if (error == 0 && retval == 0)
23962306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
24062306a36Sopenharmony_ci		error = 0;
24162306a36Sopenharmony_ci		goto out;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Move the alternate cursor one block in the direction given. */
24562306a36Sopenharmony_ci	error = xfs_da3_path_shift(ds->state, altpath, direction, false,
24662306a36Sopenharmony_ci			&retval);
24762306a36Sopenharmony_ci	if (!xchk_da_process_error(ds, level, &error))
24862306a36Sopenharmony_ci		goto out;
24962306a36Sopenharmony_ci	if (retval) {
25062306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
25162306a36Sopenharmony_ci		goto out;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	if (altpath->blk[level].bp)
25462306a36Sopenharmony_ci		xchk_buffer_recheck(ds->sc, altpath->blk[level].bp);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Compare upper level pointer to sibling pointer. */
25762306a36Sopenharmony_ci	if (altpath->blk[level].blkno != sibling)
25862306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciout:
26162306a36Sopenharmony_ci	/* Free all buffers in the altpath that aren't referenced from path. */
26262306a36Sopenharmony_ci	for (plevel = 0; plevel < altpath->active; plevel++) {
26362306a36Sopenharmony_ci		if (altpath->blk[plevel].bp == NULL ||
26462306a36Sopenharmony_ci		    (plevel < path->active &&
26562306a36Sopenharmony_ci		     altpath->blk[plevel].bp == path->blk[plevel].bp))
26662306a36Sopenharmony_ci			continue;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		xfs_trans_brelse(ds->dargs.trans, altpath->blk[plevel].bp);
26962306a36Sopenharmony_ci		altpath->blk[plevel].bp = NULL;
27062306a36Sopenharmony_ci	}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return error;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* Check a block's sibling pointers. */
27662306a36Sopenharmony_ciSTATIC int
27762306a36Sopenharmony_cixchk_da_btree_block_check_siblings(
27862306a36Sopenharmony_ci	struct xchk_da_btree	*ds,
27962306a36Sopenharmony_ci	int			level,
28062306a36Sopenharmony_ci	struct xfs_da_blkinfo	*hdr)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	xfs_dablk_t		forw;
28362306a36Sopenharmony_ci	xfs_dablk_t		back;
28462306a36Sopenharmony_ci	int			error = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	forw = be32_to_cpu(hdr->forw);
28762306a36Sopenharmony_ci	back = be32_to_cpu(hdr->back);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Top level blocks should not have sibling pointers. */
29062306a36Sopenharmony_ci	if (level == 0) {
29162306a36Sopenharmony_ci		if (forw != 0 || back != 0)
29262306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
29362306a36Sopenharmony_ci		return 0;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/*
29762306a36Sopenharmony_ci	 * Check back (left) and forw (right) pointers.  These functions
29862306a36Sopenharmony_ci	 * absorb error codes for us.
29962306a36Sopenharmony_ci	 */
30062306a36Sopenharmony_ci	error = xchk_da_btree_block_check_sibling(ds, level, 0, back);
30162306a36Sopenharmony_ci	if (error)
30262306a36Sopenharmony_ci		goto out;
30362306a36Sopenharmony_ci	error = xchk_da_btree_block_check_sibling(ds, level, 1, forw);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ciout:
30662306a36Sopenharmony_ci	memset(&ds->state->altpath, 0, sizeof(ds->state->altpath));
30762306a36Sopenharmony_ci	return error;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci/* Load a dir/attribute block from a btree. */
31162306a36Sopenharmony_ciSTATIC int
31262306a36Sopenharmony_cixchk_da_btree_block(
31362306a36Sopenharmony_ci	struct xchk_da_btree		*ds,
31462306a36Sopenharmony_ci	int				level,
31562306a36Sopenharmony_ci	xfs_dablk_t			blkno)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct xfs_da_state_blk		*blk;
31862306a36Sopenharmony_ci	struct xfs_da_intnode		*node;
31962306a36Sopenharmony_ci	struct xfs_da_node_entry	*btree;
32062306a36Sopenharmony_ci	struct xfs_da3_blkinfo		*hdr3;
32162306a36Sopenharmony_ci	struct xfs_da_args		*dargs = &ds->dargs;
32262306a36Sopenharmony_ci	struct xfs_inode		*ip = ds->dargs.dp;
32362306a36Sopenharmony_ci	xfs_ino_t			owner;
32462306a36Sopenharmony_ci	int				*pmaxrecs;
32562306a36Sopenharmony_ci	struct xfs_da3_icnode_hdr	nodehdr;
32662306a36Sopenharmony_ci	int				error = 0;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	blk = &ds->state->path.blk[level];
32962306a36Sopenharmony_ci	ds->state->path.active = level + 1;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* Release old block. */
33262306a36Sopenharmony_ci	if (blk->bp) {
33362306a36Sopenharmony_ci		xfs_trans_brelse(dargs->trans, blk->bp);
33462306a36Sopenharmony_ci		blk->bp = NULL;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Check the pointer. */
33862306a36Sopenharmony_ci	blk->blkno = blkno;
33962306a36Sopenharmony_ci	if (!xchk_da_btree_ptr_ok(ds, level, blkno))
34062306a36Sopenharmony_ci		goto out_nobuf;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Read the buffer. */
34362306a36Sopenharmony_ci	error = xfs_da_read_buf(dargs->trans, dargs->dp, blk->blkno,
34462306a36Sopenharmony_ci			XFS_DABUF_MAP_HOLE_OK, &blk->bp, dargs->whichfork,
34562306a36Sopenharmony_ci			&xchk_da_btree_buf_ops);
34662306a36Sopenharmony_ci	if (!xchk_da_process_error(ds, level, &error))
34762306a36Sopenharmony_ci		goto out_nobuf;
34862306a36Sopenharmony_ci	if (blk->bp)
34962306a36Sopenharmony_ci		xchk_buffer_recheck(ds->sc, blk->bp);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/*
35262306a36Sopenharmony_ci	 * We didn't find a dir btree root block, which means that
35362306a36Sopenharmony_ci	 * there's no LEAF1/LEAFN tree (at least not where it's supposed
35462306a36Sopenharmony_ci	 * to be), so jump out now.
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	if (ds->dargs.whichfork == XFS_DATA_FORK && level == 0 &&
35762306a36Sopenharmony_ci			blk->bp == NULL)
35862306a36Sopenharmony_ci		goto out_nobuf;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	/* It's /not/ ok for attr trees not to have a da btree. */
36162306a36Sopenharmony_ci	if (blk->bp == NULL) {
36262306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
36362306a36Sopenharmony_ci		goto out_nobuf;
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	hdr3 = blk->bp->b_addr;
36762306a36Sopenharmony_ci	blk->magic = be16_to_cpu(hdr3->hdr.magic);
36862306a36Sopenharmony_ci	pmaxrecs = &ds->maxrecs[level];
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	/* We only started zeroing the header on v5 filesystems. */
37162306a36Sopenharmony_ci	if (xfs_has_crc(ds->sc->mp) && hdr3->hdr.pad)
37262306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Check the owner. */
37562306a36Sopenharmony_ci	if (xfs_has_crc(ip->i_mount)) {
37662306a36Sopenharmony_ci		owner = be64_to_cpu(hdr3->owner);
37762306a36Sopenharmony_ci		if (owner != ip->i_ino)
37862306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* Check the siblings. */
38262306a36Sopenharmony_ci	error = xchk_da_btree_block_check_siblings(ds, level, &hdr3->hdr);
38362306a36Sopenharmony_ci	if (error)
38462306a36Sopenharmony_ci		goto out;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/* Interpret the buffer. */
38762306a36Sopenharmony_ci	switch (blk->magic) {
38862306a36Sopenharmony_ci	case XFS_ATTR_LEAF_MAGIC:
38962306a36Sopenharmony_ci	case XFS_ATTR3_LEAF_MAGIC:
39062306a36Sopenharmony_ci		xfs_trans_buf_set_type(dargs->trans, blk->bp,
39162306a36Sopenharmony_ci				XFS_BLFT_ATTR_LEAF_BUF);
39262306a36Sopenharmony_ci		blk->magic = XFS_ATTR_LEAF_MAGIC;
39362306a36Sopenharmony_ci		blk->hashval = xfs_attr_leaf_lasthash(blk->bp, pmaxrecs);
39462306a36Sopenharmony_ci		if (ds->tree_level != 0)
39562306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	case XFS_DIR2_LEAFN_MAGIC:
39862306a36Sopenharmony_ci	case XFS_DIR3_LEAFN_MAGIC:
39962306a36Sopenharmony_ci		xfs_trans_buf_set_type(dargs->trans, blk->bp,
40062306a36Sopenharmony_ci				XFS_BLFT_DIR_LEAFN_BUF);
40162306a36Sopenharmony_ci		blk->magic = XFS_DIR2_LEAFN_MAGIC;
40262306a36Sopenharmony_ci		blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs);
40362306a36Sopenharmony_ci		if (ds->tree_level != 0)
40462306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	case XFS_DIR2_LEAF1_MAGIC:
40762306a36Sopenharmony_ci	case XFS_DIR3_LEAF1_MAGIC:
40862306a36Sopenharmony_ci		xfs_trans_buf_set_type(dargs->trans, blk->bp,
40962306a36Sopenharmony_ci				XFS_BLFT_DIR_LEAF1_BUF);
41062306a36Sopenharmony_ci		blk->magic = XFS_DIR2_LEAF1_MAGIC;
41162306a36Sopenharmony_ci		blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs);
41262306a36Sopenharmony_ci		if (ds->tree_level != 0)
41362306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
41462306a36Sopenharmony_ci		break;
41562306a36Sopenharmony_ci	case XFS_DA_NODE_MAGIC:
41662306a36Sopenharmony_ci	case XFS_DA3_NODE_MAGIC:
41762306a36Sopenharmony_ci		xfs_trans_buf_set_type(dargs->trans, blk->bp,
41862306a36Sopenharmony_ci				XFS_BLFT_DA_NODE_BUF);
41962306a36Sopenharmony_ci		blk->magic = XFS_DA_NODE_MAGIC;
42062306a36Sopenharmony_ci		node = blk->bp->b_addr;
42162306a36Sopenharmony_ci		xfs_da3_node_hdr_from_disk(ip->i_mount, &nodehdr, node);
42262306a36Sopenharmony_ci		btree = nodehdr.btree;
42362306a36Sopenharmony_ci		*pmaxrecs = nodehdr.count;
42462306a36Sopenharmony_ci		blk->hashval = be32_to_cpu(btree[*pmaxrecs - 1].hashval);
42562306a36Sopenharmony_ci		if (level == 0) {
42662306a36Sopenharmony_ci			if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) {
42762306a36Sopenharmony_ci				xchk_da_set_corrupt(ds, level);
42862306a36Sopenharmony_ci				goto out_freebp;
42962306a36Sopenharmony_ci			}
43062306a36Sopenharmony_ci			ds->tree_level = nodehdr.level;
43162306a36Sopenharmony_ci		} else {
43262306a36Sopenharmony_ci			if (ds->tree_level != nodehdr.level) {
43362306a36Sopenharmony_ci				xchk_da_set_corrupt(ds, level);
43462306a36Sopenharmony_ci				goto out_freebp;
43562306a36Sopenharmony_ci			}
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		/* XXX: Check hdr3.pad32 once we know how to fix it. */
43962306a36Sopenharmony_ci		break;
44062306a36Sopenharmony_ci	default:
44162306a36Sopenharmony_ci		xchk_da_set_corrupt(ds, level);
44262306a36Sopenharmony_ci		goto out_freebp;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/*
44662306a36Sopenharmony_ci	 * If we've been handed a block that is below the dabtree root, does
44762306a36Sopenharmony_ci	 * its hashval match what the parent block expected to see?
44862306a36Sopenharmony_ci	 */
44962306a36Sopenharmony_ci	if (level > 0) {
45062306a36Sopenharmony_ci		struct xfs_da_node_entry	*key;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci		key = xchk_da_btree_node_entry(ds, level - 1);
45362306a36Sopenharmony_ci		if (be32_to_cpu(key->hashval) != blk->hashval) {
45462306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level);
45562306a36Sopenharmony_ci			goto out_freebp;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciout:
46062306a36Sopenharmony_ci	return error;
46162306a36Sopenharmony_ciout_freebp:
46262306a36Sopenharmony_ci	xfs_trans_brelse(dargs->trans, blk->bp);
46362306a36Sopenharmony_ci	blk->bp = NULL;
46462306a36Sopenharmony_ciout_nobuf:
46562306a36Sopenharmony_ci	blk->blkno = 0;
46662306a36Sopenharmony_ci	return error;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* Visit all nodes and leaves of a da btree. */
47062306a36Sopenharmony_ciint
47162306a36Sopenharmony_cixchk_da_btree(
47262306a36Sopenharmony_ci	struct xfs_scrub		*sc,
47362306a36Sopenharmony_ci	int				whichfork,
47462306a36Sopenharmony_ci	xchk_da_btree_rec_fn		scrub_fn,
47562306a36Sopenharmony_ci	void				*private)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct xchk_da_btree		*ds;
47862306a36Sopenharmony_ci	struct xfs_mount		*mp = sc->mp;
47962306a36Sopenharmony_ci	struct xfs_da_state_blk		*blks;
48062306a36Sopenharmony_ci	struct xfs_da_node_entry	*key;
48162306a36Sopenharmony_ci	xfs_dablk_t			blkno;
48262306a36Sopenharmony_ci	int				level;
48362306a36Sopenharmony_ci	int				error;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	/* Skip short format data structures; no btree to scan. */
48662306a36Sopenharmony_ci	if (!xfs_ifork_has_extents(xfs_ifork_ptr(sc->ip, whichfork)))
48762306a36Sopenharmony_ci		return 0;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	/* Set up initial da state. */
49062306a36Sopenharmony_ci	ds = kzalloc(sizeof(struct xchk_da_btree), XCHK_GFP_FLAGS);
49162306a36Sopenharmony_ci	if (!ds)
49262306a36Sopenharmony_ci		return -ENOMEM;
49362306a36Sopenharmony_ci	ds->dargs.dp = sc->ip;
49462306a36Sopenharmony_ci	ds->dargs.whichfork = whichfork;
49562306a36Sopenharmony_ci	ds->dargs.trans = sc->tp;
49662306a36Sopenharmony_ci	ds->dargs.op_flags = XFS_DA_OP_OKNOENT;
49762306a36Sopenharmony_ci	ds->state = xfs_da_state_alloc(&ds->dargs);
49862306a36Sopenharmony_ci	ds->sc = sc;
49962306a36Sopenharmony_ci	ds->private = private;
50062306a36Sopenharmony_ci	if (whichfork == XFS_ATTR_FORK) {
50162306a36Sopenharmony_ci		ds->dargs.geo = mp->m_attr_geo;
50262306a36Sopenharmony_ci		ds->lowest = 0;
50362306a36Sopenharmony_ci		ds->highest = 0;
50462306a36Sopenharmony_ci	} else {
50562306a36Sopenharmony_ci		ds->dargs.geo = mp->m_dir_geo;
50662306a36Sopenharmony_ci		ds->lowest = ds->dargs.geo->leafblk;
50762306a36Sopenharmony_ci		ds->highest = ds->dargs.geo->freeblk;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci	blkno = ds->lowest;
51062306a36Sopenharmony_ci	level = 0;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* Find the root of the da tree, if present. */
51362306a36Sopenharmony_ci	blks = ds->state->path.blk;
51462306a36Sopenharmony_ci	error = xchk_da_btree_block(ds, level, blkno);
51562306a36Sopenharmony_ci	if (error)
51662306a36Sopenharmony_ci		goto out_state;
51762306a36Sopenharmony_ci	/*
51862306a36Sopenharmony_ci	 * We didn't find a block at ds->lowest, which means that there's
51962306a36Sopenharmony_ci	 * no LEAF1/LEAFN tree (at least not where it's supposed to be),
52062306a36Sopenharmony_ci	 * so jump out now.
52162306a36Sopenharmony_ci	 */
52262306a36Sopenharmony_ci	if (blks[level].bp == NULL)
52362306a36Sopenharmony_ci		goto out_state;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	blks[level].index = 0;
52662306a36Sopenharmony_ci	while (level >= 0 && level < XFS_DA_NODE_MAXDEPTH) {
52762306a36Sopenharmony_ci		/* Handle leaf block. */
52862306a36Sopenharmony_ci		if (blks[level].magic != XFS_DA_NODE_MAGIC) {
52962306a36Sopenharmony_ci			/* End of leaf, pop back towards the root. */
53062306a36Sopenharmony_ci			if (blks[level].index >= ds->maxrecs[level]) {
53162306a36Sopenharmony_ci				if (level > 0)
53262306a36Sopenharmony_ci					blks[level - 1].index++;
53362306a36Sopenharmony_ci				ds->tree_level++;
53462306a36Sopenharmony_ci				level--;
53562306a36Sopenharmony_ci				continue;
53662306a36Sopenharmony_ci			}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci			/* Dispatch record scrubbing. */
53962306a36Sopenharmony_ci			error = scrub_fn(ds, level);
54062306a36Sopenharmony_ci			if (error)
54162306a36Sopenharmony_ci				break;
54262306a36Sopenharmony_ci			if (xchk_should_terminate(sc, &error) ||
54362306a36Sopenharmony_ci			    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
54462306a36Sopenharmony_ci				break;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci			blks[level].index++;
54762306a36Sopenharmony_ci			continue;
54862306a36Sopenharmony_ci		}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		/* End of node, pop back towards the root. */
55262306a36Sopenharmony_ci		if (blks[level].index >= ds->maxrecs[level]) {
55362306a36Sopenharmony_ci			if (level > 0)
55462306a36Sopenharmony_ci				blks[level - 1].index++;
55562306a36Sopenharmony_ci			ds->tree_level++;
55662306a36Sopenharmony_ci			level--;
55762306a36Sopenharmony_ci			continue;
55862306a36Sopenharmony_ci		}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci		/* Hashes in order for scrub? */
56162306a36Sopenharmony_ci		key = xchk_da_btree_node_entry(ds, level);
56262306a36Sopenharmony_ci		error = xchk_da_btree_hash(ds, level, &key->hashval);
56362306a36Sopenharmony_ci		if (error)
56462306a36Sopenharmony_ci			goto out;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci		/* Drill another level deeper. */
56762306a36Sopenharmony_ci		blkno = be32_to_cpu(key->before);
56862306a36Sopenharmony_ci		level++;
56962306a36Sopenharmony_ci		if (level >= XFS_DA_NODE_MAXDEPTH) {
57062306a36Sopenharmony_ci			/* Too deep! */
57162306a36Sopenharmony_ci			xchk_da_set_corrupt(ds, level - 1);
57262306a36Sopenharmony_ci			break;
57362306a36Sopenharmony_ci		}
57462306a36Sopenharmony_ci		ds->tree_level--;
57562306a36Sopenharmony_ci		error = xchk_da_btree_block(ds, level, blkno);
57662306a36Sopenharmony_ci		if (error)
57762306a36Sopenharmony_ci			goto out;
57862306a36Sopenharmony_ci		if (blks[level].bp == NULL)
57962306a36Sopenharmony_ci			goto out;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		blks[level].index = 0;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ciout:
58562306a36Sopenharmony_ci	/* Release all the buffers we're tracking. */
58662306a36Sopenharmony_ci	for (level = 0; level < XFS_DA_NODE_MAXDEPTH; level++) {
58762306a36Sopenharmony_ci		if (blks[level].bp == NULL)
58862306a36Sopenharmony_ci			continue;
58962306a36Sopenharmony_ci		xfs_trans_brelse(sc->tp, blks[level].bp);
59062306a36Sopenharmony_ci		blks[level].bp = NULL;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ciout_state:
59462306a36Sopenharmony_ci	xfs_da_state_free(ds->state);
59562306a36Sopenharmony_ci	kfree(ds);
59662306a36Sopenharmony_ci	return error;
59762306a36Sopenharmony_ci}
598