Skip to content

Commit a76647a

Browse files
Brian Fostergregkh
authored andcommitted
xfs: prevent multi-fsb dir readahead from reading random blocks
commit cb52ee334a45ae6c78a3999e4b473c43ddc528f4 upstream. Directory block readahead uses a complex iteration mechanism to map between high-level directory blocks and underlying physical extents. This mechanism attempts to traverse the higher-level dir blocks in a manner that handles multi-fsb directory blocks and simultaneously maintains a reference to the corresponding physical blocks. This logic doesn't handle certain (discontiguous) physical extent layouts correctly with multi-fsb directory blocks. For example, consider the case of a 4k FSB filesystem with a 2 FSB (8k) directory block size and a directory with the following extent layout: EXT: FILE-OFFSET BLOCK-RANGE AG AG-OFFSET TOTAL 0: [0..7]: 88..95 0 (88..95) 8 1: [8..15]: 80..87 0 (80..87) 8 2: [16..39]: 168..191 0 (168..191) 24 3: [40..63]: 5242952..5242975 1 (72..95) 24 Directory block 0 spans physical extents 0 and 1, dirblk 1 lies entirely within extent 2 and dirblk 2 spans extents 2 and 3. Because extent 2 is larger than the directory block size, the readahead code erroneously assumes the block is contiguous and issues a readahead based on the physical mapping of the first fsb of the dirblk. This results in read verifier failure and a spurious corruption or crc failure, depending on the filesystem format. Further, the subsequent readahead code responsible for walking through the physical table doesn't correctly advance the physical block reference for dirblk 2. Instead of advancing two physical filesystem blocks, the first iteration of the loop advances 1 block (correctly), but the subsequent iteration advances 2 more physical blocks because the next physical extent (extent 3, above) happens to cover more than dirblk 2. At this point, the higher-level directory block walking is completely off the rails of the actual physical layout of the directory for the respective mapping table. Update the contiguous dirblock logic to consider the current offset in the physical extent to avoid issuing directory readahead to unrelated blocks. Also, update the mapping table advancing code to consider the current offset within the current dirblock to avoid advancing the mapping reference too far beyond the dirblock. Signed-off-by: Brian Foster <bfoster@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8caa9a5 commit a76647a

1 file changed

Lines changed: 3 additions & 2 deletions

File tree

fs/xfs/xfs_dir2_readdir.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,8 @@ xfs_dir2_leaf_readbuf(
417417
* Read-ahead a contiguous directory block.
418418
*/
419419
if (i > mip->ra_current &&
420-
map[mip->ra_index].br_blockcount >= geo->fsbcount) {
420+
(map[mip->ra_index].br_blockcount - mip->ra_offset) >=
421+
geo->fsbcount) {
421422
xfs_dir3_data_readahead(dp,
422423
map[mip->ra_index].br_startoff + mip->ra_offset,
423424
XFS_FSB_TO_DADDR(dp->i_mount,
@@ -450,7 +451,7 @@ xfs_dir2_leaf_readbuf(
450451
* The rest of this extent but not more than a dir
451452
* block.
452453
*/
453-
length = min_t(int, geo->fsbcount,
454+
length = min_t(int, geo->fsbcount - j,
454455
map[mip->ra_index].br_blockcount -
455456
mip->ra_offset);
456457
mip->ra_offset += length;

0 commit comments

Comments
 (0)