diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_btree.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_btree.c | 259 |
1 files changed, 149 insertions, 110 deletions
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index 5bfb88261c7e..5f33adf8eecb 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -29,6 +29,7 @@ #include "xfs_inode_item.h" #include "xfs_buf_item.h" #include "xfs_btree.h" +#include "xfs_errortag.h" #include "xfs_error.h" #include "xfs_trace.h" #include "xfs_cksum.h" @@ -63,44 +64,63 @@ xfs_btree_magic( return magic; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_lblock( - struct xfs_btree_cur *cur, /* btree cursor */ - struct xfs_btree_block *block, /* btree long form block pointer */ - int level, /* level of the btree block */ - struct xfs_buf *bp) /* buffer for block, if any */ +/* + * Check a long btree block header. Return the address of the failing check, + * or NULL if everything is ok. + */ +xfs_failaddr_t +__xfs_btree_check_lblock( + struct xfs_btree_cur *cur, + struct xfs_btree_block *block, + int level, + struct xfs_buf *bp) { - int lblock_ok = 1; /* block passes checks */ - struct xfs_mount *mp; /* file system mount point */ + struct xfs_mount *mp = cur->bc_mp; xfs_btnum_t btnum = cur->bc_btnum; - int crc; - - mp = cur->bc_mp; - crc = xfs_sb_version_hascrc(&mp->m_sb); + int crc = xfs_sb_version_hascrc(&mp->m_sb); if (crc) { - lblock_ok = lblock_ok && - uuid_equal(&block->bb_u.l.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.l.bb_blkno == cpu_to_be64( - bp ? bp->b_bn : XFS_BUF_DADDR_NULL); + if (!uuid_equal(&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid)) + return __this_address; + if (block->bb_u.l.bb_blkno != + cpu_to_be64(bp ? bp->b_bn : XFS_BUF_DADDR_NULL)) + return __this_address; + if (block->bb_u.l.bb_pad != cpu_to_be32(0)) + return __this_address; } - lblock_ok = lblock_ok && - be32_to_cpu(block->bb_magic) == xfs_btree_magic(crc, btnum) && - be16_to_cpu(block->bb_level) == level && - be16_to_cpu(block->bb_numrecs) <= - cur->bc_ops->get_maxrecs(cur, level) && - block->bb_u.l.bb_leftsib && - (block->bb_u.l.bb_leftsib == cpu_to_be64(NULLFSBLOCK) || - XFS_FSB_SANITY_CHECK(mp, - be64_to_cpu(block->bb_u.l.bb_leftsib))) && - block->bb_u.l.bb_rightsib && - (block->bb_u.l.bb_rightsib == cpu_to_be64(NULLFSBLOCK) || - XFS_FSB_SANITY_CHECK(mp, - be64_to_cpu(block->bb_u.l.bb_rightsib))); - - if (unlikely(XFS_TEST_ERROR(!lblock_ok, mp, + if (be32_to_cpu(block->bb_magic) != xfs_btree_magic(crc, btnum)) + return __this_address; + if (be16_to_cpu(block->bb_level) != level) + return __this_address; + if (be16_to_cpu(block->bb_numrecs) > + cur->bc_ops->get_maxrecs(cur, level)) + return __this_address; + if (block->bb_u.l.bb_leftsib != cpu_to_be64(NULLFSBLOCK) && + !xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_leftsib), + level + 1)) + return __this_address; + if (block->bb_u.l.bb_rightsib != cpu_to_be64(NULLFSBLOCK) && + !xfs_btree_check_lptr(cur, be64_to_cpu(block->bb_u.l.bb_rightsib), + level + 1)) + return __this_address; + + return NULL; +} + +/* Check a long btree block header. */ +static int +xfs_btree_check_lblock( + struct xfs_btree_cur *cur, + struct xfs_btree_block *block, + int level, + struct xfs_buf *bp) +{ + struct xfs_mount *mp = cur->bc_mp; + xfs_failaddr_t fa; + + fa = __xfs_btree_check_lblock(cur, block, level, bp); + if (unlikely(XFS_TEST_ERROR(fa != NULL, mp, XFS_ERRTAG_BTREE_CHECK_LBLOCK))) { if (bp) trace_xfs_btree_corrupt(bp, _RET_IP_); @@ -110,48 +130,61 @@ xfs_btree_check_lblock( return 0; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_sblock( - struct xfs_btree_cur *cur, /* btree cursor */ - struct xfs_btree_block *block, /* btree short form block pointer */ - int level, /* level of the btree block */ - struct xfs_buf *bp) /* buffer containing block */ +/* + * Check a short btree block header. Return the address of the failing check, + * or NULL if everything is ok. + */ +xfs_failaddr_t +__xfs_btree_check_sblock( + struct xfs_btree_cur *cur, + struct xfs_btree_block *block, + int level, + struct xfs_buf *bp) { - struct xfs_mount *mp; /* file system mount point */ - struct xfs_buf *agbp; /* buffer for ag. freespace struct */ - struct xfs_agf *agf; /* ag. freespace structure */ - xfs_agblock_t agflen; /* native ag. freespace length */ - int sblock_ok = 1; /* block passes checks */ + struct xfs_mount *mp = cur->bc_mp; xfs_btnum_t btnum = cur->bc_btnum; - int crc; - - mp = cur->bc_mp; - crc = xfs_sb_version_hascrc(&mp->m_sb); - agbp = cur->bc_private.a.agbp; - agf = XFS_BUF_TO_AGF(agbp); - agflen = be32_to_cpu(agf->agf_length); + int crc = xfs_sb_version_hascrc(&mp->m_sb); if (crc) { - sblock_ok = sblock_ok && - uuid_equal(&block->bb_u.s.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.s.bb_blkno == cpu_to_be64( - bp ? bp->b_bn : XFS_BUF_DADDR_NULL); + if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + return __this_address; + if (block->bb_u.s.bb_blkno != + cpu_to_be64(bp ? bp->b_bn : XFS_BUF_DADDR_NULL)) + return __this_address; } - sblock_ok = sblock_ok && - be32_to_cpu(block->bb_magic) == xfs_btree_magic(crc, btnum) && - be16_to_cpu(block->bb_level) == level && - be16_to_cpu(block->bb_numrecs) <= - cur->bc_ops->get_maxrecs(cur, level) && - (block->bb_u.s.bb_leftsib == cpu_to_be32(NULLAGBLOCK) || - be32_to_cpu(block->bb_u.s.bb_leftsib) < agflen) && - block->bb_u.s.bb_leftsib && - (block->bb_u.s.bb_rightsib == cpu_to_be32(NULLAGBLOCK) || - be32_to_cpu(block->bb_u.s.bb_rightsib) < agflen) && - block->bb_u.s.bb_rightsib; - - if (unlikely(XFS_TEST_ERROR(!sblock_ok, mp, + if (be32_to_cpu(block->bb_magic) != xfs_btree_magic(crc, btnum)) + return __this_address; + if (be16_to_cpu(block->bb_level) != level) + return __this_address; + if (be16_to_cpu(block->bb_numrecs) > + cur->bc_ops->get_maxrecs(cur, level)) + return __this_address; + if (block->bb_u.s.bb_leftsib != cpu_to_be32(NULLAGBLOCK) && + !xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_leftsib), + level + 1)) + return __this_address; + if (block->bb_u.s.bb_rightsib != cpu_to_be32(NULLAGBLOCK) && + !xfs_btree_check_sptr(cur, be32_to_cpu(block->bb_u.s.bb_rightsib), + level + 1)) + return __this_address; + + return NULL; +} + +/* Check a short btree block header. */ +STATIC int +xfs_btree_check_sblock( + struct xfs_btree_cur *cur, + struct xfs_btree_block *block, + int level, + struct xfs_buf *bp) +{ + struct xfs_mount *mp = cur->bc_mp; + xfs_failaddr_t fa; + + fa = __xfs_btree_check_sblock(cur, block, level, bp); + if (unlikely(XFS_TEST_ERROR(fa != NULL, mp, XFS_ERRTAG_BTREE_CHECK_SBLOCK))) { if (bp) trace_xfs_btree_corrupt(bp, _RET_IP_); @@ -177,59 +210,53 @@ xfs_btree_check_block( return xfs_btree_check_sblock(cur, block, level, bp); } -/* - * Check that (long) pointer is ok. - */ -int /* error (0 or EFSCORRUPTED) */ +/* Check that this long pointer is valid and points within the fs. */ +bool xfs_btree_check_lptr( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_fsblock_t bno, /* btree block disk address */ - int level) /* btree block level */ + struct xfs_btree_cur *cur, + xfs_fsblock_t fsbno, + int level) { - XFS_WANT_CORRUPTED_RETURN(cur->bc_mp, - level > 0 && - bno != NULLFSBLOCK && - XFS_FSB_SANITY_CHECK(cur->bc_mp, bno)); - return 0; + if (level <= 0) + return false; + return xfs_verify_fsbno(cur->bc_mp, fsbno); } -#ifdef DEBUG -/* - * Check that (short) pointer is ok. - */ -STATIC int /* error (0 or EFSCORRUPTED) */ +/* Check that this short pointer is valid and points within the AG. */ +bool xfs_btree_check_sptr( - struct xfs_btree_cur *cur, /* btree cursor */ - xfs_agblock_t bno, /* btree block disk address */ - int level) /* btree block level */ + struct xfs_btree_cur *cur, + xfs_agblock_t agbno, + int level) { - xfs_agblock_t agblocks = cur->bc_mp->m_sb.sb_agblocks; - - XFS_WANT_CORRUPTED_RETURN(cur->bc_mp, - level > 0 && - bno != NULLAGBLOCK && - bno != 0 && - bno < agblocks); - return 0; + if (level <= 0) + return false; + return xfs_verify_agbno(cur->bc_mp, cur->bc_private.a.agno, agbno); } +#ifdef DEBUG /* - * Check that block ptr is ok. + * Check that a given (indexed) btree pointer at a certain level of a + * btree is valid and doesn't point past where it should. */ -STATIC int /* error (0 or EFSCORRUPTED) */ +static int xfs_btree_check_ptr( - struct xfs_btree_cur *cur, /* btree cursor */ - union xfs_btree_ptr *ptr, /* btree block disk address */ - int index, /* offset from ptr to check */ - int level) /* btree block level */ + struct xfs_btree_cur *cur, + union xfs_btree_ptr *ptr, + int index, + int level) { if (cur->bc_flags & XFS_BTREE_LONG_PTRS) { - return xfs_btree_check_lptr(cur, - be64_to_cpu((&ptr->l)[index]), level); + XFS_WANT_CORRUPTED_RETURN(cur->bc_mp, + xfs_btree_check_lptr(cur, + be64_to_cpu((&ptr->l)[index]), level)); } else { - return xfs_btree_check_sptr(cur, - be32_to_cpu((&ptr->s)[index]), level); + XFS_WANT_CORRUPTED_RETURN(cur->bc_mp, + xfs_btree_check_sptr(cur, + be32_to_cpu((&ptr->s)[index]), level)); } + + return 0; } #endif @@ -1027,7 +1054,7 @@ xfs_btree_setbuf( } } -STATIC int +bool xfs_btree_ptr_is_null( struct xfs_btree_cur *cur, union xfs_btree_ptr *ptr) @@ -1052,7 +1079,7 @@ xfs_btree_set_ptr_null( /* * Get/set/init sibling pointers */ -STATIC void +void xfs_btree_get_sibling( struct xfs_btree_cur *cur, struct xfs_btree_block *block, @@ -2001,7 +2028,7 @@ error0: } /* Find the high key storage area from a regular key. */ -STATIC union xfs_btree_key * +union xfs_btree_key * xfs_btree_high_key_from_key( struct xfs_btree_cur *cur, union xfs_btree_key *key) @@ -2075,7 +2102,7 @@ xfs_btree_get_node_keys( } /* Derive the keys for any btree block. */ -STATIC void +void xfs_btree_get_keys( struct xfs_btree_cur *cur, struct xfs_btree_block *block, @@ -4914,3 +4941,15 @@ xfs_btree_count_blocks( return xfs_btree_visit_blocks(cur, xfs_btree_count_blocks_helper, blocks); } + +/* Compare two btree pointers. */ +int64_t +xfs_btree_diff_two_ptrs( + struct xfs_btree_cur *cur, + const union xfs_btree_ptr *a, + const union xfs_btree_ptr *b) +{ + if (cur->bc_flags & XFS_BTREE_LONG_PTRS) + return (int64_t)be64_to_cpu(a->l) - be64_to_cpu(b->l); + return (int64_t)be32_to_cpu(a->s) - be32_to_cpu(b->s); +} |