]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xfs: directory scrub should check the null bestfree entries too
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 9 Nov 2020 00:32:42 +0000 (16:32 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Nov 2020 12:39:04 +0000 (13:39 +0100)
[ Upstream commit 6b48e5b8a20f653b7d64ccf99a498f2523bff752 ]

Teach the directory scrubber to check all the bestfree entries,
including the null ones.  We want to be able to detect the case where
the entry is null but there actually /is/ a directory data block.

Found by fuzzing lbests[0] = ones in xfs/391.

Fixes: df481968f33b ("xfs: scrub directory freespace")
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/dir.c

index 7c432997edade4ee1356449e47aeb32cf38346ec..b045e95c2ea734a2f9e41e42cfbadd1d8495d4be 100644 (file)
@@ -558,14 +558,27 @@ xchk_directory_leaf1_bestfree(
        /* Check all the bestfree entries. */
        for (i = 0; i < bestcount; i++, bestp++) {
                best = be16_to_cpu(*bestp);
-               if (best == NULLDATAOFF)
-                       continue;
                error = xfs_dir3_data_read(sc->tp, sc->ip,
-                               i * args->geo->fsbcount, 0, &dbp);
+                               xfs_dir2_db_to_da(args->geo, i),
+                               XFS_DABUF_MAP_HOLE_OK,
+                               &dbp);
                if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, lblk,
                                &error))
                        break;
-               xchk_directory_check_freesp(sc, lblk, dbp, best);
+
+               if (!dbp) {
+                       if (best != NULLDATAOFF) {
+                               xchk_fblock_set_corrupt(sc, XFS_DATA_FORK,
+                                               lblk);
+                               break;
+                       }
+                       continue;
+               }
+
+               if (best == NULLDATAOFF)
+                       xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);
+               else
+                       xchk_directory_check_freesp(sc, lblk, dbp, best);
                xfs_trans_brelse(sc->tp, dbp);
                if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
                        break;