aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-01-27 14:35:38 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-12 00:41:46 -0500
commita5a9cf387de6dde4c79b6e7fb6311dbd7ec86f02 (patch)
tree779a3f30e18a688fe8eca820e95a100a38e2ae24 /fs
parentfc6c2da174edd7a7b760b12c60d432d300e05cca (diff)
ext4: fix data corruption in data=journal mode
commit 3b136499e906460919f0d21a49db1aaccf0ae963 upstream. ext4_journalled_write_end() did not propely handle all the cases when generic_perform_write() did not copy all the data into the target page and could mark buffers with uninitialized contents as uptodate and dirty leading to possible data corruption (which would be quickly fixed by generic_perform_write() retrying the write but still). Fix the problem by carefully handling the case when the page that is written to is not uptodate. Reported-by: Al Viro <viro@ZenIV.linux.org.uk> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/inode.c23
1 files changed, 13 insertions, 10 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 33a509c876ee..a48b17cc0cd1 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1379,7 +1379,9 @@ errout:
1379 * set the buffer to be dirty, since in data=journalled mode we need 1379 * set the buffer to be dirty, since in data=journalled mode we need
1380 * to call ext4_handle_dirty_metadata() instead. 1380 * to call ext4_handle_dirty_metadata() instead.
1381 */ 1381 */
1382static void zero_new_buffers(struct page *page, unsigned from, unsigned to) 1382static void ext4_journalled_zero_new_buffers(handle_t *handle,
1383 struct page *page,
1384 unsigned from, unsigned to)
1383{ 1385{
1384 unsigned int block_start = 0, block_end; 1386 unsigned int block_start = 0, block_end;
1385 struct buffer_head *head, *bh; 1387 struct buffer_head *head, *bh;
@@ -1396,7 +1398,7 @@ static void zero_new_buffers(struct page *page, unsigned from, unsigned to)
1396 size = min(to, block_end) - start; 1398 size = min(to, block_end) - start;
1397 1399
1398 zero_user(page, start, size); 1400 zero_user(page, start, size);
1399 set_buffer_uptodate(bh); 1401 write_end_fn(handle, bh);
1400 } 1402 }
1401 clear_buffer_new(bh); 1403 clear_buffer_new(bh);
1402 } 1404 }
@@ -1428,15 +1430,16 @@ static int ext4_journalled_write_end(struct file *file,
1428 if (ext4_has_inline_data(inode)) 1430 if (ext4_has_inline_data(inode))
1429 copied = ext4_write_inline_data_end(inode, pos, len, 1431 copied = ext4_write_inline_data_end(inode, pos, len,
1430 copied, page); 1432 copied, page);
1431 else { 1433 else if (unlikely(copied < len) && !PageUptodate(page)) {
1432 if (copied < len) { 1434 copied = 0;
1433 if (!PageUptodate(page)) 1435 ext4_journalled_zero_new_buffers(handle, page, from, to);
1434 copied = 0; 1436 } else {
1435 zero_new_buffers(page, from+copied, to); 1437 if (unlikely(copied < len))
1436 } 1438 ext4_journalled_zero_new_buffers(handle, page,
1437 1439 from + copied, to);
1438 ret = ext4_walk_page_buffers(handle, page_buffers(page), from, 1440 ret = ext4_walk_page_buffers(handle, page_buffers(page), from,
1439 to, &partial, write_end_fn); 1441 from + copied, &partial,
1442 write_end_fn);
1440 if (!partial) 1443 if (!partial)
1441 SetPageUptodate(page); 1444 SetPageUptodate(page);
1442 } 1445 }