Message ID | 160494586556.772802.12631379595730474933.stgit@magnolia (mailing list archive) |
---|---|
State | Accepted, archived |
Headers | show |
Series | xfs: fix various scrub problems | expand |
On Monday 9 November 2020 11:47:45 PM IST Darrick J. Wong wrote: > From: Darrick J. Wong <darrick.wong@oracle.com> > > 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. > The code changes are consistent with rules provided in the comments. Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com> > Fixes: 08a3a692ef58 ("xfs: btree scrub should check minrecs") > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> > --- > 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 f52a7b8256f9..debf392e0515 100644 > --- a/fs/xfs/scrub/btree.c > +++ b/fs/xfs/scrub/btree.c > @@ -452,32 +452,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); > } > > /* > >
On Mon, Nov 09, 2020 at 10:17:45AM -0800, Darrick J. Wong wrote: > From: Darrick J. Wong <darrick.wong@oracle.com> > > 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> Looks good, Reviewed-by: Christoph Hellwig <hch@lst.de>
diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c index f52a7b8256f9..debf392e0515 100644 --- a/fs/xfs/scrub/btree.c +++ b/fs/xfs/scrub/btree.c @@ -452,32 +452,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); } /*