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 81649d9d98SChristoph Hellwig static struct xfs_da_node_entry * 82649d9d98SChristoph Hellwig xchk_da_btree_node_entry( 83c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 84649d9d98SChristoph Hellwig int level) 857c4a07a4SDarrick J. Wong { 86649d9d98SChristoph Hellwig struct xfs_da_state_blk *blk = &ds->state->path.blk[level]; 8751908ca7SChristoph Hellwig struct xfs_da3_icnode_hdr hdr; 887c4a07a4SDarrick J. Wong 89649d9d98SChristoph Hellwig ASSERT(blk->magic == XFS_DA_NODE_MAGIC); 907c4a07a4SDarrick J. Wong 9151908ca7SChristoph Hellwig xfs_da3_node_hdr_from_disk(ds->sc->mp, &hdr, blk->bp->b_addr); 9251908ca7SChristoph Hellwig return hdr.btree + blk->index; 937c4a07a4SDarrick J. Wong } 947c4a07a4SDarrick J. Wong 957c4a07a4SDarrick J. Wong /* Scrub a da btree hash (key). */ 967c4a07a4SDarrick J. Wong int 97c517b3aaSDarrick J. Wong xchk_da_btree_hash( 98c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 997c4a07a4SDarrick J. Wong int level, 1007c4a07a4SDarrick J. Wong __be32 *hashp) 1017c4a07a4SDarrick J. Wong { 1027c4a07a4SDarrick J. Wong struct xfs_da_node_entry *entry; 1037c4a07a4SDarrick J. Wong xfs_dahash_t hash; 1047c4a07a4SDarrick J. Wong xfs_dahash_t parent_hash; 1057c4a07a4SDarrick J. Wong 1067c4a07a4SDarrick J. Wong /* Is this hash in order? */ 1077c4a07a4SDarrick J. Wong hash = be32_to_cpu(*hashp); 1087c4a07a4SDarrick J. Wong if (hash < ds->hashes[level]) 109c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1107c4a07a4SDarrick J. Wong ds->hashes[level] = hash; 1117c4a07a4SDarrick J. Wong 1127c4a07a4SDarrick J. Wong if (level == 0) 1137c4a07a4SDarrick J. Wong return 0; 1147c4a07a4SDarrick J. Wong 1157c4a07a4SDarrick J. Wong /* Is this hash no larger than the parent hash? */ 116649d9d98SChristoph Hellwig entry = xchk_da_btree_node_entry(ds, level - 1); 1177c4a07a4SDarrick J. Wong parent_hash = be32_to_cpu(entry->hashval); 1187c4a07a4SDarrick J. Wong if (parent_hash < hash) 119c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1207c4a07a4SDarrick J. Wong 1217c4a07a4SDarrick J. Wong return 0; 1227c4a07a4SDarrick J. Wong } 1237c4a07a4SDarrick J. Wong 1247c4a07a4SDarrick J. Wong /* 1257c4a07a4SDarrick J. Wong * Check a da btree pointer. Returns true if it's ok to use this 1267c4a07a4SDarrick J. Wong * pointer. 1277c4a07a4SDarrick J. Wong */ 1287c4a07a4SDarrick J. Wong STATIC bool 129c517b3aaSDarrick J. Wong xchk_da_btree_ptr_ok( 130c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 1317c4a07a4SDarrick J. Wong int level, 1327c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 1337c4a07a4SDarrick J. Wong { 1347c4a07a4SDarrick J. Wong if (blkno < ds->lowest || (ds->highest != 0 && blkno >= ds->highest)) { 135c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 1367c4a07a4SDarrick J. Wong return false; 1377c4a07a4SDarrick J. Wong } 1387c4a07a4SDarrick J. Wong 1397c4a07a4SDarrick J. Wong return true; 1407c4a07a4SDarrick J. Wong } 1417c4a07a4SDarrick J. Wong 1427c4a07a4SDarrick J. Wong /* 1437c4a07a4SDarrick J. Wong * The da btree scrubber can handle leaf1 blocks as a degenerate 1447c4a07a4SDarrick J. Wong * form of leafn blocks. Since the regular da code doesn't handle 1457c4a07a4SDarrick J. Wong * leaf1, we must multiplex the verifiers. 1467c4a07a4SDarrick J. Wong */ 1477c4a07a4SDarrick J. Wong static void 148c517b3aaSDarrick J. Wong xchk_da_btree_read_verify( 1497c4a07a4SDarrick J. Wong struct xfs_buf *bp) 1507c4a07a4SDarrick J. Wong { 1517c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 1527c4a07a4SDarrick J. Wong 1537c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 1547c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 1557c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 1567c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 1577c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 1587c4a07a4SDarrick J. Wong return; 1597c4a07a4SDarrick J. Wong default: 1607c4a07a4SDarrick J. Wong /* 1617c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 1627c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 1637c4a07a4SDarrick J. Wong */ 1647c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 1657c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 1667c4a07a4SDarrick J. Wong return; 1677c4a07a4SDarrick J. Wong } 1687c4a07a4SDarrick J. Wong } 1697c4a07a4SDarrick J. Wong static void 170c517b3aaSDarrick J. Wong xchk_da_btree_write_verify( 1717c4a07a4SDarrick J. Wong struct xfs_buf *bp) 1727c4a07a4SDarrick J. Wong { 1737c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 1747c4a07a4SDarrick J. Wong 1757c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 1767c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 1777c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 1787c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 1797c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 1807c4a07a4SDarrick J. Wong return; 1817c4a07a4SDarrick J. Wong default: 1827c4a07a4SDarrick J. Wong /* 1837c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 1847c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 1857c4a07a4SDarrick J. Wong */ 1867c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 1877c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 1887c4a07a4SDarrick J. Wong return; 1897c4a07a4SDarrick J. Wong } 1907c4a07a4SDarrick J. Wong } 191cf1b0b8bSDarrick J. Wong static void * 192c517b3aaSDarrick J. Wong xchk_da_btree_verify( 193cf1b0b8bSDarrick J. Wong struct xfs_buf *bp) 194cf1b0b8bSDarrick J. Wong { 195cf1b0b8bSDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 196cf1b0b8bSDarrick J. Wong 197cf1b0b8bSDarrick J. Wong switch (be16_to_cpu(info->magic)) { 198cf1b0b8bSDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 199cf1b0b8bSDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 200cf1b0b8bSDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 201cf1b0b8bSDarrick J. Wong return bp->b_ops->verify_struct(bp); 202cf1b0b8bSDarrick J. Wong default: 203cf1b0b8bSDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 204cf1b0b8bSDarrick J. Wong return bp->b_ops->verify_struct(bp); 205cf1b0b8bSDarrick J. Wong } 206cf1b0b8bSDarrick J. Wong } 2077c4a07a4SDarrick J. Wong 208c517b3aaSDarrick J. Wong static const struct xfs_buf_ops xchk_da_btree_buf_ops = { 209c517b3aaSDarrick J. Wong .name = "xchk_da_btree", 210c517b3aaSDarrick J. Wong .verify_read = xchk_da_btree_read_verify, 211c517b3aaSDarrick J. Wong .verify_write = xchk_da_btree_write_verify, 212c517b3aaSDarrick J. Wong .verify_struct = xchk_da_btree_verify, 2137c4a07a4SDarrick J. Wong }; 2147c4a07a4SDarrick J. Wong 2157c4a07a4SDarrick J. Wong /* Check a block's sibling. */ 2167c4a07a4SDarrick J. Wong STATIC int 217c517b3aaSDarrick J. Wong xchk_da_btree_block_check_sibling( 218c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 2197c4a07a4SDarrick J. Wong int level, 2207c4a07a4SDarrick J. Wong int direction, 2217c4a07a4SDarrick J. Wong xfs_dablk_t sibling) 2227c4a07a4SDarrick J. Wong { 223afbabf56SDarrick J. Wong struct xfs_da_state_path *path = &ds->state->path; 224afbabf56SDarrick J. Wong struct xfs_da_state_path *altpath = &ds->state->altpath; 2257c4a07a4SDarrick J. Wong int retval; 226afbabf56SDarrick J. Wong int plevel; 2277c4a07a4SDarrick J. Wong int error; 2287c4a07a4SDarrick J. Wong 229afbabf56SDarrick J. Wong memcpy(altpath, path, sizeof(ds->state->altpath)); 2307c4a07a4SDarrick J. Wong 2317c4a07a4SDarrick J. Wong /* 2327c4a07a4SDarrick J. Wong * If the pointer is null, we shouldn't be able to move the upper 2337c4a07a4SDarrick J. Wong * level pointer anywhere. 2347c4a07a4SDarrick J. Wong */ 2357c4a07a4SDarrick J. Wong if (sibling == 0) { 236afbabf56SDarrick J. Wong error = xfs_da3_path_shift(ds->state, altpath, direction, 237afbabf56SDarrick J. Wong false, &retval); 2387c4a07a4SDarrick J. Wong if (error == 0 && retval == 0) 239c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 2407c4a07a4SDarrick J. Wong error = 0; 2417c4a07a4SDarrick J. Wong goto out; 2427c4a07a4SDarrick J. Wong } 2437c4a07a4SDarrick J. Wong 2447c4a07a4SDarrick J. Wong /* Move the alternate cursor one block in the direction given. */ 245afbabf56SDarrick J. Wong error = xfs_da3_path_shift(ds->state, altpath, direction, false, 246afbabf56SDarrick J. Wong &retval); 247c517b3aaSDarrick J. Wong if (!xchk_da_process_error(ds, level, &error)) 248afbabf56SDarrick J. Wong goto out; 2497c4a07a4SDarrick J. Wong if (retval) { 250c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 251afbabf56SDarrick J. Wong goto out; 2527c4a07a4SDarrick J. Wong } 253afbabf56SDarrick J. Wong if (altpath->blk[level].bp) 254afbabf56SDarrick J. Wong xchk_buffer_recheck(ds->sc, altpath->blk[level].bp); 2557c4a07a4SDarrick J. Wong 2567c4a07a4SDarrick J. Wong /* Compare upper level pointer to sibling pointer. */ 257afbabf56SDarrick J. Wong if (altpath->blk[level].blkno != sibling) 258c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 259afbabf56SDarrick J. Wong 2607c4a07a4SDarrick J. Wong out: 261afbabf56SDarrick J. Wong /* Free all buffers in the altpath that aren't referenced from path. */ 262afbabf56SDarrick J. Wong for (plevel = 0; plevel < altpath->active; plevel++) { 263afbabf56SDarrick J. Wong if (altpath->blk[plevel].bp == NULL || 264afbabf56SDarrick J. Wong (plevel < path->active && 265afbabf56SDarrick J. Wong altpath->blk[plevel].bp == path->blk[plevel].bp)) 266afbabf56SDarrick J. Wong continue; 267afbabf56SDarrick J. Wong 268afbabf56SDarrick J. Wong xfs_trans_brelse(ds->dargs.trans, altpath->blk[plevel].bp); 269afbabf56SDarrick J. Wong altpath->blk[plevel].bp = NULL; 270afbabf56SDarrick J. Wong } 271afbabf56SDarrick J. Wong 2727c4a07a4SDarrick J. Wong return error; 2737c4a07a4SDarrick J. Wong } 2747c4a07a4SDarrick J. Wong 2757c4a07a4SDarrick J. Wong /* Check a block's sibling pointers. */ 2767c4a07a4SDarrick J. Wong STATIC int 277c517b3aaSDarrick J. Wong xchk_da_btree_block_check_siblings( 278c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 2797c4a07a4SDarrick J. Wong int level, 2807c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *hdr) 2817c4a07a4SDarrick J. Wong { 2827c4a07a4SDarrick J. Wong xfs_dablk_t forw; 2837c4a07a4SDarrick J. Wong xfs_dablk_t back; 2847c4a07a4SDarrick J. Wong int error = 0; 2857c4a07a4SDarrick J. Wong 2867c4a07a4SDarrick J. Wong forw = be32_to_cpu(hdr->forw); 2877c4a07a4SDarrick J. Wong back = be32_to_cpu(hdr->back); 2887c4a07a4SDarrick J. Wong 2897c4a07a4SDarrick J. Wong /* Top level blocks should not have sibling pointers. */ 2907c4a07a4SDarrick J. Wong if (level == 0) { 2917c4a07a4SDarrick J. Wong if (forw != 0 || back != 0) 292c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 2937c4a07a4SDarrick J. Wong return 0; 2947c4a07a4SDarrick J. Wong } 2957c4a07a4SDarrick J. Wong 2967c4a07a4SDarrick J. Wong /* 2977c4a07a4SDarrick J. Wong * Check back (left) and forw (right) pointers. These functions 2987c4a07a4SDarrick J. Wong * absorb error codes for us. 2997c4a07a4SDarrick J. Wong */ 300c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_sibling(ds, level, 0, back); 3017c4a07a4SDarrick J. Wong if (error) 3027c4a07a4SDarrick J. Wong goto out; 303c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_sibling(ds, level, 1, forw); 3047c4a07a4SDarrick J. Wong 3057c4a07a4SDarrick J. Wong out: 3067c4a07a4SDarrick J. Wong memset(&ds->state->altpath, 0, sizeof(ds->state->altpath)); 3077c4a07a4SDarrick J. Wong return error; 3087c4a07a4SDarrick J. Wong } 3097c4a07a4SDarrick J. Wong 3107c4a07a4SDarrick J. Wong /* Load a dir/attribute block from a btree. */ 3117c4a07a4SDarrick J. Wong STATIC int 312c517b3aaSDarrick J. Wong xchk_da_btree_block( 313c517b3aaSDarrick J. Wong struct xchk_da_btree *ds, 3147c4a07a4SDarrick J. Wong int level, 3157c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 3167c4a07a4SDarrick J. Wong { 3177c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blk; 3187c4a07a4SDarrick J. Wong struct xfs_da_intnode *node; 3197c4a07a4SDarrick J. Wong struct xfs_da_node_entry *btree; 3207c4a07a4SDarrick J. Wong struct xfs_da3_blkinfo *hdr3; 3217c4a07a4SDarrick J. Wong struct xfs_da_args *dargs = &ds->dargs; 3227c4a07a4SDarrick J. Wong struct xfs_inode *ip = ds->dargs.dp; 3237c4a07a4SDarrick J. Wong xfs_ino_t owner; 3247c4a07a4SDarrick J. Wong int *pmaxrecs; 3257c4a07a4SDarrick J. Wong struct xfs_da3_icnode_hdr nodehdr; 3260dca060cSDarrick J. Wong int error = 0; 3277c4a07a4SDarrick J. Wong 3287c4a07a4SDarrick J. Wong blk = &ds->state->path.blk[level]; 3297c4a07a4SDarrick J. Wong ds->state->path.active = level + 1; 3307c4a07a4SDarrick J. Wong 3317c4a07a4SDarrick J. Wong /* Release old block. */ 3327c4a07a4SDarrick J. Wong if (blk->bp) { 3337c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 3347c4a07a4SDarrick J. Wong blk->bp = NULL; 3357c4a07a4SDarrick J. Wong } 3367c4a07a4SDarrick J. Wong 3377c4a07a4SDarrick J. Wong /* Check the pointer. */ 3387c4a07a4SDarrick J. Wong blk->blkno = blkno; 339c517b3aaSDarrick J. Wong if (!xchk_da_btree_ptr_ok(ds, level, blkno)) 3407c4a07a4SDarrick J. Wong goto out_nobuf; 3417c4a07a4SDarrick J. Wong 3427c4a07a4SDarrick J. Wong /* Read the buffer. */ 343cd2c9f1bSChristoph Hellwig error = xfs_da_read_buf(dargs->trans, dargs->dp, blk->blkno, 344cd2c9f1bSChristoph Hellwig XFS_DABUF_MAP_HOLE_OK, &blk->bp, dargs->whichfork, 345c517b3aaSDarrick J. Wong &xchk_da_btree_buf_ops); 346c517b3aaSDarrick J. Wong if (!xchk_da_process_error(ds, level, &error)) 3477c4a07a4SDarrick J. Wong goto out_nobuf; 348cf1b0b8bSDarrick J. Wong if (blk->bp) 349c517b3aaSDarrick J. Wong xchk_buffer_recheck(ds->sc, blk->bp); 3507c4a07a4SDarrick J. Wong 3517c4a07a4SDarrick J. Wong /* 3527c4a07a4SDarrick J. Wong * We didn't find a dir btree root block, which means that 3537c4a07a4SDarrick J. Wong * there's no LEAF1/LEAFN tree (at least not where it's supposed 3547c4a07a4SDarrick J. Wong * to be), so jump out now. 3557c4a07a4SDarrick J. Wong */ 3567c4a07a4SDarrick J. Wong if (ds->dargs.whichfork == XFS_DATA_FORK && level == 0 && 3577c4a07a4SDarrick J. Wong blk->bp == NULL) 3587c4a07a4SDarrick J. Wong goto out_nobuf; 3597c4a07a4SDarrick J. Wong 3607c4a07a4SDarrick J. Wong /* It's /not/ ok for attr trees not to have a da btree. */ 3617c4a07a4SDarrick J. Wong if (blk->bp == NULL) { 362c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3637c4a07a4SDarrick J. Wong goto out_nobuf; 3647c4a07a4SDarrick J. Wong } 3657c4a07a4SDarrick J. Wong 3667c4a07a4SDarrick J. Wong hdr3 = blk->bp->b_addr; 3677c4a07a4SDarrick J. Wong blk->magic = be16_to_cpu(hdr3->hdr.magic); 3687c4a07a4SDarrick J. Wong pmaxrecs = &ds->maxrecs[level]; 3697c4a07a4SDarrick J. Wong 3704da4b10bSDarrick J. Wong /* We only started zeroing the header on v5 filesystems. */ 37138c26bfdSDave Chinner if (xfs_has_crc(ds->sc->mp) && hdr3->hdr.pad) 372c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3737c4a07a4SDarrick J. Wong 3747c4a07a4SDarrick J. Wong /* Check the owner. */ 37538c26bfdSDave Chinner if (xfs_has_crc(ip->i_mount)) { 3767c4a07a4SDarrick J. Wong owner = be64_to_cpu(hdr3->owner); 3777c4a07a4SDarrick J. Wong if (owner != ip->i_ino) 378c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3797c4a07a4SDarrick J. Wong } 3807c4a07a4SDarrick J. Wong 3817c4a07a4SDarrick J. Wong /* Check the siblings. */ 382c517b3aaSDarrick J. Wong error = xchk_da_btree_block_check_siblings(ds, level, &hdr3->hdr); 3837c4a07a4SDarrick J. Wong if (error) 3847c4a07a4SDarrick J. Wong goto out; 3857c4a07a4SDarrick J. Wong 3867c4a07a4SDarrick J. Wong /* Interpret the buffer. */ 3877c4a07a4SDarrick J. Wong switch (blk->magic) { 3887c4a07a4SDarrick J. Wong case XFS_ATTR_LEAF_MAGIC: 3897c4a07a4SDarrick J. Wong case XFS_ATTR3_LEAF_MAGIC: 3907c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 3917c4a07a4SDarrick J. Wong XFS_BLFT_ATTR_LEAF_BUF); 3927c4a07a4SDarrick J. Wong blk->magic = XFS_ATTR_LEAF_MAGIC; 3937c4a07a4SDarrick J. Wong blk->hashval = xfs_attr_leaf_lasthash(blk->bp, pmaxrecs); 3947c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 395c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 3967c4a07a4SDarrick J. Wong break; 3977c4a07a4SDarrick J. Wong case XFS_DIR2_LEAFN_MAGIC: 3987c4a07a4SDarrick J. Wong case XFS_DIR3_LEAFN_MAGIC: 3997c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4007c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAFN_BUF); 4017c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAFN_MAGIC; 4027c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4037c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 404c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4057c4a07a4SDarrick J. Wong break; 4067c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 4077c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 4087c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4097c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAF1_BUF); 4107c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAF1_MAGIC; 4117c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4127c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 413c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4147c4a07a4SDarrick J. Wong break; 4157c4a07a4SDarrick J. Wong case XFS_DA_NODE_MAGIC: 4167c4a07a4SDarrick J. Wong case XFS_DA3_NODE_MAGIC: 4177c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4187c4a07a4SDarrick J. Wong XFS_BLFT_DA_NODE_BUF); 4197c4a07a4SDarrick J. Wong blk->magic = XFS_DA_NODE_MAGIC; 4207c4a07a4SDarrick J. Wong node = blk->bp->b_addr; 421f475dc4dSChristoph Hellwig xfs_da3_node_hdr_from_disk(ip->i_mount, &nodehdr, node); 42251908ca7SChristoph Hellwig btree = nodehdr.btree; 4237c4a07a4SDarrick J. Wong *pmaxrecs = nodehdr.count; 4247c4a07a4SDarrick J. Wong blk->hashval = be32_to_cpu(btree[*pmaxrecs - 1].hashval); 4257c4a07a4SDarrick J. Wong if (level == 0) { 4267c4a07a4SDarrick J. Wong if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) { 427c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4287c4a07a4SDarrick J. Wong goto out_freebp; 4297c4a07a4SDarrick J. Wong } 4307c4a07a4SDarrick J. Wong ds->tree_level = nodehdr.level; 4317c4a07a4SDarrick J. Wong } else { 4327c4a07a4SDarrick J. Wong if (ds->tree_level != nodehdr.level) { 433c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4347c4a07a4SDarrick J. Wong goto out_freebp; 4357c4a07a4SDarrick J. Wong } 4367c4a07a4SDarrick J. Wong } 4377c4a07a4SDarrick J. Wong 4387c4a07a4SDarrick J. Wong /* XXX: Check hdr3.pad32 once we know how to fix it. */ 4397c4a07a4SDarrick J. Wong break; 4407c4a07a4SDarrick J. Wong default: 441c517b3aaSDarrick J. Wong xchk_da_set_corrupt(ds, level); 4427c4a07a4SDarrick J. Wong goto out_freebp; 4437c4a07a4SDarrick J. Wong } 4447c4a07a4SDarrick J. Wong 445e581c939SDarrick J. Wong /* 446e581c939SDarrick J. Wong * If we've been handed a block that is below the dabtree root, does 447e581c939SDarrick J. Wong * its hashval match what the parent block expected to see? 448e581c939SDarrick J. Wong */ 449e581c939SDarrick J. Wong if (level > 0) { 450e581c939SDarrick J. Wong struct xfs_da_node_entry *key; 451e581c939SDarrick J. Wong 452e581c939SDarrick J. Wong key = xchk_da_btree_node_entry(ds, level - 1); 453e581c939SDarrick J. Wong if (be32_to_cpu(key->hashval) != blk->hashval) { 454e581c939SDarrick J. Wong xchk_da_set_corrupt(ds, level); 455e581c939SDarrick J. Wong goto out_freebp; 456e581c939SDarrick J. Wong } 457e581c939SDarrick J. Wong } 458e581c939SDarrick J. Wong 4597c4a07a4SDarrick J. Wong out: 4607c4a07a4SDarrick J. Wong return error; 4617c4a07a4SDarrick J. Wong out_freebp: 4627c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 4637c4a07a4SDarrick J. Wong blk->bp = NULL; 4647c4a07a4SDarrick J. Wong out_nobuf: 4657c4a07a4SDarrick J. Wong blk->blkno = 0; 4667c4a07a4SDarrick J. Wong return error; 4677c4a07a4SDarrick J. Wong } 4687c4a07a4SDarrick J. Wong 4697c4a07a4SDarrick J. Wong /* Visit all nodes and leaves of a da btree. */ 4707c4a07a4SDarrick J. Wong int 471c517b3aaSDarrick J. Wong xchk_da_btree( 4721d8a748aSDarrick J. Wong struct xfs_scrub *sc, 4737c4a07a4SDarrick J. Wong int whichfork, 474c517b3aaSDarrick J. Wong xchk_da_btree_rec_fn scrub_fn, 47513791d3bSDarrick J. Wong void *private) 4767c4a07a4SDarrick J. Wong { 477510a28e1SDarrick J. Wong struct xchk_da_btree *ds; 4787c4a07a4SDarrick J. Wong struct xfs_mount *mp = sc->mp; 4797c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blks; 4807c4a07a4SDarrick J. Wong struct xfs_da_node_entry *key; 4817c4a07a4SDarrick J. Wong xfs_dablk_t blkno; 4827c4a07a4SDarrick J. Wong int level; 4837c4a07a4SDarrick J. Wong int error; 4847c4a07a4SDarrick J. Wong 4857c4a07a4SDarrick J. Wong /* Skip short format data structures; no btree to scan. */ 486732436efSDarrick J. Wong if (!xfs_ifork_has_extents(xfs_ifork_ptr(sc->ip, whichfork))) 4877c4a07a4SDarrick J. Wong return 0; 4887c4a07a4SDarrick J. Wong 4897c4a07a4SDarrick J. Wong /* Set up initial da state. */ 490306195f3SDarrick J. Wong ds = kzalloc(sizeof(struct xchk_da_btree), XCHK_GFP_FLAGS); 491510a28e1SDarrick J. Wong if (!ds) 492510a28e1SDarrick J. Wong return -ENOMEM; 493510a28e1SDarrick J. Wong ds->dargs.dp = sc->ip; 494510a28e1SDarrick J. Wong ds->dargs.whichfork = whichfork; 495510a28e1SDarrick J. Wong ds->dargs.trans = sc->tp; 496510a28e1SDarrick J. Wong ds->dargs.op_flags = XFS_DA_OP_OKNOENT; 497*9eef772fSDarrick J. Wong ds->dargs.owner = sc->ip->i_ino; 498510a28e1SDarrick J. Wong ds->state = xfs_da_state_alloc(&ds->dargs); 499510a28e1SDarrick J. Wong ds->sc = sc; 500510a28e1SDarrick J. Wong ds->private = private; 5017c4a07a4SDarrick J. Wong if (whichfork == XFS_ATTR_FORK) { 502510a28e1SDarrick J. Wong ds->dargs.geo = mp->m_attr_geo; 503510a28e1SDarrick J. Wong ds->lowest = 0; 504510a28e1SDarrick J. Wong ds->highest = 0; 5057c4a07a4SDarrick J. Wong } else { 506510a28e1SDarrick J. Wong ds->dargs.geo = mp->m_dir_geo; 507510a28e1SDarrick J. Wong ds->lowest = ds->dargs.geo->leafblk; 508510a28e1SDarrick J. Wong ds->highest = ds->dargs.geo->freeblk; 5097c4a07a4SDarrick J. Wong } 510510a28e1SDarrick J. Wong blkno = ds->lowest; 5117c4a07a4SDarrick J. Wong level = 0; 5127c4a07a4SDarrick J. Wong 5137c4a07a4SDarrick J. Wong /* Find the root of the da tree, if present. */ 514510a28e1SDarrick J. Wong blks = ds->state->path.blk; 515510a28e1SDarrick J. Wong error = xchk_da_btree_block(ds, level, blkno); 5167c4a07a4SDarrick J. Wong if (error) 5177c4a07a4SDarrick J. Wong goto out_state; 5187c4a07a4SDarrick J. Wong /* 519510a28e1SDarrick J. Wong * We didn't find a block at ds->lowest, which means that there's 5207c4a07a4SDarrick J. Wong * no LEAF1/LEAFN tree (at least not where it's supposed to be), 5217c4a07a4SDarrick J. Wong * so jump out now. 5227c4a07a4SDarrick J. Wong */ 5237c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5247c4a07a4SDarrick J. Wong goto out_state; 5257c4a07a4SDarrick J. Wong 5267c4a07a4SDarrick J. Wong blks[level].index = 0; 5277c4a07a4SDarrick J. Wong while (level >= 0 && level < XFS_DA_NODE_MAXDEPTH) { 5287c4a07a4SDarrick J. Wong /* Handle leaf block. */ 5297c4a07a4SDarrick J. Wong if (blks[level].magic != XFS_DA_NODE_MAGIC) { 5307c4a07a4SDarrick J. Wong /* End of leaf, pop back towards the root. */ 531510a28e1SDarrick J. Wong if (blks[level].index >= ds->maxrecs[level]) { 5327c4a07a4SDarrick J. Wong if (level > 0) 5337c4a07a4SDarrick J. Wong blks[level - 1].index++; 534510a28e1SDarrick J. Wong ds->tree_level++; 5357c4a07a4SDarrick J. Wong level--; 5367c4a07a4SDarrick J. Wong continue; 5377c4a07a4SDarrick J. Wong } 5387c4a07a4SDarrick J. Wong 5397c4a07a4SDarrick J. Wong /* Dispatch record scrubbing. */ 540510a28e1SDarrick J. Wong error = scrub_fn(ds, level); 5417c4a07a4SDarrick J. Wong if (error) 5427c4a07a4SDarrick J. Wong break; 543c517b3aaSDarrick J. Wong if (xchk_should_terminate(sc, &error) || 5447c4a07a4SDarrick J. Wong (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 5457c4a07a4SDarrick J. Wong break; 5467c4a07a4SDarrick J. Wong 5477c4a07a4SDarrick J. Wong blks[level].index++; 5487c4a07a4SDarrick J. Wong continue; 5497c4a07a4SDarrick J. Wong } 5507c4a07a4SDarrick J. Wong 5517c4a07a4SDarrick J. Wong 5527c4a07a4SDarrick J. Wong /* End of node, pop back towards the root. */ 553510a28e1SDarrick J. Wong if (blks[level].index >= ds->maxrecs[level]) { 5547c4a07a4SDarrick J. Wong if (level > 0) 5557c4a07a4SDarrick J. Wong blks[level - 1].index++; 556510a28e1SDarrick J. Wong ds->tree_level++; 5577c4a07a4SDarrick J. Wong level--; 5587c4a07a4SDarrick J. Wong continue; 5597c4a07a4SDarrick J. Wong } 5607c4a07a4SDarrick J. Wong 5617c4a07a4SDarrick J. Wong /* Hashes in order for scrub? */ 562510a28e1SDarrick J. Wong key = xchk_da_btree_node_entry(ds, level); 563510a28e1SDarrick J. Wong error = xchk_da_btree_hash(ds, level, &key->hashval); 5647c4a07a4SDarrick J. Wong if (error) 5657c4a07a4SDarrick J. Wong goto out; 5667c4a07a4SDarrick J. Wong 5677c4a07a4SDarrick J. Wong /* Drill another level deeper. */ 5687c4a07a4SDarrick J. Wong blkno = be32_to_cpu(key->before); 5697c4a07a4SDarrick J. Wong level++; 570228de124SDarrick J. Wong if (level >= XFS_DA_NODE_MAXDEPTH) { 571228de124SDarrick J. Wong /* Too deep! */ 572510a28e1SDarrick J. Wong xchk_da_set_corrupt(ds, level - 1); 573228de124SDarrick J. Wong break; 574228de124SDarrick J. Wong } 575510a28e1SDarrick J. Wong ds->tree_level--; 576510a28e1SDarrick J. Wong error = xchk_da_btree_block(ds, level, blkno); 5777c4a07a4SDarrick J. Wong if (error) 5787c4a07a4SDarrick J. Wong goto out; 5797c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5807c4a07a4SDarrick J. Wong goto out; 5817c4a07a4SDarrick J. Wong 5827c4a07a4SDarrick J. Wong blks[level].index = 0; 5837c4a07a4SDarrick J. Wong } 5847c4a07a4SDarrick J. Wong 5857c4a07a4SDarrick J. Wong out: 5867c4a07a4SDarrick J. Wong /* Release all the buffers we're tracking. */ 5877c4a07a4SDarrick J. Wong for (level = 0; level < XFS_DA_NODE_MAXDEPTH; level++) { 5887c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5897c4a07a4SDarrick J. Wong continue; 5907c4a07a4SDarrick J. Wong xfs_trans_brelse(sc->tp, blks[level].bp); 5917c4a07a4SDarrick J. Wong blks[level].bp = NULL; 5927c4a07a4SDarrick J. Wong } 5937c4a07a4SDarrick J. Wong 5947c4a07a4SDarrick J. Wong out_state: 595510a28e1SDarrick J. Wong xfs_da_state_free(ds->state); 596306195f3SDarrick J. Wong kfree(ds); 5977c4a07a4SDarrick J. Wong return error; 5987c4a07a4SDarrick J. Wong } 599