diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 713d3d21d8ddb2a7ab254697554a4cbea7586da7..cd00b19746bdbb8710d64de088ae7d1a3767419d 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2969,14 +2969,17 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, if (err < 0) goto out; - } else if (sbi->s_cluster_ratio > 1 && end >= ex_end) { + } else if (sbi->s_cluster_ratio > 1 && end >= ex_end && + partial.state == initial) { /* - * If there's an extent to the right its first cluster - * contains the immediate right boundary of the - * truncated/punched region. Set partial_cluster to - * its negative value so it won't be freed if shared - * with the current extent. The end < ee_block case - * is handled in ext4_ext_rm_leaf(). + * If we're punching, there's an extent to the right. + * If the partial cluster hasn't been set, set it to + * that extent's first cluster and its state to nofree + * so it won't be freed should it contain blocks to be + * removed. If it's already set (tofree/nofree), we're + * retrying and keep the original partial cluster info + * so a cluster marked tofree as a result of earlier + * extent removal is not lost. */ lblk = ex_end + 1; err = ext4_ext_search_right(inode, path, &lblk, &pblk,