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_bit.h" 148c2ecf20Sopenharmony_ci#include "xfs_log_format.h" 158c2ecf20Sopenharmony_ci#include "xfs_trans.h" 168c2ecf20Sopenharmony_ci#include "xfs_inode.h" 178c2ecf20Sopenharmony_ci#include "xfs_alloc.h" 188c2ecf20Sopenharmony_ci#include "xfs_bmap.h" 198c2ecf20Sopenharmony_ci#include "xfs_bmap_btree.h" 208c2ecf20Sopenharmony_ci#include "xfs_rmap.h" 218c2ecf20Sopenharmony_ci#include "xfs_rmap_btree.h" 228c2ecf20Sopenharmony_ci#include "scrub/scrub.h" 238c2ecf20Sopenharmony_ci#include "scrub/common.h" 248c2ecf20Sopenharmony_ci#include "scrub/btree.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Set us up with an inode's bmap. */ 278c2ecf20Sopenharmony_ciint 288c2ecf20Sopenharmony_cixchk_setup_inode_bmap( 298c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 308c2ecf20Sopenharmony_ci struct xfs_inode *ip) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int error; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci error = xchk_get_inode(sc, ip); 358c2ecf20Sopenharmony_ci if (error) 368c2ecf20Sopenharmony_ci goto out; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci sc->ilock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL; 398c2ecf20Sopenharmony_ci xfs_ilock(sc->ip, sc->ilock_flags); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci /* 428c2ecf20Sopenharmony_ci * We don't want any ephemeral data fork updates sitting around 438c2ecf20Sopenharmony_ci * while we inspect block mappings, so wait for directio to finish 448c2ecf20Sopenharmony_ci * and flush dirty data if we have delalloc reservations. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci if (S_ISREG(VFS_I(sc->ip)->i_mode) && 478c2ecf20Sopenharmony_ci sc->sm->sm_type == XFS_SCRUB_TYPE_BMBTD) { 488c2ecf20Sopenharmony_ci struct address_space *mapping = VFS_I(sc->ip)->i_mapping; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci inode_dio_wait(VFS_I(sc->ip)); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Try to flush all incore state to disk before we examine the 548c2ecf20Sopenharmony_ci * space mappings for the data fork. Leave accumulated errors 558c2ecf20Sopenharmony_ci * in the mapping for the writer threads to consume. 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * On ENOSPC or EIO writeback errors, we continue into the 588c2ecf20Sopenharmony_ci * extent mapping checks because write failures do not 598c2ecf20Sopenharmony_ci * necessarily imply anything about the correctness of the file 608c2ecf20Sopenharmony_ci * metadata. The metadata and the file data could be on 618c2ecf20Sopenharmony_ci * completely separate devices; a media failure might only 628c2ecf20Sopenharmony_ci * affect a subset of the disk, etc. We can handle delalloc 638c2ecf20Sopenharmony_ci * extents in the scrubber, so leaving them in memory is fine. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci error = filemap_fdatawrite(mapping); 668c2ecf20Sopenharmony_ci if (!error) 678c2ecf20Sopenharmony_ci error = filemap_fdatawait_keep_errors(mapping); 688c2ecf20Sopenharmony_ci if (error && (error != -ENOSPC && error != -EIO)) 698c2ecf20Sopenharmony_ci goto out; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Got the inode, lock it and we're ready to go. */ 738c2ecf20Sopenharmony_ci error = xchk_trans_alloc(sc, 0); 748c2ecf20Sopenharmony_ci if (error) 758c2ecf20Sopenharmony_ci goto out; 768c2ecf20Sopenharmony_ci sc->ilock_flags |= XFS_ILOCK_EXCL; 778c2ecf20Sopenharmony_ci xfs_ilock(sc->ip, XFS_ILOCK_EXCL); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciout: 808c2ecf20Sopenharmony_ci /* scrub teardown will unlock and release the inode */ 818c2ecf20Sopenharmony_ci return error; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * Inode fork block mapping (BMBT) scrubber. 868c2ecf20Sopenharmony_ci * More complex than the others because we have to scrub 878c2ecf20Sopenharmony_ci * all the extents regardless of whether or not the fork 888c2ecf20Sopenharmony_ci * is in btree format. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistruct xchk_bmap_info { 928c2ecf20Sopenharmony_ci struct xfs_scrub *sc; 938c2ecf20Sopenharmony_ci xfs_fileoff_t lastoff; 948c2ecf20Sopenharmony_ci bool is_rt; 958c2ecf20Sopenharmony_ci bool is_shared; 968c2ecf20Sopenharmony_ci bool was_loaded; 978c2ecf20Sopenharmony_ci int whichfork; 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* Look for a corresponding rmap for this irec. */ 1018c2ecf20Sopenharmony_cistatic inline bool 1028c2ecf20Sopenharmony_cixchk_bmap_get_rmap( 1038c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 1048c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec, 1058c2ecf20Sopenharmony_ci xfs_agblock_t agbno, 1068c2ecf20Sopenharmony_ci uint64_t owner, 1078c2ecf20Sopenharmony_ci struct xfs_rmap_irec *rmap) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci xfs_fileoff_t offset; 1108c2ecf20Sopenharmony_ci unsigned int rflags = 0; 1118c2ecf20Sopenharmony_ci int has_rmap; 1128c2ecf20Sopenharmony_ci int error; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (info->whichfork == XFS_ATTR_FORK) 1158c2ecf20Sopenharmony_ci rflags |= XFS_RMAP_ATTR_FORK; 1168c2ecf20Sopenharmony_ci if (irec->br_state == XFS_EXT_UNWRITTEN) 1178c2ecf20Sopenharmony_ci rflags |= XFS_RMAP_UNWRITTEN; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * CoW staging extents are owned (on disk) by the refcountbt, so 1218c2ecf20Sopenharmony_ci * their rmaps do not have offsets. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_ci if (info->whichfork == XFS_COW_FORK) 1248c2ecf20Sopenharmony_ci offset = 0; 1258c2ecf20Sopenharmony_ci else 1268c2ecf20Sopenharmony_ci offset = irec->br_startoff; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* 1298c2ecf20Sopenharmony_ci * If the caller thinks this could be a shared bmbt extent (IOWs, 1308c2ecf20Sopenharmony_ci * any data fork extent of a reflink inode) then we have to use the 1318c2ecf20Sopenharmony_ci * range rmap lookup to make sure we get the correct owner/offset. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (info->is_shared) { 1348c2ecf20Sopenharmony_ci error = xfs_rmap_lookup_le_range(info->sc->sa.rmap_cur, agbno, 1358c2ecf20Sopenharmony_ci owner, offset, rflags, rmap, &has_rmap); 1368c2ecf20Sopenharmony_ci if (!xchk_should_check_xref(info->sc, &error, 1378c2ecf20Sopenharmony_ci &info->sc->sa.rmap_cur)) 1388c2ecf20Sopenharmony_ci return false; 1398c2ecf20Sopenharmony_ci goto out; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Otherwise, use the (faster) regular lookup. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci error = xfs_rmap_lookup_le(info->sc->sa.rmap_cur, agbno, 0, owner, 1468c2ecf20Sopenharmony_ci offset, rflags, &has_rmap); 1478c2ecf20Sopenharmony_ci if (!xchk_should_check_xref(info->sc, &error, 1488c2ecf20Sopenharmony_ci &info->sc->sa.rmap_cur)) 1498c2ecf20Sopenharmony_ci return false; 1508c2ecf20Sopenharmony_ci if (!has_rmap) 1518c2ecf20Sopenharmony_ci goto out; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci error = xfs_rmap_get_rec(info->sc->sa.rmap_cur, rmap, &has_rmap); 1548c2ecf20Sopenharmony_ci if (!xchk_should_check_xref(info->sc, &error, 1558c2ecf20Sopenharmony_ci &info->sc->sa.rmap_cur)) 1568c2ecf20Sopenharmony_ci return false; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ciout: 1598c2ecf20Sopenharmony_ci if (!has_rmap) 1608c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 1618c2ecf20Sopenharmony_ci irec->br_startoff); 1628c2ecf20Sopenharmony_ci return has_rmap; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* Make sure that we have rmapbt records for this extent. */ 1668c2ecf20Sopenharmony_ciSTATIC void 1678c2ecf20Sopenharmony_cixchk_bmap_xref_rmap( 1688c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 1698c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec, 1708c2ecf20Sopenharmony_ci xfs_agblock_t agbno) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci struct xfs_rmap_irec rmap; 1738c2ecf20Sopenharmony_ci unsigned long long rmap_end; 1748c2ecf20Sopenharmony_ci uint64_t owner; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!info->sc->sa.rmap_cur || xchk_skip_xref(info->sc->sm)) 1778c2ecf20Sopenharmony_ci return; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (info->whichfork == XFS_COW_FORK) 1808c2ecf20Sopenharmony_ci owner = XFS_RMAP_OWN_COW; 1818c2ecf20Sopenharmony_ci else 1828c2ecf20Sopenharmony_ci owner = info->sc->ip->i_ino; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Find the rmap record for this irec. */ 1858c2ecf20Sopenharmony_ci if (!xchk_bmap_get_rmap(info, irec, agbno, owner, &rmap)) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Check the rmap. */ 1898c2ecf20Sopenharmony_ci rmap_end = (unsigned long long)rmap.rm_startblock + rmap.rm_blockcount; 1908c2ecf20Sopenharmony_ci if (rmap.rm_startblock > agbno || 1918c2ecf20Sopenharmony_ci agbno + irec->br_blockcount > rmap_end) 1928c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 1938c2ecf20Sopenharmony_ci irec->br_startoff); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* 1968c2ecf20Sopenharmony_ci * Check the logical offsets if applicable. CoW staging extents 1978c2ecf20Sopenharmony_ci * don't track logical offsets since the mappings only exist in 1988c2ecf20Sopenharmony_ci * memory. 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ci if (info->whichfork != XFS_COW_FORK) { 2018c2ecf20Sopenharmony_ci rmap_end = (unsigned long long)rmap.rm_offset + 2028c2ecf20Sopenharmony_ci rmap.rm_blockcount; 2038c2ecf20Sopenharmony_ci if (rmap.rm_offset > irec->br_startoff || 2048c2ecf20Sopenharmony_ci irec->br_startoff + irec->br_blockcount > rmap_end) 2058c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, 2068c2ecf20Sopenharmony_ci info->whichfork, irec->br_startoff); 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (rmap.rm_owner != owner) 2108c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 2118c2ecf20Sopenharmony_ci irec->br_startoff); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* 2148c2ecf20Sopenharmony_ci * Check for discrepancies between the unwritten flag in the irec and 2158c2ecf20Sopenharmony_ci * the rmap. Note that the (in-memory) CoW fork distinguishes between 2168c2ecf20Sopenharmony_ci * unwritten and written extents, but we don't track that in the rmap 2178c2ecf20Sopenharmony_ci * records because the blocks are owned (on-disk) by the refcountbt, 2188c2ecf20Sopenharmony_ci * which doesn't track unwritten state. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci if (owner != XFS_RMAP_OWN_COW && 2218c2ecf20Sopenharmony_ci !!(irec->br_state == XFS_EXT_UNWRITTEN) != 2228c2ecf20Sopenharmony_ci !!(rmap.rm_flags & XFS_RMAP_UNWRITTEN)) 2238c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 2248c2ecf20Sopenharmony_ci irec->br_startoff); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!!(info->whichfork == XFS_ATTR_FORK) != 2278c2ecf20Sopenharmony_ci !!(rmap.rm_flags & XFS_RMAP_ATTR_FORK)) 2288c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 2298c2ecf20Sopenharmony_ci irec->br_startoff); 2308c2ecf20Sopenharmony_ci if (rmap.rm_flags & XFS_RMAP_BMBT_BLOCK) 2318c2ecf20Sopenharmony_ci xchk_fblock_xref_set_corrupt(info->sc, info->whichfork, 2328c2ecf20Sopenharmony_ci irec->br_startoff); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/* Cross-reference a single rtdev extent record. */ 2368c2ecf20Sopenharmony_ciSTATIC void 2378c2ecf20Sopenharmony_cixchk_bmap_rt_iextent_xref( 2388c2ecf20Sopenharmony_ci struct xfs_inode *ip, 2398c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 2408c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci xchk_xref_is_used_rt_space(info->sc, irec->br_startblock, 2438c2ecf20Sopenharmony_ci irec->br_blockcount); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/* Cross-reference a single datadev extent record. */ 2478c2ecf20Sopenharmony_ciSTATIC void 2488c2ecf20Sopenharmony_cixchk_bmap_iextent_xref( 2498c2ecf20Sopenharmony_ci struct xfs_inode *ip, 2508c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 2518c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct xfs_mount *mp = info->sc->mp; 2548c2ecf20Sopenharmony_ci xfs_agnumber_t agno; 2558c2ecf20Sopenharmony_ci xfs_agblock_t agbno; 2568c2ecf20Sopenharmony_ci xfs_extlen_t len; 2578c2ecf20Sopenharmony_ci int error; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci agno = XFS_FSB_TO_AGNO(mp, irec->br_startblock); 2608c2ecf20Sopenharmony_ci agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock); 2618c2ecf20Sopenharmony_ci len = irec->br_blockcount; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci error = xchk_ag_init(info->sc, agno, &info->sc->sa); 2648c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(info->sc, info->whichfork, 2658c2ecf20Sopenharmony_ci irec->br_startoff, &error)) 2668c2ecf20Sopenharmony_ci return; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci xchk_xref_is_used_space(info->sc, agbno, len); 2698c2ecf20Sopenharmony_ci xchk_xref_is_not_inode_chunk(info->sc, agbno, len); 2708c2ecf20Sopenharmony_ci xchk_bmap_xref_rmap(info, irec, agbno); 2718c2ecf20Sopenharmony_ci switch (info->whichfork) { 2728c2ecf20Sopenharmony_ci case XFS_DATA_FORK: 2738c2ecf20Sopenharmony_ci if (xfs_is_reflink_inode(info->sc->ip)) 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci /* fall through */ 2768c2ecf20Sopenharmony_ci case XFS_ATTR_FORK: 2778c2ecf20Sopenharmony_ci xchk_xref_is_not_shared(info->sc, agbno, 2788c2ecf20Sopenharmony_ci irec->br_blockcount); 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case XFS_COW_FORK: 2818c2ecf20Sopenharmony_ci xchk_xref_is_cow_staging(info->sc, agbno, 2828c2ecf20Sopenharmony_ci irec->br_blockcount); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci xchk_ag_free(info->sc, &info->sc->sa); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * Directories and attr forks should never have blocks that can't be addressed 2918c2ecf20Sopenharmony_ci * by a xfs_dablk_t. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ciSTATIC void 2948c2ecf20Sopenharmony_cixchk_bmap_dirattr_extent( 2958c2ecf20Sopenharmony_ci struct xfs_inode *ip, 2968c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 2978c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct xfs_mount *mp = ip->i_mount; 3008c2ecf20Sopenharmony_ci xfs_fileoff_t off; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!S_ISDIR(VFS_I(ip)->i_mode) && info->whichfork != XFS_ATTR_FORK) 3038c2ecf20Sopenharmony_ci return; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!xfs_verify_dablk(mp, irec->br_startoff)) 3068c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3078c2ecf20Sopenharmony_ci irec->br_startoff); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci off = irec->br_startoff + irec->br_blockcount - 1; 3108c2ecf20Sopenharmony_ci if (!xfs_verify_dablk(mp, off)) 3118c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, off); 3128c2ecf20Sopenharmony_ci} 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci/* Scrub a single extent record. */ 3158c2ecf20Sopenharmony_ciSTATIC int 3168c2ecf20Sopenharmony_cixchk_bmap_iextent( 3178c2ecf20Sopenharmony_ci struct xfs_inode *ip, 3188c2ecf20Sopenharmony_ci struct xchk_bmap_info *info, 3198c2ecf20Sopenharmony_ci struct xfs_bmbt_irec *irec) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct xfs_mount *mp = info->sc->mp; 3228c2ecf20Sopenharmony_ci xfs_filblks_t end; 3238c2ecf20Sopenharmony_ci int error = 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * Check for out-of-order extents. This record could have come 3278c2ecf20Sopenharmony_ci * from the incore list, for which there is no ordering check. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci if (irec->br_startoff < info->lastoff) 3308c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3318c2ecf20Sopenharmony_ci irec->br_startoff); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci xchk_bmap_dirattr_extent(ip, info, irec); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* There should never be a "hole" extent in either extent list. */ 3368c2ecf20Sopenharmony_ci if (irec->br_startblock == HOLESTARTBLOCK) 3378c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3388c2ecf20Sopenharmony_ci irec->br_startoff); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci /* 3418c2ecf20Sopenharmony_ci * Check for delalloc extents. We never iterate the ones in the 3428c2ecf20Sopenharmony_ci * in-core extent scan, and we should never see these in the bmbt. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci if (isnullstartblock(irec->br_startblock)) 3458c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3468c2ecf20Sopenharmony_ci irec->br_startoff); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Make sure the extent points to a valid place. */ 3498c2ecf20Sopenharmony_ci if (irec->br_blockcount > MAXEXTLEN) 3508c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3518c2ecf20Sopenharmony_ci irec->br_startoff); 3528c2ecf20Sopenharmony_ci if (irec->br_startblock + irec->br_blockcount <= irec->br_startblock) 3538c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3548c2ecf20Sopenharmony_ci irec->br_startoff); 3558c2ecf20Sopenharmony_ci end = irec->br_startblock + irec->br_blockcount - 1; 3568c2ecf20Sopenharmony_ci if (info->is_rt && 3578c2ecf20Sopenharmony_ci (!xfs_verify_rtbno(mp, irec->br_startblock) || 3588c2ecf20Sopenharmony_ci !xfs_verify_rtbno(mp, end))) 3598c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3608c2ecf20Sopenharmony_ci irec->br_startoff); 3618c2ecf20Sopenharmony_ci if (!info->is_rt && 3628c2ecf20Sopenharmony_ci (!xfs_verify_fsbno(mp, irec->br_startblock) || 3638c2ecf20Sopenharmony_ci !xfs_verify_fsbno(mp, end) || 3648c2ecf20Sopenharmony_ci XFS_FSB_TO_AGNO(mp, irec->br_startblock) != 3658c2ecf20Sopenharmony_ci XFS_FSB_TO_AGNO(mp, end))) 3668c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3678c2ecf20Sopenharmony_ci irec->br_startoff); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* We don't allow unwritten extents on attr forks. */ 3708c2ecf20Sopenharmony_ci if (irec->br_state == XFS_EXT_UNWRITTEN && 3718c2ecf20Sopenharmony_ci info->whichfork == XFS_ATTR_FORK) 3728c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(info->sc, info->whichfork, 3738c2ecf20Sopenharmony_ci irec->br_startoff); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (info->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (info->is_rt) 3798c2ecf20Sopenharmony_ci xchk_bmap_rt_iextent_xref(ip, info, irec); 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci xchk_bmap_iextent_xref(ip, info, irec); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci info->lastoff = irec->br_startoff + irec->br_blockcount; 3848c2ecf20Sopenharmony_ci return error; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* Scrub a bmbt record. */ 3888c2ecf20Sopenharmony_ciSTATIC int 3898c2ecf20Sopenharmony_cixchk_bmapbt_rec( 3908c2ecf20Sopenharmony_ci struct xchk_btree *bs, 3918c2ecf20Sopenharmony_ci union xfs_btree_rec *rec) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct xfs_bmbt_irec irec; 3948c2ecf20Sopenharmony_ci struct xfs_bmbt_irec iext_irec; 3958c2ecf20Sopenharmony_ci struct xfs_iext_cursor icur; 3968c2ecf20Sopenharmony_ci struct xchk_bmap_info *info = bs->private; 3978c2ecf20Sopenharmony_ci struct xfs_inode *ip = bs->cur->bc_ino.ip; 3988c2ecf20Sopenharmony_ci struct xfs_buf *bp = NULL; 3998c2ecf20Sopenharmony_ci struct xfs_btree_block *block; 4008c2ecf20Sopenharmony_ci struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, info->whichfork); 4018c2ecf20Sopenharmony_ci uint64_t owner; 4028c2ecf20Sopenharmony_ci int i; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Check the owners of the btree blocks up to the level below 4068c2ecf20Sopenharmony_ci * the root since the verifiers don't do that. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci if (xfs_sb_version_hascrc(&bs->cur->bc_mp->m_sb) && 4098c2ecf20Sopenharmony_ci bs->cur->bc_ptrs[0] == 1) { 4108c2ecf20Sopenharmony_ci for (i = 0; i < bs->cur->bc_nlevels - 1; i++) { 4118c2ecf20Sopenharmony_ci block = xfs_btree_get_block(bs->cur, i, &bp); 4128c2ecf20Sopenharmony_ci owner = be64_to_cpu(block->bb_u.l.bb_owner); 4138c2ecf20Sopenharmony_ci if (owner != ip->i_ino) 4148c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(bs->sc, 4158c2ecf20Sopenharmony_ci info->whichfork, 0); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* 4208c2ecf20Sopenharmony_ci * Check that the incore extent tree contains an extent that matches 4218c2ecf20Sopenharmony_ci * this one exactly. We validate those cached bmaps later, so we don't 4228c2ecf20Sopenharmony_ci * need to check them here. If the incore extent tree was just loaded 4238c2ecf20Sopenharmony_ci * from disk by the scrubber, we assume that its contents match what's 4248c2ecf20Sopenharmony_ci * on disk (we still hold the ILOCK) and skip the equivalence check. 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci if (!info->was_loaded) 4278c2ecf20Sopenharmony_ci return 0; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci xfs_bmbt_disk_get_all(&rec->bmbt, &irec); 4308c2ecf20Sopenharmony_ci if (!xfs_iext_lookup_extent(ip, ifp, irec.br_startoff, &icur, 4318c2ecf20Sopenharmony_ci &iext_irec) || 4328c2ecf20Sopenharmony_ci irec.br_startoff != iext_irec.br_startoff || 4338c2ecf20Sopenharmony_ci irec.br_startblock != iext_irec.br_startblock || 4348c2ecf20Sopenharmony_ci irec.br_blockcount != iext_irec.br_blockcount || 4358c2ecf20Sopenharmony_ci irec.br_state != iext_irec.br_state) 4368c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(bs->sc, info->whichfork, 4378c2ecf20Sopenharmony_ci irec.br_startoff); 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* Scan the btree records. */ 4428c2ecf20Sopenharmony_ciSTATIC int 4438c2ecf20Sopenharmony_cixchk_bmap_btree( 4448c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 4458c2ecf20Sopenharmony_ci int whichfork, 4468c2ecf20Sopenharmony_ci struct xchk_bmap_info *info) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct xfs_owner_info oinfo; 4498c2ecf20Sopenharmony_ci struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); 4508c2ecf20Sopenharmony_ci struct xfs_mount *mp = sc->mp; 4518c2ecf20Sopenharmony_ci struct xfs_inode *ip = sc->ip; 4528c2ecf20Sopenharmony_ci struct xfs_btree_cur *cur; 4538c2ecf20Sopenharmony_ci int error; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Load the incore bmap cache if it's not loaded. */ 4568c2ecf20Sopenharmony_ci info->was_loaded = ifp->if_flags & XFS_IFEXTENTS; 4578c2ecf20Sopenharmony_ci if (!info->was_loaded) { 4588c2ecf20Sopenharmony_ci error = xfs_iread_extents(sc->tp, ip, whichfork); 4598c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sc, whichfork, 0, &error)) 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Check the btree structure. */ 4648c2ecf20Sopenharmony_ci cur = xfs_bmbt_init_cursor(mp, sc->tp, ip, whichfork); 4658c2ecf20Sopenharmony_ci xfs_rmap_ino_bmbt_owner(&oinfo, ip->i_ino, whichfork); 4668c2ecf20Sopenharmony_ci error = xchk_btree(sc, cur, xchk_bmapbt_rec, &oinfo, info); 4678c2ecf20Sopenharmony_ci xfs_btree_del_cursor(cur, error); 4688c2ecf20Sopenharmony_ciout: 4698c2ecf20Sopenharmony_ci return error; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistruct xchk_bmap_check_rmap_info { 4738c2ecf20Sopenharmony_ci struct xfs_scrub *sc; 4748c2ecf20Sopenharmony_ci int whichfork; 4758c2ecf20Sopenharmony_ci struct xfs_iext_cursor icur; 4768c2ecf20Sopenharmony_ci}; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/* Can we find bmaps that fit this rmap? */ 4798c2ecf20Sopenharmony_ciSTATIC int 4808c2ecf20Sopenharmony_cixchk_bmap_check_rmap( 4818c2ecf20Sopenharmony_ci struct xfs_btree_cur *cur, 4828c2ecf20Sopenharmony_ci struct xfs_rmap_irec *rec, 4838c2ecf20Sopenharmony_ci void *priv) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci struct xfs_bmbt_irec irec; 4868c2ecf20Sopenharmony_ci struct xchk_bmap_check_rmap_info *sbcri = priv; 4878c2ecf20Sopenharmony_ci struct xfs_ifork *ifp; 4888c2ecf20Sopenharmony_ci struct xfs_scrub *sc = sbcri->sc; 4898c2ecf20Sopenharmony_ci bool have_map; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci /* Is this even the right fork? */ 4928c2ecf20Sopenharmony_ci if (rec->rm_owner != sc->ip->i_ino) 4938c2ecf20Sopenharmony_ci return 0; 4948c2ecf20Sopenharmony_ci if ((sbcri->whichfork == XFS_ATTR_FORK) ^ 4958c2ecf20Sopenharmony_ci !!(rec->rm_flags & XFS_RMAP_ATTR_FORK)) 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci if (rec->rm_flags & XFS_RMAP_BMBT_BLOCK) 4988c2ecf20Sopenharmony_ci return 0; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* Now look up the bmbt record. */ 5018c2ecf20Sopenharmony_ci ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork); 5028c2ecf20Sopenharmony_ci if (!ifp) { 5038c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5048c2ecf20Sopenharmony_ci rec->rm_offset); 5058c2ecf20Sopenharmony_ci goto out; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci have_map = xfs_iext_lookup_extent(sc->ip, ifp, rec->rm_offset, 5088c2ecf20Sopenharmony_ci &sbcri->icur, &irec); 5098c2ecf20Sopenharmony_ci if (!have_map) 5108c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5118c2ecf20Sopenharmony_ci rec->rm_offset); 5128c2ecf20Sopenharmony_ci /* 5138c2ecf20Sopenharmony_ci * bmap extent record lengths are constrained to 2^21 blocks in length 5148c2ecf20Sopenharmony_ci * because of space constraints in the on-disk metadata structure. 5158c2ecf20Sopenharmony_ci * However, rmap extent record lengths are constrained only by AG 5168c2ecf20Sopenharmony_ci * length, so we have to loop through the bmbt to make sure that the 5178c2ecf20Sopenharmony_ci * entire rmap is covered by bmbt records. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci while (have_map) { 5208c2ecf20Sopenharmony_ci if (irec.br_startoff != rec->rm_offset) 5218c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5228c2ecf20Sopenharmony_ci rec->rm_offset); 5238c2ecf20Sopenharmony_ci if (irec.br_startblock != XFS_AGB_TO_FSB(sc->mp, 5248c2ecf20Sopenharmony_ci cur->bc_ag.agno, rec->rm_startblock)) 5258c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5268c2ecf20Sopenharmony_ci rec->rm_offset); 5278c2ecf20Sopenharmony_ci if (irec.br_blockcount > rec->rm_blockcount) 5288c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5298c2ecf20Sopenharmony_ci rec->rm_offset); 5308c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 5318c2ecf20Sopenharmony_ci break; 5328c2ecf20Sopenharmony_ci rec->rm_startblock += irec.br_blockcount; 5338c2ecf20Sopenharmony_ci rec->rm_offset += irec.br_blockcount; 5348c2ecf20Sopenharmony_ci rec->rm_blockcount -= irec.br_blockcount; 5358c2ecf20Sopenharmony_ci if (rec->rm_blockcount == 0) 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci have_map = xfs_iext_next_extent(ifp, &sbcri->icur, &irec); 5388c2ecf20Sopenharmony_ci if (!have_map) 5398c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, sbcri->whichfork, 5408c2ecf20Sopenharmony_ci rec->rm_offset); 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciout: 5448c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 5458c2ecf20Sopenharmony_ci return -ECANCELED; 5468c2ecf20Sopenharmony_ci return 0; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* Make sure each rmap has a corresponding bmbt entry. */ 5508c2ecf20Sopenharmony_ciSTATIC int 5518c2ecf20Sopenharmony_cixchk_bmap_check_ag_rmaps( 5528c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 5538c2ecf20Sopenharmony_ci int whichfork, 5548c2ecf20Sopenharmony_ci xfs_agnumber_t agno) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct xchk_bmap_check_rmap_info sbcri; 5578c2ecf20Sopenharmony_ci struct xfs_btree_cur *cur; 5588c2ecf20Sopenharmony_ci struct xfs_buf *agf; 5598c2ecf20Sopenharmony_ci int error; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf); 5628c2ecf20Sopenharmony_ci if (error) 5638c2ecf20Sopenharmony_ci return error; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci cur = xfs_rmapbt_init_cursor(sc->mp, sc->tp, agf, agno); 5668c2ecf20Sopenharmony_ci if (!cur) { 5678c2ecf20Sopenharmony_ci error = -ENOMEM; 5688c2ecf20Sopenharmony_ci goto out_agf; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci sbcri.sc = sc; 5728c2ecf20Sopenharmony_ci sbcri.whichfork = whichfork; 5738c2ecf20Sopenharmony_ci error = xfs_rmap_query_all(cur, xchk_bmap_check_rmap, &sbcri); 5748c2ecf20Sopenharmony_ci if (error == -ECANCELED) 5758c2ecf20Sopenharmony_ci error = 0; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci xfs_btree_del_cursor(cur, error); 5788c2ecf20Sopenharmony_ciout_agf: 5798c2ecf20Sopenharmony_ci xfs_trans_brelse(sc->tp, agf); 5808c2ecf20Sopenharmony_ci return error; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci/* Make sure each rmap has a corresponding bmbt entry. */ 5848c2ecf20Sopenharmony_ciSTATIC int 5858c2ecf20Sopenharmony_cixchk_bmap_check_rmaps( 5868c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 5878c2ecf20Sopenharmony_ci int whichfork) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); 5908c2ecf20Sopenharmony_ci xfs_agnumber_t agno; 5918c2ecf20Sopenharmony_ci bool zero_size; 5928c2ecf20Sopenharmony_ci int error; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci if (!xfs_sb_version_hasrmapbt(&sc->mp->m_sb) || 5958c2ecf20Sopenharmony_ci whichfork == XFS_COW_FORK || 5968c2ecf20Sopenharmony_ci (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Don't support realtime rmap checks yet. */ 6008c2ecf20Sopenharmony_ci if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK) 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci ASSERT(XFS_IFORK_PTR(sc->ip, whichfork) != NULL); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * Only do this for complex maps that are in btree format, or for 6078c2ecf20Sopenharmony_ci * situations where we would seem to have a size but zero extents. 6088c2ecf20Sopenharmony_ci * The inode repair code can zap broken iforks, which means we have 6098c2ecf20Sopenharmony_ci * to flag this bmap as corrupt if there are rmaps that need to be 6108c2ecf20Sopenharmony_ci * reattached. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (whichfork == XFS_DATA_FORK) 6148c2ecf20Sopenharmony_ci zero_size = i_size_read(VFS_I(sc->ip)) == 0; 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci zero_size = false; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (ifp->if_format != XFS_DINODE_FMT_BTREE && 6198c2ecf20Sopenharmony_ci (zero_size || ifp->if_nextents > 0)) 6208c2ecf20Sopenharmony_ci return 0; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci for (agno = 0; agno < sc->mp->m_sb.sb_agcount; agno++) { 6238c2ecf20Sopenharmony_ci error = xchk_bmap_check_ag_rmaps(sc, whichfork, agno); 6248c2ecf20Sopenharmony_ci if (error) 6258c2ecf20Sopenharmony_ci return error; 6268c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 6278c2ecf20Sopenharmony_ci break; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return 0; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci/* 6348c2ecf20Sopenharmony_ci * Scrub an inode fork's block mappings. 6358c2ecf20Sopenharmony_ci * 6368c2ecf20Sopenharmony_ci * First we scan every record in every btree block, if applicable. 6378c2ecf20Sopenharmony_ci * Then we unconditionally scan the incore extent cache. 6388c2ecf20Sopenharmony_ci */ 6398c2ecf20Sopenharmony_ciSTATIC int 6408c2ecf20Sopenharmony_cixchk_bmap( 6418c2ecf20Sopenharmony_ci struct xfs_scrub *sc, 6428c2ecf20Sopenharmony_ci int whichfork) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct xfs_bmbt_irec irec; 6458c2ecf20Sopenharmony_ci struct xchk_bmap_info info = { NULL }; 6468c2ecf20Sopenharmony_ci struct xfs_mount *mp = sc->mp; 6478c2ecf20Sopenharmony_ci struct xfs_inode *ip = sc->ip; 6488c2ecf20Sopenharmony_ci struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); 6498c2ecf20Sopenharmony_ci xfs_fileoff_t endoff; 6508c2ecf20Sopenharmony_ci struct xfs_iext_cursor icur; 6518c2ecf20Sopenharmony_ci int error = 0; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci /* Non-existent forks can be ignored. */ 6548c2ecf20Sopenharmony_ci if (!ifp) 6558c2ecf20Sopenharmony_ci goto out; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci info.is_rt = whichfork == XFS_DATA_FORK && XFS_IS_REALTIME_INODE(ip); 6588c2ecf20Sopenharmony_ci info.whichfork = whichfork; 6598c2ecf20Sopenharmony_ci info.is_shared = whichfork == XFS_DATA_FORK && xfs_is_reflink_inode(ip); 6608c2ecf20Sopenharmony_ci info.sc = sc; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci switch (whichfork) { 6638c2ecf20Sopenharmony_ci case XFS_COW_FORK: 6648c2ecf20Sopenharmony_ci /* No CoW forks on non-reflink inodes/filesystems. */ 6658c2ecf20Sopenharmony_ci if (!xfs_is_reflink_inode(ip)) { 6668c2ecf20Sopenharmony_ci xchk_ino_set_corrupt(sc, sc->ip->i_ino); 6678c2ecf20Sopenharmony_ci goto out; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci case XFS_ATTR_FORK: 6718c2ecf20Sopenharmony_ci if (!xfs_sb_version_hasattr(&mp->m_sb) && 6728c2ecf20Sopenharmony_ci !xfs_sb_version_hasattr2(&mp->m_sb)) 6738c2ecf20Sopenharmony_ci xchk_ino_set_corrupt(sc, sc->ip->i_ino); 6748c2ecf20Sopenharmony_ci break; 6758c2ecf20Sopenharmony_ci default: 6768c2ecf20Sopenharmony_ci ASSERT(whichfork == XFS_DATA_FORK); 6778c2ecf20Sopenharmony_ci break; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Check the fork values */ 6818c2ecf20Sopenharmony_ci switch (ifp->if_format) { 6828c2ecf20Sopenharmony_ci case XFS_DINODE_FMT_UUID: 6838c2ecf20Sopenharmony_ci case XFS_DINODE_FMT_DEV: 6848c2ecf20Sopenharmony_ci case XFS_DINODE_FMT_LOCAL: 6858c2ecf20Sopenharmony_ci /* No mappings to check. */ 6868c2ecf20Sopenharmony_ci goto out; 6878c2ecf20Sopenharmony_ci case XFS_DINODE_FMT_EXTENTS: 6888c2ecf20Sopenharmony_ci if (!(ifp->if_flags & XFS_IFEXTENTS)) { 6898c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, whichfork, 0); 6908c2ecf20Sopenharmony_ci goto out; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case XFS_DINODE_FMT_BTREE: 6948c2ecf20Sopenharmony_ci if (whichfork == XFS_COW_FORK) { 6958c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, whichfork, 0); 6968c2ecf20Sopenharmony_ci goto out; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci error = xchk_bmap_btree(sc, whichfork, &info); 7008c2ecf20Sopenharmony_ci if (error) 7018c2ecf20Sopenharmony_ci goto out; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci default: 7048c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, whichfork, 0); 7058c2ecf20Sopenharmony_ci goto out; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT) 7098c2ecf20Sopenharmony_ci goto out; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* Find the offset of the last extent in the mapping. */ 7128c2ecf20Sopenharmony_ci error = xfs_bmap_last_offset(ip, &endoff, whichfork); 7138c2ecf20Sopenharmony_ci if (!xchk_fblock_process_error(sc, whichfork, 0, &error)) 7148c2ecf20Sopenharmony_ci goto out; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* Scrub extent records. */ 7178c2ecf20Sopenharmony_ci info.lastoff = 0; 7188c2ecf20Sopenharmony_ci ifp = XFS_IFORK_PTR(ip, whichfork); 7198c2ecf20Sopenharmony_ci for_each_xfs_iext(ifp, &icur, &irec) { 7208c2ecf20Sopenharmony_ci if (xchk_should_terminate(sc, &error) || 7218c2ecf20Sopenharmony_ci (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 7228c2ecf20Sopenharmony_ci goto out; 7238c2ecf20Sopenharmony_ci if (isnullstartblock(irec.br_startblock)) 7248c2ecf20Sopenharmony_ci continue; 7258c2ecf20Sopenharmony_ci if (irec.br_startoff >= endoff) { 7268c2ecf20Sopenharmony_ci xchk_fblock_set_corrupt(sc, whichfork, 7278c2ecf20Sopenharmony_ci irec.br_startoff); 7288c2ecf20Sopenharmony_ci goto out; 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci error = xchk_bmap_iextent(ip, &info, &irec); 7318c2ecf20Sopenharmony_ci if (error) 7328c2ecf20Sopenharmony_ci goto out; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci error = xchk_bmap_check_rmaps(sc, whichfork); 7368c2ecf20Sopenharmony_ci if (!xchk_fblock_xref_process_error(sc, whichfork, 0, &error)) 7378c2ecf20Sopenharmony_ci goto out; 7388c2ecf20Sopenharmony_ciout: 7398c2ecf20Sopenharmony_ci return error; 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci/* Scrub an inode's data fork. */ 7438c2ecf20Sopenharmony_ciint 7448c2ecf20Sopenharmony_cixchk_bmap_data( 7458c2ecf20Sopenharmony_ci struct xfs_scrub *sc) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci return xchk_bmap(sc, XFS_DATA_FORK); 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/* Scrub an inode's attr fork. */ 7518c2ecf20Sopenharmony_ciint 7528c2ecf20Sopenharmony_cixchk_bmap_attr( 7538c2ecf20Sopenharmony_ci struct xfs_scrub *sc) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci return xchk_bmap(sc, XFS_ATTR_FORK); 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/* Scrub an inode's CoW fork. */ 7598c2ecf20Sopenharmony_ciint 7608c2ecf20Sopenharmony_cixchk_bmap_cow( 7618c2ecf20Sopenharmony_ci struct xfs_scrub *sc) 7628c2ecf20Sopenharmony_ci{ 7638c2ecf20Sopenharmony_ci if (!xfs_is_reflink_inode(sc->ip)) 7648c2ecf20Sopenharmony_ci return -ENOENT; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return xchk_bmap(sc, XFS_COW_FORK); 7678c2ecf20Sopenharmony_ci} 768