diff options
| -rw-r--r-- | fs/ext4/inode.c | 18 |
1 files changed, 18 insertions, 0 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 309c656247e0..86d1e027e543 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)) { @@ -784,6 +785,8 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, down_read(&EXT4_I(inode)->i_data_sem); retval = ext4_map_query_blocks(handle, inode, map, flags); up_read((&EXT4_I(inode)->i_data_sem)); + if (retval < 0) + return retval; found: if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { @@ -820,6 +823,21 @@ found: * 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 may not hold i_rwsem or the folio locks + * covering the full extent(e.g., the iomap writeback path may + * allocate an extent that extends beyond the length of the locked + * folio in advance). + */ + 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)); |
