FALLOC_FL_WRITE_ZEROES requires that all blocks in the requested range end up as written extents with zeroed content. For unaligned edges that were already allocated, ext4_zero_partial_blocks() zeros them directly. However, for unaligned edges whose underlying extent is a clean unwritten extent or a hole, the extent type remains unwritten after partial zeroing, which does not align with the semantics of WRITE_ZEROES. Therefore, when ext4_zero_partial_blocks() skips partial zeroing, it indicates that the corresponding edges are clean unwritten extents or holes. In this case, we need to expand the aligned allocation range outward to cover such edges, so that ext4_alloc_file_blocks() can correctly allocate blocks for the unaligned range. Edges that were partial-zeroed (i.e., written or dirty) are left untouched. Fixes: f4265b8d32c4 ("ext4: add FALLOC_FL_WRITE_ZEROES support") Cc: stable@vger.kernel.org Signed-off-by: Zhang Yi --- fs/ext4/extents.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 7a8f9f188a29..c34505765521 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -4760,6 +4760,21 @@ static long ext4_zero_range(struct file *file, loff_t offset, /* Zero range excluding the unaligned edges */ align_start = round_up(offset, blocksize); align_end = round_down(end, blocksize); + + /* + * In WRITE_ZEROES mode, edges that were not partial-zeroed (clean + * unwritten or hole) must be allocated and zeroed as whole blocks. + * Expand the aligned range outward to cover them. + */ + if (mode & FALLOC_FL_WRITE_ZEROES) { + if (!IS_ALIGNED(offset, blocksize) && + !(partial_zeroed & EXT4_PARTIAL_ZERO_START)) + align_start = round_down(offset, blocksize); + if (!IS_ALIGNED(end, blocksize) && + !(partial_zeroed & EXT4_PARTIAL_ZERO_END)) + align_end = round_up(end, blocksize); + } + if (align_end > align_start) { if (mode & FALLOC_FL_WRITE_ZEROES) flags = EXT4_GET_BLOCKS_CREATE_ZERO | EXT4_EX_NOCACHE; -- 2.52.0