| From 7ddbcf9c5f9904bdec74c4998bf9d5174c6df480 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 8 Nov 2020 16:32:41 -0800 |
| Subject: xfs: fix the minrecs logic when dealing with inode root child blocks |
| |
| From: Darrick J. Wong <darrick.wong@oracle.com> |
| |
| [ Upstream commit e95b6c3ef1311dd7b20467d932a24b6d0fd88395 ] |
| |
| 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: 08a3a692ef58 ("xfs: btree scrub should check minrecs") |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com> |
| Reviewed-by: Christoph Hellwig <hch@lst.de> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/xfs/scrub/btree.c | 45 ++++++++++++++++++++++++++------------------ |
| 1 file changed, 27 insertions(+), 18 deletions(-) |
| |
| diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c |
| index 4ae959f7ad2c0..c924fe3cdad65 100644 |
| --- a/fs/xfs/scrub/btree.c |
| +++ b/fs/xfs/scrub/btree.c |
| @@ -450,32 +450,41 @@ xchk_btree_check_minrecs( |
| int level, |
| struct xfs_btree_block *block) |
| { |
| - unsigned int numrecs; |
| - int ok_level; |
| - |
| - numrecs = be16_to_cpu(block->bb_numrecs); |
| + struct xfs_btree_cur *cur = bs->cur; |
| + unsigned int root_level = cur->bc_nlevels - 1; |
| + unsigned int 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)) |
| + if (numrecs >= cur->bc_ops->get_minrecs(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. |
| + * For btrees rooted in the inode, it's possible that the root block |
| + * contents spilled into a regular ondisk block because there wasn't |
| + * enough space in the inode root. The number of records in that |
| + * child block might be less than the standard minrecs, but that's ok |
| + * provided that there's only one direct child of the root. |
| */ |
| - ok_level = bs->cur->bc_nlevels - 1; |
| - if (bs->cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) |
| - ok_level--; |
| - if (level >= ok_level) |
| + if ((cur->bc_flags & XFS_BTREE_ROOT_IN_INODE) && |
| + level == cur->bc_nlevels - 2) { |
| + struct xfs_btree_block *root_block; |
| + struct xfs_buf *root_bp; |
| + int root_maxrecs; |
| + |
| + root_block = xfs_btree_get_block(cur, root_level, &root_bp); |
| + root_maxrecs = cur->bc_ops->get_dmaxrecs(cur, root_level); |
| + if (be16_to_cpu(root_block->bb_numrecs) != 1 || |
| + numrecs <= root_maxrecs) |
| + xchk_btree_set_corrupt(bs->sc, cur, level); |
| return; |
| + } |
| |
| - xchk_btree_set_corrupt(bs->sc, bs->cur, level); |
| + /* |
| + * Otherwise, only the root level is allowed to have fewer than minrecs |
| + * records or keyptrs. |
| + */ |
| + if (level < root_level) |
| + xchk_btree_set_corrupt(bs->sc, cur, level); |
| } |
| |
| /* |
| -- |
| 2.27.0 |
| |