aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2009-12-08 21:24:33 -0500
committerTheodore Ts'o <tytso@mit.edu>2009-12-08 21:24:33 -0500
commitb9a4207d5e911b938f73079a83cc2ae10524ec7f (patch)
tree6926354a2c6cc8e5f0e930845774ffe73d3766b8
parent24b584240a0006ea7436cd35f5e8983eb76f1e6f (diff)
ext4: Avoid data / filesystem corruption when write fails to copy data
When ext4_write_begin fails after allocating some blocks or generic_perform_write fails to copy data to write, we truncate blocks already instantiated beyond i_size. Although these blocks were never inside i_size, we have to truncate the pagecache of these blocks so that corresponding buffers get unmapped. Otherwise subsequent __block_prepare_write (called because we are retrying the write) will find the buffers mapped, not call ->get_block, and thus the page will be backed by already freed blocks leading to filesystem and data corruption. Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/inode.c20
1 files changed, 15 insertions, 5 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index d3f99e9e8a3..0e2ea572856 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1492,6 +1492,16 @@ static int do_journal_get_write_access(handle_t *handle,
1492 return ext4_journal_get_write_access(handle, bh); 1492 return ext4_journal_get_write_access(handle, bh);
1493} 1493}
1494 1494
1495/*
1496 * Truncate blocks that were not used by write. We have to truncate the
1497 * pagecache as well so that corresponding buffers get properly unmapped.
1498 */
1499static void ext4_truncate_failed_write(struct inode *inode)
1500{
1501 truncate_inode_pages(inode->i_mapping, inode->i_size);
1502 ext4_truncate(inode);
1503}
1504
1495static int ext4_write_begin(struct file *file, struct address_space *mapping, 1505static int ext4_write_begin(struct file *file, struct address_space *mapping,
1496 loff_t pos, unsigned len, unsigned flags, 1506 loff_t pos, unsigned len, unsigned flags,
1497 struct page **pagep, void **fsdata) 1507 struct page **pagep, void **fsdata)
@@ -1557,7 +1567,7 @@ retry:
1557 1567
1558 ext4_journal_stop(handle); 1568 ext4_journal_stop(handle);
1559 if (pos + len > inode->i_size) { 1569 if (pos + len > inode->i_size) {
1560 ext4_truncate(inode); 1570 ext4_truncate_failed_write(inode);
1561 /* 1571 /*
1562 * If truncate failed early the inode might 1572 * If truncate failed early the inode might
1563 * still be on the orphan list; we need to 1573 * still be on the orphan list; we need to
@@ -1667,7 +1677,7 @@ static int ext4_ordered_write_end(struct file *file,
1667 ret = ret2; 1677 ret = ret2;
1668 1678
1669 if (pos + len > inode->i_size) { 1679 if (pos + len > inode->i_size) {
1670 ext4_truncate(inode); 1680 ext4_truncate_failed_write(inode);
1671 /* 1681 /*
1672 * If truncate failed early the inode might still be 1682 * If truncate failed early the inode might still be
1673 * on the orphan list; we need to make sure the inode 1683 * on the orphan list; we need to make sure the inode
@@ -1709,7 +1719,7 @@ static int ext4_writeback_write_end(struct file *file,
1709 ret = ret2; 1719 ret = ret2;
1710 1720
1711 if (pos + len > inode->i_size) { 1721 if (pos + len > inode->i_size) {
1712 ext4_truncate(inode); 1722 ext4_truncate_failed_write(inode);
1713 /* 1723 /*
1714 * If truncate failed early the inode might still be 1724 * If truncate failed early the inode might still be
1715 * on the orphan list; we need to make sure the inode 1725 * on the orphan list; we need to make sure the inode
@@ -1772,7 +1782,7 @@ static int ext4_journalled_write_end(struct file *file,
1772 if (!ret) 1782 if (!ret)
1773 ret = ret2; 1783 ret = ret2;
1774 if (pos + len > inode->i_size) { 1784 if (pos + len > inode->i_size) {
1775 ext4_truncate(inode); 1785 ext4_truncate_failed_write(inode);
1776 /* 1786 /*
1777 * If truncate failed early the inode might still be 1787 * If truncate failed early the inode might still be
1778 * on the orphan list; we need to make sure the inode 1788 * on the orphan list; we need to make sure the inode
@@ -3048,7 +3058,7 @@ retry:
3048 * i_size_read because we hold i_mutex. 3058 * i_size_read because we hold i_mutex.
3049 */ 3059 */
3050 if (pos + len > inode->i_size) 3060 if (pos + len > inode->i_size)
3051 ext4_truncate(inode); 3061 ext4_truncate_failed_write(inode);
3052 } 3062 }
3053 3063
3054 if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) 3064 if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))