]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - libxfs/xfs_da_btree.c
xfs: explicitly pass buffer size to xfs_corruption_error
[thirdparty/xfsprogs-dev.git] / libxfs / xfs_da_btree.c
index 08447be82926b2cfc982cfd971513b2dddb3a62d..467e96dc9e350522b3435fe6cf84991cb3415946 100644 (file)
@@ -123,7 +123,7 @@ xfs_da_state_free(xfs_da_state_t *state)
        kmem_zone_free(xfs_da_state_zone, state);
 }
 
-static bool
+static xfs_failaddr_t
 xfs_da3_node_verify(
        struct xfs_buf          *bp)
 {
@@ -140,24 +140,24 @@ xfs_da3_node_verify(
                struct xfs_da3_node_hdr *hdr3 = bp->b_addr;
 
                if (ichdr.magic != XFS_DA3_NODE_MAGIC)
-                       return false;
+                       return __this_address;
 
                if (!uuid_equal(&hdr3->info.uuid, &mp->m_sb.sb_meta_uuid))
-                       return false;
+                       return __this_address;
                if (be64_to_cpu(hdr3->info.blkno) != bp->b_bn)
-                       return false;
+                       return __this_address;
                if (!xfs_log_check_lsn(mp, be64_to_cpu(hdr3->info.lsn)))
-                       return false;
+                       return __this_address;
        } else {
                if (ichdr.magic != XFS_DA_NODE_MAGIC)
-                       return false;
+                       return __this_address;
        }
        if (ichdr.level == 0)
-               return false;
+               return __this_address;
        if (ichdr.level > XFS_DA_NODE_MAXDEPTH)
-               return false;
+               return __this_address;
        if (ichdr.count == 0)
-               return false;
+               return __this_address;
 
        /*
         * we don't know if the node is for and attribute or directory tree,
@@ -165,11 +165,11 @@ xfs_da3_node_verify(
         */
        if (ichdr.count > mp->m_dir_geo->node_ents &&
            ichdr.count > mp->m_attr_geo->node_ents)
-               return false;
+               return __this_address;
 
        /* XXX: hash order check? */
 
-       return true;
+       return NULL;
 }
 
 static void
