17c4a07a4SDarrick J. Wong /* 27c4a07a4SDarrick J. Wong * Copyright (C) 2017 Oracle. All Rights Reserved. 37c4a07a4SDarrick J. Wong * 47c4a07a4SDarrick J. Wong * Author: Darrick J. Wong <darrick.wong@oracle.com> 57c4a07a4SDarrick J. Wong * 67c4a07a4SDarrick J. Wong * This program is free software; you can redistribute it and/or 77c4a07a4SDarrick J. Wong * modify it under the terms of the GNU General Public License 87c4a07a4SDarrick J. Wong * as published by the Free Software Foundation; either version 2 97c4a07a4SDarrick J. Wong * of the License, or (at your option) any later version. 107c4a07a4SDarrick J. Wong * 117c4a07a4SDarrick J. Wong * This program is distributed in the hope that it would be useful, 127c4a07a4SDarrick J. Wong * but WITHOUT ANY WARRANTY; without even the implied warranty of 137c4a07a4SDarrick J. Wong * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 147c4a07a4SDarrick J. Wong * GNU General Public License for more details. 157c4a07a4SDarrick J. Wong * 167c4a07a4SDarrick J. Wong * You should have received a copy of the GNU General Public License 177c4a07a4SDarrick J. Wong * along with this program; if not, write the Free Software Foundation, 187c4a07a4SDarrick J. Wong * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 197c4a07a4SDarrick J. Wong */ 207c4a07a4SDarrick J. Wong #include "xfs.h" 217c4a07a4SDarrick J. Wong #include "xfs_fs.h" 227c4a07a4SDarrick J. Wong #include "xfs_shared.h" 237c4a07a4SDarrick J. Wong #include "xfs_format.h" 247c4a07a4SDarrick J. Wong #include "xfs_trans_resv.h" 257c4a07a4SDarrick J. Wong #include "xfs_mount.h" 267c4a07a4SDarrick J. Wong #include "xfs_defer.h" 277c4a07a4SDarrick J. Wong #include "xfs_btree.h" 287c4a07a4SDarrick J. Wong #include "xfs_bit.h" 297c4a07a4SDarrick J. Wong #include "xfs_log_format.h" 307c4a07a4SDarrick J. Wong #include "xfs_trans.h" 317c4a07a4SDarrick J. Wong #include "xfs_sb.h" 327c4a07a4SDarrick J. Wong #include "xfs_inode.h" 337c4a07a4SDarrick J. Wong #include "xfs_inode_fork.h" 347c4a07a4SDarrick J. Wong #include "xfs_da_format.h" 357c4a07a4SDarrick J. Wong #include "xfs_da_btree.h" 367c4a07a4SDarrick J. Wong #include "xfs_dir2.h" 377c4a07a4SDarrick J. Wong #include "xfs_dir2_priv.h" 387c4a07a4SDarrick J. Wong #include "xfs_attr_leaf.h" 397c4a07a4SDarrick J. Wong #include "scrub/xfs_scrub.h" 407c4a07a4SDarrick J. Wong #include "scrub/scrub.h" 417c4a07a4SDarrick J. Wong #include "scrub/common.h" 427c4a07a4SDarrick J. Wong #include "scrub/trace.h" 437c4a07a4SDarrick J. Wong #include "scrub/dabtree.h" 447c4a07a4SDarrick J. Wong 457c4a07a4SDarrick J. Wong /* Directory/Attribute Btree */ 467c4a07a4SDarrick J. Wong 477c4a07a4SDarrick J. Wong /* 487c4a07a4SDarrick J. Wong * Check for da btree operation errors. See the section about handling 497c4a07a4SDarrick J. Wong * operational errors in common.c. 507c4a07a4SDarrick J. Wong */ 517c4a07a4SDarrick J. Wong bool 527c4a07a4SDarrick J. Wong xfs_scrub_da_process_error( 537c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 547c4a07a4SDarrick J. Wong int level, 557c4a07a4SDarrick J. Wong int *error) 567c4a07a4SDarrick J. Wong { 577c4a07a4SDarrick J. Wong struct xfs_scrub_context *sc = ds->sc; 587c4a07a4SDarrick J. Wong 597c4a07a4SDarrick J. Wong if (*error == 0) 607c4a07a4SDarrick J. Wong return true; 617c4a07a4SDarrick J. Wong 627c4a07a4SDarrick J. Wong switch (*error) { 637c4a07a4SDarrick J. Wong case -EDEADLOCK: 647c4a07a4SDarrick J. Wong /* Used to restart an op with deadlock avoidance. */ 657c4a07a4SDarrick J. Wong trace_xfs_scrub_deadlock_retry(sc->ip, sc->sm, *error); 667c4a07a4SDarrick J. Wong break; 677c4a07a4SDarrick J. Wong case -EFSBADCRC: 687c4a07a4SDarrick J. Wong case -EFSCORRUPTED: 697c4a07a4SDarrick J. Wong /* Note the badness but don't abort. */ 707c4a07a4SDarrick J. Wong sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; 717c4a07a4SDarrick J. Wong *error = 0; 727c4a07a4SDarrick J. Wong /* fall through */ 737c4a07a4SDarrick J. Wong default: 747c4a07a4SDarrick J. Wong trace_xfs_scrub_file_op_error(sc, ds->dargs.whichfork, 757c4a07a4SDarrick J. Wong xfs_dir2_da_to_db(ds->dargs.geo, 767c4a07a4SDarrick J. Wong ds->state->path.blk[level].blkno), 777c4a07a4SDarrick J. Wong *error, __return_address); 787c4a07a4SDarrick J. Wong break; 797c4a07a4SDarrick J. Wong } 807c4a07a4SDarrick J. Wong return false; 817c4a07a4SDarrick J. Wong } 827c4a07a4SDarrick J. Wong 837c4a07a4SDarrick J. Wong /* 847c4a07a4SDarrick J. Wong * Check for da btree corruption. See the section about handling 857c4a07a4SDarrick J. Wong * operational errors in common.c. 867c4a07a4SDarrick J. Wong */ 877c4a07a4SDarrick J. Wong void 887c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt( 897c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 907c4a07a4SDarrick J. Wong int level) 917c4a07a4SDarrick J. Wong { 927c4a07a4SDarrick J. Wong struct xfs_scrub_context *sc = ds->sc; 937c4a07a4SDarrick J. Wong 947c4a07a4SDarrick J. Wong sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT; 957c4a07a4SDarrick J. Wong 967c4a07a4SDarrick J. Wong trace_xfs_scrub_fblock_error(sc, ds->dargs.whichfork, 977c4a07a4SDarrick J. Wong xfs_dir2_da_to_db(ds->dargs.geo, 987c4a07a4SDarrick J. Wong ds->state->path.blk[level].blkno), 997c4a07a4SDarrick J. Wong __return_address); 1007c4a07a4SDarrick J. Wong } 1017c4a07a4SDarrick J. Wong 1027c4a07a4SDarrick J. Wong /* Find an entry at a certain level in a da btree. */ 1037c4a07a4SDarrick J. Wong STATIC void * 1047c4a07a4SDarrick J. Wong xfs_scrub_da_btree_entry( 1057c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 1067c4a07a4SDarrick J. Wong int level, 1077c4a07a4SDarrick J. Wong int rec) 1087c4a07a4SDarrick J. Wong { 1097c4a07a4SDarrick J. Wong char *ents; 1107c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blk; 1117c4a07a4SDarrick J. Wong void *baddr; 1127c4a07a4SDarrick J. Wong 1137c4a07a4SDarrick J. Wong /* Dispatch the entry finding function. */ 1147c4a07a4SDarrick J. Wong blk = &ds->state->path.blk[level]; 1157c4a07a4SDarrick J. Wong baddr = blk->bp->b_addr; 1167c4a07a4SDarrick J. Wong switch (blk->magic) { 1177c4a07a4SDarrick J. Wong case XFS_ATTR_LEAF_MAGIC: 1187c4a07a4SDarrick J. Wong case XFS_ATTR3_LEAF_MAGIC: 1197c4a07a4SDarrick J. Wong ents = (char *)xfs_attr3_leaf_entryp(baddr); 1207c4a07a4SDarrick J. Wong return ents + (rec * sizeof(struct xfs_attr_leaf_entry)); 1217c4a07a4SDarrick J. Wong case XFS_DIR2_LEAFN_MAGIC: 1227c4a07a4SDarrick J. Wong case XFS_DIR3_LEAFN_MAGIC: 1237c4a07a4SDarrick J. Wong ents = (char *)ds->dargs.dp->d_ops->leaf_ents_p(baddr); 1247c4a07a4SDarrick J. Wong return ents + (rec * sizeof(struct xfs_dir2_leaf_entry)); 1257c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 1267c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 1277c4a07a4SDarrick J. Wong ents = (char *)ds->dargs.dp->d_ops->leaf_ents_p(baddr); 1287c4a07a4SDarrick J. Wong return ents + (rec * sizeof(struct xfs_dir2_leaf_entry)); 1297c4a07a4SDarrick J. Wong case XFS_DA_NODE_MAGIC: 1307c4a07a4SDarrick J. Wong case XFS_DA3_NODE_MAGIC: 1317c4a07a4SDarrick J. Wong ents = (char *)ds->dargs.dp->d_ops->node_tree_p(baddr); 1327c4a07a4SDarrick J. Wong return ents + (rec * sizeof(struct xfs_da_node_entry)); 1337c4a07a4SDarrick J. Wong } 1347c4a07a4SDarrick J. Wong 1357c4a07a4SDarrick J. Wong return NULL; 1367c4a07a4SDarrick J. Wong } 1377c4a07a4SDarrick J. Wong 1387c4a07a4SDarrick J. Wong /* Scrub a da btree hash (key). */ 1397c4a07a4SDarrick J. Wong int 1407c4a07a4SDarrick J. Wong xfs_scrub_da_btree_hash( 1417c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 1427c4a07a4SDarrick J. Wong int level, 1437c4a07a4SDarrick J. Wong __be32 *hashp) 1447c4a07a4SDarrick J. Wong { 1457c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blks; 1467c4a07a4SDarrick J. Wong struct xfs_da_node_entry *entry; 1477c4a07a4SDarrick J. Wong xfs_dahash_t hash; 1487c4a07a4SDarrick J. Wong xfs_dahash_t parent_hash; 1497c4a07a4SDarrick J. Wong 1507c4a07a4SDarrick J. Wong /* Is this hash in order? */ 1517c4a07a4SDarrick J. Wong hash = be32_to_cpu(*hashp); 1527c4a07a4SDarrick J. Wong if (hash < ds->hashes[level]) 1537c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 1547c4a07a4SDarrick J. Wong ds->hashes[level] = hash; 1557c4a07a4SDarrick J. Wong 1567c4a07a4SDarrick J. Wong if (level == 0) 1577c4a07a4SDarrick J. Wong return 0; 1587c4a07a4SDarrick J. Wong 1597c4a07a4SDarrick J. Wong /* Is this hash no larger than the parent hash? */ 1607c4a07a4SDarrick J. Wong blks = ds->state->path.blk; 1617c4a07a4SDarrick J. Wong entry = xfs_scrub_da_btree_entry(ds, level - 1, blks[level - 1].index); 1627c4a07a4SDarrick J. Wong parent_hash = be32_to_cpu(entry->hashval); 1637c4a07a4SDarrick J. Wong if (parent_hash < hash) 1647c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 1657c4a07a4SDarrick J. Wong 1667c4a07a4SDarrick J. Wong return 0; 1677c4a07a4SDarrick J. Wong } 1687c4a07a4SDarrick J. Wong 1697c4a07a4SDarrick J. Wong /* 1707c4a07a4SDarrick J. Wong * Check a da btree pointer. Returns true if it's ok to use this 1717c4a07a4SDarrick J. Wong * pointer. 1727c4a07a4SDarrick J. Wong */ 1737c4a07a4SDarrick J. Wong STATIC bool 1747c4a07a4SDarrick J. Wong xfs_scrub_da_btree_ptr_ok( 1757c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 1767c4a07a4SDarrick J. Wong int level, 1777c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 1787c4a07a4SDarrick J. Wong { 1797c4a07a4SDarrick J. Wong if (blkno < ds->lowest || (ds->highest != 0 && blkno >= ds->highest)) { 1807c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 1817c4a07a4SDarrick J. Wong return false; 1827c4a07a4SDarrick J. Wong } 1837c4a07a4SDarrick J. Wong 1847c4a07a4SDarrick J. Wong return true; 1857c4a07a4SDarrick J. Wong } 1867c4a07a4SDarrick J. Wong 1877c4a07a4SDarrick J. Wong /* 1887c4a07a4SDarrick J. Wong * The da btree scrubber can handle leaf1 blocks as a degenerate 1897c4a07a4SDarrick J. Wong * form of leafn blocks. Since the regular da code doesn't handle 1907c4a07a4SDarrick J. Wong * leaf1, we must multiplex the verifiers. 1917c4a07a4SDarrick J. Wong */ 1927c4a07a4SDarrick J. Wong static void 1937c4a07a4SDarrick J. Wong xfs_scrub_da_btree_read_verify( 1947c4a07a4SDarrick J. Wong struct xfs_buf *bp) 1957c4a07a4SDarrick J. Wong { 1967c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 1977c4a07a4SDarrick J. Wong 1987c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 1997c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 2007c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 2017c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 2027c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 2037c4a07a4SDarrick J. Wong return; 2047c4a07a4SDarrick J. Wong default: 2057c4a07a4SDarrick J. Wong /* 2067c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 2077c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 2087c4a07a4SDarrick J. Wong */ 2097c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 2107c4a07a4SDarrick J. Wong bp->b_ops->verify_read(bp); 2117c4a07a4SDarrick J. Wong return; 2127c4a07a4SDarrick J. Wong } 2137c4a07a4SDarrick J. Wong } 2147c4a07a4SDarrick J. Wong static void 2157c4a07a4SDarrick J. Wong xfs_scrub_da_btree_write_verify( 2167c4a07a4SDarrick J. Wong struct xfs_buf *bp) 2177c4a07a4SDarrick J. Wong { 2187c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *info = bp->b_addr; 2197c4a07a4SDarrick J. Wong 2207c4a07a4SDarrick J. Wong switch (be16_to_cpu(info->magic)) { 2217c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 2227c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 2237c4a07a4SDarrick J. Wong bp->b_ops = &xfs_dir3_leaf1_buf_ops; 2247c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 2257c4a07a4SDarrick J. Wong return; 2267c4a07a4SDarrick J. Wong default: 2277c4a07a4SDarrick J. Wong /* 2287c4a07a4SDarrick J. Wong * xfs_da3_node_buf_ops already know how to handle 2297c4a07a4SDarrick J. Wong * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks. 2307c4a07a4SDarrick J. Wong */ 2317c4a07a4SDarrick J. Wong bp->b_ops = &xfs_da3_node_buf_ops; 2327c4a07a4SDarrick J. Wong bp->b_ops->verify_write(bp); 2337c4a07a4SDarrick J. Wong return; 2347c4a07a4SDarrick J. Wong } 2357c4a07a4SDarrick J. Wong } 2367c4a07a4SDarrick J. Wong 2377c4a07a4SDarrick J. Wong static const struct xfs_buf_ops xfs_scrub_da_btree_buf_ops = { 2387c4a07a4SDarrick J. Wong .name = "xfs_scrub_da_btree", 2397c4a07a4SDarrick J. Wong .verify_read = xfs_scrub_da_btree_read_verify, 2407c4a07a4SDarrick J. Wong .verify_write = xfs_scrub_da_btree_write_verify, 2417c4a07a4SDarrick J. Wong }; 2427c4a07a4SDarrick J. Wong 2437c4a07a4SDarrick J. Wong /* Check a block's sibling. */ 2447c4a07a4SDarrick J. Wong STATIC int 2457c4a07a4SDarrick J. Wong xfs_scrub_da_btree_block_check_sibling( 2467c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 2477c4a07a4SDarrick J. Wong int level, 2487c4a07a4SDarrick J. Wong int direction, 2497c4a07a4SDarrick J. Wong xfs_dablk_t sibling) 2507c4a07a4SDarrick J. Wong { 2517c4a07a4SDarrick J. Wong int retval; 2527c4a07a4SDarrick J. Wong int error; 2537c4a07a4SDarrick J. Wong 2547c4a07a4SDarrick J. Wong memcpy(&ds->state->altpath, &ds->state->path, 2557c4a07a4SDarrick J. Wong sizeof(ds->state->altpath)); 2567c4a07a4SDarrick J. Wong 2577c4a07a4SDarrick J. Wong /* 2587c4a07a4SDarrick J. Wong * If the pointer is null, we shouldn't be able to move the upper 2597c4a07a4SDarrick J. Wong * level pointer anywhere. 2607c4a07a4SDarrick J. Wong */ 2617c4a07a4SDarrick J. Wong if (sibling == 0) { 2627c4a07a4SDarrick J. Wong error = xfs_da3_path_shift(ds->state, &ds->state->altpath, 2637c4a07a4SDarrick J. Wong direction, false, &retval); 2647c4a07a4SDarrick J. Wong if (error == 0 && retval == 0) 2657c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 2667c4a07a4SDarrick J. Wong error = 0; 2677c4a07a4SDarrick J. Wong goto out; 2687c4a07a4SDarrick J. Wong } 2697c4a07a4SDarrick J. Wong 2707c4a07a4SDarrick J. Wong /* Move the alternate cursor one block in the direction given. */ 2717c4a07a4SDarrick J. Wong error = xfs_da3_path_shift(ds->state, &ds->state->altpath, 2727c4a07a4SDarrick J. Wong direction, false, &retval); 2737c4a07a4SDarrick J. Wong if (!xfs_scrub_da_process_error(ds, level, &error)) 2747c4a07a4SDarrick J. Wong return error; 2757c4a07a4SDarrick J. Wong if (retval) { 2767c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 2777c4a07a4SDarrick J. Wong return error; 2787c4a07a4SDarrick J. Wong } 2797c4a07a4SDarrick J. Wong 2807c4a07a4SDarrick J. Wong /* Compare upper level pointer to sibling pointer. */ 2817c4a07a4SDarrick J. Wong if (ds->state->altpath.blk[level].blkno != sibling) 2827c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 2837c4a07a4SDarrick J. Wong xfs_trans_brelse(ds->dargs.trans, ds->state->altpath.blk[level].bp); 2847c4a07a4SDarrick J. Wong out: 2857c4a07a4SDarrick J. Wong return error; 2867c4a07a4SDarrick J. Wong } 2877c4a07a4SDarrick J. Wong 2887c4a07a4SDarrick J. Wong /* Check a block's sibling pointers. */ 2897c4a07a4SDarrick J. Wong STATIC int 2907c4a07a4SDarrick J. Wong xfs_scrub_da_btree_block_check_siblings( 2917c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 2927c4a07a4SDarrick J. Wong int level, 2937c4a07a4SDarrick J. Wong struct xfs_da_blkinfo *hdr) 2947c4a07a4SDarrick J. Wong { 2957c4a07a4SDarrick J. Wong xfs_dablk_t forw; 2967c4a07a4SDarrick J. Wong xfs_dablk_t back; 2977c4a07a4SDarrick J. Wong int error = 0; 2987c4a07a4SDarrick J. Wong 2997c4a07a4SDarrick J. Wong forw = be32_to_cpu(hdr->forw); 3007c4a07a4SDarrick J. Wong back = be32_to_cpu(hdr->back); 3017c4a07a4SDarrick J. Wong 3027c4a07a4SDarrick J. Wong /* Top level blocks should not have sibling pointers. */ 3037c4a07a4SDarrick J. Wong if (level == 0) { 3047c4a07a4SDarrick J. Wong if (forw != 0 || back != 0) 3057c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 3067c4a07a4SDarrick J. Wong return 0; 3077c4a07a4SDarrick J. Wong } 3087c4a07a4SDarrick J. Wong 3097c4a07a4SDarrick J. Wong /* 3107c4a07a4SDarrick J. Wong * Check back (left) and forw (right) pointers. These functions 3117c4a07a4SDarrick J. Wong * absorb error codes for us. 3127c4a07a4SDarrick J. Wong */ 3137c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_block_check_sibling(ds, level, 0, back); 3147c4a07a4SDarrick J. Wong if (error) 3157c4a07a4SDarrick J. Wong goto out; 3167c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_block_check_sibling(ds, level, 1, forw); 3177c4a07a4SDarrick J. Wong 3187c4a07a4SDarrick J. Wong out: 3197c4a07a4SDarrick J. Wong memset(&ds->state->altpath, 0, sizeof(ds->state->altpath)); 3207c4a07a4SDarrick J. Wong return error; 3217c4a07a4SDarrick J. Wong } 3227c4a07a4SDarrick J. Wong 3237c4a07a4SDarrick J. Wong /* Load a dir/attribute block from a btree. */ 3247c4a07a4SDarrick J. Wong STATIC int 3257c4a07a4SDarrick J. Wong xfs_scrub_da_btree_block( 3267c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree *ds, 3277c4a07a4SDarrick J. Wong int level, 3287c4a07a4SDarrick J. Wong xfs_dablk_t blkno) 3297c4a07a4SDarrick J. Wong { 3307c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blk; 3317c4a07a4SDarrick J. Wong struct xfs_da_intnode *node; 3327c4a07a4SDarrick J. Wong struct xfs_da_node_entry *btree; 3337c4a07a4SDarrick J. Wong struct xfs_da3_blkinfo *hdr3; 3347c4a07a4SDarrick J. Wong struct xfs_da_args *dargs = &ds->dargs; 3357c4a07a4SDarrick J. Wong struct xfs_inode *ip = ds->dargs.dp; 3367c4a07a4SDarrick J. Wong xfs_ino_t owner; 3377c4a07a4SDarrick J. Wong int *pmaxrecs; 3387c4a07a4SDarrick J. Wong struct xfs_da3_icnode_hdr nodehdr; 3390dca060cSDarrick J. Wong int error = 0; 3407c4a07a4SDarrick J. Wong 3417c4a07a4SDarrick J. Wong blk = &ds->state->path.blk[level]; 3427c4a07a4SDarrick J. Wong ds->state->path.active = level + 1; 3437c4a07a4SDarrick J. Wong 3447c4a07a4SDarrick J. Wong /* Release old block. */ 3457c4a07a4SDarrick J. Wong if (blk->bp) { 3467c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 3477c4a07a4SDarrick J. Wong blk->bp = NULL; 3487c4a07a4SDarrick J. Wong } 3497c4a07a4SDarrick J. Wong 3507c4a07a4SDarrick J. Wong /* Check the pointer. */ 3517c4a07a4SDarrick J. Wong blk->blkno = blkno; 3527c4a07a4SDarrick J. Wong if (!xfs_scrub_da_btree_ptr_ok(ds, level, blkno)) 3537c4a07a4SDarrick J. Wong goto out_nobuf; 3547c4a07a4SDarrick J. Wong 3557c4a07a4SDarrick J. Wong /* Read the buffer. */ 3567c4a07a4SDarrick J. Wong error = xfs_da_read_buf(dargs->trans, dargs->dp, blk->blkno, -2, 3577c4a07a4SDarrick J. Wong &blk->bp, dargs->whichfork, 3587c4a07a4SDarrick J. Wong &xfs_scrub_da_btree_buf_ops); 3597c4a07a4SDarrick J. Wong if (!xfs_scrub_da_process_error(ds, level, &error)) 3607c4a07a4SDarrick J. Wong goto out_nobuf; 3617c4a07a4SDarrick J. Wong 3627c4a07a4SDarrick J. Wong /* 3637c4a07a4SDarrick J. Wong * We didn't find a dir btree root block, which means that 3647c4a07a4SDarrick J. Wong * there's no LEAF1/LEAFN tree (at least not where it's supposed 3657c4a07a4SDarrick J. Wong * to be), so jump out now. 3667c4a07a4SDarrick J. Wong */ 3677c4a07a4SDarrick J. Wong if (ds->dargs.whichfork == XFS_DATA_FORK && level == 0 && 3687c4a07a4SDarrick J. Wong blk->bp == NULL) 3697c4a07a4SDarrick J. Wong goto out_nobuf; 3707c4a07a4SDarrick J. Wong 3717c4a07a4SDarrick J. Wong /* It's /not/ ok for attr trees not to have a da btree. */ 3727c4a07a4SDarrick J. Wong if (blk->bp == NULL) { 3737c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 3747c4a07a4SDarrick J. Wong goto out_nobuf; 3757c4a07a4SDarrick J. Wong } 3767c4a07a4SDarrick J. Wong 3777c4a07a4SDarrick J. Wong hdr3 = blk->bp->b_addr; 3787c4a07a4SDarrick J. Wong blk->magic = be16_to_cpu(hdr3->hdr.magic); 3797c4a07a4SDarrick J. Wong pmaxrecs = &ds->maxrecs[level]; 3807c4a07a4SDarrick J. Wong 381*4da4b10bSDarrick J. Wong /* We only started zeroing the header on v5 filesystems. */ 382*4da4b10bSDarrick J. Wong if (xfs_sb_version_hascrc(&ds->sc->mp->m_sb) && hdr3->hdr.pad) 3837c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 3847c4a07a4SDarrick J. Wong 3857c4a07a4SDarrick J. Wong /* Check the owner. */ 3867c4a07a4SDarrick J. Wong if (xfs_sb_version_hascrc(&ip->i_mount->m_sb)) { 3877c4a07a4SDarrick J. Wong owner = be64_to_cpu(hdr3->owner); 3887c4a07a4SDarrick J. Wong if (owner != ip->i_ino) 3897c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 3907c4a07a4SDarrick J. Wong } 3917c4a07a4SDarrick J. Wong 3927c4a07a4SDarrick J. Wong /* Check the siblings. */ 3937c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_block_check_siblings(ds, level, &hdr3->hdr); 3947c4a07a4SDarrick J. Wong if (error) 3957c4a07a4SDarrick J. Wong goto out; 3967c4a07a4SDarrick J. Wong 3977c4a07a4SDarrick J. Wong /* Interpret the buffer. */ 3987c4a07a4SDarrick J. Wong switch (blk->magic) { 3997c4a07a4SDarrick J. Wong case XFS_ATTR_LEAF_MAGIC: 4007c4a07a4SDarrick J. Wong case XFS_ATTR3_LEAF_MAGIC: 4017c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4027c4a07a4SDarrick J. Wong XFS_BLFT_ATTR_LEAF_BUF); 4037c4a07a4SDarrick J. Wong blk->magic = XFS_ATTR_LEAF_MAGIC; 4047c4a07a4SDarrick J. Wong blk->hashval = xfs_attr_leaf_lasthash(blk->bp, pmaxrecs); 4057c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 4067c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4077c4a07a4SDarrick J. Wong break; 4087c4a07a4SDarrick J. Wong case XFS_DIR2_LEAFN_MAGIC: 4097c4a07a4SDarrick J. Wong case XFS_DIR3_LEAFN_MAGIC: 4107c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4117c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAFN_BUF); 4127c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAFN_MAGIC; 4137c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4147c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 4157c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4167c4a07a4SDarrick J. Wong break; 4177c4a07a4SDarrick J. Wong case XFS_DIR2_LEAF1_MAGIC: 4187c4a07a4SDarrick J. Wong case XFS_DIR3_LEAF1_MAGIC: 4197c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4207c4a07a4SDarrick J. Wong XFS_BLFT_DIR_LEAF1_BUF); 4217c4a07a4SDarrick J. Wong blk->magic = XFS_DIR2_LEAF1_MAGIC; 4227c4a07a4SDarrick J. Wong blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs); 4237c4a07a4SDarrick J. Wong if (ds->tree_level != 0) 4247c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4257c4a07a4SDarrick J. Wong break; 4267c4a07a4SDarrick J. Wong case XFS_DA_NODE_MAGIC: 4277c4a07a4SDarrick J. Wong case XFS_DA3_NODE_MAGIC: 4287c4a07a4SDarrick J. Wong xfs_trans_buf_set_type(dargs->trans, blk->bp, 4297c4a07a4SDarrick J. Wong XFS_BLFT_DA_NODE_BUF); 4307c4a07a4SDarrick J. Wong blk->magic = XFS_DA_NODE_MAGIC; 4317c4a07a4SDarrick J. Wong node = blk->bp->b_addr; 4327c4a07a4SDarrick J. Wong ip->d_ops->node_hdr_from_disk(&nodehdr, node); 4337c4a07a4SDarrick J. Wong btree = ip->d_ops->node_tree_p(node); 4347c4a07a4SDarrick J. Wong *pmaxrecs = nodehdr.count; 4357c4a07a4SDarrick J. Wong blk->hashval = be32_to_cpu(btree[*pmaxrecs - 1].hashval); 4367c4a07a4SDarrick J. Wong if (level == 0) { 4377c4a07a4SDarrick J. Wong if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) { 4387c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4397c4a07a4SDarrick J. Wong goto out_freebp; 4407c4a07a4SDarrick J. Wong } 4417c4a07a4SDarrick J. Wong ds->tree_level = nodehdr.level; 4427c4a07a4SDarrick J. Wong } else { 4437c4a07a4SDarrick J. Wong if (ds->tree_level != nodehdr.level) { 4447c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4457c4a07a4SDarrick J. Wong goto out_freebp; 4467c4a07a4SDarrick J. Wong } 4477c4a07a4SDarrick J. Wong } 4487c4a07a4SDarrick J. Wong 4497c4a07a4SDarrick J. Wong /* XXX: Check hdr3.pad32 once we know how to fix it. */ 4507c4a07a4SDarrick J. Wong break; 4517c4a07a4SDarrick J. Wong default: 4527c4a07a4SDarrick J. Wong xfs_scrub_da_set_corrupt(ds, level); 4537c4a07a4SDarrick J. Wong goto out_freebp; 4547c4a07a4SDarrick J. Wong } 4557c4a07a4SDarrick J. Wong 4567c4a07a4SDarrick J. Wong out: 4577c4a07a4SDarrick J. Wong return error; 4587c4a07a4SDarrick J. Wong out_freebp: 4597c4a07a4SDarrick J. Wong xfs_trans_brelse(dargs->trans, blk->bp); 4607c4a07a4SDarrick J. Wong blk->bp = NULL; 4617c4a07a4SDarrick J. Wong out_nobuf: 4627c4a07a4SDarrick J. Wong blk->blkno = 0; 4637c4a07a4SDarrick J. Wong return error; 4647c4a07a4SDarrick J. Wong } 4657c4a07a4SDarrick J. Wong 4667c4a07a4SDarrick J. Wong /* Visit all nodes and leaves of a da btree. */ 4677c4a07a4SDarrick J. Wong int 4687c4a07a4SDarrick J. Wong xfs_scrub_da_btree( 4697c4a07a4SDarrick J. Wong struct xfs_scrub_context *sc, 4707c4a07a4SDarrick J. Wong int whichfork, 47113791d3bSDarrick J. Wong xfs_scrub_da_btree_rec_fn scrub_fn, 47213791d3bSDarrick J. Wong void *private) 4737c4a07a4SDarrick J. Wong { 4747c4a07a4SDarrick J. Wong struct xfs_scrub_da_btree ds = {}; 4757c4a07a4SDarrick J. Wong struct xfs_mount *mp = sc->mp; 4767c4a07a4SDarrick J. Wong struct xfs_da_state_blk *blks; 4777c4a07a4SDarrick J. Wong struct xfs_da_node_entry *key; 4787c4a07a4SDarrick J. Wong void *rec; 4797c4a07a4SDarrick J. Wong xfs_dablk_t blkno; 4807c4a07a4SDarrick J. Wong int level; 4817c4a07a4SDarrick J. Wong int error; 4827c4a07a4SDarrick J. Wong 4837c4a07a4SDarrick J. Wong /* Skip short format data structures; no btree to scan. */ 4847c4a07a4SDarrick J. Wong if (XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_EXTENTS && 4857c4a07a4SDarrick J. Wong XFS_IFORK_FORMAT(sc->ip, whichfork) != XFS_DINODE_FMT_BTREE) 4867c4a07a4SDarrick J. Wong return 0; 4877c4a07a4SDarrick J. Wong 4887c4a07a4SDarrick J. Wong /* Set up initial da state. */ 4897c4a07a4SDarrick J. Wong ds.dargs.dp = sc->ip; 4907c4a07a4SDarrick J. Wong ds.dargs.whichfork = whichfork; 4917c4a07a4SDarrick J. Wong ds.dargs.trans = sc->tp; 4927c4a07a4SDarrick J. Wong ds.dargs.op_flags = XFS_DA_OP_OKNOENT; 4937c4a07a4SDarrick J. Wong ds.state = xfs_da_state_alloc(); 4947c4a07a4SDarrick J. Wong ds.state->args = &ds.dargs; 4957c4a07a4SDarrick J. Wong ds.state->mp = mp; 4967c4a07a4SDarrick J. Wong ds.sc = sc; 49713791d3bSDarrick J. Wong ds.private = private; 4987c4a07a4SDarrick J. Wong if (whichfork == XFS_ATTR_FORK) { 4997c4a07a4SDarrick J. Wong ds.dargs.geo = mp->m_attr_geo; 5007c4a07a4SDarrick J. Wong ds.lowest = 0; 5017c4a07a4SDarrick J. Wong ds.highest = 0; 5027c4a07a4SDarrick J. Wong } else { 5037c4a07a4SDarrick J. Wong ds.dargs.geo = mp->m_dir_geo; 5047c4a07a4SDarrick J. Wong ds.lowest = ds.dargs.geo->leafblk; 5057c4a07a4SDarrick J. Wong ds.highest = ds.dargs.geo->freeblk; 5067c4a07a4SDarrick J. Wong } 5077c4a07a4SDarrick J. Wong blkno = ds.lowest; 5087c4a07a4SDarrick J. Wong level = 0; 5097c4a07a4SDarrick J. Wong 5107c4a07a4SDarrick J. Wong /* Find the root of the da tree, if present. */ 5117c4a07a4SDarrick J. Wong blks = ds.state->path.blk; 5127c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_block(&ds, level, blkno); 5137c4a07a4SDarrick J. Wong if (error) 5147c4a07a4SDarrick J. Wong goto out_state; 5157c4a07a4SDarrick J. Wong /* 5167c4a07a4SDarrick J. Wong * We didn't find a block at ds.lowest, which means that there's 5177c4a07a4SDarrick J. Wong * no LEAF1/LEAFN tree (at least not where it's supposed to be), 5187c4a07a4SDarrick J. Wong * so jump out now. 5197c4a07a4SDarrick J. Wong */ 5207c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5217c4a07a4SDarrick J. Wong goto out_state; 5227c4a07a4SDarrick J. Wong 5237c4a07a4SDarrick J. Wong blks[level].index = 0; 5247c4a07a4SDarrick J. Wong while (level >= 0 && level < XFS_DA_NODE_MAXDEPTH) { 5257c4a07a4SDarrick J. Wong /* Handle leaf block. */ 5267c4a07a4SDarrick J. Wong if (blks[level].magic != XFS_DA_NODE_MAGIC) { 5277c4a07a4SDarrick J. Wong /* End of leaf, pop back towards the root. */ 5287c4a07a4SDarrick J. Wong if (blks[level].index >= ds.maxrecs[level]) { 5297c4a07a4SDarrick J. Wong if (level > 0) 5307c4a07a4SDarrick J. Wong blks[level - 1].index++; 5317c4a07a4SDarrick J. Wong ds.tree_level++; 5327c4a07a4SDarrick J. Wong level--; 5337c4a07a4SDarrick J. Wong continue; 5347c4a07a4SDarrick J. Wong } 5357c4a07a4SDarrick J. Wong 5367c4a07a4SDarrick J. Wong /* Dispatch record scrubbing. */ 5377c4a07a4SDarrick J. Wong rec = xfs_scrub_da_btree_entry(&ds, level, 5387c4a07a4SDarrick J. Wong blks[level].index); 5397c4a07a4SDarrick J. Wong error = scrub_fn(&ds, level, rec); 5407c4a07a4SDarrick J. Wong if (error) 5417c4a07a4SDarrick J. Wong break; 5427c4a07a4SDarrick J. Wong if (xfs_scrub_should_terminate(sc, &error) || 5437c4a07a4SDarrick J. Wong (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) 5447c4a07a4SDarrick J. Wong break; 5457c4a07a4SDarrick J. Wong 5467c4a07a4SDarrick J. Wong blks[level].index++; 5477c4a07a4SDarrick J. Wong continue; 5487c4a07a4SDarrick J. Wong } 5497c4a07a4SDarrick J. Wong 5507c4a07a4SDarrick J. Wong 5517c4a07a4SDarrick J. Wong /* End of node, pop back towards the root. */ 5527c4a07a4SDarrick J. Wong if (blks[level].index >= ds.maxrecs[level]) { 5537c4a07a4SDarrick J. Wong if (level > 0) 5547c4a07a4SDarrick J. Wong blks[level - 1].index++; 5557c4a07a4SDarrick J. Wong ds.tree_level++; 5567c4a07a4SDarrick J. Wong level--; 5577c4a07a4SDarrick J. Wong continue; 5587c4a07a4SDarrick J. Wong } 5597c4a07a4SDarrick J. Wong 5607c4a07a4SDarrick J. Wong /* Hashes in order for scrub? */ 5617c4a07a4SDarrick J. Wong key = xfs_scrub_da_btree_entry(&ds, level, blks[level].index); 5627c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_hash(&ds, level, &key->hashval); 5637c4a07a4SDarrick J. Wong if (error) 5647c4a07a4SDarrick J. Wong goto out; 5657c4a07a4SDarrick J. Wong 5667c4a07a4SDarrick J. Wong /* Drill another level deeper. */ 5677c4a07a4SDarrick J. Wong blkno = be32_to_cpu(key->before); 5687c4a07a4SDarrick J. Wong level++; 5697c4a07a4SDarrick J. Wong ds.tree_level--; 5707c4a07a4SDarrick J. Wong error = xfs_scrub_da_btree_block(&ds, level, blkno); 5717c4a07a4SDarrick J. Wong if (error) 5727c4a07a4SDarrick J. Wong goto out; 5737c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5747c4a07a4SDarrick J. Wong goto out; 5757c4a07a4SDarrick J. Wong 5767c4a07a4SDarrick J. Wong blks[level].index = 0; 5777c4a07a4SDarrick J. Wong } 5787c4a07a4SDarrick J. Wong 5797c4a07a4SDarrick J. Wong out: 5807c4a07a4SDarrick J. Wong /* Release all the buffers we're tracking. */ 5817c4a07a4SDarrick J. Wong for (level = 0; level < XFS_DA_NODE_MAXDEPTH; level++) { 5827c4a07a4SDarrick J. Wong if (blks[level].bp == NULL) 5837c4a07a4SDarrick J. Wong continue; 5847c4a07a4SDarrick J. Wong xfs_trans_brelse(sc->tp, blks[level].bp); 5857c4a07a4SDarrick J. Wong blks[level].bp = NULL; 5867c4a07a4SDarrick J. Wong } 5877c4a07a4SDarrick J. Wong 5887c4a07a4SDarrick J. Wong out_state: 5897c4a07a4SDarrick J. Wong xfs_da_state_free(ds.state); 5907c4a07a4SDarrick J. Wong return error; 5917c4a07a4SDarrick J. Wong } 592