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_btree.h"
1362306a36Sopenharmony_ci#include "xfs_bit.h"
1462306a36Sopenharmony_ci#include "xfs_log_format.h"
1562306a36Sopenharmony_ci#include "xfs_trans.h"
1662306a36Sopenharmony_ci#include "xfs_inode.h"
1762306a36Sopenharmony_ci#include "xfs_alloc.h"
1862306a36Sopenharmony_ci#include "xfs_bmap.h"
1962306a36Sopenharmony_ci#include "xfs_bmap_btree.h"
2062306a36Sopenharmony_ci#include "xfs_rmap.h"
2162306a36Sopenharmony_ci#include "xfs_rmap_btree.h"
2262306a36Sopenharmony_ci#include "scrub/scrub.h"
2362306a36Sopenharmony_ci#include "scrub/common.h"
2462306a36Sopenharmony_ci#include "scrub/btree.h"
2562306a36Sopenharmony_ci#include "xfs_ag.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* Set us up with an inode's bmap. */
2862306a36Sopenharmony_ciint
2962306a36Sopenharmony_cixchk_setup_inode_bmap(
3062306a36Sopenharmony_ci	struct xfs_scrub	*sc)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int			error;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (xchk_need_intent_drain(sc))
3562306a36Sopenharmony_ci		xchk_fsgates_enable(sc, XCHK_FSGATES_DRAIN);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	error = xchk_iget_for_scrubbing(sc);
3862306a36Sopenharmony_ci	if (error)
3962306a36Sopenharmony_ci		goto out;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	xchk_ilock(sc, XFS_IOLOCK_EXCL);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * We don't want any ephemeral data/cow fork updates sitting around
4562306a36Sopenharmony_ci	 * while we inspect block mappings, so wait for directio to finish
4662306a36Sopenharmony_ci	 * and flush dirty data if we have delalloc reservations.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	if (S_ISREG(VFS_I(sc->ip)->i_mode) &&
4962306a36Sopenharmony_ci	    sc->sm->sm_type != XFS_SCRUB_TYPE_BMBTA) {
5062306a36Sopenharmony_ci		struct address_space	*mapping = VFS_I(sc->ip)->i_mapping;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		xchk_ilock(sc, XFS_MMAPLOCK_EXCL);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		inode_dio_wait(VFS_I(sc->ip));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci		/*
5762306a36Sopenharmony_ci		 * Try to flush all incore state to disk before we examine the
5862306a36Sopenharmony_ci		 * space mappings for the data fork.  Leave accumulated errors
5962306a36Sopenharmony_ci		 * in the mapping for the writer threads to consume.
6062306a36Sopenharmony_ci		 *
6162306a36Sopenharmony_ci		 * On ENOSPC or EIO writeback errors, we continue into the
6262306a36Sopenharmony_ci		 * extent mapping checks because write failures do not
6362306a36Sopenharmony_ci		 * necessarily imply anything about the correctness of the file
6462306a36Sopenharmony_ci		 * metadata.  The metadata and the file data could be on
6562306a36Sopenharmony_ci		 * completely separate devices; a media failure might only
6662306a36Sopenharmony_ci		 * affect a subset of the disk, etc.  We can handle delalloc
6762306a36Sopenharmony_ci		 * extents in the scrubber, so leaving them in memory is fine.
6862306a36Sopenharmony_ci		 */
6962306a36Sopenharmony_ci		error = filemap_fdatawrite(mapping);
7062306a36Sopenharmony_ci		if (!error)
7162306a36Sopenharmony_ci			error = filemap_fdatawait_keep_errors(mapping);
7262306a36Sopenharmony_ci		if (error && (error != -ENOSPC && error != -EIO))
7362306a36Sopenharmony_ci			goto out;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Got the inode, lock it and we're ready to go. */
7762306a36Sopenharmony_ci	error = xchk_trans_alloc(sc, 0);
7862306a36Sopenharmony_ci	if (error)
7962306a36Sopenharmony_ci		goto out;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	xchk_ilock(sc, XFS_ILOCK_EXCL);
8262306a36Sopenharmony_ciout:
8362306a36Sopenharmony_ci	/* scrub teardown will unlock and release the inode */
8462306a36Sopenharmony_ci	return error;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/*
8862306a36Sopenharmony_ci * Inode fork block mapping (BMBT) scrubber.
8962306a36Sopenharmony_ci * More complex than the others because we have to scrub
9062306a36Sopenharmony_ci * all the extents regardless of whether or not the fork
9162306a36Sopenharmony_ci * is in btree format.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistruct xchk_bmap_info {
9562306a36Sopenharmony_ci	struct xfs_scrub	*sc;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* Incore extent tree cursor */
9862306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Previous fork mapping that we examined */
10162306a36Sopenharmony_ci	struct xfs_bmbt_irec	prev_rec;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Is this a realtime fork? */
10462306a36Sopenharmony_ci	bool			is_rt;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* May mappings point to shared space? */
10762306a36Sopenharmony_ci	bool			is_shared;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Was the incore extent tree loaded? */
11062306a36Sopenharmony_ci	bool			was_loaded;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Which inode fork are we checking? */
11362306a36Sopenharmony_ci	int			whichfork;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci/* Look for a corresponding rmap for this irec. */
11762306a36Sopenharmony_cistatic inline bool
11862306a36Sopenharmony_cixchk_bmap_get_rmap(
11962306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
12062306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec,
12162306a36Sopenharmony_ci	xfs_agblock_t		agbno,
12262306a36Sopenharmony_ci	uint64_t		owner,
12362306a36Sopenharmony_ci	struct xfs_rmap_irec	*rmap)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	xfs_fileoff_t		offset;
12662306a36Sopenharmony_ci	unsigned int		rflags = 0;
12762306a36Sopenharmony_ci	int			has_rmap;
12862306a36Sopenharmony_ci	int			error;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (info->whichfork == XFS_ATTR_FORK)
13162306a36Sopenharmony_ci		rflags |= XFS_RMAP_ATTR_FORK;
13262306a36Sopenharmony_ci	if (irec->br_state == XFS_EXT_UNWRITTEN)
13362306a36Sopenharmony_ci		rflags |= XFS_RMAP_UNWRITTEN;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/*
13662306a36Sopenharmony_ci	 * CoW staging extents are owned (on disk) by the refcountbt, so
13762306a36Sopenharmony_ci	 * their rmaps do not have offsets.
13862306a36Sopenharmony_ci	 */
13962306a36Sopenharmony_ci	if (info->whichfork == XFS_COW_FORK)
14062306a36Sopenharmony_ci		offset = 0;
14162306a36Sopenharmony_ci	else
14262306a36Sopenharmony_ci		offset = irec->br_startoff;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/*
14562306a36Sopenharmony_ci	 * If the caller thinks this could be a shared bmbt extent (IOWs,
14662306a36Sopenharmony_ci	 * any data fork extent of a reflink inode) then we have to use the
14762306a36Sopenharmony_ci	 * range rmap lookup to make sure we get the correct owner/offset.
14862306a36Sopenharmony_ci	 */
14962306a36Sopenharmony_ci	if (info->is_shared) {
15062306a36Sopenharmony_ci		error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno,
15162306a36Sopenharmony_ci				owner, offset, rflags, rmap, &has_rmap);
15262306a36Sopenharmony_ci	} else {
15362306a36Sopenharmony_ci		error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno,
15462306a36Sopenharmony_ci				owner, offset, rflags, rmap, &has_rmap);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	if (!xchk_should_check_xref(info->sc, &error, &info->sc->sa.rmap_cur))
15762306a36Sopenharmony_ci		return false;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!has_rmap)
16062306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
16162306a36Sopenharmony_ci			irec->br_startoff);
16262306a36Sopenharmony_ci	return has_rmap;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/* Make sure that we have rmapbt records for this data/attr fork extent. */
16662306a36Sopenharmony_ciSTATIC void
16762306a36Sopenharmony_cixchk_bmap_xref_rmap(
16862306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
16962306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec,
17062306a36Sopenharmony_ci	xfs_agblock_t		agbno)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct xfs_rmap_irec	rmap;
17362306a36Sopenharmony_ci	unsigned long long	rmap_end;
17462306a36Sopenharmony_ci	uint64_t		owner = info->sc->ip->i_ino;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
17762306a36Sopenharmony_ci		return;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* Find the rmap record for this irec. */
18062306a36Sopenharmony_ci	if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap))
18162306a36Sopenharmony_ci		return;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/*
18462306a36Sopenharmony_ci	 * The rmap must be an exact match for this incore file mapping record,
18562306a36Sopenharmony_ci	 * which may have arisen from multiple ondisk records.
18662306a36Sopenharmony_ci	 */
18762306a36Sopenharmony_ci	if (rmap.rm_startblock != agbno)
18862306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
18962306a36Sopenharmony_ci				irec->br_startoff);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
19262306a36Sopenharmony_ci	if (rmap_end != agbno + irec->br_blockcount)
19362306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
19462306a36Sopenharmony_ci				irec->br_startoff);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/* Check the logical offsets. */
19762306a36Sopenharmony_ci	if (rmap.rm_offset != irec->br_startoff)
19862306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
19962306a36Sopenharmony_ci				irec->br_startoff);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	rmap_end = (unsigned long long)rmap.rm_offset + rmap.rm_blockcount;
20262306a36Sopenharmony_ci	if (rmap_end != irec->br_startoff + irec->br_blockcount)
20362306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
20462306a36Sopenharmony_ci				irec->br_startoff);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Check the owner */
20762306a36Sopenharmony_ci	if (rmap.rm_owner != owner)
20862306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
20962306a36Sopenharmony_ci				irec->br_startoff);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/*
21262306a36Sopenharmony_ci	 * Check for discrepancies between the unwritten flag in the irec and
21362306a36Sopenharmony_ci	 * the rmap.  Note that the (in-memory) CoW fork distinguishes between
21462306a36Sopenharmony_ci	 * unwritten and written extents, but we don't track that in the rmap
21562306a36Sopenharmony_ci	 * records because the blocks are owned (on-disk) by the refcountbt,
21662306a36Sopenharmony_ci	 * which doesn't track unwritten state.
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci	if (!!(irec->br_state == XFS_EXT_UNWRITTEN) !=
21962306a36Sopenharmony_ci	    !!(rmap.rm_flags & XFS_RMAP_UNWRITTEN))
22062306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
22162306a36Sopenharmony_ci				irec->br_startoff);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (!!(info->whichfork == XFS_ATTR_FORK) !=
22462306a36Sopenharmony_ci	    !!(rmap.rm_flags & XFS_RMAP_ATTR_FORK))
22562306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
22662306a36Sopenharmony_ci				irec->br_startoff);
22762306a36Sopenharmony_ci	if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
22862306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
22962306a36Sopenharmony_ci				irec->br_startoff);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/* Make sure that we have rmapbt records for this COW fork extent. */
23362306a36Sopenharmony_ciSTATIC void
23462306a36Sopenharmony_cixchk_bmap_xref_rmap_cow(
23562306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
23662306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec,
23762306a36Sopenharmony_ci	xfs_agblock_t		agbno)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct xfs_rmap_irec	rmap;
24062306a36Sopenharmony_ci	unsigned long long	rmap_end;
24162306a36Sopenharmony_ci	uint64_t		owner = XFS_RMAP_OWN_COW;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm))
24462306a36Sopenharmony_ci		return;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	/* Find the rmap record for this irec. */
24762306a36Sopenharmony_ci	if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap))
24862306a36Sopenharmony_ci		return;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * CoW staging extents are owned by the refcount btree, so the rmap
25262306a36Sopenharmony_ci	 * can start before and end after the physical space allocated to this
25362306a36Sopenharmony_ci	 * mapping.  There are no offsets to check.
25462306a36Sopenharmony_ci	 */
25562306a36Sopenharmony_ci	if (rmap.rm_startblock > agbno)
25662306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
25762306a36Sopenharmony_ci				irec->br_startoff);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount;
26062306a36Sopenharmony_ci	if (rmap_end < agbno + irec->br_blockcount)
26162306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
26262306a36Sopenharmony_ci				irec->br_startoff);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Check the owner */
26562306a36Sopenharmony_ci	if (rmap.rm_owner != owner)
26662306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
26762306a36Sopenharmony_ci				irec->br_startoff);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/*
27062306a36Sopenharmony_ci	 * No flags allowed.  Note that the (in-memory) CoW fork distinguishes
27162306a36Sopenharmony_ci	 * between unwritten and written extents, but we don't track that in
27262306a36Sopenharmony_ci	 * the rmap records because the blocks are owned (on-disk) by the
27362306a36Sopenharmony_ci	 * refcountbt, which doesn't track unwritten state.
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	if (rmap.rm_flags & XFS_RMAP_ATTR_FORK)
27662306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
27762306a36Sopenharmony_ci				irec->br_startoff);
27862306a36Sopenharmony_ci	if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK)
27962306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
28062306a36Sopenharmony_ci				irec->br_startoff);
28162306a36Sopenharmony_ci	if (rmap.rm_flags & XFS_RMAP_UNWRITTEN)
28262306a36Sopenharmony_ci		xchk_fblock_xref_set_corrupt(info->sc, info->whichfork,
28362306a36Sopenharmony_ci				irec->br_startoff);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/* Cross-reference a single rtdev extent record. */
28762306a36Sopenharmony_ciSTATIC void
28862306a36Sopenharmony_cixchk_bmap_rt_iextent_xref(
28962306a36Sopenharmony_ci	struct xfs_inode	*ip,
29062306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
29162306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	xchk_xref_is_used_rt_space(info->sc, irec->br_startblock,
29462306a36Sopenharmony_ci			irec->br_blockcount);
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* Cross-reference a single datadev extent record. */
29862306a36Sopenharmony_ciSTATIC void
29962306a36Sopenharmony_cixchk_bmap_iextent_xref(
30062306a36Sopenharmony_ci	struct xfs_inode	*ip,
30162306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
30262306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct xfs_owner_info	oinfo;
30562306a36Sopenharmony_ci	struct xfs_mount	*mp = info->sc->mp;
30662306a36Sopenharmony_ci	xfs_agnumber_t		agno;
30762306a36Sopenharmony_ci	xfs_agblock_t		agbno;
30862306a36Sopenharmony_ci	xfs_extlen_t		len;
30962306a36Sopenharmony_ci	int			error;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock);
31262306a36Sopenharmony_ci	agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock);
31362306a36Sopenharmony_ci	len = irec->br_blockcount;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	error = xchk_ag_init_existing(info->sc, agno, &info->sc->sa);
31662306a36Sopenharmony_ci	if (!xchk_fblock_process_error(info->sc, info->whichfork,
31762306a36Sopenharmony_ci			irec->br_startoff, &error))
31862306a36Sopenharmony_ci		goto out_free;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	xchk_xref_is_used_space(info->sc, agbno, len);
32162306a36Sopenharmony_ci	xchk_xref_is_not_inode_chunk(info->sc, agbno, len);
32262306a36Sopenharmony_ci	switch (info->whichfork) {
32362306a36Sopenharmony_ci	case XFS_DATA_FORK:
32462306a36Sopenharmony_ci		xchk_bmap_xref_rmap(info, irec, agbno);
32562306a36Sopenharmony_ci		if (!xfs_is_reflink_inode(info->sc->ip)) {
32662306a36Sopenharmony_ci			xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino,
32762306a36Sopenharmony_ci					info->whichfork, irec->br_startoff);
32862306a36Sopenharmony_ci			xchk_xref_is_only_owned_by(info->sc, agbno,
32962306a36Sopenharmony_ci					irec->br_blockcount, &oinfo);
33062306a36Sopenharmony_ci			xchk_xref_is_not_shared(info->sc, agbno,
33162306a36Sopenharmony_ci					irec->br_blockcount);
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci		xchk_xref_is_not_cow_staging(info->sc, agbno,
33462306a36Sopenharmony_ci				irec->br_blockcount);
33562306a36Sopenharmony_ci		break;
33662306a36Sopenharmony_ci	case XFS_ATTR_FORK:
33762306a36Sopenharmony_ci		xchk_bmap_xref_rmap(info, irec, agbno);
33862306a36Sopenharmony_ci		xfs_rmap_ino_owner(&oinfo, info->sc->ip->i_ino,
33962306a36Sopenharmony_ci				info->whichfork, irec->br_startoff);
34062306a36Sopenharmony_ci		xchk_xref_is_only_owned_by(info->sc, agbno, irec->br_blockcount,
34162306a36Sopenharmony_ci				&oinfo);
34262306a36Sopenharmony_ci		xchk_xref_is_not_shared(info->sc, agbno,
34362306a36Sopenharmony_ci				irec->br_blockcount);
34462306a36Sopenharmony_ci		xchk_xref_is_not_cow_staging(info->sc, agbno,
34562306a36Sopenharmony_ci				irec->br_blockcount);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	case XFS_COW_FORK:
34862306a36Sopenharmony_ci		xchk_bmap_xref_rmap_cow(info, irec, agbno);
34962306a36Sopenharmony_ci		xchk_xref_is_only_owned_by(info->sc, agbno, irec->br_blockcount,
35062306a36Sopenharmony_ci				&XFS_RMAP_OINFO_COW);
35162306a36Sopenharmony_ci		xchk_xref_is_cow_staging(info->sc, agbno,
35262306a36Sopenharmony_ci				irec->br_blockcount);
35362306a36Sopenharmony_ci		xchk_xref_is_not_shared(info->sc, agbno,
35462306a36Sopenharmony_ci				irec->br_blockcount);
35562306a36Sopenharmony_ci		break;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ciout_free:
35962306a36Sopenharmony_ci	xchk_ag_free(info->sc, &info->sc->sa);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci * Directories and attr forks should never have blocks that can't be addressed
36462306a36Sopenharmony_ci * by a xfs_dablk_t.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_ciSTATIC void
36762306a36Sopenharmony_cixchk_bmap_dirattr_extent(
36862306a36Sopenharmony_ci	struct xfs_inode	*ip,
36962306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
37062306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
37362306a36Sopenharmony_ci	xfs_fileoff_t		off;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (!S_ISDIR(VFS_I(ip)->i_mode) && info->whichfork != XFS_ATTR_FORK)
37662306a36Sopenharmony_ci		return;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	if (!xfs_verify_dablk(mp, irec->br_startoff))
37962306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
38062306a36Sopenharmony_ci				irec->br_startoff);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	off = irec->br_startoff + irec->br_blockcount - 1;
38362306a36Sopenharmony_ci	if (!xfs_verify_dablk(mp, off))
38462306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork, off);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/* Scrub a single extent record. */
38862306a36Sopenharmony_ciSTATIC void
38962306a36Sopenharmony_cixchk_bmap_iextent(
39062306a36Sopenharmony_ci	struct xfs_inode	*ip,
39162306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
39262306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct xfs_mount	*mp = info->sc->mp;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * Check for out-of-order extents.  This record could have come
39862306a36Sopenharmony_ci	 * from the incore list, for which there is no ordering check.
39962306a36Sopenharmony_ci	 */
40062306a36Sopenharmony_ci	if (irec->br_startoff < info->prev_rec.br_startoff +
40162306a36Sopenharmony_ci				info->prev_rec.br_blockcount)
40262306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
40362306a36Sopenharmony_ci				irec->br_startoff);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
40662306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
40762306a36Sopenharmony_ci				irec->br_startoff);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	xchk_bmap_dirattr_extent(ip, info, irec);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* Make sure the extent points to a valid place. */
41262306a36Sopenharmony_ci	if (info->is_rt &&
41362306a36Sopenharmony_ci	    !xfs_verify_rtext(mp, irec->br_startblock, irec->br_blockcount))
41462306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
41562306a36Sopenharmony_ci				irec->br_startoff);
41662306a36Sopenharmony_ci	if (!info->is_rt &&
41762306a36Sopenharmony_ci	    !xfs_verify_fsbext(mp, irec->br_startblock, irec->br_blockcount))
41862306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
41962306a36Sopenharmony_ci				irec->br_startoff);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* We don't allow unwritten extents on attr forks. */
42262306a36Sopenharmony_ci	if (irec->br_state == XFS_EXT_UNWRITTEN &&
42362306a36Sopenharmony_ci	    info->whichfork == XFS_ATTR_FORK)
42462306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
42562306a36Sopenharmony_ci				irec->br_startoff);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
42862306a36Sopenharmony_ci		return;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	if (info->is_rt)
43162306a36Sopenharmony_ci		xchk_bmap_rt_iextent_xref(ip, info, irec);
43262306a36Sopenharmony_ci	else
43362306a36Sopenharmony_ci		xchk_bmap_iextent_xref(ip, info, irec);
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/* Scrub a bmbt record. */
43762306a36Sopenharmony_ciSTATIC int
43862306a36Sopenharmony_cixchk_bmapbt_rec(
43962306a36Sopenharmony_ci	struct xchk_btree	*bs,
44062306a36Sopenharmony_ci	const union xfs_btree_rec *rec)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct xfs_bmbt_irec	irec;
44362306a36Sopenharmony_ci	struct xfs_bmbt_irec	iext_irec;
44462306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
44562306a36Sopenharmony_ci	struct xchk_bmap_info	*info = bs->private;
44662306a36Sopenharmony_ci	struct xfs_inode	*ip = bs->cur->bc_ino.ip;
44762306a36Sopenharmony_ci	struct xfs_buf		*bp = NULL;
44862306a36Sopenharmony_ci	struct xfs_btree_block	*block;
44962306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, info->whichfork);
45062306a36Sopenharmony_ci	uint64_t		owner;
45162306a36Sopenharmony_ci	int			i;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/*
45462306a36Sopenharmony_ci	 * Check the owners of the btree blocks up to the level below
45562306a36Sopenharmony_ci	 * the root since the verifiers don't do that.
45662306a36Sopenharmony_ci	 */
45762306a36Sopenharmony_ci	if (xfs_has_crc(bs->cur->bc_mp) &&
45862306a36Sopenharmony_ci	    bs->cur->bc_levels[0].ptr == 1) {
45962306a36Sopenharmony_ci		for (i = 0; i < bs->cur->bc_nlevels - 1; i++) {
46062306a36Sopenharmony_ci			block = xfs_btree_get_block(bs->cur, i, &bp);
46162306a36Sopenharmony_ci			owner = be64_to_cpu(block->bb_u.l.bb_owner);
46262306a36Sopenharmony_ci			if (owner != ip->i_ino)
46362306a36Sopenharmony_ci				xchk_fblock_set_corrupt(bs->sc,
46462306a36Sopenharmony_ci						info->whichfork, 0);
46562306a36Sopenharmony_ci		}
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/*
46962306a36Sopenharmony_ci	 * Check that the incore extent tree contains an extent that matches
47062306a36Sopenharmony_ci	 * this one exactly.  We validate those cached bmaps later, so we don't
47162306a36Sopenharmony_ci	 * need to check them here.  If the incore extent tree was just loaded
47262306a36Sopenharmony_ci	 * from disk by the scrubber, we assume that its contents match what's
47362306a36Sopenharmony_ci	 * on disk (we still hold the ILOCK) and skip the equivalence check.
47462306a36Sopenharmony_ci	 */
47562306a36Sopenharmony_ci	if (!info->was_loaded)
47662306a36Sopenharmony_ci		return 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	xfs_bmbt_disk_get_all(&rec->bmbt, &irec);
47962306a36Sopenharmony_ci	if (xfs_bmap_validate_extent(ip, info->whichfork, &irec) != NULL) {
48062306a36Sopenharmony_ci		xchk_fblock_set_corrupt(bs->sc, info->whichfork,
48162306a36Sopenharmony_ci				irec.br_startoff);
48262306a36Sopenharmony_ci		return 0;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (!xfs_iext_lookup_extent(ip, ifp, irec.br_startoff, &icur,
48662306a36Sopenharmony_ci				&iext_irec) ||
48762306a36Sopenharmony_ci	    irec.br_startoff != iext_irec.br_startoff ||
48862306a36Sopenharmony_ci	    irec.br_startblock != iext_irec.br_startblock ||
48962306a36Sopenharmony_ci	    irec.br_blockcount != iext_irec.br_blockcount ||
49062306a36Sopenharmony_ci	    irec.br_state != iext_irec.br_state)
49162306a36Sopenharmony_ci		xchk_fblock_set_corrupt(bs->sc, info->whichfork,
49262306a36Sopenharmony_ci				irec.br_startoff);
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci/* Scan the btree records. */
49762306a36Sopenharmony_ciSTATIC int
49862306a36Sopenharmony_cixchk_bmap_btree(
49962306a36Sopenharmony_ci	struct xfs_scrub	*sc,
50062306a36Sopenharmony_ci	int			whichfork,
50162306a36Sopenharmony_ci	struct xchk_bmap_info	*info)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct xfs_owner_info	oinfo;
50462306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(sc->ip, whichfork);
50562306a36Sopenharmony_ci	struct xfs_mount	*mp = sc->mp;
50662306a36Sopenharmony_ci	struct xfs_inode	*ip = sc->ip;
50762306a36Sopenharmony_ci	struct xfs_btree_cur	*cur;
50862306a36Sopenharmony_ci	int			error;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* Load the incore bmap cache if it's not loaded. */
51162306a36Sopenharmony_ci	info->was_loaded = !xfs_need_iread_extents(ifp);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	error = xfs_iread_extents(sc->tp, ip, whichfork);
51462306a36Sopenharmony_ci	if (!xchk_fblock_process_error(sc, whichfork, 0, &error))
51562306a36Sopenharmony_ci		goto out;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	/* Check the btree structure. */
51862306a36Sopenharmony_ci	cur = xfs_bmbt_init_cursor(mp, sc->tp, ip, whichfork);
51962306a36Sopenharmony_ci	xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork);
52062306a36Sopenharmony_ci	error = xchk_btree(sc, cur, xchk_bmapbt_rec, &oinfo, info);
52162306a36Sopenharmony_ci	xfs_btree_del_cursor(cur, error);
52262306a36Sopenharmony_ciout:
52362306a36Sopenharmony_ci	return error;
52462306a36Sopenharmony_ci}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_cistruct xchk_bmap_check_rmap_info {
52762306a36Sopenharmony_ci	struct xfs_scrub	*sc;
52862306a36Sopenharmony_ci	int			whichfork;
52962306a36Sopenharmony_ci	struct xfs_iext_cursor	icur;
53062306a36Sopenharmony_ci};
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci/* Can we find bmaps that fit this rmap? */
53362306a36Sopenharmony_ciSTATIC int
53462306a36Sopenharmony_cixchk_bmap_check_rmap(
53562306a36Sopenharmony_ci	struct xfs_btree_cur		*cur,
53662306a36Sopenharmony_ci	const struct xfs_rmap_irec	*rec,
53762306a36Sopenharmony_ci	void				*priv)
53862306a36Sopenharmony_ci{
53962306a36Sopenharmony_ci	struct xfs_bmbt_irec		irec;
54062306a36Sopenharmony_ci	struct xfs_rmap_irec		check_rec;
54162306a36Sopenharmony_ci	struct xchk_bmap_check_rmap_info	*sbcri = priv;
54262306a36Sopenharmony_ci	struct xfs_ifork		*ifp;
54362306a36Sopenharmony_ci	struct xfs_scrub		*sc = sbcri->sc;
54462306a36Sopenharmony_ci	bool				have_map;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* Is this even the right fork? */
54762306a36Sopenharmony_ci	if (rec->rm_owner != sc->ip->i_ino)
54862306a36Sopenharmony_ci		return 0;
54962306a36Sopenharmony_ci	if ((sbcri->whichfork == XFS_ATTR_FORK) ^
55062306a36Sopenharmony_ci	    !!(rec->rm_flags & XFS_RMAP_ATTR_FORK))
55162306a36Sopenharmony_ci		return 0;
55262306a36Sopenharmony_ci	if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK)
55362306a36Sopenharmony_ci		return 0;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	/* Now look up the bmbt record. */
55662306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(sc->ip, sbcri->whichfork);
55762306a36Sopenharmony_ci	if (!ifp) {
55862306a36Sopenharmony_ci		xchk_fblock_set_corrupt(sc, sbcri->whichfork,
55962306a36Sopenharmony_ci				rec->rm_offset);
56062306a36Sopenharmony_ci		goto out;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset,
56362306a36Sopenharmony_ci			&sbcri->icur, &irec);
56462306a36Sopenharmony_ci	if (!have_map)
56562306a36Sopenharmony_ci		xchk_fblock_set_corrupt(sc, sbcri->whichfork,
56662306a36Sopenharmony_ci				rec->rm_offset);
56762306a36Sopenharmony_ci	/*
56862306a36Sopenharmony_ci	 * bmap extent record lengths are constrained to 2^21 blocks in length
56962306a36Sopenharmony_ci	 * because of space constraints in the on-disk metadata structure.
57062306a36Sopenharmony_ci	 * However, rmap extent record lengths are constrained only by AG
57162306a36Sopenharmony_ci	 * length, so we have to loop through the bmbt to make sure that the
57262306a36Sopenharmony_ci	 * entire rmap is covered by bmbt records.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	check_rec = *rec;
57562306a36Sopenharmony_ci	while (have_map) {
57662306a36Sopenharmony_ci		if (irec.br_startoff != check_rec.rm_offset)
57762306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, sbcri->whichfork,
57862306a36Sopenharmony_ci					check_rec.rm_offset);
57962306a36Sopenharmony_ci		if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp,
58062306a36Sopenharmony_ci				cur->bc_ag.pag->pag_agno,
58162306a36Sopenharmony_ci				check_rec.rm_startblock))
58262306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, sbcri->whichfork,
58362306a36Sopenharmony_ci					check_rec.rm_offset);
58462306a36Sopenharmony_ci		if (irec.br_blockcount > check_rec.rm_blockcount)
58562306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, sbcri->whichfork,
58662306a36Sopenharmony_ci					check_rec.rm_offset);
58762306a36Sopenharmony_ci		if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
58862306a36Sopenharmony_ci			break;
58962306a36Sopenharmony_ci		check_rec.rm_startblock += irec.br_blockcount;
59062306a36Sopenharmony_ci		check_rec.rm_offset += irec.br_blockcount;
59162306a36Sopenharmony_ci		check_rec.rm_blockcount -= irec.br_blockcount;
59262306a36Sopenharmony_ci		if (check_rec.rm_blockcount == 0)
59362306a36Sopenharmony_ci			break;
59462306a36Sopenharmony_ci		have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec);
59562306a36Sopenharmony_ci		if (!have_map)
59662306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, sbcri->whichfork,
59762306a36Sopenharmony_ci					check_rec.rm_offset);
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ciout:
60162306a36Sopenharmony_ci	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
60262306a36Sopenharmony_ci		return -ECANCELED;
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci/* Make sure each rmap has a corresponding bmbt entry. */
60762306a36Sopenharmony_ciSTATIC int
60862306a36Sopenharmony_cixchk_bmap_check_ag_rmaps(
60962306a36Sopenharmony_ci	struct xfs_scrub		*sc,
61062306a36Sopenharmony_ci	int				whichfork,
61162306a36Sopenharmony_ci	struct xfs_perag		*pag)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct xchk_bmap_check_rmap_info	sbcri;
61462306a36Sopenharmony_ci	struct xfs_btree_cur		*cur;
61562306a36Sopenharmony_ci	struct xfs_buf			*agf;
61662306a36Sopenharmony_ci	int				error;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	error = xfs_alloc_read_agf(pag, sc->tp, 0, &agf);
61962306a36Sopenharmony_ci	if (error)
62062306a36Sopenharmony_ci		return error;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, pag);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	sbcri.sc = sc;
62562306a36Sopenharmony_ci	sbcri.whichfork = whichfork;
62662306a36Sopenharmony_ci	error = xfs_rmap_query_all(cur, xchk_bmap_check_rmap, &sbcri);
62762306a36Sopenharmony_ci	if (error == -ECANCELED)
62862306a36Sopenharmony_ci		error = 0;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	xfs_btree_del_cursor(cur, error);
63162306a36Sopenharmony_ci	xfs_trans_brelse(sc->tp, agf);
63262306a36Sopenharmony_ci	return error;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci/*
63662306a36Sopenharmony_ci * Decide if we want to walk every rmap btree in the fs to make sure that each
63762306a36Sopenharmony_ci * rmap for this file fork has corresponding bmbt entries.
63862306a36Sopenharmony_ci */
63962306a36Sopenharmony_cistatic bool
64062306a36Sopenharmony_cixchk_bmap_want_check_rmaps(
64162306a36Sopenharmony_ci	struct xchk_bmap_info	*info)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct xfs_scrub	*sc = info->sc;
64462306a36Sopenharmony_ci	struct xfs_ifork	*ifp;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!xfs_has_rmapbt(sc->mp))
64762306a36Sopenharmony_ci		return false;
64862306a36Sopenharmony_ci	if (info->whichfork == XFS_COW_FORK)
64962306a36Sopenharmony_ci		return false;
65062306a36Sopenharmony_ci	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
65162306a36Sopenharmony_ci		return false;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* Don't support realtime rmap checks yet. */
65462306a36Sopenharmony_ci	if (info->is_rt)
65562306a36Sopenharmony_ci		return false;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	/*
65862306a36Sopenharmony_ci	 * The inode repair code zaps broken inode forks by resetting them back
65962306a36Sopenharmony_ci	 * to EXTENTS format and zero extent records.  If we encounter a fork
66062306a36Sopenharmony_ci	 * in this state along with evidence that the fork isn't supposed to be
66162306a36Sopenharmony_ci	 * empty, we need to scan the reverse mappings to decide if we're going
66262306a36Sopenharmony_ci	 * to rebuild the fork.  Data forks with nonzero file size are scanned.
66362306a36Sopenharmony_ci	 * xattr forks are never empty of content, so they are always scanned.
66462306a36Sopenharmony_ci	 */
66562306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(sc->ip, info->whichfork);
66662306a36Sopenharmony_ci	if (ifp->if_format == XFS_DINODE_FMT_EXTENTS && ifp->if_nextents == 0) {
66762306a36Sopenharmony_ci		if (info->whichfork == XFS_DATA_FORK &&
66862306a36Sopenharmony_ci		    i_size_read(VFS_I(sc->ip)) == 0)
66962306a36Sopenharmony_ci			return false;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		return true;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return false;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci/* Make sure each rmap has a corresponding bmbt entry. */
67862306a36Sopenharmony_ciSTATIC int
67962306a36Sopenharmony_cixchk_bmap_check_rmaps(
68062306a36Sopenharmony_ci	struct xfs_scrub	*sc,
68162306a36Sopenharmony_ci	int			whichfork)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	struct xfs_perag	*pag;
68462306a36Sopenharmony_ci	xfs_agnumber_t		agno;
68562306a36Sopenharmony_ci	int			error;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	for_each_perag(sc->mp, agno, pag) {
68862306a36Sopenharmony_ci		error = xchk_bmap_check_ag_rmaps(sc, whichfork, pag);
68962306a36Sopenharmony_ci		if (error ||
69062306a36Sopenharmony_ci		    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) {
69162306a36Sopenharmony_ci			xfs_perag_rele(pag);
69262306a36Sopenharmony_ci			return error;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return 0;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci/* Scrub a delalloc reservation from the incore extent map tree. */
70062306a36Sopenharmony_ciSTATIC void
70162306a36Sopenharmony_cixchk_bmap_iextent_delalloc(
70262306a36Sopenharmony_ci	struct xfs_inode	*ip,
70362306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
70462306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct xfs_mount	*mp = info->sc->mp;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	/*
70962306a36Sopenharmony_ci	 * Check for out-of-order extents.  This record could have come
71062306a36Sopenharmony_ci	 * from the incore list, for which there is no ordering check.
71162306a36Sopenharmony_ci	 */
71262306a36Sopenharmony_ci	if (irec->br_startoff < info->prev_rec.br_startoff +
71362306a36Sopenharmony_ci				info->prev_rec.br_blockcount)
71462306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
71562306a36Sopenharmony_ci				irec->br_startoff);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	if (!xfs_verify_fileext(mp, irec->br_startoff, irec->br_blockcount))
71862306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
71962306a36Sopenharmony_ci				irec->br_startoff);
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	/* Make sure the extent points to a valid place. */
72262306a36Sopenharmony_ci	if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN)
72362306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
72462306a36Sopenharmony_ci				irec->br_startoff);
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci/* Decide if this individual fork mapping is ok. */
72862306a36Sopenharmony_cistatic bool
72962306a36Sopenharmony_cixchk_bmap_iext_mapping(
73062306a36Sopenharmony_ci	struct xchk_bmap_info		*info,
73162306a36Sopenharmony_ci	const struct xfs_bmbt_irec	*irec)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	/* There should never be a "hole" extent in either extent list. */
73462306a36Sopenharmony_ci	if (irec->br_startblock == HOLESTARTBLOCK)
73562306a36Sopenharmony_ci		return false;
73662306a36Sopenharmony_ci	if (irec->br_blockcount > XFS_MAX_BMBT_EXTLEN)
73762306a36Sopenharmony_ci		return false;
73862306a36Sopenharmony_ci	return true;
73962306a36Sopenharmony_ci}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci/* Are these two mappings contiguous with each other? */
74262306a36Sopenharmony_cistatic inline bool
74362306a36Sopenharmony_cixchk_are_bmaps_contiguous(
74462306a36Sopenharmony_ci	const struct xfs_bmbt_irec	*b1,
74562306a36Sopenharmony_ci	const struct xfs_bmbt_irec	*b2)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	/* Don't try to combine unallocated mappings. */
74862306a36Sopenharmony_ci	if (!xfs_bmap_is_real_extent(b1))
74962306a36Sopenharmony_ci		return false;
75062306a36Sopenharmony_ci	if (!xfs_bmap_is_real_extent(b2))
75162306a36Sopenharmony_ci		return false;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	/* Does b2 come right after b1 in the logical and physical range? */
75462306a36Sopenharmony_ci	if (b1->br_startoff + b1->br_blockcount != b2->br_startoff)
75562306a36Sopenharmony_ci		return false;
75662306a36Sopenharmony_ci	if (b1->br_startblock + b1->br_blockcount != b2->br_startblock)
75762306a36Sopenharmony_ci		return false;
75862306a36Sopenharmony_ci	if (b1->br_state != b2->br_state)
75962306a36Sopenharmony_ci		return false;
76062306a36Sopenharmony_ci	return true;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci/*
76462306a36Sopenharmony_ci * Walk the incore extent records, accumulating consecutive contiguous records
76562306a36Sopenharmony_ci * into a single incore mapping.  Returns true if @irec has been set to a
76662306a36Sopenharmony_ci * mapping or false if there are no more mappings.  Caller must ensure that
76762306a36Sopenharmony_ci * @info.icur is zeroed before the first call.
76862306a36Sopenharmony_ci */
76962306a36Sopenharmony_cistatic bool
77062306a36Sopenharmony_cixchk_bmap_iext_iter(
77162306a36Sopenharmony_ci	struct xchk_bmap_info	*info,
77262306a36Sopenharmony_ci	struct xfs_bmbt_irec	*irec)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct xfs_bmbt_irec	got;
77562306a36Sopenharmony_ci	struct xfs_ifork	*ifp;
77662306a36Sopenharmony_ci	unsigned int		nr = 0;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	ifp = xfs_ifork_ptr(info->sc->ip, info->whichfork);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/* Advance to the next iextent record and check the mapping. */
78162306a36Sopenharmony_ci	xfs_iext_next(ifp, &info->icur);
78262306a36Sopenharmony_ci	if (!xfs_iext_get_extent(ifp, &info->icur, irec))
78362306a36Sopenharmony_ci		return false;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (!xchk_bmap_iext_mapping(info, irec)) {
78662306a36Sopenharmony_ci		xchk_fblock_set_corrupt(info->sc, info->whichfork,
78762306a36Sopenharmony_ci				irec->br_startoff);
78862306a36Sopenharmony_ci		return false;
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci	nr++;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/*
79362306a36Sopenharmony_ci	 * Iterate subsequent iextent records and merge them with the one
79462306a36Sopenharmony_ci	 * that we just read, if possible.
79562306a36Sopenharmony_ci	 */
79662306a36Sopenharmony_ci	while (xfs_iext_peek_next_extent(ifp, &info->icur, &got)) {
79762306a36Sopenharmony_ci		if (!xchk_are_bmaps_contiguous(irec, &got))
79862306a36Sopenharmony_ci			break;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci		if (!xchk_bmap_iext_mapping(info, &got)) {
80162306a36Sopenharmony_ci			xchk_fblock_set_corrupt(info->sc, info->whichfork,
80262306a36Sopenharmony_ci					got.br_startoff);
80362306a36Sopenharmony_ci			return false;
80462306a36Sopenharmony_ci		}
80562306a36Sopenharmony_ci		nr++;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci		irec->br_blockcount += got.br_blockcount;
80862306a36Sopenharmony_ci		xfs_iext_next(ifp, &info->icur);
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/*
81262306a36Sopenharmony_ci	 * If the merged mapping could be expressed with fewer bmbt records
81362306a36Sopenharmony_ci	 * than we actually found, notify the user that this fork could be
81462306a36Sopenharmony_ci	 * optimized.  CoW forks only exist in memory so we ignore them.
81562306a36Sopenharmony_ci	 */
81662306a36Sopenharmony_ci	if (nr > 1 && info->whichfork != XFS_COW_FORK &&
81762306a36Sopenharmony_ci	    howmany_64(irec->br_blockcount, XFS_MAX_BMBT_EXTLEN) < nr)
81862306a36Sopenharmony_ci		xchk_ino_set_preen(info->sc, info->sc->ip->i_ino);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	return true;
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci/*
82462306a36Sopenharmony_ci * Scrub an inode fork's block mappings.
82562306a36Sopenharmony_ci *
82662306a36Sopenharmony_ci * First we scan every record in every btree block, if applicable.
82762306a36Sopenharmony_ci * Then we unconditionally scan the incore extent cache.
82862306a36Sopenharmony_ci */
82962306a36Sopenharmony_ciSTATIC int
83062306a36Sopenharmony_cixchk_bmap(
83162306a36Sopenharmony_ci	struct xfs_scrub	*sc,
83262306a36Sopenharmony_ci	int			whichfork)
83362306a36Sopenharmony_ci{
83462306a36Sopenharmony_ci	struct xfs_bmbt_irec	irec;
83562306a36Sopenharmony_ci	struct xchk_bmap_info	info = { NULL };
83662306a36Sopenharmony_ci	struct xfs_mount	*mp = sc->mp;
83762306a36Sopenharmony_ci	struct xfs_inode	*ip = sc->ip;
83862306a36Sopenharmony_ci	struct xfs_ifork	*ifp = xfs_ifork_ptr(ip, whichfork);
83962306a36Sopenharmony_ci	xfs_fileoff_t		endoff;
84062306a36Sopenharmony_ci	int			error = 0;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	/* Non-existent forks can be ignored. */
84362306a36Sopenharmony_ci	if (!ifp)
84462306a36Sopenharmony_ci		return -ENOENT;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip);
84762306a36Sopenharmony_ci	info.whichfork = whichfork;
84862306a36Sopenharmony_ci	info.is_shared = whichfork == XFS_DATA_FORK && xfs_is_reflink_inode(ip);
84962306a36Sopenharmony_ci	info.sc = sc;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	switch (whichfork) {
85262306a36Sopenharmony_ci	case XFS_COW_FORK:
85362306a36Sopenharmony_ci		/* No CoW forks on non-reflink filesystems. */
85462306a36Sopenharmony_ci		if (!xfs_has_reflink(mp)) {
85562306a36Sopenharmony_ci			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
85662306a36Sopenharmony_ci			return 0;
85762306a36Sopenharmony_ci		}
85862306a36Sopenharmony_ci		break;
85962306a36Sopenharmony_ci	case XFS_ATTR_FORK:
86062306a36Sopenharmony_ci		if (!xfs_has_attr(mp) && !xfs_has_attr2(mp))
86162306a36Sopenharmony_ci			xchk_ino_set_corrupt(sc, sc->ip->i_ino);
86262306a36Sopenharmony_ci		break;
86362306a36Sopenharmony_ci	default:
86462306a36Sopenharmony_ci		ASSERT(whichfork == XFS_DATA_FORK);
86562306a36Sopenharmony_ci		break;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	/* Check the fork values */
86962306a36Sopenharmony_ci	switch (ifp->if_format) {
87062306a36Sopenharmony_ci	case XFS_DINODE_FMT_UUID:
87162306a36Sopenharmony_ci	case XFS_DINODE_FMT_DEV:
87262306a36Sopenharmony_ci	case XFS_DINODE_FMT_LOCAL:
87362306a36Sopenharmony_ci		/* No mappings to check. */
87462306a36Sopenharmony_ci		if (whichfork == XFS_COW_FORK)
87562306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, whichfork, 0);
87662306a36Sopenharmony_ci		return 0;
87762306a36Sopenharmony_ci	case XFS_DINODE_FMT_EXTENTS:
87862306a36Sopenharmony_ci		break;
87962306a36Sopenharmony_ci	case XFS_DINODE_FMT_BTREE:
88062306a36Sopenharmony_ci		if (whichfork == XFS_COW_FORK) {
88162306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, whichfork, 0);
88262306a36Sopenharmony_ci			return 0;
88362306a36Sopenharmony_ci		}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		error = xchk_bmap_btree(sc, whichfork, &info);
88662306a36Sopenharmony_ci		if (error)
88762306a36Sopenharmony_ci			return error;
88862306a36Sopenharmony_ci		break;
88962306a36Sopenharmony_ci	default:
89062306a36Sopenharmony_ci		xchk_fblock_set_corrupt(sc, whichfork, 0);
89162306a36Sopenharmony_ci		return 0;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
89562306a36Sopenharmony_ci		return 0;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* Find the offset of the last extent in the mapping. */
89862306a36Sopenharmony_ci	error = xfs_bmap_last_offset(ip, &endoff, whichfork);
89962306a36Sopenharmony_ci	if (!xchk_fblock_process_error(sc, whichfork, 0, &error))
90062306a36Sopenharmony_ci		return error;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	/*
90362306a36Sopenharmony_ci	 * Scrub extent records.  We use a special iterator function here that
90462306a36Sopenharmony_ci	 * combines adjacent mappings if they are logically and physically
90562306a36Sopenharmony_ci	 * contiguous.   For large allocations that require multiple bmbt
90662306a36Sopenharmony_ci	 * records, this reduces the number of cross-referencing calls, which
90762306a36Sopenharmony_ci	 * reduces runtime.  Cross referencing with the rmap is simpler because
90862306a36Sopenharmony_ci	 * the rmap must match the combined mapping exactly.
90962306a36Sopenharmony_ci	 */
91062306a36Sopenharmony_ci	while (xchk_bmap_iext_iter(&info, &irec)) {
91162306a36Sopenharmony_ci		if (xchk_should_terminate(sc, &error) ||
91262306a36Sopenharmony_ci		    (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
91362306a36Sopenharmony_ci			return 0;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci		if (irec.br_startoff >= endoff) {
91662306a36Sopenharmony_ci			xchk_fblock_set_corrupt(sc, whichfork,
91762306a36Sopenharmony_ci					irec.br_startoff);
91862306a36Sopenharmony_ci			return 0;
91962306a36Sopenharmony_ci		}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci		if (isnullstartblock(irec.br_startblock))
92262306a36Sopenharmony_ci			xchk_bmap_iextent_delalloc(ip, &info, &irec);
92362306a36Sopenharmony_ci		else
92462306a36Sopenharmony_ci			xchk_bmap_iextent(ip, &info, &irec);
92562306a36Sopenharmony_ci		memcpy(&info.prev_rec, &irec, sizeof(struct xfs_bmbt_irec));
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	if (xchk_bmap_want_check_rmaps(&info)) {
92962306a36Sopenharmony_ci		error = xchk_bmap_check_rmaps(sc, whichfork);
93062306a36Sopenharmony_ci		if (!xchk_fblock_xref_process_error(sc, whichfork, 0, &error))
93162306a36Sopenharmony_ci			return error;
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	return 0;
93562306a36Sopenharmony_ci}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci/* Scrub an inode's data fork. */
93862306a36Sopenharmony_ciint
93962306a36Sopenharmony_cixchk_bmap_data(
94062306a36Sopenharmony_ci	struct xfs_scrub	*sc)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	return xchk_bmap(sc, XFS_DATA_FORK);
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci/* Scrub an inode's attr fork. */
94662306a36Sopenharmony_ciint
94762306a36Sopenharmony_cixchk_bmap_attr(
94862306a36Sopenharmony_ci	struct xfs_scrub	*sc)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	return xchk_bmap(sc, XFS_ATTR_FORK);
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci/* Scrub an inode's CoW fork. */
95462306a36Sopenharmony_ciint
95562306a36Sopenharmony_cixchk_bmap_cow(
95662306a36Sopenharmony_ci	struct xfs_scrub	*sc)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	return xchk_bmap(sc, XFS_COW_FORK);
95962306a36Sopenharmony_ci}
960