1 ext4: fix end of leaf partial cluster handling
3 From: Eric Whitney <enwlinux@gmail.com>
5 The fix in commit ad6599ab3ac9 ("ext4: fix premature freeing of
6 partial clusters split across leaf blocks"), intended to avoid
7 dereferencing an invalid extent pointer when determining whether a
8 partial cluster should be freed, wasn't quite good enough. Assure that
9 at least one extent remains at the start of the leaf once the hole has
10 been punched. Otherwise, the pointer to the extent to the right of the
11 hole will be invalid and a partial cluster will be incorrectly freed.
13 Set partial_cluster to 0 when we can tell we've hit the left edge of
14 the punched region within the leaf. This prevents incorrect freeing
15 of a partial cluster when ext4_ext_rm_leaf is called one last time
16 during extent tree traversal after the punched region has been removed.
18 Adjust comments to reflect code changes and a correction. Remove a bit
21 Signed-off-by: Eric Whitney <enwlinux@gmail.com>
22 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
24 fs/ext4/extents.c | 36 +++++++++++++++++-------------------
25 1 file changed, 17 insertions(+), 19 deletions(-)
27 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
28 index 57794a7..859ab37 100644
29 --- a/fs/ext4/extents.c
30 +++ b/fs/ext4/extents.c
31 @@ -2574,15 +2574,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
34 * ext4_ext_rm_leaf() Removes the extents associated with the
35 - * blocks appearing between "start" and "end", and splits the extents
36 - * if "start" and "end" appear in the same extent
37 + * blocks appearing between "start" and "end". Both "start"
38 + * and "end" must appear in the same extent or EIO is returned.
40 * @handle: The journal handle
41 * @inode: The files inode
42 * @path: The path to the leaf
43 * @partial_cluster: The cluster which we'll have to free if all extents
44 - * has been released from it. It gets negative in case
45 - * that the cluster is still used.
46 + * has been released from it. However, if this value is
47 + * negative, it's a cluster just to the right of the
48 + * punched region and it must not be freed.
49 * @start: The first block to remove
50 * @end: The last block to remove
52 @@ -2730,8 +2731,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
53 sizeof(struct ext4_extent));
55 le16_add_cpu(&eh->eh_entries, -1);
56 - } else if (*partial_cluster > 0)
57 - *partial_cluster = 0;
60 err = ext4_ext_dirty(handle, inode, path + depth);
62 @@ -2750,20 +2750,18 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
64 * If there's a partial cluster and at least one extent remains in
65 * the leaf, free the partial cluster if it isn't shared with the
66 - * current extent. If there's a partial cluster and no extents
67 - * remain in the leaf, it can't be freed here. It can only be
68 - * freed when it's possible to determine if it's not shared with
69 - * any other extent - when the next leaf is processed or when space
70 - * removal is complete.
71 + * current extent. If it is shared with the current extent
72 + * we zero partial_cluster because we've reached the start of the
73 + * truncated/punched region and we're done removing blocks.
75 - if (*partial_cluster > 0 && eh->eh_entries &&
76 - (EXT4_B2C(sbi, ext4_ext_pblock(ex) + ex_ee_len - 1) !=
77 - *partial_cluster)) {
78 - int flags = get_default_free_blocks_flags(inode);
80 - ext4_free_blocks(handle, inode, NULL,
81 - EXT4_C2B(sbi, *partial_cluster),
82 - sbi->s_cluster_ratio, flags);
83 + if (*partial_cluster > 0 && ex >= EXT_FIRST_EXTENT(eh)) {
84 + pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
85 + if (*partial_cluster != (long long) EXT4_B2C(sbi, pblk)) {
86 + ext4_free_blocks(handle, inode, NULL,
87 + EXT4_C2B(sbi, *partial_cluster),
88 + sbi->s_cluster_ratio,
89 + get_default_free_blocks_flags(inode));