Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c36575e6 authored by Forrest Liu's avatar Forrest Liu Committed by Theodore Ts'o
Browse files

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: default avatarForrest Liu <forrestl@synology.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Reviewed-by: default avatarAshish Sangwan <ashishsangwan2@gmail.com>
Cc: stable@vger.kernel.org
parent bd9926e8
Loading
Loading
Loading
Loading
+18 −4
Original line number Original line Diff line number Diff line
@@ -2226,13 +2226,14 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block,
 * removes index from the index block.
 * removes index from the index block.
 */
 */
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
static int ext4_ext_rm_idx(handle_t *handle, struct inode *inode,
			struct ext4_ext_path *path)
			struct ext4_ext_path *path, int depth)
{
{
	int err;
	int err;
	ext4_fsblk_t leaf;
	ext4_fsblk_t leaf;


	/* free index block */
	/* free index block */
	path--;
	depth--;
	path = path + depth;
	leaf = ext4_idx_pblock(path->p_idx);
	leaf = ext4_idx_pblock(path->p_idx);
	if (unlikely(path->p_hdr->eh_entries == 0)) {
	if (unlikely(path->p_hdr->eh_entries == 0)) {
		EXT4_ERROR_INODE(inode, "path->p_hdr->eh_entries == 0");
		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,


	ext4_free_blocks(handle, inode, NULL, leaf, 1,
	ext4_free_blocks(handle, inode, NULL, leaf, 1,
			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
			 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);

	while (--depth >= 0) {
		if (path->p_idx != EXT_FIRST_INDEX(path->p_hdr))
			break;
		path--;
		err = ext4_ext_get_access(handle, inode, path);
		if (err)
			break;
		path->p_idx->ei_block = (path+1)->p_idx->ei_block;
		err = ext4_ext_dirty(handle, inode, path);
		if (err)
			break;
	}
	return err;
	return err;
}
}


@@ -2599,7 +2613,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
	/* if this leaf is free, then we should
	/* if this leaf is free, then we should
	 * remove it from index block above */
	 * remove it from index block above */
	if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
	if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
		err = ext4_ext_rm_idx(handle, inode, path + depth);
		err = ext4_ext_rm_idx(handle, inode, path, depth);


out:
out:
	return err;
	return err;
@@ -2802,7 +2816,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
				/* index is empty, remove it;
				/* index is empty, remove it;
				 * handle must be already prepared by the
				 * handle must be already prepared by the
				 * truncatei_leaf() */
				 * truncatei_leaf() */
				err = ext4_ext_rm_idx(handle, inode, path + i);
				err = ext4_ext_rm_idx(handle, inode, path, i);
			}
			}
			/* root level has p_bh == NULL, brelse() eats this */
			/* root level has p_bh == NULL, brelse() eats this */
			brelse(path[i].p_bh);
			brelse(path[i].p_bh);