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