diff options
author | Jan Kara <jack@suse.cz> | 2009-12-01 10:53:06 -0500 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2009-12-10 09:02:55 -0500 |
commit | 68eb3db08344286733adac48304d9fb7a0e53b27 (patch) | |
tree | 214133860fdb6ceac4adbeb621837df8c494f1fb /fs/ext3 | |
parent | 5a20bdfcdc5c5e5f0647d8d99a998066ef5496ac (diff) |
ext3: Fix data / filesystem corruption when write fails to copy data
When ext3_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 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.
Reported-by: James Y Knight <foom@fuhm.net>
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/ext3')
-rw-r--r-- | fs/ext3/inode.c | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 2db957778903..ad14227f509e 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
@@ -1151,6 +1151,16 @@ static int do_journal_get_write_access(handle_t *handle, | |||
1151 | return ext3_journal_get_write_access(handle, bh); | 1151 | return ext3_journal_get_write_access(handle, bh); |
1152 | } | 1152 | } |
1153 | 1153 | ||
1154 | /* | ||
1155 | * Truncate blocks that were not used by write. We have to truncate the | ||
1156 | * pagecache as well so that corresponding buffers get properly unmapped. | ||
1157 | */ | ||
1158 | static void ext3_truncate_failed_write(struct inode *inode) | ||
1159 | { | ||
1160 | truncate_inode_pages(inode->i_mapping, inode->i_size); | ||
1161 | ext3_truncate(inode); | ||
1162 | } | ||
1163 | |||
1154 | static int ext3_write_begin(struct file *file, struct address_space *mapping, | 1164 | static int ext3_write_begin(struct file *file, struct address_space *mapping, |
1155 | loff_t pos, unsigned len, unsigned flags, | 1165 | loff_t pos, unsigned len, unsigned flags, |
1156 | struct page **pagep, void **fsdata) | 1166 | struct page **pagep, void **fsdata) |
@@ -1209,7 +1219,7 @@ write_begin_failed: | |||
1209 | unlock_page(page); | 1219 | unlock_page(page); |
1210 | page_cache_release(page); | 1220 | page_cache_release(page); |
1211 | if (pos + len > inode->i_size) | 1221 | if (pos + len > inode->i_size) |
1212 | ext3_truncate(inode); | 1222 | ext3_truncate_failed_write(inode); |
1213 | } | 1223 | } |
1214 | if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) | 1224 | if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) |
1215 | goto retry; | 1225 | goto retry; |
@@ -1304,7 +1314,7 @@ static int ext3_ordered_write_end(struct file *file, | |||
1304 | page_cache_release(page); | 1314 | page_cache_release(page); |
1305 | 1315 | ||
1306 | if (pos + len > inode->i_size) | 1316 | if (pos + len > inode->i_size) |
1307 | ext3_truncate(inode); | 1317 | ext3_truncate_failed_write(inode); |
1308 | return ret ? ret : copied; | 1318 | return ret ? ret : copied; |
1309 | } | 1319 | } |
1310 | 1320 | ||
@@ -1330,7 +1340,7 @@ static int ext3_writeback_write_end(struct file *file, | |||
1330 | page_cache_release(page); | 1340 | page_cache_release(page); |
1331 | 1341 | ||
1332 | if (pos + len > inode->i_size) | 1342 | if (pos + len > inode->i_size) |
1333 | ext3_truncate(inode); | 1343 | ext3_truncate_failed_write(inode); |
1334 | return ret ? ret : copied; | 1344 | return ret ? ret : copied; |
1335 | } | 1345 | } |
1336 | 1346 | ||
@@ -1383,7 +1393,7 @@ static int ext3_journalled_write_end(struct file *file, | |||
1383 | page_cache_release(page); | 1393 | page_cache_release(page); |
1384 | 1394 | ||
1385 | if (pos + len > inode->i_size) | 1395 | if (pos + len > inode->i_size) |
1386 | ext3_truncate(inode); | 1396 | ext3_truncate_failed_write(inode); |
1387 | return ret ? ret : copied; | 1397 | return ret ? ret : copied; |
1388 | } | 1398 | } |
1389 | 1399 | ||