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