aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorForrest Liu <forrestl@synology.com>2012-12-17 09:55:39 -0500
committerTheodore Ts'o <tytso@mit.edu>2012-12-17 09:55:39 -0500
commitc36575e663e302dbaa4d16b9c72d2c9a913a9aef (patch)
tree924ba9ba84f3c9eca9f77d2c3ad9ff43ca5f2c02
parentbd9926e80330d43f15b710c2935fa41b792d56fd (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.c22
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 */
2228static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode, 2228static 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
2604out: 2618out:
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);