diff options
author | Jan Kara <jack@suse.cz> | 2009-12-08 21:24:33 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-12-08 21:24:33 -0500 |
commit | b9a4207d5e911b938f73079a83cc2ae10524ec7f (patch) | |
tree | 6926354a2c6cc8e5f0e930845774ffe73d3766b8 /fs/ext4 | |
parent | 24b584240a0006ea7436cd35f5e8983eb76f1e6f (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>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/inode.c | 20 |
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 | */ | ||
1499 | static 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 | |||
1495 | static int ext4_write_begin(struct file *file, struct address_space *mapping, | 1505 | static 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)) |