mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
ext4: zero post-EOF partial block before appending write
In cases of appending write beyond EOF, ext4_zero_partial_blocks() is called within ext4_*_write_end() to zero out the partial block beyond EOF. This prevents exposing stale data that might be written through mmap. However, supporting only the regular buffered write path is insufficient. It is also necessary to support the DAX path as well as the upcoming iomap buffered write path. Therefore, move this operation to ext4_write_checks(). In addition, this may introduce a race window in which a post-EOF buffered write can race with an mmap write after the old EOF block has been zeroed. As a result, the data in this block written by the buffer-write and the data written by the mmap-write may be mixed. However, this is safe because users should not rely on the result of the race condition. Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/20260327102939.1095257-14-yi.zhang@huaweicloud.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
1ad0f42823
commit
3f60efd654
|
|
@ -271,6 +271,8 @@ static ssize_t ext4_generic_write_checks(struct kiocb *iocb,
|
|||
|
||||
static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
struct inode *inode = file_inode(iocb->ki_filp);
|
||||
loff_t old_size = i_size_read(inode);
|
||||
ssize_t ret, count;
|
||||
|
||||
count = ext4_generic_write_checks(iocb, from);
|
||||
|
|
@ -280,6 +282,21 @@ static ssize_t ext4_write_checks(struct kiocb *iocb, struct iov_iter *from)
|
|||
ret = file_modified(iocb->ki_filp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the position is beyond the EOF, it is necessary to zero out the
|
||||
* partial block that beyond the existing EOF, as it may contains
|
||||
* stale data written through mmap.
|
||||
*/
|
||||
if (iocb->ki_pos > old_size && !ext4_verity_in_progress(inode)) {
|
||||
if (iocb->ki_flags & IOCB_NOWAIT)
|
||||
return -EAGAIN;
|
||||
|
||||
ret = ext4_block_zero_eof(inode, old_size, iocb->ki_pos);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1466,10 +1466,9 @@ static int ext4_write_end(const struct kiocb *iocb,
|
|||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (old_size < pos && !verity) {
|
||||
if (old_size < pos && !verity)
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
ext4_block_zero_eof(inode, old_size, pos);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't mark the inode dirty under folio lock. First, it unnecessarily
|
||||
* makes the holding time of folio lock longer. Second, it forces lock
|
||||
|
|
@ -1584,10 +1583,8 @@ static int ext4_journalled_write_end(const struct kiocb *iocb,
|
|||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (old_size < pos && !verity) {
|
||||
if (old_size < pos && !verity)
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
ext4_block_zero_eof(inode, old_size, pos);
|
||||
}
|
||||
|
||||
if (size_changed) {
|
||||
ret2 = ext4_mark_inode_dirty(handle, inode);
|
||||
|
|
@ -3226,7 +3223,7 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
|||
struct inode *inode = mapping->host;
|
||||
loff_t old_size = inode->i_size;
|
||||
bool disksize_changed = false;
|
||||
loff_t new_i_size, zero_len = 0;
|
||||
loff_t new_i_size;
|
||||
handle_t *handle;
|
||||
|
||||
if (unlikely(!folio_buffers(folio))) {
|
||||
|
|
@ -3270,19 +3267,15 @@ static int ext4_da_do_write_end(struct address_space *mapping,
|
|||
folio_unlock(folio);
|
||||
folio_put(folio);
|
||||
|
||||
if (pos > old_size) {
|
||||
if (pos > old_size)
|
||||
pagecache_isize_extended(inode, old_size, pos);
|
||||
zero_len = pos - old_size;
|
||||
}
|
||||
|
||||
if (!disksize_changed && !zero_len)
|
||||
if (!disksize_changed)
|
||||
return copied;
|
||||
|
||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 2);
|
||||
handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
|
||||
if (IS_ERR(handle))
|
||||
return PTR_ERR(handle);
|
||||
if (zero_len)
|
||||
ext4_block_zero_eof(inode, old_size, pos);
|
||||
ext4_mark_inode_dirty(handle, inode);
|
||||
ext4_journal_stop(handle);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user