Skip to content

Commit 181aaeb

Browse files
Jan Karasashalevin
authored andcommitted
ext4: fix races between buffered IO and collapse / insert range
Current code implementing FALLOC_FL_COLLAPSE_RANGE and FALLOC_FL_INSERT_RANGE is prone to races with buffered writes and page faults. If buffered write or write via mmap manages to squeeze between filemap_write_and_wait_range() and truncate_pagecache() in the fallocate implementations, the written data is simply discarded by truncate_pagecache() although it should have been shifted. Fix the problem by moving filemap_write_and_wait_range() call inside i_mutex and i_mmap_sem. That way we are protected against races with both buffered writes and page faults. Signed-off-by: Jan Kara <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Reviewed-by: Mingming Cao <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 9621787 commit 181aaeb

File tree

1 file changed

+21
-14
lines changed

1 file changed

+21
-14
lines changed

fs/ext4/extents.c

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5423,21 +5423,7 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
54235423
return ret;
54245424
}
54255425

5426-
/*
5427-
* Need to round down offset to be aligned with page size boundary
5428-
* for page size > block size.
5429-
*/
5430-
ioffset = round_down(offset, PAGE_SIZE);
5431-
5432-
/* Write out all dirty pages */
5433-
ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
5434-
LLONG_MAX);
5435-
if (ret)
5436-
return ret;
5437-
5438-
/* Take mutex lock */
54395426
mutex_lock(&inode->i_mutex);
5440-
54415427
/*
54425428
* There is no need to overlap collapse range with EOF, in which case
54435429
* it is effectively a truncate operation
@@ -5462,6 +5448,27 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
54625448
* page cache.
54635449
*/
54645450
down_write(&EXT4_I(inode)->i_mmap_sem);
5451+
/*
5452+
* Need to round down offset to be aligned with page size boundary
5453+
* for page size > block size.
5454+
*/
5455+
ioffset = round_down(offset, PAGE_SIZE);
5456+
/*
5457+
* Write tail of the last page before removed range since it will get
5458+
* removed from the page cache below.
5459+
*/
5460+
ret = filemap_write_and_wait_range(inode->i_mapping, ioffset, offset);
5461+
if (ret)
5462+
goto out_mmap;
5463+
/*
5464+
* Write data that will be shifted to preserve them when discarding
5465+
* page cache below. We are also protected from pages becoming dirty
5466+
* by i_mmap_sem.
5467+
*/
5468+
ret = filemap_write_and_wait_range(inode->i_mapping, offset + len,
5469+
LLONG_MAX);
5470+
if (ret)
5471+
goto out_mmap;
54655472
truncate_pagecache(inode, ioffset);
54665473

54675474
credits = ext4_writepage_trans_blocks(inode);

0 commit comments

Comments
 (0)