diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index ae19f242c237ee33e777b00332f977d960d6f009..8bb20e1cf57be8367b0533349e6999c8fcf8faee 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -63,44 +63,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. */ +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 +129,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_); diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h index 8f52eda8eb82bf19f60f5af22b8addd6d703702a..3f8001de2493e56ddede286f8c03ddc1a499bff0 100644 --- a/fs/xfs/libxfs/xfs_btree.h +++ b/fs/xfs/libxfs/xfs_btree.h @@ -255,6 +255,14 @@ typedef struct xfs_btree_cur */ #define XFS_BUF_TO_BLOCK(bp) ((struct xfs_btree_block *)((bp)->b_addr)) +/* + * Internal long and short btree block checks. They return NULL if the + * block is ok or the address of the failed check otherwise. + */ +xfs_failaddr_t __xfs_btree_check_lblock(struct xfs_btree_cur *cur, + struct xfs_btree_block *block, int level, struct xfs_buf *bp); +xfs_failaddr_t __xfs_btree_check_sblock(struct xfs_btree_cur *cur, + struct xfs_btree_block *block, int level, struct xfs_buf *bp); /* * Check that block header is ok. diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h index 0220159bd4636e8febb3ece887aef02a4e519791..f04dbfb2f50d6de70c25151d169adb4e2a4f12c0 100644 --- a/fs/xfs/libxfs/xfs_types.h +++ b/fs/xfs/libxfs/xfs_types.h @@ -47,6 +47,12 @@ typedef uint64_t xfs_filblks_t; /* number of blocks in a file */ typedef int64_t xfs_srtblock_t; /* signed version of xfs_rtblock_t */ typedef int64_t xfs_sfiloff_t; /* signed block number in a file */ +/* + * New verifiers will return the instruction address of the failing check. + * NULL means everything is ok. + */ +typedef void * xfs_failaddr_t; + /* * Null values for the types. */ diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h index dcd1292664b3404b25bd93b9d40e58ab81d72376..00a5efeec49616cfc6b473e5d8348eb879051016 100644 --- a/fs/xfs/xfs_linux.h +++ b/fs/xfs/xfs_linux.h @@ -142,6 +142,13 @@ typedef __u32 xfs_nlink_t; #define SYNCHRONIZE() barrier() #define __return_address __builtin_return_address(0) +/* + * Return the address of a label. Use barrier() so that the optimizer + * won't reorder code to refactor the error jumpouts into a single + * return, which throws off the reported address. + */ +#define __this_address ({ __label__ __here; __here: barrier(); &&__here; }) + #define XFS_PROJID_DEFAULT 0 #define MIN(a,b) (min(a,b))