diff options
author | Forrest Liu <forrestl@synology.com> | 2012-12-17 09:55:39 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-12-17 09:55:39 -0500 |
commit | c36575e663e302dbaa4d16b9c72d2c9a913a9aef (patch) | |
tree | 924ba9ba84f3c9eca9f77d2c3ad9ff43ca5f2c02 | |
parent | bd9926e80330d43f15b710c2935fa41b792d56fd (diff) |
ext4: fix extent tree corruption caused by hole punch
When depth of extent tree is greater than 1, logical start value of
interior node is not correctly updated in ext4_ext_rm_idx.
Signed-off-by: Forrest Liu <forrestl@synology.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Reviewed-by: Ashish Sangwan <ashishsangwan2@gmail.com>
Cc: stable@vger.kernel.org
-rw-r--r-- | fs/ext4/extents.c | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 26af22832a84..5ae1674ec12f 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2226,13 +2226,14 @@ errout: | |||
2226 | * removes index from the index block. | 2226 | * removes index from the index block. |
2227 | */ | 2227 | */ |
2228 | static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, | 2228 | static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, |
2229 | struct ext4_ext_path *path) | 2229 | struct ext4_ext_path *path, int depth) |
2230 | { | 2230 | { |
2231 | int err; | 2231 | int err; |
2232 | ext4_fsblk_t leaf; | 2232 | ext4_fsblk_t leaf; |
2233 | 2233 | ||
2234 | /* free index block */ | 2234 | /* free index block */ |
2235 | path--; | 2235 | depth--; |
2236 | path = path + depth; | ||
2236 | leaf = ext4_idx_pblock(path->p_idx); | 2237 | leaf = ext4_idx_pblock(path->p_idx); |
2237 | if (unlikely(path->p_hdr->eh_entries == 0)) { | 2238 | if (unlikely(path->p_hdr->eh_entries == 0)) { |
2238 | EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0"); | 2239 | EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0"); |
@@ -2257,6 +2258,19 @@ static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, | |||
2257 | 2258 | ||
2258 | ext4_free_blocks(handle, inode, NULL, leaf, 1, | 2259 | ext4_free_blocks(handle, inode, NULL, leaf, 1, |
2259 | EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET); | 2260 | EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET); |
2261 | |||
2262 | while (--depth >= 0) { | ||
2263 | if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr)) | ||
2264 | break; | ||
2265 | path--; | ||
2266 | err = ext4_ext_get_access(handle, inode, path); | ||
2267 | if (err) | ||
2268 | break; | ||
2269 | path->p_idx->ei_block = (path+1)->p_idx->ei_block; | ||
2270 | err = ext4_ext_dirty(handle, inode, path); | ||
2271 | if (err) | ||
2272 | break; | ||
2273 | } | ||
2260 | return err; | 2274 | return err; |
2261 | } | 2275 | } |
2262 | 2276 | ||
@@ -2599,7 +2613,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, | |||
2599 | /* if this leaf is free, then we should | 2613 | /* if this leaf is free, then we should |
2600 | * remove it from index block above */ | 2614 | * remove it from index block above */ |
2601 | if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL) | 2615 | if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL) |
2602 | err = ext4_ext_rm_idx(handle, inode, path + depth); | 2616 | err = ext4_ext_rm_idx(handle, inode, path, depth); |
2603 | 2617 | ||
2604 | out: | 2618 | out: |
2605 | return err; | 2619 | return err; |
@@ -2802,7 +2816,7 @@ again: | |||
2802 | /* index is empty, remove it; | 2816 | /* index is empty, remove it; |
2803 | * handle must be already prepared by the | 2817 | * handle must be already prepared by the |
2804 | * truncatei_leaf() */ | 2818 | * truncatei_leaf() */ |
2805 | err = ext4_ext_rm_idx(handle, inode, path + i); | 2819 | err = ext4_ext_rm_idx(handle, inode, path, i); |
2806 | } | 2820 | } |
2807 | /* root level has p_bh == NULL, brelse() eats this */ | 2821 | /* root level has p_bh == NULL, brelse() eats this */ |
2808 | brelse(path[i].p_bh); | 2822 | brelse(path[i].p_bh); |