// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2018-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_sb.h" #include "xfs_alloc.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc.h" #include "xfs_ialloc_btree.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" #include "xfs_refcount_btree.h" #include "xfs_ag.h" #include "xfs_inode.h" #include "xfs_iunlink_item.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/bitmap.h" #include "scrub/agb_bitmap.h" #include "scrub/agino_bitmap.h" #include "scrub/reap.h" #include "scrub/xfile.h" #include "scrub/xfarray.h" /* Superblock */ /* Repair the superblock. */ int xrep_superblock( struct xfs_scrub *sc) { … } /* AGF */ struct xrep_agf_allocbt { … }; /* Record free space shape information. */ STATIC int xrep_agf_walk_allocbt( struct xfs_btree_cur *cur, const struct xfs_alloc_rec_incore *rec, void *priv) { … } /* Does this AGFL block look sane? */ STATIC int xrep_agf_check_agfl_block( struct xfs_mount *mp, xfs_agblock_t agbno, void *priv) { … } /* * Offset within the xrep_find_ag_btree array for each btree type. Avoid the * XFS_BTNUM_ names here to avoid creating a sparse array. */ enum { … }; /* Check a btree root candidate. */ static inline bool xrep_check_btree_root( struct xfs_scrub *sc, struct xrep_find_ag_btree *fab) { … } /* * Given the btree roots described by *fab, find the roots, check them for * sanity, and pass the root data back out via *fab. * * This is /also/ a chicken and egg problem because we have to use the rmapbt * (rooted in the AGF) to find the btrees rooted in the AGF. We also have no * idea if the btrees make any sense. If we hit obvious corruptions in those * btrees we'll bail out. */ STATIC int xrep_agf_find_btrees( struct xfs_scrub *sc, struct xfs_buf *agf_bp, struct xrep_find_ag_btree *fab, struct xfs_buf *agfl_bp) { … } /* * Reinitialize the AGF header, making an in-core copy of the old contents so * that we know which in-core state needs to be reinitialized. */ STATIC void xrep_agf_init_header( struct xfs_scrub *sc, struct xfs_buf *agf_bp, struct xfs_agf *old_agf) { … } /* Set btree root information in an AGF. */ STATIC void xrep_agf_set_roots( struct xfs_scrub *sc, struct xfs_agf *agf, struct xrep_find_ag_btree *fab) { … } /* Update all AGF fields which derive from btree contents. */ STATIC int xrep_agf_calc_from_btrees( struct xfs_scrub *sc, struct xfs_buf *agf_bp) { … } /* Commit the new AGF and reinitialize the incore state. */ STATIC int xrep_agf_commit_new( struct xfs_scrub *sc, struct xfs_buf *agf_bp) { … } /* Repair the AGF. v5 filesystems only. */ int xrep_agf( struct xfs_scrub *sc) { … } /* AGFL */ struct xrep_agfl { … }; /* Record all OWN_AG (free space btree) information from the rmap data. */ STATIC int xrep_agfl_walk_rmap( struct xfs_btree_cur *cur, const struct xfs_rmap_irec *rec, void *priv) { … } /* Strike out the blocks that are cross-linked according to the rmapbt. */ STATIC int xrep_agfl_check_extent( uint32_t agbno, uint32_t len, void *priv) { … } /* * Map out all the non-AGFL OWN_AG space in this AG so that we can deduce * which blocks belong to the AGFL. * * Compute the set of old AGFL blocks by subtracting from the list of OWN_AG * blocks the list of blocks owned by all other OWN_AG metadata (bnobt, cntbt, * rmapbt). These are the old AGFL blocks, so return that list and the number * of blocks we're actually going to put back on the AGFL. */ STATIC int xrep_agfl_collect_blocks( struct xfs_scrub *sc, struct xfs_buf *agf_bp, struct xagb_bitmap *agfl_extents, xfs_agblock_t *flcount) { … } /* Update the AGF and reset the in-core state. */ STATIC void xrep_agfl_update_agf( struct xfs_scrub *sc, struct xfs_buf *agf_bp, xfs_agblock_t flcount) { … } struct xrep_agfl_fill { … }; /* Fill the AGFL with whatever blocks are in this extent. */ static int xrep_agfl_fill( uint32_t start, uint32_t len, void *priv) { … } /* Write out a totally new AGFL. */ STATIC int xrep_agfl_init_header( struct xfs_scrub *sc, struct xfs_buf *agfl_bp, struct xagb_bitmap *agfl_extents, xfs_agblock_t flcount) { … } /* Repair the AGFL. */ int xrep_agfl( struct xfs_scrub *sc) { … } /* AGI */ /* * Offset within the xrep_find_ag_btree array for each btree type. Avoid the * XFS_BTNUM_ names here to avoid creating a sparse array. */ enum { … }; #define XREP_AGI_LOOKUP_BATCH … struct xrep_agi { … }; static void xrep_agi_buf_cleanup( void *buf) { … } /* * Given the inode btree roots described by *fab, find the roots, check them * for sanity, and pass the root data back out via *fab. */ STATIC int xrep_agi_find_btrees( struct xrep_agi *ragi) { … } /* * Reinitialize the AGI header, making an in-core copy of the old contents so * that we know which in-core state needs to be reinitialized. */ STATIC void xrep_agi_init_header( struct xrep_agi *ragi) { … } /* Set btree root information in an AGI. */ STATIC void xrep_agi_set_roots( struct xrep_agi *ragi) { … } /* Update the AGI counters. */ STATIC int xrep_agi_calc_from_btrees( struct xrep_agi *ragi) { … } /* * Record a forwards unlinked chain pointer from agino -> next_agino in our * staging information. */ static inline int xrep_iunlink_store_next( struct xrep_agi *ragi, xfs_agino_t agino, xfs_agino_t next_agino) { … } /* * Record a backwards unlinked chain pointer from prev_ino <- agino in our * staging information. */ static inline int xrep_iunlink_store_prev( struct xrep_agi *ragi, xfs_agino_t agino, xfs_agino_t prev_agino) { … } /* * Given an @agino, look up the next inode in the iunlink bucket. Returns * NULLAGINO if we're at the end of the chain, 0 if @agino is not in memory * like it should be, or a per-AG inode number. */ static inline xfs_agino_t xrep_iunlink_next( struct xfs_scrub *sc, xfs_agino_t agino) { … } /* * Load the inode @agino into memory, set its i_prev_unlinked, and drop the * inode so it can be inactivated. Returns NULLAGINO if we're at the end of * the chain or if we should stop walking the chain due to corruption; or a * per-AG inode number. */ STATIC xfs_agino_t xrep_iunlink_reload_next( struct xrep_agi *ragi, xfs_agino_t prev_agino, xfs_agino_t agino) { … } /* * Walk an AGI unlinked bucket's list to load incore any unlinked inodes that * still existed at mount time. This can happen if iunlink processing fails * during log recovery. */ STATIC int xrep_iunlink_walk_ondisk_bucket( struct xrep_agi *ragi, unsigned int bucket) { … } /* Decide if this is an unlinked inode in this AG. */ STATIC bool xrep_iunlink_igrab( struct xfs_perag *pag, struct xfs_inode *ip) { … } /* * Mark the given inode in the lookup batch in our unlinked inode bitmap, and * remember if this inode is the start of the unlinked chain. */ STATIC int xrep_iunlink_visit( struct xrep_agi *ragi, unsigned int batch_idx) { … } /* * Find all incore unlinked inodes so that we can rebuild the unlinked buckets. * We hold the AGI so there should not be any modifications to the unlinked * list. */ STATIC int xrep_iunlink_mark_incore( struct xrep_agi *ragi) { … } /* Mark all the unlinked ondisk inodes in this inobt record in iunlink_bmp. */ STATIC int xrep_iunlink_mark_ondisk_rec( struct xfs_btree_cur *cur, const union xfs_btree_rec *rec, void *priv) { … } /* * Find ondisk inodes that are unlinked and not in cache, and mark them in * iunlink_bmp. We haven't checked the inobt yet, so we don't error out if * the btree is corrupt. */ STATIC void xrep_iunlink_mark_ondisk( struct xrep_agi *ragi) { … } /* * Walk an iunlink bucket's inode list. For each inode that should be on this * chain, clear its entry in in iunlink_bmp because it's ok and we don't need * to touch it further. */ STATIC int xrep_iunlink_resolve_bucket( struct xrep_agi *ragi, unsigned int bucket) { … } /* Reinsert this unlinked inode into the head of the staged bucket list. */ STATIC int xrep_iunlink_add_to_bucket( struct xrep_agi *ragi, xfs_agino_t agino) { … } /* Reinsert unlinked inodes into the staged iunlink buckets. */ STATIC int xrep_iunlink_add_lost_inodes( uint32_t start, uint32_t len, void *priv) { … } /* * Figure out the iunlink bucket values and find inodes that need to be * reinserted into the list. */ STATIC int xrep_iunlink_rebuild_buckets( struct xrep_agi *ragi) { … } /* Update i_next_iunlinked for the inode @agino. */ STATIC int xrep_iunlink_relink_next( struct xrep_agi *ragi, xfarray_idx_t idx, xfs_agino_t next_agino) { … } /* Update i_prev_iunlinked for the inode @agino. */ STATIC int xrep_iunlink_relink_prev( struct xrep_agi *ragi, xfarray_idx_t idx, xfs_agino_t prev_agino) { … } /* Log all the iunlink updates we need to finish regenerating the AGI. */ STATIC int xrep_iunlink_commit( struct xrep_agi *ragi) { … } /* Trigger reinitialization of the in-core data. */ STATIC int xrep_agi_commit_new( struct xrep_agi *ragi) { … } /* Repair the AGI. */ int xrep_agi( struct xfs_scrub *sc) { … }