aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>2009-06-05 01:00:26 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-06-05 01:00:26 -0400
commit1938a150c25bf7c2c47182e753a1038945b70b0e (patch)
treecab711b2868d719c6f460a75387acf84f6b37576
parentb31e15527a9bb71b6a11a425d17ce139a62f5af5 (diff)
ext4: Avoid leaking blocks after a block allocation failure
We should add inode to the orphan list in the same transaction as block allocation. This ensures that if we crash after a failed block allocation and before we do a vmtruncate we don't leak block (ie block marked as used in bitmap but not claimed by the inode). Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> CC: Jan Kara <jack@suse.cz> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/inode.c24
1 files changed, 22 insertions, 2 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 17ed0d244dbb..8d215881172f 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1459,7 +1459,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
1459 struct page **pagep, void **fsdata) 1459 struct page **pagep, void **fsdata)
1460{ 1460{
1461 struct inode *inode = mapping->host; 1461 struct inode *inode = mapping->host;
1462 int ret, needed_blocks = ext4_writepage_trans_blocks(inode); 1462 int ret, needed_blocks;
1463 handle_t *handle; 1463 handle_t *handle;
1464 int retries = 0; 1464 int retries = 0;
1465 struct page *page; 1465 struct page *page;
@@ -1470,6 +1470,11 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
1470 "dev %s ino %lu pos %llu len %u flags %u", 1470 "dev %s ino %lu pos %llu len %u flags %u",
1471 inode->i_sb->s_id, inode->i_ino, 1471 inode->i_sb->s_id, inode->i_ino,
1472 (unsigned long long) pos, len, flags); 1472 (unsigned long long) pos, len, flags);
1473 /*
1474 * Reserve one block more for addition to orphan list in case
1475 * we allocate blocks but write fails for some reason
1476 */
1477 needed_blocks = ext4_writepage_trans_blocks(inode) + 1;
1473 index = pos >> PAGE_CACHE_SHIFT; 1478 index = pos >> PAGE_CACHE_SHIFT;
1474 from = pos & (PAGE_CACHE_SIZE - 1); 1479 from = pos & (PAGE_CACHE_SIZE - 1);
1475 to = from + len; 1480 to = from + len;
@@ -1503,15 +1508,30 @@ retry:
1503 1508
1504 if (ret) { 1509 if (ret) {
1505 unlock_page(page); 1510 unlock_page(page);
1506 ext4_journal_stop(handle);
1507 page_cache_release(page); 1511 page_cache_release(page);
1508 /* 1512 /*
1509 * block_write_begin may have instantiated a few blocks 1513 * block_write_begin may have instantiated a few blocks
1510 * outside i_size. Trim these off again. Don't need 1514 * outside i_size. Trim these off again. Don't need
1511 * i_size_read because we hold i_mutex. 1515 * i_size_read because we hold i_mutex.
1516 *
1517 * Add inode to orphan list in case we crash before
1518 * truncate finishes
1512 */ 1519 */
1513 if (pos + len > inode->i_size) 1520 if (pos + len > inode->i_size)
1521 ext4_orphan_add(handle, inode);
1522
1523 ext4_journal_stop(handle);
1524 if (pos + len > inode->i_size) {
1514 vmtruncate(inode, inode->i_size); 1525 vmtruncate(inode, inode->i_size);
1526 /*
1527 * If vmtruncate failed early the inode might
1528 * still be on the orphan list; we need to
1529 * make sure the inode is removed from the
1530 * orphan list in that case.
1531 */
1532 if (inode->i_nlink)
1533 ext4_orphan_del(NULL, inode);
1534 }
1515 } 1535 }
1516 1536
1517 if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) 1537 if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))