@@ -177,12 +177,13 @@ xfs_da3_node_write_verify(
        struct xfs_buf  *bp)
 {
        struct xfs_mount        *mp = bp->b_target->bt_mount;
-       struct xfs_buf_log_item *bip = bp->b_fspriv;
+       struct xfs_buf_log_item *bip = bp->b_log_item;
        struct xfs_da3_node_hdr *hdr3 = bp->b_addr;
+       xfs_failaddr_t          fa;
 
-       if (!xfs_da3_node_verify(bp)) {
-               xfs_buf_ioerror(bp, -EFSCORRUPTED);
-               xfs_verifier_error(bp);
+       fa = xfs_da3_node_verify(bp);
+       if (fa) {
+               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
                return;
        }
 
@@ -206,19 +207,20 @@ xfs_da3_node_read_verify(
        struct xfs_buf          *bp)
 {
        struct xfs_da_blkinfo   *info = bp->b_addr;
+       xfs_failaddr_t          fa;
 
        switch (be16_to_cpu(info->magic)) {
                case XFS_DA3_NODE_MAGIC:
                        if (!xfs_buf_verify_cksum(bp, XFS_DA3_NODE_CRC_OFF)) {
-                               xfs_buf_ioerror(bp, -EFSBADCRC);
+                               xfs_verifier_error(bp, -EFSBADCRC,
+                                               __this_address);
                                break;
                        }
                        /* fall through */
                case XFS_DA_NODE_MAGIC:
-                       if (!xfs_da3_node_verify(bp)) {
-                               xfs_buf_ioerror(bp, -EFSCORRUPTED);
-                               break;
-                       }
+                       fa = xfs_da3_node_verify(bp);
+                       if (fa)
+                               xfs_verifier_error(bp, -EFSCORRUPTED, fa);
                        return;
                case XFS_ATTR_LEAF_MAGIC:
                case XFS_ATTR3_LEAF_MAGIC:
@@ -231,18 +233,40 @@ xfs_da3_node_read_verify(
                        bp->b_ops->verify_read(bp);
                        return;
                default:
-                       xfs_buf_ioerror(bp, -EFSCORRUPTED);
+                       xfs_verifier_error(bp, -EFSCORRUPTED, __this_address);
                        break;
        }
+}
+
+/* Verify the structure of a da3 block. */
+static xfs_failaddr_t
+xfs_da3_node_verify_struct(
+       struct xfs_buf          *bp)
+{
+       struct xfs_da_blkinfo   *info = bp->b_addr;
 
-       /* corrupt block */
-       xfs_verifier_error(bp);
+       switch (be16_to_cpu(info->magic)) {
+       case XFS_DA3_NODE_MAGIC:
+       case XFS_DA_NODE_MAGIC:
+               return xfs_da3_node_verify(bp);
+       case XFS_ATTR_LEAF_MAGIC:
+       case XFS_ATTR3_LEAF_MAGIC:
+               bp->b_ops = &xfs_attr3_leaf_buf_ops;
+               return bp->b_ops->verify_struct(bp);
+       case XFS_DIR2_LEAFN_MAGIC:
+       case XFS_DIR3_LEAFN_MAGIC:
+               bp->b_ops = &xfs_dir3_leafn_buf_ops;
+               return bp->b_ops->verify_struct(bp);
+       default:
+               return __this_address;
+       }
 }
 
 const struct xfs_buf_ops xfs_da3_node_buf_ops = {
        .name = "xfs_da3_node",
        .verify_read = xfs_da3_node_read_verify,
        .verify_write = xfs_da3_node_write_verify,
+       .verify_struct = xfs_da3_node_verify_struct,
 };
 
 int
@@ -258,7 +282,7 @@ xfs_da3_node_read(
 
        err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
                                        which_fork, &xfs_da3_node_buf_ops);
-       if (!err && tp) {
+       if (!err && tp && *bpp) {
                struct xfs_da_blkinfo   *info = (*bpp)->b_addr;
                int                     type;
 
@@ -276,9 +300,11 @@ xfs_da3_node_read(
                        type = XFS_BLFT_DIR_LEAFN_BUF;
                        break;
                default:
-                       type = 0;
-                       ASSERT(0);
-                       break;
+                       XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW,
+                                       tp->t_mountp, info, sizeof(*info));
+                       xfs_trans_brelse(tp, *bpp);
+                       *bpp = NULL;
+                       return -EFSCORRUPTED;
                }
                xfs_trans_buf_set_type(tp, *bpp, type);
        }
@@ -1277,7 +1303,7 @@ xfs_da3_fixhashpath(
                        return;
                break;
        case XFS_DIR2_LEAFN_MAGIC:
-               lasthash = xfs_dir2_leafn_lasthash(dp, blk->bp, &count);
+               lasthash = xfs_dir2_leaf_lasthash(dp, blk->bp, &count);
                if (count == 0)
                        return;
                break;
@@ -1461,6 +1487,7 @@ xfs_da3_node_lookup_int(
        int                     max;
        int                     error;
        int                     retval;
+       unsigned int            expected_level = 0;
        struct xfs_inode        *dp = state->args->dp;
 
        args = state->args;
@@ -1469,7 +1496,7 @@ xfs_da3_node_lookup_int(
         * Descend thru the B-tree searching each level for the right
         * node to use, until the right hashval is found.
         */
-       blkno = (args->whichfork == XFS_DATA_FORK)? args->geo->leafblk : 0;
+       blkno = args->geo->leafblk;
        for (blk = &state->path.blk[0], state->path.active = 1;
                         state->path.active <= XFS_DA_NODE_MAXDEPTH;
                         blk++, state->path.active++) {
@@ -1497,8 +1524,8 @@ xfs_da3_node_lookup_int(
                if (blk->magic == XFS_DIR2_LEAFN_MAGIC ||
                    blk->magic == XFS_DIR3_LEAFN_MAGIC) {
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
-                       blk->hashval = xfs_dir2_leafn_lasthash(args->dp,
-                                                              blk->bp, NULL);
+                       blk->hashval = xfs_dir2_leaf_lasthash(args->dp,
+                                                             blk->bp, NULL);
                        break;
                }
 
@@ -1512,6 +1539,18 @@ xfs_da3_node_lookup_int(
                dp->d_ops->node_hdr_from_disk(&nodehdr, node);
                btree = dp->d_ops->node_tree_p(node);
 
+               /* Tree taller than we can handle; bail out! */
+               if (nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+                       return -EFSCORRUPTED;
+
+               /* Check the level from the root. */
+               if (blkno == args->geo->leafblk)
+                       expected_level = nodehdr.level - 1;
+               else if (expected_level != nodehdr.level)
+                       return -EFSCORRUPTED;
+               else
+                       expected_level--;
+
                max = nodehdr.count;
                blk->hashval = be32_to_cpu(btree[max - 1].hashval);
 
@@ -1557,8 +1596,15 @@ xfs_da3_node_lookup_int(
                        blk->index = probe;
                        blkno = be32_to_cpu(btree[probe].before);
                }
+
+               /* We can't point back to the root. */
+               if (blkno == args->geo->leafblk)
+                       return -EFSCORRUPTED;
        }
 
+       if (expected_level != 0)
+               return -EFSCORRUPTED;
+
        /*
         * A leaf block that ends in the hashval that we are interested in
         * (final hashval == search hashval) means that the next block may
@@ -1924,8 +1970,8 @@ xfs_da3_path_shift(
                        blk->magic = XFS_DIR2_LEAFN_MAGIC;
                        ASSERT(level == path->active-1);
                        blk->index = 0;
-                       blk->hashval = xfs_dir2_leafn_lasthash(args->dp,
-                                                              blk->bp, NULL);
+                       blk->hashval = xfs_dir2_leaf_lasthash(args->dp,
+                                                             blk->bp, NULL);
                        break;
                default:
                        ASSERT(0);