1739a2fe0SDarrick J. Wong // SPDX-License-Identifier: GPL-2.0-or-later 27c4a07a4SDarrick J. Wong /* 3ecc73f8aSDarrick J. Wong * Copyright (C) 2017-2023 Oracle. All Rights Reserved. 4739a2fe0SDarrick J. Wong * Author: Darrick J. Wong <djwong@kernel.org> 57c4a07a4SDarrick J. Wong */ 67c4a07a4SDarrick J. Wong #include "xfs.h" 77c4a07a4SDarrick J. Wong #include "xfs_fs.h" 87c4a07a4SDarrick J. Wong #include "xfs_shared.h" 97c4a07a4SDarrick J. Wong #include "xfs_format.h" 107c4a07a4SDarrick J. Wong #include "xfs_trans_resv.h" 117c4a07a4SDarrick J. Wong #include "xfs_mount.h" 127c4a07a4SDarrick J. Wong #include "xfs_log_format.h" 137c4a07a4SDarrick J. Wong #include "xfs_trans.h" 147c4a07a4SDarrick J. Wong #include "xfs_inode.h" 157c4a07a4SDarrick J. Wong #include "xfs_dir2.h" 167c4a07a4SDarrick J. Wong #include "xfs_dir2_priv.h" 177c4a07a4SDarrick J. Wong #include "xfs_attr_leaf.h" 187c4a07a4SDarrick J. Wong #include "scrub/scrub.h" 197c4a07a4SDarrick J. Wong #include "scrub/common.h" 207c4a07a4SDarrick J. Wong #include "scrub/trace.h" 217c4a07a4SDarrick J. Wong #include "scrub/dabtree.h" 227c4a07a4SDarrick J. Wong 237c4a07a4SDarrick J. Wong /* Directory/Attribute Btree */ 247c4a07a4SDarrick J. Wong 257c4a07a4SDarrick J. Wong /* 267c4a07a4SDarrick J. Wong * Check for da btree operation errors. See the section about handling 277c4a07a4SDarrick J. Wong * operational errors in common.c. 287c4a07a4SDarrick J. Wong */ 297c4a07a4SDarrick J. Wong bool 30c517b3aaSDarrick J. Wong xchk_da_process_error( 31c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 327c4a07a4SDarrick J. Wong int level, 337c4a07a4SDarrick J. Wong int *error) 347c4a07a4SDarrick J. Wong { 351d8a748aSDarrick J. Wong struct xfs_scrub *sc = ds->sc; 367c4a07a4SDarrick J. Wong 377c4a07a4SDarrick J. Wong if (*error == 0) 387c4a07a4SDarrick J. Wong return true; 397c4a07a4SDarrick J. Wong 407c4a07a4SDarrick J. Wong switch (*error) { 417c4a07a4SDarrick J. Wong case -EDEADLOCK: 4288accf17SDarrick J. Wong case -ECHRNG: 437c4a07a4SDarrick J. Wong /* Used to restart an op with deadlock avoidance. */ 44c517b3aaSDarrick J. Wong trace_xchk_deadlock_retry(sc->ip, sc->sm, *error); 457c4a07a4SDarrick J. Wong break; 467c4a07a4SDarrick J. Wong case -EFSBADCRC: 477c4a07a4SDarrick J. Wong case -EFSCORRUPTED: 487c4a07a4SDarrick J. Wong /* Note the badness but don't abort. */ 497c4a07a4SDarrick J. Wong sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; 507c4a07a4SDarrick J. Wong *error = 0; 5153004ee7SGustavo A. R. Silva fallthrough; 527c4a07a4SDarrick J. Wong default: 53c517b3aaSDarrick J. Wong trace_xchk_file_op_error(sc, ds->dargs.whichfork, 547c4a07a4SDarrick J. Wong xfs_dir2_da_to_db(ds->dargs.geo, 557c4a07a4SDarrick J. Wong ds->state->path.blk[level].blkno), 567c4a07a4SDarrick J. Wong *error, __return_address); 577c4a07a4SDarrick J. Wong break; 587c4a07a4SDarrick J. Wong } 597c4a07a4SDarrick J. Wong return false; 607c4a07a4SDarrick J. Wong } 617c4a07a4SDarrick J. Wong 627c4a07a4SDarrick J. Wong /* 637c4a07a4SDarrick J. Wong * Check for da btree corruption. See the section about handling 647c4a07a4SDarrick J. Wong * operational errors in common.c. 657c4a07a4SDarrick J. Wong */ 667c4a07a4SDarrick J. Wong void 67c517b3aaSDarrick J. Wong xchk_da_set_corrupt( 68c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 697c4a07a4SDarrick J. Wong int level) 707c4a07a4SDarrick J. Wong { 711d8a748aSDarrick J. Wong struct xfs_scrub *sc = ds->sc; 727c4a07a4SDarrick J. Wong 737c4a07a4SDarrick J. Wong sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; 747c4a07a4SDarrick J. Wong 75c517b3aaSDarrick J. Wong trace_xchk_fblock_error(sc, ds->dargs.whichfork, 767c4a07a4SDarrick J. Wong xfs_dir2_da_to_db(ds->dargs.geo, 777c4a07a4SDarrick J. Wong ds->state->path.blk[level].blkno), 787c4a07a4SDarrick J. Wong __return_address); 797c4a07a4SDarrick J. Wong } 807c4a07a4SDarrick J. Wong 81*0ee230deSDarrick J. Wong /* Flag a da btree node in need of optimization. */ 82*0ee230deSDarrick J. Wong void 83*0ee230deSDarrick J. Wong xchk_da_set_preen( 84*0ee230deSDarrick J. Wong struct xchk_da_btree *ds, 85*0ee230deSDarrick J. Wong int level) 86*0ee230deSDarrick J. Wong { 87*0ee230deSDarrick J. Wong struct xfs_scrub *sc = ds->sc; 88*0ee230deSDarrick J. Wong 89*0ee230deSDarrick J. Wong sc->sm->sm_flags |= XFS_SCRUB_OFLAG_PREEN; 90*0ee230deSDarrick J. Wong trace_xchk_fblock_preen(sc, ds->dargs.whichfork, 91*0ee230deSDarrick J. Wong xfs_dir2_da_to_db(ds->dargs.geo, 92*0ee230deSDarrick J. Wong ds->state->path.blk[level].blkno), 93*0ee230deSDarrick J. Wong __return_address); 94*0ee230deSDarrick J. Wong } 95*0ee230deSDarrick J. Wong 96*0ee230deSDarrick J. Wong /* Find an entry at a certain level in a da btree. */ 97649d9d98SChristoph Hellwig static struct xfs_da_node_entry * 98649d9d98SChristoph Hellwig xchk_da_btree_node_entry( 99c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 100649d9d98SChristoph Hellwig int level) 1017c4a07a4SDarrick J. Wong { 102649d9d98SChristoph Hellwig struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 10351908ca7SChristoph Hellwig struct xfs_da3_icnode_hdr hdr; 1047c4a07a4SDarrick J. Wong 105649d9d98SChristoph Hellwig ASSERT(blk->magic == XFS_DA_NODE_MAGIC); 1067c4a07a4SDarrick J. Wong 10751908ca7SChristoph Hellwig xfs_da3_node_hdr_from_disk(ds->sc->mp, &hdr, blk->bp->b_addr); 10851908ca7SChristoph Hellwig return hdr.btree + blk->index; 1097c4a07a4SDarrick J. Wong } 1107c4a07a4SDarrick J. Wong 1117c4a07a4SDarrick J. Wong /* Scrub a da btree hash (key). */ 1127c4a07a4SDarrick J. Wong int 113c517b3aaSDarrick J. Wong xchk_da_btree_hash( 114c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 1157c4a07a4SDarrick J. Wong int level, 1167c4a07a4SDarrick J. Wong __be32 *hashp) 1177c4a07a4SDarrick J. Wong { 1187c4a07a4SDarrick J. Wong struct xfs_da_node_entry *entry; 1197c4a07a4SDarrick J. Wong xfs_dahash_t hash; 1207c4a07a4SDarrick J. Wong xfs_dahash_t parent_hash; 1217c4a07a4SDarrick J. Wong 1227c4a07a4SDarrick J. Wong /* Is this hash in order? */ 1237c4a07a4SDarrick J. Wong hash = be32_to_cpu(*hashp); 1247c4a07a4SDarrick J. Wong if (hash < ds->hashes[level]) 125c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1267c4a07a4SDarrick J. Wong ds->hashes[level] = hash; 1277c4a07a4SDarrick J. Wong 1287c4a07a4SDarrick J. Wong if (level == 0) 1297c4a07a4SDarrick J. Wong return 0; 1307c4a07a4SDarrick J. Wong 1317c4a07a4SDarrick J. Wong /* Is this hash no larger than the parent hash? */ 132649d9d98SChristoph Hellwig entry = xchk_da_btree_node_entry(ds, level - 1); 1337c4a07a4SDarrick J. Wong parent_hash = be32_to_cpu(entry->hashval); 1347c4a07a4SDarrick J. Wong if (parent_hash < hash) 135c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1367c4a07a4SDarrick J. Wong 1377c4a07a4SDarrick J. Wong return 0; 1387c4a07a4SDarrick J. Wong } 1397c4a07a4SDarrick J. Wong 1407c4a07a4SDarrick J. Wong /* 1417c4a07a4SDarrick J. Wong * Check a da btree pointer. Returns true if it's ok to use this 1427c4a07a4SDarrick J. Wong * pointer. 1437c4a07a4SDarrick J. Wong */ 1447c4a07a4SDarrick J. Wong STATIC bool 145c517b3aaSDarrick J. Wong xchk_da_btree_ptr_ok( 146c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 1477c4a07a4SDarrick J. Wong int level, 1487c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 1497c4a07a4SDarrick J. Wong { 1507c4a07a4SDarrick J. Wong if (blkno < ds->lowest || (ds->highest != 0 && blkno >= ds->highest)) { 151c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1527c4a07a4SDarrick J. Wong return false; 1537c4a07a4SDarrick J. Wong } 1547c4a07a4SDarrick J. Wong 1557c4a07a4SDarrick J. Wong return true; 1567c4a07a4SDarrick J. Wong } 1577c4a07a4SDarrick J. Wong 1587c4a07a4SDarrick J. Wong /* 1597c4a07a4SDarrick J. Wong * The da btree scrubber can handle leaf1 blocks as a degenerate 1607c4a07a4SDarrick J. Wong * form of leafn blocks. Since the regular da code doesn't handle 1617c4a07a4SDarrick J. Wong * leaf1, we must multiplex the verifiers. 1627c4a07a4SDarrick J. Wong */ 1637c4a07a4SDarrick J. Wong static void 164c517b3aaSDarrick J. Wong xchk_da_btree_read_verify( 1657c4a07a4SDarrick J. Wong struct xfs_buf *bp) 1667c4a07a4SDarrick J. Wong { 1677c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 1687c4a07a4SDarrick J. Wong 1697c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 1707c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 1717c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 1727c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 1737c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 1747c4a07a4SDarrick J. Wong return; 1757c4a07a4SDarrick J. Wong default: 1767c4a07a4SDarrick J. Wong /* 1777c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 1787c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 1797c4a07a4SDarrick J. Wong */ 1807c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 1817c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 1827c4a07a4SDarrick J. Wong return; 1837c4a07a4SDarrick J. Wong } 1847c4a07a4SDarrick J. Wong } 1857c4a07a4SDarrick J. Wong static void 186c517b3aaSDarrick J. Wong xchk_da_btree_write_verify( 1877c4a07a4SDarrick J. Wong struct xfs_buf *bp) 1887c4a07a4SDarrick J. Wong { 1897c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 1907c4a07a4SDarrick J. Wong 1917c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 1927c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 1937c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 1947c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 1957c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 1967c4a07a4SDarrick J. Wong return; 1977c4a07a4SDarrick J. Wong default: 1987c4a07a4SDarrick J. Wong /* 1997c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 2007c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 2017c4a07a4SDarrick J. Wong */ 2027c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 2037c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 2047c4a07a4SDarrick J. Wong return; 2057c4a07a4SDarrick J. Wong } 2067c4a07a4SDarrick J. Wong } 207cf1b0b8bSDarrick J. Wong static void * 208c517b3aaSDarrick J. Wong xchk_da_btree_verify( 209cf1b0b8bSDarrick J. Wong struct xfs_buf *bp) 210cf1b0b8bSDarrick J. Wong { 211cf1b0b8bSDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 212cf1b0b8bSDarrick J. Wong 213cf1b0b8bSDarrick J. Wong switch (be16_to_cpu(info->magic)) { 214cf1b0b8bSDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 215cf1b0b8bSDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 216cf1b0b8bSDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 217cf1b0b8bSDarrick J. Wong return bp->b_ops->verify_struct(bp); 218cf1b0b8bSDarrick J. Wong default: 219cf1b0b8bSDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 220cf1b0b8bSDarrick J. Wong return bp->b_ops->verify_struct(bp); 221cf1b0b8bSDarrick J. Wong } 222cf1b0b8bSDarrick J. Wong } 2237c4a07a4SDarrick J. Wong 224c517b3aaSDarrick J. Wong static const struct xfs_buf_ops xchk_da_btree_buf_ops = { 225c517b3aaSDarrick J. Wong .name = "xchk_da_btree", 226c517b3aaSDarrick J. Wong .verify_read = xchk_da_btree_read_verify, 227c517b3aaSDarrick J. Wong .verify_write = xchk_da_btree_write_verify, 228c517b3aaSDarrick J. Wong .verify_struct = xchk_da_btree_verify, 2297c4a07a4SDarrick J. Wong }; 2307c4a07a4SDarrick J. Wong 2317c4a07a4SDarrick J. Wong /* Check a block's sibling. */ 2327c4a07a4SDarrick J. Wong STATIC int 233c517b3aaSDarrick J. Wong xchk_da_btree_block_check_sibling( 234c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 2357c4a07a4SDarrick J. Wong int level, 2367c4a07a4SDarrick J. Wong int direction, 2377c4a07a4SDarrick J. Wong xfs_dablk_t sibling) 2387c4a07a4SDarrick J. Wong { 239afbabf56SDarrick J. Wong struct xfs_da_state_path *path = &ds->state->path; 240afbabf56SDarrick J. Wong struct xfs_da_state_path *altpath = &ds->state->altpath; 2417c4a07a4SDarrick J. Wong int retval; 242afbabf56SDarrick J. Wong int plevel; 2437c4a07a4SDarrick J. Wong int error; 2447c4a07a4SDarrick J. Wong 245afbabf56SDarrick J. Wong memcpy(altpath, path, sizeof(ds->state->altpath)); 2467c4a07a4SDarrick J. Wong 2477c4a07a4SDarrick J. Wong /* 2487c4a07a4SDarrick J. Wong * If the pointer is null, we shouldn't be able to move the upper 2497c4a07a4SDarrick J. Wong * level pointer anywhere. 2507c4a07a4SDarrick J. Wong */ 2517c4a07a4SDarrick J. Wong if (sibling == 0) { 252afbabf56SDarrick J. Wong error = xfs_da3_path_shift(ds->state, altpath, direction, 253afbabf56SDarrick J. Wong false, &retval); 2547c4a07a4SDarrick J. Wong if (error == 0 && retval == 0) 255c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 2567c4a07a4SDarrick J. Wong error = 0; 2577c4a07a4SDarrick J. Wong goto out; 2587c4a07a4SDarrick J. Wong } 2597c4a07a4SDarrick J. Wong 2607c4a07a4SDarrick J. Wong /* Move the alternate cursor one block in the direction given. */ 261afbabf56SDarrick J. Wong error = xfs_da3_path_shift(ds->state, altpath, direction, false, 262afbabf56SDarrick J. Wong &retval); 263c517b3aaSDarrick J. Wong if (!xchk_da_process_error(ds, level, &error)) 264afbabf56SDarrick J. Wong goto out; 2657c4a07a4SDarrick J. Wong if (retval) { 266c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 267afbabf56SDarrick J. Wong goto out; 2687c4a07a4SDarrick J. Wong } 269afbabf56SDarrick J. Wong if (altpath->blk[level].bp) 270afbabf56SDarrick J. Wong xchk_buffer_recheck(ds->sc, altpath->blk[level].bp); 2717c4a07a4SDarrick J. Wong 2727c4a07a4SDarrick J. Wong /* Compare upper level pointer to sibling pointer. */ 273afbabf56SDarrick J. Wong if (altpath->blk[level].blkno != sibling) 274c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 275afbabf56SDarrick J. Wong 2767c4a07a4SDarrick J. Wong out: 277afbabf56SDarrick J. Wong /* Free all buffers in the altpath that aren't referenced from path. */ 278afbabf56SDarrick J. Wong for (plevel = 0; plevel < altpath->active; plevel++) { 279afbabf56SDarrick J. Wong if (altpath->blk[plevel].bp == NULL || 280afbabf56SDarrick J. Wong (plevel < path->active && 281afbabf56SDarrick J. Wong altpath->blk[plevel].bp == path->blk[plevel].bp)) 282afbabf56SDarrick J. Wong continue; 283afbabf56SDarrick J. Wong 284afbabf56SDarrick J. Wong xfs_trans_brelse(ds->dargs.trans, altpath->blk[plevel].bp); 285afbabf56SDarrick J. Wong altpath->blk[plevel].bp = NULL; 286afbabf56SDarrick J. Wong } 287afbabf56SDarrick J. Wong 2887c4a07a4SDarrick J. Wong return error; 2897c4a07a4SDarrick J. Wong } 2907c4a07a4SDarrick J. Wong 2917c4a07a4SDarrick J. Wong /* Check a block's sibling pointers. */ 2927c4a07a4SDarrick J. Wong STATIC int 293c517b3aaSDarrick J. Wong xchk_da_btree_block_check_siblings( 294c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 2957c4a07a4SDarrick J. Wong int level, 2967c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *hdr) 2977c4a07a4SDarrick J. Wong { 2987c4a07a4SDarrick J. Wong xfs_dablk_t forw; 2997c4a07a4SDarrick J. Wong xfs_dablk_t back; 3007c4a07a4SDarrick J. Wong int error = 0; 3017c4a07a4SDarrick J. Wong 3027c4a07a4SDarrick J. Wong forw = be32_to_cpu(hdr->forw); 3037c4a07a4SDarrick J. Wong back = be32_to_cpu(hdr->back); 3047c4a07a4SDarrick J. Wong 3057c4a07a4SDarrick J. Wong /* Top level blocks should not have sibling pointers. */ 3067c4a07a4SDarrick J. Wong if (level == 0) { 3077c4a07a4SDarrick J. Wong if (forw != 0 || back != 0) 308c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3097c4a07a4SDarrick J. Wong return 0; 3107c4a07a4SDarrick J. Wong } 3117c4a07a4SDarrick J. Wong 3127c4a07a4SDarrick J. Wong /* 3137c4a07a4SDarrick J. Wong * Check back (left) and forw (right) pointers. These functions 3147c4a07a4SDarrick J. Wong * absorb error codes for us. 3157c4a07a4SDarrick J. Wong */ 316c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_sibling(ds, level, 0, back); 3177c4a07a4SDarrick J. Wong if (error) 3187c4a07a4SDarrick J. Wong goto out; 319c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_sibling(ds, level, 1, forw); 3207c4a07a4SDarrick J. Wong 3217c4a07a4SDarrick J. Wong out: 3227c4a07a4SDarrick J. Wong memset(&ds->state->altpath, 0, sizeof(ds->state->altpath)); 3237c4a07a4SDarrick J. Wong return error; 3247c4a07a4SDarrick J. Wong } 3257c4a07a4SDarrick J. Wong 3267c4a07a4SDarrick J. Wong /* Load a dir/attribute block from a btree. */ 3277c4a07a4SDarrick J. Wong STATIC int 328c517b3aaSDarrick J. Wong xchk_da_btree_block( 329c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 3307c4a07a4SDarrick J. Wong int level, 3317c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 3327c4a07a4SDarrick J. Wong { 3337c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blk; 3347c4a07a4SDarrick J. Wong struct xfs_da_intnode *node; 3357c4a07a4SDarrick J. Wong struct xfs_da_node_entry *btree; 3367c4a07a4SDarrick J. Wong struct xfs_da3_blkinfo *hdr3; 3377c4a07a4SDarrick J. Wong struct xfs_da_args *dargs = &ds->dargs; 3387c4a07a4SDarrick J. Wong struct xfs_inode *ip = ds->dargs.dp; 339f4887fbcSDarrick J. Wong xfs_failaddr_t fa; 3407c4a07a4SDarrick J. Wong xfs_ino_t owner; 3417c4a07a4SDarrick J. Wong int *pmaxrecs; 3427c4a07a4SDarrick J. Wong struct xfs_da3_icnode_hdr nodehdr; 3430dca060cSDarrick J. Wong int error = 0; 3447c4a07a4SDarrick J. Wong 3457c4a07a4SDarrick J. Wong blk = &ds->state->path.blk[level]; 3467c4a07a4SDarrick J. Wong ds->state->path.active = level + 1; 3477c4a07a4SDarrick J. Wong 3487c4a07a4SDarrick J. Wong /* Release old block. */ 3497c4a07a4SDarrick J. Wong if (blk->bp) { 3507c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 3517c4a07a4SDarrick J. Wong blk->bp = NULL; 3527c4a07a4SDarrick J. Wong } 3537c4a07a4SDarrick J. Wong 3547c4a07a4SDarrick J. Wong /* Check the pointer. */ 3557c4a07a4SDarrick J. Wong blk->blkno = blkno; 356c517b3aaSDarrick J. Wong if (!xchk_da_btree_ptr_ok(ds, level, blkno)) 3577c4a07a4SDarrick J. Wong goto out_nobuf; 3587c4a07a4SDarrick J. Wong 3597c4a07a4SDarrick J. Wong /* Read the buffer. */ 360cd2c9f1bSChristoph Hellwig error = xfs_da_read_buf(dargs->trans, dargs->dp, blk->blkno, 361cd2c9f1bSChristoph Hellwig XFS_DABUF_MAP_HOLE_OK, &blk->bp, dargs->whichfork, 362c517b3aaSDarrick J. Wong &xchk_da_btree_buf_ops); 363c517b3aaSDarrick J. Wong if (!xchk_da_process_error(ds, level, &error)) 3647c4a07a4SDarrick J. Wong goto out_nobuf; 365cf1b0b8bSDarrick J. Wong if (blk->bp) 366c517b3aaSDarrick J. Wong xchk_buffer_recheck(ds->sc, blk->bp); 3677c4a07a4SDarrick J. Wong 3687c4a07a4SDarrick J. Wong /* 3697c4a07a4SDarrick J. Wong * We didn't find a dir btree root block, which means that 3707c4a07a4SDarrick J. Wong * there's no LEAF1/LEAFN tree (at least not where it's supposed 3717c4a07a4SDarrick J. Wong * to be), so jump out now. 3727c4a07a4SDarrick J. Wong */ 3737c4a07a4SDarrick J. Wong if (ds->dargs.whichfork == XFS_DATA_FORK && level == 0 && 3747c4a07a4SDarrick J. Wong blk->bp == NULL) 3757c4a07a4SDarrick J. Wong goto out_nobuf; 3767c4a07a4SDarrick J. Wong 3777c4a07a4SDarrick J. Wong /* It's /not/ ok for attr trees not to have a da btree. */ 3787c4a07a4SDarrick J. Wong if (blk->bp == NULL) { 379c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3807c4a07a4SDarrick J. Wong goto out_nobuf; 3817c4a07a4SDarrick J. Wong } 3827c4a07a4SDarrick J. Wong 3837c4a07a4SDarrick J. Wong hdr3 = blk->bp->b_addr; 3847c4a07a4SDarrick J. Wong blk->magic = be16_to_cpu(hdr3->hdr.magic); 3857c4a07a4SDarrick J. Wong pmaxrecs = &ds->maxrecs[level]; 3867c4a07a4SDarrick J. Wong 3874da4b10bSDarrick J. Wong /* We only started zeroing the header on v5 filesystems. */ 38838c26bfdSDave Chinner if (xfs_has_crc(ds->sc->mp) && hdr3->hdr.pad) 389c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3907c4a07a4SDarrick J. Wong 3917c4a07a4SDarrick J. Wong /* Check the owner. */ 39238c26bfdSDave Chinner if (xfs_has_crc(ip->i_mount)) { 3937c4a07a4SDarrick J. Wong owner = be64_to_cpu(hdr3->owner); 3947c4a07a4SDarrick J. Wong if (owner != ip->i_ino) 395c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3967c4a07a4SDarrick J. Wong } 3977c4a07a4SDarrick J. Wong 3987c4a07a4SDarrick J. Wong /* Check the siblings. */ 399c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_siblings(ds, level, &hdr3->hdr); 4007c4a07a4SDarrick J. Wong if (error) 4017c4a07a4SDarrick J. Wong goto out; 4027c4a07a4SDarrick J. Wong 4037c4a07a4SDarrick J. Wong /* Interpret the buffer. */ 4047c4a07a4SDarrick J. Wong switch (blk->magic) { 4057c4a07a4SDarrick J. Wong case XFS_ATTR_LEAF_MAGIC: 4067c4a07a4SDarrick J. Wong case XFS_ATTR3_LEAF_MAGIC: 4077c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4087c4a07a4SDarrick J. Wong XFS_BLFT_ATTR_LEAF_BUF); 4097c4a07a4SDarrick J. Wong blk->magic = XFS_ATTR_LEAF_MAGIC; 4107c4a07a4SDarrick J. Wong blk->hashval = xfs_attr_leaf_lasthash(blk->bp, pmaxrecs); 4117c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 412c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4137c4a07a4SDarrick J. Wong break; 4147c4a07a4SDarrick J. Wong case XFS_DIR2_LEAFN_MAGIC: 4157c4a07a4SDarrick J. Wong case XFS_DIR3_LEAFN_MAGIC: 4167c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4177c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAFN_BUF); 4187c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAFN_MAGIC; 4197c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4207c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 421c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4227c4a07a4SDarrick J. Wong break; 4237c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 4247c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 4257c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4267c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAF1_BUF); 4277c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAF1_MAGIC; 4287c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4297c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 430c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4317c4a07a4SDarrick J. Wong break; 4327c4a07a4SDarrick J. Wong case XFS_DA_NODE_MAGIC: 4337c4a07a4SDarrick J. Wong case XFS_DA3_NODE_MAGIC: 4347c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4357c4a07a4SDarrick J. Wong XFS_BLFT_DA_NODE_BUF); 4367c4a07a4SDarrick J. Wong blk->magic = XFS_DA_NODE_MAGIC; 4377c4a07a4SDarrick J. Wong node = blk->bp->b_addr; 438f475dc4dSChristoph Hellwig xfs_da3_node_hdr_from_disk(ip->i_mount, &nodehdr, node); 43951908ca7SChristoph Hellwig btree = nodehdr.btree; 4407c4a07a4SDarrick J. Wong *pmaxrecs = nodehdr.count; 4417c4a07a4SDarrick J. Wong blk->hashval = be32_to_cpu(btree[*pmaxrecs - 1].hashval); 4427c4a07a4SDarrick J. Wong if (level == 0) { 4437c4a07a4SDarrick J. Wong if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) { 444c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4457c4a07a4SDarrick J. Wong goto out_freebp; 4467c4a07a4SDarrick J. Wong } 4477c4a07a4SDarrick J. Wong ds->tree_level = nodehdr.level; 4487c4a07a4SDarrick J. Wong } else { 4497c4a07a4SDarrick J. Wong if (ds->tree_level != nodehdr.level) { 450c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4517c4a07a4SDarrick J. Wong goto out_freebp; 4527c4a07a4SDarrick J. Wong } 4537c4a07a4SDarrick J. Wong } 4547c4a07a4SDarrick J. Wong 4557c4a07a4SDarrick J. Wong /* XXX: Check hdr3.pad32 once we know how to fix it. */ 4567c4a07a4SDarrick J. Wong break; 4577c4a07a4SDarrick J. Wong default: 458c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4597c4a07a4SDarrick J. Wong goto out_freebp; 4607c4a07a4SDarrick J. Wong } 4617c4a07a4SDarrick J. Wong 462f4887fbcSDarrick J. Wong fa = xfs_da3_header_check(blk->bp, dargs->owner); 463f4887fbcSDarrick J. Wong if (fa) { 464f4887fbcSDarrick J. Wong xchk_da_set_corrupt(ds, level); 465f4887fbcSDarrick J. Wong goto out_freebp; 466f4887fbcSDarrick J. Wong } 467f4887fbcSDarrick J. Wong 468e581c939SDarrick J. Wong /* 469e581c939SDarrick J. Wong * If we've been handed a block that is below the dabtree root, does 470e581c939SDarrick J. Wong * its hashval match what the parent block expected to see? 471e581c939SDarrick J. Wong */ 472e581c939SDarrick J. Wong if (level > 0) { 473e581c939SDarrick J. Wong struct xfs_da_node_entry *key; 474e581c939SDarrick J. Wong 475e581c939SDarrick J. Wong key = xchk_da_btree_node_entry(ds, level - 1); 476e581c939SDarrick J. Wong if (be32_to_cpu(key->hashval) != blk->hashval) { 477e581c939SDarrick J. Wong xchk_da_set_corrupt(ds, level); 478e581c939SDarrick J. Wong goto out_freebp; 479e581c939SDarrick J. Wong } 480e581c939SDarrick J. Wong } 481e581c939SDarrick J. Wong 4827c4a07a4SDarrick J. Wong out: 4837c4a07a4SDarrick J. Wong return error; 4847c4a07a4SDarrick J. Wong out_freebp: 4857c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 4867c4a07a4SDarrick J. Wong blk->bp = NULL; 4877c4a07a4SDarrick J. Wong out_nobuf: 4887c4a07a4SDarrick J. Wong blk->blkno = 0; 4897c4a07a4SDarrick J. Wong return error; 4907c4a07a4SDarrick J. Wong } 4917c4a07a4SDarrick J. Wong 4927c4a07a4SDarrick J. Wong /* Visit all nodes and leaves of a da btree. */ 4937c4a07a4SDarrick J. Wong int 494c517b3aaSDarrick J. Wong xchk_da_btree( 4951d8a748aSDarrick J. Wong struct xfs_scrub *sc, 4967c4a07a4SDarrick J. Wong int whichfork, 497c517b3aaSDarrick J. Wong xchk_da_btree_rec_fn scrub_fn, 49813791d3bSDarrick J. Wong void *private) 4997c4a07a4SDarrick J. Wong { 500510a28e1SDarrick J. Wong struct xchk_da_btree *ds; 5017c4a07a4SDarrick J. Wong struct xfs_mount *mp = sc->mp; 5027c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blks; 5037c4a07a4SDarrick J. Wong struct xfs_da_node_entry *key; 5047c4a07a4SDarrick J. Wong xfs_dablk_t blkno; 5057c4a07a4SDarrick J. Wong int level; 5067c4a07a4SDarrick J. Wong int error; 5077c4a07a4SDarrick J. Wong 5087c4a07a4SDarrick J. Wong /* Skip short format data structures; no btree to scan. */ 509732436efSDarrick J. Wong if (!xfs_ifork_has_extents(xfs_ifork_ptr(sc->ip, whichfork))) 5107c4a07a4SDarrick J. Wong return 0; 5117c4a07a4SDarrick J. Wong 5127c4a07a4SDarrick J. Wong /* Set up initial da state. */ 513306195f3SDarrick J. Wong ds = kzalloc(sizeof(struct xchk_da_btree), XCHK_GFP_FLAGS); 514510a28e1SDarrick J. Wong if (!ds) 515510a28e1SDarrick J. Wong return -ENOMEM; 516510a28e1SDarrick J. Wong ds->dargs.dp = sc->ip; 517510a28e1SDarrick J. Wong ds->dargs.whichfork = whichfork; 518510a28e1SDarrick J. Wong ds->dargs.trans = sc->tp; 519510a28e1SDarrick J. Wong ds->dargs.op_flags = XFS_DA_OP_OKNOENT; 5209eef772fSDarrick J. Wong ds->dargs.owner = sc->ip->i_ino; 521510a28e1SDarrick J. Wong ds->state = xfs_da_state_alloc(&ds->dargs); 522510a28e1SDarrick J. Wong ds->sc = sc; 523510a28e1SDarrick J. Wong ds->private = private; 5247c4a07a4SDarrick J. Wong if (whichfork == XFS_ATTR_FORK) { 525510a28e1SDarrick J. Wong ds->dargs.geo = mp->m_attr_geo; 526510a28e1SDarrick J. Wong ds->lowest = 0; 527510a28e1SDarrick J. Wong ds->highest = 0; 5287c4a07a4SDarrick J. Wong } else { 529510a28e1SDarrick J. Wong ds->dargs.geo = mp->m_dir_geo; 530510a28e1SDarrick J. Wong ds->lowest = ds->dargs.geo->leafblk; 531510a28e1SDarrick J. Wong ds->highest = ds->dargs.geo->freeblk; 5327c4a07a4SDarrick J. Wong } 533510a28e1SDarrick J. Wong blkno = ds->lowest; 5347c4a07a4SDarrick J. Wong level = 0; 5357c4a07a4SDarrick J. Wong 5367c4a07a4SDarrick J. Wong /* Find the root of the da tree, if present. */ 537510a28e1SDarrick J. Wong blks = ds->state->path.blk; 538510a28e1SDarrick J. Wong error = xchk_da_btree_block(ds, level, blkno); 5397c4a07a4SDarrick J. Wong if (error) 5407c4a07a4SDarrick J. Wong goto out_state; 5417c4a07a4SDarrick J. Wong /* 542510a28e1SDarrick J. Wong * We didn't find a block at ds->lowest, which means that there's 5437c4a07a4SDarrick J. Wong * no LEAF1/LEAFN tree (at least not where it's supposed to be), 5447c4a07a4SDarrick J. Wong * so jump out now. 5457c4a07a4SDarrick J. Wong */ 5467c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5477c4a07a4SDarrick J. Wong goto out_state; 5487c4a07a4SDarrick J. Wong 5497c4a07a4SDarrick J. Wong blks[level].index = 0; 5507c4a07a4SDarrick J. Wong while (level >= 0 && level < XFS_DA_NODE_MAXDEPTH) { 5517c4a07a4SDarrick J. Wong /* Handle leaf block. */ 5527c4a07a4SDarrick J. Wong if (blks[level].magic != XFS_DA_NODE_MAGIC) { 5537c4a07a4SDarrick J. Wong /* End of leaf, pop back towards the root. */ 554510a28e1SDarrick J. Wong if (blks[level].index >= ds->maxrecs[level]) { 5557c4a07a4SDarrick J. Wong if (level > 0) 5567c4a07a4SDarrick J. Wong blks[level - 1].index++; 557510a28e1SDarrick J. Wong ds->tree_level++; 5587c4a07a4SDarrick J. Wong level--; 5597c4a07a4SDarrick J. Wong continue; 5607c4a07a4SDarrick J. Wong } 5617c4a07a4SDarrick J. Wong 5627c4a07a4SDarrick J. Wong /* Dispatch record scrubbing. */ 563510a28e1SDarrick J. Wong error = scrub_fn(ds, level); 5647c4a07a4SDarrick J. Wong if (error) 5657c4a07a4SDarrick J. Wong break; 566c517b3aaSDarrick J. Wong if (xchk_should_terminate(sc, &error) || 5677c4a07a4SDarrick J. Wong (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 5687c4a07a4SDarrick J. Wong break; 5697c4a07a4SDarrick J. Wong 5707c4a07a4SDarrick J. Wong blks[level].index++; 5717c4a07a4SDarrick J. Wong continue; 5727c4a07a4SDarrick J. Wong } 5737c4a07a4SDarrick J. Wong 5747c4a07a4SDarrick J. Wong 5757c4a07a4SDarrick J. Wong /* End of node, pop back towards the root. */ 576510a28e1SDarrick J. Wong if (blks[level].index >= ds->maxrecs[level]) { 5777c4a07a4SDarrick J. Wong if (level > 0) 5787c4a07a4SDarrick J. Wong blks[level - 1].index++; 579510a28e1SDarrick J. Wong ds->tree_level++; 5807c4a07a4SDarrick J. Wong level--; 5817c4a07a4SDarrick J. Wong continue; 5827c4a07a4SDarrick J. Wong } 5837c4a07a4SDarrick J. Wong 5847c4a07a4SDarrick J. Wong /* Hashes in order for scrub? */ 585510a28e1SDarrick J. Wong key = xchk_da_btree_node_entry(ds, level); 586510a28e1SDarrick J. Wong error = xchk_da_btree_hash(ds, level, &key->hashval); 5877c4a07a4SDarrick J. Wong if (error) 5887c4a07a4SDarrick J. Wong goto out; 5897c4a07a4SDarrick J. Wong 5907c4a07a4SDarrick J. Wong /* Drill another level deeper. */ 5917c4a07a4SDarrick J. Wong blkno = be32_to_cpu(key->before); 5927c4a07a4SDarrick J. Wong level++; 593228de124SDarrick J. Wong if (level >= XFS_DA_NODE_MAXDEPTH) { 594228de124SDarrick J. Wong /* Too deep! */ 595510a28e1SDarrick J. Wong xchk_da_set_corrupt(ds, level - 1); 596228de124SDarrick J. Wong break; 597228de124SDarrick J. Wong } 598510a28e1SDarrick J. Wong ds->tree_level--; 599510a28e1SDarrick J. Wong error = xchk_da_btree_block(ds, level, blkno); 6007c4a07a4SDarrick J. Wong if (error) 6017c4a07a4SDarrick J. Wong goto out; 6027c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 6037c4a07a4SDarrick J. Wong goto out; 6047c4a07a4SDarrick J. Wong 6057c4a07a4SDarrick J. Wong blks[level].index = 0; 6067c4a07a4SDarrick J. Wong } 6077c4a07a4SDarrick J. Wong 6087c4a07a4SDarrick J. Wong out: 6097c4a07a4SDarrick J. Wong /* Release all the buffers we're tracking. */ 6107c4a07a4SDarrick J. Wong for (level = 0; level < XFS_DA_NODE_MAXDEPTH; level++) { 6117c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 6127c4a07a4SDarrick J. Wong continue; 6137c4a07a4SDarrick J. Wong xfs_trans_brelse(sc->tp, blks[level].bp); 6147c4a07a4SDarrick J. Wong blks[level].bp = NULL; 6157c4a07a4SDarrick J. Wong } 6167c4a07a4SDarrick J. Wong 6177c4a07a4SDarrick J. Wong out_state: 618510a28e1SDarrick J. Wong xfs_da_state_free(ds->state); 619306195f3SDarrick J. Wong kfree(ds); 6207c4a07a4SDarrick J. Wong return error; 6217c4a07a4SDarrick J. Wong } 622