| From 968dee77220768a5f52cf8b21d0bdb73486febef Mon Sep 17 00:00:00 2001 |
| From: Ashish Sangwan <ashishsangwan2@gmail.com> |
| Date: Sun, 22 Jul 2012 22:49:08 -0400 |
| Subject: ext4: fix hole punch failure when depth is greater than 0 |
| |
| From: Ashish Sangwan <ashishsangwan2@gmail.com> |
| |
| commit 968dee77220768a5f52cf8b21d0bdb73486febef upstream. |
| |
| Whether to continue removing extents or not is decided by the return |
| value of function ext4_ext_more_to_rm() which checks 2 conditions: |
| a) if there are no more indexes to process. |
| b) if the number of entries are decreased in the header of "depth -1". |
| |
| In case of hole punch, if the last block to be removed is not part of |
| the last extent index than this index will not be deleted, hence the |
| number of valid entries in the extent header of "depth - 1" will |
| remain as it is and ext4_ext_more_to_rm will return 0 although the |
| required blocks are not yet removed. |
| |
| This patch fixes the above mentioned problem as instead of removing |
| the extents from the end of file, it starts removing the blocks from |
| the particular extent from which removing blocks is actually required |
| and continue backward until done. |
| |
| Signed-off-by: Ashish Sangwan <ashish.sangwan2@gmail.com> |
| Signed-off-by: Namjae Jeon <linkinjeon@gmail.com> |
| Reviewed-by: Lukas Czerner <lczerner@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/ext4/extents.c | 48 ++++++++++++++++++++++++++++++------------------ |
| 1 file changed, 30 insertions(+), 18 deletions(-) |
| |
| --- a/fs/ext4/extents.c |
| +++ b/fs/ext4/extents.c |
| @@ -2503,10 +2503,10 @@ static int ext4_ext_remove_space(struct |
| { |
| struct super_block *sb = inode->i_sb; |
| int depth = ext_depth(inode); |
| - struct ext4_ext_path *path; |
| + struct ext4_ext_path *path = NULL; |
| ext4_fsblk_t partial_cluster = 0; |
| handle_t *handle; |
| - int i, err; |
| + int i = 0, err; |
| |
| ext_debug("truncate since %u to %u\n", start, end); |
| |
| @@ -2539,8 +2539,12 @@ again: |
| } |
| depth = ext_depth(inode); |
| ex = path[depth].p_ext; |
| - if (!ex) |
| + if (!ex) { |
| + ext4_ext_drop_refs(path); |
| + kfree(path); |
| + path = NULL; |
| goto cont; |
| + } |
| |
| ee_block = le32_to_cpu(ex->ee_block); |
| |
| @@ -2570,8 +2574,6 @@ again: |
| if (err < 0) |
| goto out; |
| } |
| - ext4_ext_drop_refs(path); |
| - kfree(path); |
| } |
| cont: |
| |
| @@ -2580,19 +2582,27 @@ cont: |
| * after i_size and walking into the tree depth-wise. |
| */ |
| depth = ext_depth(inode); |
| - path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), GFP_NOFS); |
| - if (path == NULL) { |
| - ext4_journal_stop(handle); |
| - return -ENOMEM; |
| - } |
| - path[0].p_depth = depth; |
| - path[0].p_hdr = ext_inode_hdr(inode); |
| - |
| - if (ext4_ext_check(inode, path[0].p_hdr, depth)) { |
| - err = -EIO; |
| - goto out; |
| + if (path) { |
| + int k = i = depth; |
| + while (--k > 0) |
| + path[k].p_block = |
| + le16_to_cpu(path[k].p_hdr->eh_entries)+1; |
| + } else { |
| + path = kzalloc(sizeof(struct ext4_ext_path) * (depth + 1), |
| + GFP_NOFS); |
| + if (path == NULL) { |
| + ext4_journal_stop(handle); |
| + return -ENOMEM; |
| + } |
| + path[0].p_depth = depth; |
| + path[0].p_hdr = ext_inode_hdr(inode); |
| + |
| + if (ext4_ext_check(inode, path[0].p_hdr, depth)) { |
| + err = -EIO; |
| + goto out; |
| + } |
| } |
| - i = err = 0; |
| + err = 0; |
| |
| while (i >= 0 && err == 0) { |
| if (i == depth) { |
| @@ -2706,8 +2716,10 @@ cont: |
| out: |
| ext4_ext_drop_refs(path); |
| kfree(path); |
| - if (err == -EAGAIN) |
| + if (err == -EAGAIN) { |
| + path = NULL; |
| goto again; |
| + } |
| ext4_journal_stop(handle); |
| |
| return err; |