diff options
Diffstat (limited to 'fs/ext4/inode.c')
-rw-r--r-- | fs/ext4/inode.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 7d11e02ad01d..5560f78690ac 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4429,8 +4429,8 @@ void ext4_truncate(struct inode *inode) | |||
4429 | Indirect chain[4]; | 4429 | Indirect chain[4]; |
4430 | Indirect *partial; | 4430 | Indirect *partial; |
4431 | __le32 nr = 0; | 4431 | __le32 nr = 0; |
4432 | int n; | 4432 | int n = 0; |
4433 | ext4_lblk_t last_block; | 4433 | ext4_lblk_t last_block, max_block; |
4434 | unsigned blocksize = inode->i_sb->s_blocksize; | 4434 | unsigned blocksize = inode->i_sb->s_blocksize; |
4435 | 4435 | ||
4436 | trace_ext4_truncate_enter(inode); | 4436 | trace_ext4_truncate_enter(inode); |
@@ -4455,14 +4455,18 @@ void ext4_truncate(struct inode *inode) | |||
4455 | 4455 | ||
4456 | last_block = (inode->i_size + blocksize-1) | 4456 | last_block = (inode->i_size + blocksize-1) |
4457 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | 4457 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); |
4458 | max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) | ||
4459 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | ||
4458 | 4460 | ||
4459 | if (inode->i_size & (blocksize - 1)) | 4461 | if (inode->i_size & (blocksize - 1)) |
4460 | if (ext4_block_truncate_page(handle, mapping, inode->i_size)) | 4462 | if (ext4_block_truncate_page(handle, mapping, inode->i_size)) |
4461 | goto out_stop; | 4463 | goto out_stop; |
4462 | 4464 | ||
4463 | n = ext4_block_to_path(inode, last_block, offsets, NULL); | 4465 | if (last_block != max_block) { |
4464 | if (n == 0) | 4466 | n = ext4_block_to_path(inode, last_block, offsets, NULL); |
4465 | goto out_stop; /* error */ | 4467 | if (n == 0) |
4468 | goto out_stop; /* error */ | ||
4469 | } | ||
4466 | 4470 | ||
4467 | /* | 4471 | /* |
4468 | * OK. This truncate is going to happen. We add the inode to the | 4472 | * OK. This truncate is going to happen. We add the inode to the |
@@ -4493,7 +4497,13 @@ void ext4_truncate(struct inode *inode) | |||
4493 | */ | 4497 | */ |
4494 | ei->i_disksize = inode->i_size; | 4498 | ei->i_disksize = inode->i_size; |
4495 | 4499 | ||
4496 | if (n == 1) { /* direct blocks */ | 4500 | if (last_block == max_block) { |
4501 | /* | ||
4502 | * It is unnecessary to free any data blocks if last_block is | ||
4503 | * equal to the indirect block limit. | ||
4504 | */ | ||
4505 | goto out_unlock; | ||
4506 | } else if (n == 1) { /* direct blocks */ | ||
4497 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], | 4507 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], |
4498 | i_data + EXT4_NDIR_BLOCKS); | 4508 | i_data + EXT4_NDIR_BLOCKS); |
4499 | goto do_indirects; | 4509 | goto do_indirects; |
@@ -4553,6 +4563,7 @@ do_indirects: | |||
4553 | ; | 4563 | ; |
4554 | } | 4564 | } |
4555 | 4565 | ||
4566 | out_unlock: | ||
4556 | up_write(&ei->i_data_sem); | 4567 | up_write(&ei->i_data_sem); |
4557 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | 4568 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); |
4558 | ext4_mark_inode_dirty(handle, inode); | 4569 | ext4_mark_inode_dirty(handle, inode); |