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>
This commit is contained in:
parent
b31e15527a
commit
1938a150c2
|
@ -1459,7 +1459,7 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
|
|||
struct page **pagep, void **fsdata)
|
||||
{
|
||||
struct inode *inode = mapping->host;
|
||||
int ret, needed_blocks = ext4_writepage_trans_blocks(inode);
|
||||
int ret, needed_blocks;
|
||||
handle_t *handle;
|
||||
int retries = 0;
|
||||
struct page *page;
|
||||
|
@ -1470,6 +1470,11 @@ static int ext4_write_begin(struct file *file, struct address_space *mapping,
|
|||
"dev %s ino %lu pos %llu len %u flags %u",
|
||||
inode->i_sb->s_id, inode->i_ino,
|
||||
(unsigned long long) pos, len, flags);
|
||||
/*
|
||||
* Reserve one block more for addition to orphan list in case
|
||||
* we allocate blocks but write fails for some reason
|
||||
*/
|
||||
needed_blocks = ext4_writepage_trans_blocks(inode) + 1;
|
||||
index = pos >> PAGE_CACHE_SHIFT;
|
||||
from = pos & (PAGE_CACHE_SIZE - 1);
|
||||
to = from + len;
|
||||
|
@ -1503,15 +1508,30 @@ retry:
|
|||
|
||||
if (ret) {
|
||||
unlock_page(page);
|
||||
ext4_journal_stop(handle);
|
||||
page_cache_release(page);
|
||||
/*
|
||||
* block_write_begin may have instantiated a few blocks
|
||||
* outside i_size. Trim these off again. Don't need
|
||||
* i_size_read because we hold i_mutex.
|
||||
*
|
||||
* Add inode to orphan list in case we crash before
|
||||
* truncate finishes
|
||||
*/
|
||||
if (pos + len > inode->i_size)
|
||||
ext4_orphan_add(handle, inode);
|
||||
|
||||
ext4_journal_stop(handle);
|
||||
if (pos + len > inode->i_size) {
|
||||
vmtruncate(inode, inode->i_size);
|
||||
/*
|
||||
* If vmtruncate failed early the inode might
|
||||
* still be on the orphan list; we need to
|
||||
* make sure the inode is removed from the
|
||||
* orphan list in that case.
|
||||
*/
|
||||
if (inode->i_nlink)
|
||||
ext4_orphan_del(NULL, inode);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
|
||||
|
|
Loading…
Reference in New Issue