Skip to content

Commit 98571b6

Browse files
zhangyi089gregkh
authored andcommitted
ext4: fix stale data if it bail out of the extents mapping loop
commit ded2d72 upstream. During the process of writing back folios, if mpage_map_and_submit_extent() exits the extent mapping loop due to an ENOSPC or ENOMEM error, it may result in stale data or filesystem inconsistency in environments where the block size is smaller than the folio size. When mapping a discontinuous folio in mpage_map_and_submit_extent(), some buffers may have already be mapped. If we exit the mapping loop prematurely, the folio data within the mapped range will not be written back, and the file's disk size will not be updated. Once the transaction that includes this range of extents is committed, this can lead to stale data or filesystem inconsistency. Fix this by submitting the current processing partially mapped folio. Suggested-by: Jan Kara <[email protected]> Signed-off-by: Zhang Yi <[email protected]> Reviewed-by: Jan Kara <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Theodore Ts'o <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent de83915 commit 98571b6

1 file changed

Lines changed: 50 additions & 1 deletion

File tree

fs/ext4/inode.c

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2362,6 +2362,47 @@ static int mpage_map_one_extent(handle_t *handle, struct mpage_da_data *mpd)
23622362
return 0;
23632363
}
23642364

2365+
/*
2366+
* This is used to submit mapped buffers in a single folio that is not fully
2367+
* mapped for various reasons, such as insufficient space or journal credits.
2368+
*/
2369+
static int mpage_submit_partial_folio(struct mpage_da_data *mpd)
2370+
{
2371+
struct inode *inode = mpd->inode;
2372+
struct folio *folio;
2373+
loff_t pos;
2374+
int ret;
2375+
2376+
folio = filemap_get_folio(inode->i_mapping,
2377+
mpd->start_pos >> PAGE_SHIFT);
2378+
if (IS_ERR(folio))
2379+
return PTR_ERR(folio);
2380+
/*
2381+
* The mapped position should be within the current processing folio
2382+
* but must not be the folio start position.
2383+
*/
2384+
pos = ((loff_t)mpd->map.m_lblk) << inode->i_blkbits;
2385+
if (WARN_ON_ONCE((folio_pos(folio) == pos) ||
2386+
!folio_contains(folio, pos >> PAGE_SHIFT)))
2387+
return -EINVAL;
2388+
2389+
ret = mpage_submit_folio(mpd, folio);
2390+
if (ret)
2391+
goto out;
2392+
/*
2393+
* Update start_pos to prevent this folio from being released in
2394+
* mpage_release_unused_pages(), it will be reset to the aligned folio
2395+
* pos when this folio is written again in the next round. Additionally,
2396+
* do not update wbc->nr_to_write here, as it will be updated once the
2397+
* entire folio has finished processing.
2398+
*/
2399+
mpd->start_pos = pos;
2400+
out:
2401+
folio_unlock(folio);
2402+
folio_put(folio);
2403+
return ret;
2404+
}
2405+
23652406
/*
23662407
* mpage_map_and_submit_extent - map extent starting at mpd->lblk of length
23672408
* mpd->len and submit pages underlying it for IO
@@ -2412,8 +2453,16 @@ static int mpage_map_and_submit_extent(handle_t *handle,
24122453
*/
24132454
if ((err == -ENOMEM) ||
24142455
(err == -ENOSPC && ext4_count_free_clusters(sb))) {
2415-
if (progress)
2456+
/*
2457+
* We may have already allocated extents for
2458+
* some bhs inside the folio, issue the
2459+
* corresponding data to prevent stale data.
2460+
*/
2461+
if (progress) {
2462+
if (mpage_submit_partial_folio(mpd))
2463+
goto invalidate_dirty_pages;
24162464
goto update_disksize;
2465+
}
24172466
return err;
24182467
}
24192468
ext4_msg(sb, KERN_CRIT,

0 commit comments

Comments
 (0)