// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2017-2023 Oracle. All Rights Reserved. * Author: Darrick J. Wong <[email protected]> */ #include "xfs.h" #include "xfs_fs.h" #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" #include "xfs_btree.h" #include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_inode.h" #include "xfs_ialloc.h" #include "xfs_ialloc_btree.h" #include "xfs_icache.h" #include "xfs_rmap.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" #include "scrub/trace.h" #include "xfs_ag.h" /* * Set us up to scrub inode btrees. * If we detect a discrepancy between the inobt and the inode, * try again after forcing logged inode cores out to disk. */ int xchk_setup_ag_iallocbt( struct xfs_scrub *sc) { … } /* Inode btree scrubber. */ struct xchk_iallocbt { … }; /* * Does the finobt have a record for this inode with the same hole/free state? * This is a bit complicated because of the following: * * - The finobt need not have a record if all inodes in the inobt record are * allocated. * - The finobt need not have a record if all inodes in the inobt record are * free. * - The finobt need not have a record if the inobt record says this is a hole. * This likely doesn't happen in practice. */ STATIC int xchk_inobt_xref_finobt( struct xfs_scrub *sc, struct xfs_inobt_rec_incore *irec, xfs_agino_t agino, bool free, bool hole) { … } /* * Make sure that each inode of this part of an inobt record has the same * sparse and free status as the finobt. */ STATIC void xchk_inobt_chunk_xref_finobt( struct xfs_scrub *sc, struct xfs_inobt_rec_incore *irec, xfs_agino_t agino, unsigned int nr_inodes) { … } /* * Does the inobt have a record for this inode with the same hole/free state? * The inobt must always have a record if there's a finobt record. */ STATIC int xchk_finobt_xref_inobt( struct xfs_scrub *sc, struct xfs_inobt_rec_incore *frec, xfs_agino_t agino, bool ffree, bool fhole) { … } /* * Make sure that each inode of this part of an finobt record has the same * sparse and free status as the inobt. */ STATIC void xchk_finobt_chunk_xref_inobt( struct xfs_scrub *sc, struct xfs_inobt_rec_incore *frec, xfs_agino_t agino, unsigned int nr_inodes) { … } /* Is this chunk worth checking and cross-referencing? */ STATIC bool xchk_iallocbt_chunk( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec, xfs_agino_t agino, unsigned int nr_inodes) { … } /* * Check that an inode's allocation status matches ir_free in the inobt * record. First we try querying the in-core inode state, and if the inode * isn't loaded we examine the on-disk inode directly. * * Since there can be 1:M and M:1 mappings between inobt records and inode * clusters, we pass in the inode location information as an inobt record; * the index of an inode cluster within the inobt record (as well as the * cluster buffer itself); and the index of the inode within the cluster. * * @irec is the inobt record. * @irec_ino is the inode offset from the start of the record. * @dip is the on-disk inode. */ STATIC int xchk_iallocbt_check_cluster_ifree( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec, unsigned int irec_ino, struct xfs_dinode *dip) { … } /* * Check that the holemask and freemask of a hypothetical inode cluster match * what's actually on disk. If sparse inodes are enabled, the cluster does * not actually have to map to inodes if the corresponding holemask bit is set. * * @cluster_base is the first inode in the cluster within the @irec. */ STATIC int xchk_iallocbt_check_cluster( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec, unsigned int cluster_base) { … } /* * For all the inode clusters that could map to this inobt record, make sure * that the holemask makes sense and that the allocation status of each inode * matches the freemask. */ STATIC int xchk_iallocbt_check_clusters( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec) { … } /* * Make sure this inode btree record is aligned properly. Because a fs block * contains multiple inodes, we check that the inobt record is aligned to the * correct inode, not just the correct block on disk. This results in a finer * grained corruption check. */ STATIC void xchk_iallocbt_rec_alignment( struct xchk_btree *bs, struct xfs_inobt_rec_incore *irec) { … } /* Scrub an inobt/finobt record. */ STATIC int xchk_iallocbt_rec( struct xchk_btree *bs, const union xfs_btree_rec *rec) { … } /* * Make sure the inode btrees are as large as the rmap thinks they are. * Don't bother if we're missing btree cursors, as we're already corrupt. */ STATIC void xchk_iallocbt_xref_rmap_btreeblks( struct xfs_scrub *sc) { … } /* * Make sure that the inobt records point to the same number of blocks as * the rmap says are owned by inodes. */ STATIC void xchk_iallocbt_xref_rmap_inodes( struct xfs_scrub *sc, unsigned long long inodes) { … } /* Scrub one of the inode btrees for some AG. */ int xchk_iallocbt( struct xfs_scrub *sc) { … } /* See if an inode btree has (or doesn't have) an inode chunk record. */ static inline void xchk_xref_inode_check( struct xfs_scrub *sc, xfs_agblock_t agbno, xfs_extlen_t len, struct xfs_btree_cur **icur, enum xbtree_recpacking expected) { … } /* xref check that the extent is not covered by inodes */ void xchk_xref_is_not_inode_chunk( struct xfs_scrub *sc, xfs_agblock_t agbno, xfs_extlen_t len) { … } /* xref check that the extent is covered by inodes */ void xchk_xref_is_inode_chunk( struct xfs_scrub *sc, xfs_agblock_t agbno, xfs_extlen_t len) { … }