18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Oracle.  All Rights Reserved.
48c2ecf20Sopenharmony_ci * Author: Darrick J. Wong <darrick.wong@oracle.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci#include "xfs.h"
78c2ecf20Sopenharmony_ci#include "xfs_fs.h"
88c2ecf20Sopenharmony_ci#include "xfs_shared.h"
98c2ecf20Sopenharmony_ci#include "xfs_format.h"
108c2ecf20Sopenharmony_ci#include "xfs_trans_resv.h"
118c2ecf20Sopenharmony_ci#include "xfs_mount.h"
128c2ecf20Sopenharmony_ci#include "xfs_btree.h"
138c2ecf20Sopenharmony_ci#include "xfs_rmap.h"
148c2ecf20Sopenharmony_ci#include "xfs_refcount.h"
158c2ecf20Sopenharmony_ci#include "scrub/scrub.h"
168c2ecf20Sopenharmony_ci#include "scrub/common.h"
178c2ecf20Sopenharmony_ci#include "scrub/btree.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * Set us up to scrub reverse mapping btrees.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ciint
238c2ecf20Sopenharmony_cixchk_setup_ag_rmapbt(
248c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
258c2ecf20Sopenharmony_ci	struct xfs_inode	*ip)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	return xchk_setup_ag_btree(sc, ip, false);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/* Reverse-mapping scrubber. */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci/* Cross-reference a rmap against the refcount btree. */
338c2ecf20Sopenharmony_ciSTATIC void
348c2ecf20Sopenharmony_cixchk_rmapbt_xref_refc(
358c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
368c2ecf20Sopenharmony_ci	struct xfs_rmap_irec	*irec)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	xfs_agblock_t		fbno;
398c2ecf20Sopenharmony_ci	xfs_extlen_t		flen;
408c2ecf20Sopenharmony_ci	bool			non_inode;
418c2ecf20Sopenharmony_ci	bool			is_bmbt;
428c2ecf20Sopenharmony_ci	bool			is_attr;
438c2ecf20Sopenharmony_ci	bool			is_unwritten;
448c2ecf20Sopenharmony_ci	int			error;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!sc->sa.refc_cur || xchk_skip_xref(sc->sm))
478c2ecf20Sopenharmony_ci		return;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	non_inode = XFS_RMAP_NON_INODE_OWNER(irec->rm_owner);
508c2ecf20Sopenharmony_ci	is_bmbt = irec->rm_flags & XFS_RMAP_BMBT_BLOCK;
518c2ecf20Sopenharmony_ci	is_attr = irec->rm_flags & XFS_RMAP_ATTR_FORK;
528c2ecf20Sopenharmony_ci	is_unwritten = irec->rm_flags & XFS_RMAP_UNWRITTEN;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	/* If this is shared, must be a data fork extent. */
558c2ecf20Sopenharmony_ci	error = xfs_refcount_find_shared(sc->sa.refc_cur, irec->rm_startblock,
568c2ecf20Sopenharmony_ci			irec->rm_blockcount, &fbno, &flen, false);
578c2ecf20Sopenharmony_ci	if (!xchk_should_check_xref(sc, &error, &sc->sa.refc_cur))
588c2ecf20Sopenharmony_ci		return;
598c2ecf20Sopenharmony_ci	if (flen != 0 && (non_inode || is_attr || is_bmbt || is_unwritten))
608c2ecf20Sopenharmony_ci		xchk_btree_xref_set_corrupt(sc, sc->sa.refc_cur, 0);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* Cross-reference with the other btrees. */
648c2ecf20Sopenharmony_ciSTATIC void
658c2ecf20Sopenharmony_cixchk_rmapbt_xref(
668c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
678c2ecf20Sopenharmony_ci	struct xfs_rmap_irec	*irec)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	xfs_agblock_t		agbno = irec->rm_startblock;
708c2ecf20Sopenharmony_ci	xfs_extlen_t		len = irec->rm_blockcount;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
738c2ecf20Sopenharmony_ci		return;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	xchk_xref_is_used_space(sc, agbno, len);
768c2ecf20Sopenharmony_ci	if (irec->rm_owner == XFS_RMAP_OWN_INODES)
778c2ecf20Sopenharmony_ci		xchk_xref_is_inode_chunk(sc, agbno, len);
788c2ecf20Sopenharmony_ci	else
798c2ecf20Sopenharmony_ci		xchk_xref_is_not_inode_chunk(sc, agbno, len);
808c2ecf20Sopenharmony_ci	if (irec->rm_owner == XFS_RMAP_OWN_COW)
818c2ecf20Sopenharmony_ci		xchk_xref_is_cow_staging(sc, irec->rm_startblock,
828c2ecf20Sopenharmony_ci				irec->rm_blockcount);
838c2ecf20Sopenharmony_ci	else
848c2ecf20Sopenharmony_ci		xchk_rmapbt_xref_refc(sc, irec);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* Scrub an rmapbt record. */
888c2ecf20Sopenharmony_ciSTATIC int
898c2ecf20Sopenharmony_cixchk_rmapbt_rec(
908c2ecf20Sopenharmony_ci	struct xchk_btree	*bs,
918c2ecf20Sopenharmony_ci	union xfs_btree_rec	*rec)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct xfs_mount	*mp = bs->cur->bc_mp;
948c2ecf20Sopenharmony_ci	struct xfs_rmap_irec	irec;
958c2ecf20Sopenharmony_ci	xfs_agnumber_t		agno = bs->cur->bc_ag.agno;
968c2ecf20Sopenharmony_ci	bool			non_inode;
978c2ecf20Sopenharmony_ci	bool			is_unwritten;
988c2ecf20Sopenharmony_ci	bool			is_bmbt;
998c2ecf20Sopenharmony_ci	bool			is_attr;
1008c2ecf20Sopenharmony_ci	int			error;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	error = xfs_rmap_btrec_to_irec(rec, &irec);
1038c2ecf20Sopenharmony_ci	if (!xchk_btree_process_error(bs->sc, bs->cur, 0, &error))
1048c2ecf20Sopenharmony_ci		goto out;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* Check extent. */
1078c2ecf20Sopenharmony_ci	if (irec.rm_startblock + irec.rm_blockcount <= irec.rm_startblock)
1088c2ecf20Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	if (irec.rm_owner == XFS_RMAP_OWN_FS) {
1118c2ecf20Sopenharmony_ci		/*
1128c2ecf20Sopenharmony_ci		 * xfs_verify_agbno returns false for static fs metadata.
1138c2ecf20Sopenharmony_ci		 * Since that only exists at the start of the AG, validate
1148c2ecf20Sopenharmony_ci		 * that by hand.
1158c2ecf20Sopenharmony_ci		 */
1168c2ecf20Sopenharmony_ci		if (irec.rm_startblock != 0 ||
1178c2ecf20Sopenharmony_ci		    irec.rm_blockcount != XFS_AGFL_BLOCK(mp) + 1)
1188c2ecf20Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1198c2ecf20Sopenharmony_ci	} else {
1208c2ecf20Sopenharmony_ci		/*
1218c2ecf20Sopenharmony_ci		 * Otherwise we must point somewhere past the static metadata
1228c2ecf20Sopenharmony_ci		 * but before the end of the FS.  Run the regular check.
1238c2ecf20Sopenharmony_ci		 */
1248c2ecf20Sopenharmony_ci		if (!xfs_verify_agbno(mp, agno, irec.rm_startblock) ||
1258c2ecf20Sopenharmony_ci		    !xfs_verify_agbno(mp, agno, irec.rm_startblock +
1268c2ecf20Sopenharmony_ci				irec.rm_blockcount - 1))
1278c2ecf20Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Check flags. */
1318c2ecf20Sopenharmony_ci	non_inode = XFS_RMAP_NON_INODE_OWNER(irec.rm_owner);
1328c2ecf20Sopenharmony_ci	is_bmbt = irec.rm_flags & XFS_RMAP_BMBT_BLOCK;
1338c2ecf20Sopenharmony_ci	is_attr = irec.rm_flags & XFS_RMAP_ATTR_FORK;
1348c2ecf20Sopenharmony_ci	is_unwritten = irec.rm_flags & XFS_RMAP_UNWRITTEN;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (is_bmbt && irec.rm_offset != 0)
1378c2ecf20Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (non_inode && irec.rm_offset != 0)
1408c2ecf20Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (is_unwritten && (is_bmbt || non_inode || is_attr))
1438c2ecf20Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (non_inode && (is_bmbt || is_unwritten || is_attr))
1468c2ecf20Sopenharmony_ci		xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (!non_inode) {
1498c2ecf20Sopenharmony_ci		if (!xfs_verify_ino(mp, irec.rm_owner))
1508c2ecf20Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1518c2ecf20Sopenharmony_ci	} else {
1528c2ecf20Sopenharmony_ci		/* Non-inode owner within the magic values? */
1538c2ecf20Sopenharmony_ci		if (irec.rm_owner <= XFS_RMAP_OWN_MIN ||
1548c2ecf20Sopenharmony_ci		    irec.rm_owner > XFS_RMAP_OWN_FS)
1558c2ecf20Sopenharmony_ci			xchk_btree_set_corrupt(bs->sc, bs->cur, 0);
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	xchk_rmapbt_xref(bs->sc, &irec);
1598c2ecf20Sopenharmony_ciout:
1608c2ecf20Sopenharmony_ci	return error;
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci/* Scrub the rmap btree for some AG. */
1648c2ecf20Sopenharmony_ciint
1658c2ecf20Sopenharmony_cixchk_rmapbt(
1668c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return xchk_btree(sc, sc->sa.rmap_cur, xchk_rmapbt_rec,
1698c2ecf20Sopenharmony_ci			&XFS_RMAP_OINFO_AG, NULL);
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci/* xref check that the extent is owned by a given owner */
1738c2ecf20Sopenharmony_cistatic inline void
1748c2ecf20Sopenharmony_cixchk_xref_check_owner(
1758c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc,
1768c2ecf20Sopenharmony_ci	xfs_agblock_t			bno,
1778c2ecf20Sopenharmony_ci	xfs_extlen_t			len,
1788c2ecf20Sopenharmony_ci	const struct xfs_owner_info	*oinfo,
1798c2ecf20Sopenharmony_ci	bool				should_have_rmap)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	bool				has_rmap;
1828c2ecf20Sopenharmony_ci	int				error;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	error = xfs_rmap_record_exists(sc->sa.rmap_cur, bno, len, oinfo,
1888c2ecf20Sopenharmony_ci			&has_rmap);
1898c2ecf20Sopenharmony_ci	if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
1908c2ecf20Sopenharmony_ci		return;
1918c2ecf20Sopenharmony_ci	if (has_rmap != should_have_rmap)
1928c2ecf20Sopenharmony_ci		xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* xref check that the extent is owned by a given owner */
1968c2ecf20Sopenharmony_civoid
1978c2ecf20Sopenharmony_cixchk_xref_is_owned_by(
1988c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc,
1998c2ecf20Sopenharmony_ci	xfs_agblock_t			bno,
2008c2ecf20Sopenharmony_ci	xfs_extlen_t			len,
2018c2ecf20Sopenharmony_ci	const struct xfs_owner_info	*oinfo)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	xchk_xref_check_owner(sc, bno, len, oinfo, true);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/* xref check that the extent is not owned by a given owner */
2078c2ecf20Sopenharmony_civoid
2088c2ecf20Sopenharmony_cixchk_xref_is_not_owned_by(
2098c2ecf20Sopenharmony_ci	struct xfs_scrub		*sc,
2108c2ecf20Sopenharmony_ci	xfs_agblock_t			bno,
2118c2ecf20Sopenharmony_ci	xfs_extlen_t			len,
2128c2ecf20Sopenharmony_ci	const struct xfs_owner_info	*oinfo)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	xchk_xref_check_owner(sc, bno, len, oinfo, false);
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/* xref check that the extent has no reverse mapping at all */
2188c2ecf20Sopenharmony_civoid
2198c2ecf20Sopenharmony_cixchk_xref_has_no_owner(
2208c2ecf20Sopenharmony_ci	struct xfs_scrub	*sc,
2218c2ecf20Sopenharmony_ci	xfs_agblock_t		bno,
2228c2ecf20Sopenharmony_ci	xfs_extlen_t		len)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	bool			has_rmap;
2258c2ecf20Sopenharmony_ci	int			error;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!sc->sa.rmap_cur || xchk_skip_xref(sc->sm))
2288c2ecf20Sopenharmony_ci		return;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	error = xfs_rmap_has_record(sc->sa.rmap_cur, bno, len, &has_rmap);
2318c2ecf20Sopenharmony_ci	if (!xchk_should_check_xref(sc, &error, &sc->sa.rmap_cur))
2328c2ecf20Sopenharmony_ci		return;
2338c2ecf20Sopenharmony_ci	if (has_rmap)
2348c2ecf20Sopenharmony_ci		xchk_btree_xref_set_corrupt(sc, sc->sa.rmap_cur, 0);
2358c2ecf20Sopenharmony_ci}
236