As written in
73fb78e5ee8940, if libxfs_file_write is passed an
unaligned file range to write, it will zero the unaligned regions at the
head and tail of the block. This is what we want for a newly allocated
(and hence unwritten) block, but this is definitely not what we want
if some other part of the block has already been written.
Fix this by extending the data/hole_pos range to be aligned to the block
size of the new filesystem. This means we read slightly more, but we
never rewrite blocks in the new filesystem, sidestepping the behavior.
Found by xfs/841 when the test filesystem has a 1k fsblock size.
Cc: <linux-xfs@vger.kernel.org> # v6.13.0
Fixes: 73fb78e5ee8940 ("mkfs: support copying in large or sparse files")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
break;
}
+ /*
+ * If we pass an unaligned range to libxfs_file_write, it will
+ * zero the unaligned head and tail parts of each block. If
+ * the fd filesystem has a smaller blocksize, then we can end
+ * up writing to the same block twice, causing unwanted zeroing
+ * and hence data corruption.
+ */
+ data_pos = rounddown_64(data_pos, mp->m_sb.sb_blocksize);
+ hole_pos = min(roundup_64(hole_pos, mp->m_sb.sb_blocksize),
+ statbuf.st_size);
+
writefile_range(ip, fname, fd, data_pos, hole_pos - data_pos);
data_pos = lseek(fd, hole_pos, SEEK_DATA);
}