From: Zhang Yi After acquiring i_data_sem in write mode, recheck that the mapping found via the extent status tree or disk query has not changed. A racing truncate may have trimmed the extent between the earlier lookup and the write lock acquisition, since writeback does not hold i_rwsem or the folio locks covering the full extent. This could cause ext4_map_create_blocks() to allocate blocks beyond the truncated range, potentially leading to quota leaks in the upcomming iomap buffered writeback path since the iomap writeback infrastructure caches extents beyond the folio range. Therefore, if we find a valid extent and the sequence number has changed, retry the entire lookup to obtain the correct trimmed mapping. Suggested-by: Jan Kara Signed-off-by: Zhang Yi --- fs/ext4/inode.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9dbece14ae56..548a3968c5a7 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -734,6 +734,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, else ext4_check_map_extents_env(inode); +create_retry: /* Lookup extent status tree firstly */ if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) { if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { @@ -820,6 +821,19 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, * with create == 1 flag. */ down_write(&EXT4_I(inode)->i_data_sem); + + /* + * Check the validity of the mapping found via the extent status + * tree or the disk query. A racing truncate may have changed the + * extent, since writeback does not hold i_rwsem or the folio locks + * covering the full extent. + */ + if (map->m_seq != READ_ONCE(EXT4_I(inode)->i_es_seq)) { + up_write(&EXT4_I(inode)->i_data_sem); + map->m_flags = 0; + map->m_len = orig_mlen; + goto create_retry; + } retval = ext4_map_create_blocks(handle, inode, map, flags); up_write((&EXT4_I(inode)->i_data_sem)); -- 2.52.0