diff options
author | Jan Kara <jack@suse.cz> | 2011-06-14 00:58:27 +0200 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-06-16 11:44:46 -0400 |
commit | f9f07b6c1372b1436aa6b45333445b443ffd8c95 (patch) | |
tree | b8bd034260b2c1eee2ca585397ee598079ac261b /fs/buffer.c | |
parent | 5e7f23373bf9a853e9256e81e86724cdd0a33c29 (diff) | |
download | linux-stable-f9f07b6c1372b1436aa6b45333445b443ffd8c95.tar.gz linux-stable-f9f07b6c1372b1436aa6b45333445b443ffd8c95.tar.bz2 linux-stable-f9f07b6c1372b1436aa6b45333445b443ffd8c95.zip |
vfs: Fix data corruption after failed write in __block_write_begin()
I've got a report of a file corruption from fsxlinux on ext3. The important
operations to the page were:
mapwrite to a hole
partial write to the page
read - found the page zeroed from the end of the normal write
The culprit seems to be that if get_block() fails in __block_write_begin()
(e.g. transient ENOSPC in ext3), the function does ClearPageUptodate(page).
Thus when we retry the write, the logic in __block_write_begin() thinks zeroing
of the page is needed and overwrites old data. In fact, I don't see why we
should ever need to zero the uptodate bit here - either the page was uptodate
when we entered __block_write_begin() and it should stay so when we leave it,
or it was not uptodate and noone had right to set it uptodate during
__block_write_begin() so it remains !uptodate when we leave as well. So just
remove clearing of the bit.
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/buffer.c')
-rw-r--r-- | fs/buffer.c | 4 |
1 files changed, 1 insertions, 3 deletions
diff --git a/fs/buffer.c b/fs/buffer.c index 49c9aada0374..1a80b048ade8 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1902,10 +1902,8 @@ int __block_write_begin(struct page *page, loff_t pos, unsigned len, if (!buffer_uptodate(*wait_bh)) err = -EIO; } - if (unlikely(err)) { + if (unlikely(err)) page_zero_new_buffers(page, from, to); - ClearPageUptodate(page); - } return err; } EXPORT_SYMBOL(__block_write_begin); |