123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2017 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_log_format.h"
- #include "xfs_trans.h"
- #include "xfs_inode.h"
- #include "xfs_dir2.h"
- #include "xfs_dir2_priv.h"
- #include "xfs_attr_leaf.h"
- #include "scrub/scrub.h"
- #include "scrub/common.h"
- #include "scrub/trace.h"
- #include "scrub/dabtree.h"
- /* Directory/Attribute Btree */
- /*
- * Check for da btree operation errors. See the section about handling
- * operational errors in common.c.
- */
- bool
- xchk_da_process_error(
- struct xchk_da_btree *ds,
- int level,
- int *error)
- {
- struct xfs_scrub *sc = ds->sc;
- if (*error == 0)
- return true;
- switch (*error) {
- case -EDEADLOCK:
- /* Used to restart an op with deadlock avoidance. */
- trace_xchk_deadlock_retry(sc->ip, sc->sm, *error);
- break;
- case -EFSBADCRC:
- case -EFSCORRUPTED:
- /* Note the badness but don't abort. */
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
- *error = 0;
- fallthrough;
- default:
- trace_xchk_file_op_error(sc, ds->dargs.whichfork,
- xfs_dir2_da_to_db(ds->dargs.geo,
- ds->state->path.blk[level].blkno),
- *error, __return_address);
- break;
- }
- return false;
- }
- /*
- * Check for da btree corruption. See the section about handling
- * operational errors in common.c.
- */
- void
- xchk_da_set_corrupt(
- struct xchk_da_btree *ds,
- int level)
- {
- struct xfs_scrub *sc = ds->sc;
- sc->sm->sm_flags |= XFS_SCRUB_OFLAG_CORRUPT;
- trace_xchk_fblock_error(sc, ds->dargs.whichfork,
- xfs_dir2_da_to_db(ds->dargs.geo,
- ds->state->path.blk[level].blkno),
- __return_address);
- }
- static struct xfs_da_node_entry *
- xchk_da_btree_node_entry(
- struct xchk_da_btree *ds,
- int level)
- {
- struct xfs_da_state_blk *blk = &ds->state->path.blk[level];
- struct xfs_da3_icnode_hdr hdr;
- ASSERT(blk->magic == XFS_DA_NODE_MAGIC);
- xfs_da3_node_hdr_from_disk(ds->sc->mp, &hdr, blk->bp->b_addr);
- return hdr.btree + blk->index;
- }
- /* Scrub a da btree hash (key). */
- int
- xchk_da_btree_hash(
- struct xchk_da_btree *ds,
- int level,
- __be32 *hashp)
- {
- struct xfs_da_node_entry *entry;
- xfs_dahash_t hash;
- xfs_dahash_t parent_hash;
- /* Is this hash in order? */
- hash = be32_to_cpu(*hashp);
- if (hash < ds->hashes[level])
- xchk_da_set_corrupt(ds, level);
- ds->hashes[level] = hash;
- if (level == 0)
- return 0;
- /* Is this hash no larger than the parent hash? */
- entry = xchk_da_btree_node_entry(ds, level - 1);
- parent_hash = be32_to_cpu(entry->hashval);
- if (parent_hash < hash)
- xchk_da_set_corrupt(ds, level);
- return 0;
- }
- /*
- * Check a da btree pointer. Returns true if it's ok to use this
- * pointer.
- */
- STATIC bool
- xchk_da_btree_ptr_ok(
- struct xchk_da_btree *ds,
- int level,
- xfs_dablk_t blkno)
- {
- if (blkno < ds->lowest || (ds->highest != 0 && blkno >= ds->highest)) {
- xchk_da_set_corrupt(ds, level);
- return false;
- }
- return true;
- }
- /*
- * The da btree scrubber can handle leaf1 blocks as a degenerate
- * form of leafn blocks. Since the regular da code doesn't handle
- * leaf1, we must multiplex the verifiers.
- */
- static void
- xchk_da_btree_read_verify(
- struct xfs_buf *bp)
- {
- struct xfs_da_blkinfo *info = bp->b_addr;
- switch (be16_to_cpu(info->magic)) {
- case XFS_DIR2_LEAF1_MAGIC:
- case XFS_DIR3_LEAF1_MAGIC:
- bp->b_ops = &xfs_dir3_leaf1_buf_ops;
- bp->b_ops->verify_read(bp);
- return;
- default:
- /*
- * xfs_da3_node_buf_ops already know how to handle
- * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks.
- */
- bp->b_ops = &xfs_da3_node_buf_ops;
- bp->b_ops->verify_read(bp);
- return;
- }
- }
- static void
- xchk_da_btree_write_verify(
- struct xfs_buf *bp)
- {
- struct xfs_da_blkinfo *info = bp->b_addr;
- switch (be16_to_cpu(info->magic)) {
- case XFS_DIR2_LEAF1_MAGIC:
- case XFS_DIR3_LEAF1_MAGIC:
- bp->b_ops = &xfs_dir3_leaf1_buf_ops;
- bp->b_ops->verify_write(bp);
- return;
- default:
- /*
- * xfs_da3_node_buf_ops already know how to handle
- * DA*_NODE, ATTR*_LEAF, and DIR*_LEAFN blocks.
- */
- bp->b_ops = &xfs_da3_node_buf_ops;
- bp->b_ops->verify_write(bp);
- return;
- }
- }
- static void *
- xchk_da_btree_verify(
- struct xfs_buf *bp)
- {
- struct xfs_da_blkinfo *info = bp->b_addr;
- switch (be16_to_cpu(info->magic)) {
- case XFS_DIR2_LEAF1_MAGIC:
- case XFS_DIR3_LEAF1_MAGIC:
- bp->b_ops = &xfs_dir3_leaf1_buf_ops;
- return bp->b_ops->verify_struct(bp);
- default:
- bp->b_ops = &xfs_da3_node_buf_ops;
- return bp->b_ops->verify_struct(bp);
- }
- }
- static const struct xfs_buf_ops xchk_da_btree_buf_ops = {
- .name = "xchk_da_btree",
- .verify_read = xchk_da_btree_read_verify,
- .verify_write = xchk_da_btree_write_verify,
- .verify_struct = xchk_da_btree_verify,
- };
- /* Check a block's sibling. */
- STATIC int
- xchk_da_btree_block_check_sibling(
- struct xchk_da_btree *ds,
- int level,
- int direction,
- xfs_dablk_t sibling)
- {
- struct xfs_da_state_path *path = &ds->state->path;
- struct xfs_da_state_path *altpath = &ds->state->altpath;
- int retval;
- int plevel;
- int error;
- memcpy(altpath, path, sizeof(ds->state->altpath));
- /*
- * If the pointer is null, we shouldn't be able to move the upper
- * level pointer anywhere.
- */
- if (sibling == 0) {
- error = xfs_da3_path_shift(ds->state, altpath, direction,
- false, &retval);
- if (error == 0 && retval == 0)
- xchk_da_set_corrupt(ds, level);
- error = 0;
- goto out;
- }
- /* Move the alternate cursor one block in the direction given. */
- error = xfs_da3_path_shift(ds->state, altpath, direction, false,
- &retval);
- if (!xchk_da_process_error(ds, level, &error))
- goto out;
- if (retval) {
- xchk_da_set_corrupt(ds, level);
- goto out;
- }
- if (altpath->blk[level].bp)
- xchk_buffer_recheck(ds->sc, altpath->blk[level].bp);
- /* Compare upper level pointer to sibling pointer. */
- if (altpath->blk[level].blkno != sibling)
- xchk_da_set_corrupt(ds, level);
- out:
- /* Free all buffers in the altpath that aren't referenced from path. */
- for (plevel = 0; plevel < altpath->active; plevel++) {
- if (altpath->blk[plevel].bp == NULL ||
- (plevel < path->active &&
- altpath->blk[plevel].bp == path->blk[plevel].bp))
- continue;
- xfs_trans_brelse(ds->dargs.trans, altpath->blk[plevel].bp);
- altpath->blk[plevel].bp = NULL;
- }
- return error;
- }
- /* Check a block's sibling pointers. */
- STATIC int
- xchk_da_btree_block_check_siblings(
- struct xchk_da_btree *ds,
- int level,
- struct xfs_da_blkinfo *hdr)
- {
- xfs_dablk_t forw;
- xfs_dablk_t back;
- int error = 0;
- forw = be32_to_cpu(hdr->forw);
- back = be32_to_cpu(hdr->back);
- /* Top level blocks should not have sibling pointers. */
- if (level == 0) {
- if (forw != 0 || back != 0)
- xchk_da_set_corrupt(ds, level);
- return 0;
- }
- /*
- * Check back (left) and forw (right) pointers. These functions
- * absorb error codes for us.
- */
- error = xchk_da_btree_block_check_sibling(ds, level, 0, back);
- if (error)
- goto out;
- error = xchk_da_btree_block_check_sibling(ds, level, 1, forw);
- out:
- memset(&ds->state->altpath, 0, sizeof(ds->state->altpath));
- return error;
- }
- /* Load a dir/attribute block from a btree. */
- STATIC int
- xchk_da_btree_block(
- struct xchk_da_btree *ds,
- int level,
- xfs_dablk_t blkno)
- {
- struct xfs_da_state_blk *blk;
- struct xfs_da_intnode *node;
- struct xfs_da_node_entry *btree;
- struct xfs_da3_blkinfo *hdr3;
- struct xfs_da_args *dargs = &ds->dargs;
- struct xfs_inode *ip = ds->dargs.dp;
- xfs_ino_t owner;
- int *pmaxrecs;
- struct xfs_da3_icnode_hdr nodehdr;
- int error = 0;
- blk = &ds->state->path.blk[level];
- ds->state->path.active = level + 1;
- /* Release old block. */
- if (blk->bp) {
- xfs_trans_brelse(dargs->trans, blk->bp);
- blk->bp = NULL;
- }
- /* Check the pointer. */
- blk->blkno = blkno;
- if (!xchk_da_btree_ptr_ok(ds, level, blkno))
- goto out_nobuf;
- /* Read the buffer. */
- error = xfs_da_read_buf(dargs->trans, dargs->dp, blk->blkno,
- XFS_DABUF_MAP_HOLE_OK, &blk->bp, dargs->whichfork,
- &xchk_da_btree_buf_ops);
- if (!xchk_da_process_error(ds, level, &error))
- goto out_nobuf;
- if (blk->bp)
- xchk_buffer_recheck(ds->sc, blk->bp);
- /*
- * We didn't find a dir btree root block, which means that
- * there's no LEAF1/LEAFN tree (at least not where it's supposed
- * to be), so jump out now.
- */
- if (ds->dargs.whichfork == XFS_DATA_FORK && level == 0 &&
- blk->bp == NULL)
- goto out_nobuf;
- /* It's /not/ ok for attr trees not to have a da btree. */
- if (blk->bp == NULL) {
- xchk_da_set_corrupt(ds, level);
- goto out_nobuf;
- }
- hdr3 = blk->bp->b_addr;
- blk->magic = be16_to_cpu(hdr3->hdr.magic);
- pmaxrecs = &ds->maxrecs[level];
- /* We only started zeroing the header on v5 filesystems. */
- if (xfs_has_crc(ds->sc->mp) && hdr3->hdr.pad)
- xchk_da_set_corrupt(ds, level);
- /* Check the owner. */
- if (xfs_has_crc(ip->i_mount)) {
- owner = be64_to_cpu(hdr3->owner);
- if (owner != ip->i_ino)
- xchk_da_set_corrupt(ds, level);
- }
- /* Check the siblings. */
- error = xchk_da_btree_block_check_siblings(ds, level, &hdr3->hdr);
- if (error)
- goto out;
- /* Interpret the buffer. */
- switch (blk->magic) {
- case XFS_ATTR_LEAF_MAGIC:
- case XFS_ATTR3_LEAF_MAGIC:
- xfs_trans_buf_set_type(dargs->trans, blk->bp,
- XFS_BLFT_ATTR_LEAF_BUF);
- blk->magic = XFS_ATTR_LEAF_MAGIC;
- blk->hashval = xfs_attr_leaf_lasthash(blk->bp, pmaxrecs);
- if (ds->tree_level != 0)
- xchk_da_set_corrupt(ds, level);
- break;
- case XFS_DIR2_LEAFN_MAGIC:
- case XFS_DIR3_LEAFN_MAGIC:
- xfs_trans_buf_set_type(dargs->trans, blk->bp,
- XFS_BLFT_DIR_LEAFN_BUF);
- blk->magic = XFS_DIR2_LEAFN_MAGIC;
- blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs);
- if (ds->tree_level != 0)
- xchk_da_set_corrupt(ds, level);
- break;
- case XFS_DIR2_LEAF1_MAGIC:
- case XFS_DIR3_LEAF1_MAGIC:
- xfs_trans_buf_set_type(dargs->trans, blk->bp,
- XFS_BLFT_DIR_LEAF1_BUF);
- blk->magic = XFS_DIR2_LEAF1_MAGIC;
- blk->hashval = xfs_dir2_leaf_lasthash(ip, blk->bp, pmaxrecs);
- if (ds->tree_level != 0)
- xchk_da_set_corrupt(ds, level);
- break;
- case XFS_DA_NODE_MAGIC:
- case XFS_DA3_NODE_MAGIC:
- xfs_trans_buf_set_type(dargs->trans, blk->bp,
- XFS_BLFT_DA_NODE_BUF);
- blk->magic = XFS_DA_NODE_MAGIC;
- node = blk->bp->b_addr;
- xfs_da3_node_hdr_from_disk(ip->i_mount, &nodehdr, node);
- btree = nodehdr.btree;
- *pmaxrecs = nodehdr.count;
- blk->hashval = be32_to_cpu(btree[*pmaxrecs - 1].hashval);
- if (level == 0) {
- if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH) {
- xchk_da_set_corrupt(ds, level);
- goto out_freebp;
- }
- ds->tree_level = nodehdr.level;
- } else {
- if (ds->tree_level != nodehdr.level) {
- xchk_da_set_corrupt(ds, level);
- goto out_freebp;
- }
- }
- /* XXX: Check hdr3.pad32 once we know how to fix it. */
- break;
- default:
- xchk_da_set_corrupt(ds, level);
- goto out_freebp;
- }
- /*
- * If we've been handed a block that is below the dabtree root, does
- * its hashval match what the parent block expected to see?
- */
- if (level > 0) {
- struct xfs_da_node_entry *key;
- key = xchk_da_btree_node_entry(ds, level - 1);
- if (be32_to_cpu(key->hashval) != blk->hashval) {
- xchk_da_set_corrupt(ds, level);
- goto out_freebp;
- }
- }
- out:
- return error;
- out_freebp:
- xfs_trans_brelse(dargs->trans, blk->bp);
- blk->bp = NULL;
- out_nobuf:
- blk->blkno = 0;
- return error;
- }
- /* Visit all nodes and leaves of a da btree. */
- int
- xchk_da_btree(
- struct xfs_scrub *sc,
- int whichfork,
- xchk_da_btree_rec_fn scrub_fn,
- void *private)
- {
- struct xchk_da_btree *ds;
- struct xfs_mount *mp = sc->mp;
- struct xfs_da_state_blk *blks;
- struct xfs_da_node_entry *key;
- xfs_dablk_t blkno;
- int level;
- int error;
- /* Skip short format data structures; no btree to scan. */
- if (!xfs_ifork_has_extents(xfs_ifork_ptr(sc->ip, whichfork)))
- return 0;
- /* Set up initial da state. */
- ds = kmem_zalloc(sizeof(struct xchk_da_btree), KM_NOFS | KM_MAYFAIL);
- if (!ds)
- return -ENOMEM;
- ds->dargs.dp = sc->ip;
- ds->dargs.whichfork = whichfork;
- ds->dargs.trans = sc->tp;
- ds->dargs.op_flags = XFS_DA_OP_OKNOENT;
- ds->state = xfs_da_state_alloc(&ds->dargs);
- ds->sc = sc;
- ds->private = private;
- if (whichfork == XFS_ATTR_FORK) {
- ds->dargs.geo = mp->m_attr_geo;
- ds->lowest = 0;
- ds->highest = 0;
- } else {
- ds->dargs.geo = mp->m_dir_geo;
- ds->lowest = ds->dargs.geo->leafblk;
- ds->highest = ds->dargs.geo->freeblk;
- }
- blkno = ds->lowest;
- level = 0;
- /* Find the root of the da tree, if present. */
- blks = ds->state->path.blk;
- error = xchk_da_btree_block(ds, level, blkno);
- if (error)
- goto out_state;
- /*
- * We didn't find a block at ds->lowest, which means that there's
- * no LEAF1/LEAFN tree (at least not where it's supposed to be),
- * so jump out now.
- */
- if (blks[level].bp == NULL)
- goto out_state;
- blks[level].index = 0;
- while (level >= 0 && level < XFS_DA_NODE_MAXDEPTH) {
- /* Handle leaf block. */
- if (blks[level].magic != XFS_DA_NODE_MAGIC) {
- /* End of leaf, pop back towards the root. */
- if (blks[level].index >= ds->maxrecs[level]) {
- if (level > 0)
- blks[level - 1].index++;
- ds->tree_level++;
- level--;
- continue;
- }
- /* Dispatch record scrubbing. */
- error = scrub_fn(ds, level);
- if (error)
- break;
- if (xchk_should_terminate(sc, &error) ||
- (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT))
- break;
- blks[level].index++;
- continue;
- }
- /* End of node, pop back towards the root. */
- if (blks[level].index >= ds->maxrecs[level]) {
- if (level > 0)
- blks[level - 1].index++;
- ds->tree_level++;
- level--;
- continue;
- }
- /* Hashes in order for scrub? */
- key = xchk_da_btree_node_entry(ds, level);
- error = xchk_da_btree_hash(ds, level, &key->hashval);
- if (error)
- goto out;
- /* Drill another level deeper. */
- blkno = be32_to_cpu(key->before);
- level++;
- if (level >= XFS_DA_NODE_MAXDEPTH) {
- /* Too deep! */
- xchk_da_set_corrupt(ds, level - 1);
- break;
- }
- ds->tree_level--;
- error = xchk_da_btree_block(ds, level, blkno);
- if (error)
- goto out;
- if (blks[level].bp == NULL)
- goto out;
- blks[level].index = 0;
- }
- out:
- /* Release all the buffers we're tracking. */
- for (level = 0; level < XFS_DA_NODE_MAXDEPTH; level++) {
- if (blks[level].bp == NULL)
- continue;
- xfs_trans_brelse(sc->tp, blks[level].bp);
- blks[level].bp = NULL;
- }
- out_state:
- xfs_da_state_free(ds->state);
- kmem_free(ds);
- return error;
- }
|