xfs: btree scrub should check minrecs
authorDarrick J. Wong <darrick.wong@oracle.com>
Wed, 9 May 2018 17:02:00 +0000 (10:02 -0700)
committerDarrick J. Wong <darrick.wong@oracle.com>
Wed, 16 May 2018 00:57:05 +0000 (17:57 -0700)
Strengthen the btree block header checks to detect the number of records
being less than the btree type's minimum record count.  Certain blocks
are allowed to violate this constraint -- specifically any btree block
at the top of the tree can have fewer than minrecs records.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
fs/xfs/scrub/btree.c

index ea972da..2d29dce 100644 (file)
@@ -455,6 +455,44 @@ xfs_scrub_btree_check_owner(
 }
 
 /*
+ * Check that this btree block has at least minrecs records or is one of the
+ * special blocks that don't require that.
+ */
+STATIC void
+xfs_scrub_btree_check_minrecs(
+       struct xfs_scrub_btree  *bs,
+       int                     level,
+       struct xfs_btree_block  *block)
+{
+       unsigned int            numrecs;
+       int                     ok_level;
+
+       numrecs = be16_to_cpu(block->bb_numrecs);
+
+       /* More records than minrecs means the block is ok. */
+       if (numrecs >= bs->cur->bc_ops->get_minrecs(bs->cur, level))
+               return;
+
+       /*
+        * Certain btree blocks /can/ have fewer than minrecs records.  Any
+        * level greater than or equal to the level of the highest dedicated
+        * btree block are allowed to violate this constraint.
+        *
+        * For a btree rooted in a block, the btree root can have fewer than
+        * minrecs records.  If the btree is rooted in an inode and does not
+        * store records in the root, the direct children of the root and the
+        * root itself can have fewer than minrecs records.
+        */
+       ok_level = bs->cur->bc_nlevels - 1;
+       if (bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
+               ok_level--;
+       if (level >= ok_level)
+               return;
+
+       xfs_scrub_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.
  */
@@ -491,6 +529,8 @@ xfs_scrub_btree_get_block(
        if (*pbp)
                xfs_scrub_buffer_recheck(bs->sc, *pbp);
 
+       xfs_scrub_btree_check_minrecs(bs, level, *pblock);
+
        /*
         * Check the block's owner; this function absorbs error codes
         * for us.