xfs: check btree keys reflect the child block
authorDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:08 +0000 (19:00 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Wed, 12 Apr 2023 02:00:08 +0000 (19:00 -0700)
When scrub is checking a non-root btree block, it should make sure that
the keys in the parent btree block accurately capture the keyspace that
the child block stores.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
fs/xfs/scrub/btree.c

index de4b29a..d9ab280 100644 (file)
@@ -530,6 +530,48 @@ xchk_btree_check_minrecs(
 }
 
 /*
+ * If this btree block has a parent, make sure that the parent's keys capture
+ * the keyspace contained in this block.
+ */
+STATIC void
+xchk_btree_block_check_keys(
+       struct xchk_btree       *bs,
+       int                     level,
+       struct xfs_btree_block  *block)
+{
+       union xfs_btree_key     block_key;
+       union xfs_btree_key     *block_high_key;
+       union xfs_btree_key     *parent_low_key, *parent_high_key;
+       struct xfs_btree_cur    *cur = bs->cur;
+       struct xfs_btree_block  *parent_block;
+       struct xfs_buf          *bp;
+
+       if (level == cur->bc_nlevels - 1)
+               return;
+
+       xfs_btree_get_keys(cur, block, &block_key);
+
+       /* Make sure the low key of this block matches the parent. */
+       parent_block = xfs_btree_get_block(cur, level + 1, &bp);
+       parent_low_key = xfs_btree_key_addr(cur, cur->bc_levels[level + 1].ptr,
+                       parent_block);
+       if (cur->bc_ops->diff_two_keys(cur, &block_key, parent_low_key)) {
+               xchk_btree_set_corrupt(bs->sc, bs->cur, level);
+               return;
+       }
+
+       if (!(cur->bc_flags & XFS_BTREE_OVERLAPPING))
+               return;
+
+       /* Make sure the high key of this block matches the parent. */
+       parent_high_key = xfs_btree_high_key_addr(cur,
+                       cur->bc_levels[level + 1].ptr, parent_block);
+       block_high_key = xfs_btree_high_key_from_key(cur, &block_key);
+       if (cur->bc_ops->diff_two_keys(cur, block_high_key, parent_high_key))
+               xchk_btree_set_corrupt(bs->sc, bs->cur, level);
+}
+
+/*
  * Grab and scrub a btree block given a btree pointer.  Returns block
  * and buffer pointers (if applicable) if they're ok to use.
  */
@@ -580,7 +622,12 @@ xchk_btree_get_block(
         * Check the block's siblings; this function absorbs error codes
         * for us.
         */
-       return xchk_btree_block_check_siblings(bs, *pblock);
+       error = xchk_btree_block_check_siblings(bs, *pblock);
+       if (error)
+               return error;
+
+       xchk_btree_block_check_keys(bs, level, *pblock);
+       return 0;
 }
 
 /*