Skip to content

Commit e95b6c3

Browse files
committed
xfs: fix the minrecs logic when dealing with inode root child blocks
The comment and logic in xchk_btree_check_minrecs for dealing with inode-rooted btrees isn't quite correct. While the direct children of the inode root are allowed to have fewer records than what would normally be allowed for a regular ondisk btree block, this is only true if there is only one child block and the number of records don't fit in the inode root. Fixes: 08a3a69 ("xfs: btree scrub should check minrecs") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Chandan Babu R <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 2bd3fa7 commit e95b6c3

File tree

1 file changed

+27
-18
lines changed

1 file changed

+27
-18
lines changed

fs/xfs/scrub/btree.c

+27-18
Original file line numberDiff line numberDiff line change
@@ -452,32 +452,41 @@ xchk_btree_check_minrecs(
452452
int level,
453453
struct xfs_btree_block *block)
454454
{
455-
unsigned int numrecs;
456-
int ok_level;
457-
458-
numrecs = be16_to_cpu(block->bb_numrecs);
455+
struct xfs_btree_cur *cur = bs->cur;
456+
unsigned int root_level = cur->bc_nlevels - 1;
457+
unsigned int numrecs = be16_to_cpu(block->bb_numrecs);
459458

460459
/* More records than minrecs means the block is ok. */
461-
if (numrecs >= bs->cur->bc_ops->get_minrecs(bs->cur, level))
460+
if (numrecs >= cur->bc_ops->get_minrecs(cur, level))
462461
return;
463462

464463
/*
465-
* Certain btree blocks /can/ have fewer than minrecs records. Any
466-
* level greater than or equal to the level of the highest dedicated
467-
* btree block are allowed to violate this constraint.
468-
*
469-
* For a btree rooted in a block, the btree root can have fewer than
470-
* minrecs records. If the btree is rooted in an inode and does not
471-
* store records in the root, the direct children of the root and the
472-
* root itself can have fewer than minrecs records.
464+
* For btrees rooted in the inode, it's possible that the root block
465+
* contents spilled into a regular ondisk block because there wasn't
466+
* enough space in the inode root. The number of records in that
467+
* child block might be less than the standard minrecs, but that's ok
468+
* provided that there's only one direct child of the root.
473469
*/
474-
ok_level = bs->cur->bc_nlevels - 1;
475-
if (bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE)
476-
ok_level--;
477-
if (level >= ok_level)
470+
if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) &&
471+
level == cur->bc_nlevels - 2) {
472+
struct xfs_btree_block *root_block;
473+
struct xfs_buf *root_bp;
474+
int root_maxrecs;
475+
476+
root_block = xfs_btree_get_block(cur, root_level, &root_bp);
477+
root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level);
478+
if (be16_to_cpu(root_block->bb_numrecs) != 1 ||
479+
numrecs <= root_maxrecs)
480+
xchk_btree_set_corrupt(bs->sc, cur, level);
478481
return;
482+
}
479483

480-
xchk_btree_set_corrupt(bs->sc, bs->cur, level);
484+
/*
485+
* Otherwise, only the root level is allowed to have fewer than minrecs
486+
* records or keyptrs.
487+
*/
488+
if (level < root_level)
489+
xchk_btree_set_corrupt(bs->sc, cur, level);
481490
}
482491

483492
/*

0 commit comments

Comments
 (0)