From 5cc1876f14f90430b24f1ad2f231de936691940f Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 9 Sep 2023 13:22:36 -0400 Subject: [PATCH] Add more constraints for block cloning. - We cannot clone into files with smaller block size if there is more than one block, since we can not grow the block size. - Block size must be power-of-2 if destination offset != 0, since there can be no multiple blocks of non-power-of-2 size. The first should handle the case when destination file has several blocks but still is not bigger than one block of the source file. The second fixes panic in dmu_buf_hold_array_by_dnode() on attempt to concatenate files with equal but non-power-of-2 block sizes. While there, assert that error is reported if we made no progress. Reviewed-by: Pawel Jakub Dawidek Reviewed-by: Brian Behlendorf Reviewed-by: Rob Norris Reviewed-by: Kay Pedersen Reviewed-by: Umer Saleem Signed-off-by: Alexander Motin Sponsored by: iXsystems, Inc. Closes #15251 --- module/zfs/zfs_vnops.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c index f8d13075d5..a64e1e2dc8 100644 --- a/module/zfs/zfs_vnops.c +++ b/module/zfs/zfs_vnops.c @@ -1172,9 +1172,20 @@ zfs_clone_range(znode_t *inzp, uint64_t *inoffp, znode_t *outzp, inblksz = inzp->z_blksz; /* - * We cannot clone into files with different block size. + * We cannot clone into files with different block size if we can't + * grow it (block size is already bigger or more than one block). */ - if (inblksz != outzp->z_blksz && outzp->z_size > inblksz) { + if (inblksz != outzp->z_blksz && (outzp->z_size > outzp->z_blksz || + outzp->z_size > inblksz)) { + error = SET_ERROR(EINVAL); + goto unlock; + } + + /* + * Block size must be power-of-2 if destination offset != 0. + * There can be no multiple blocks of non-power-of-2 size. + */ + if (outoff != 0 && !ISP2(inblksz)) { error = SET_ERROR(EINVAL); goto unlock; } @@ -1358,6 +1369,12 @@ unlock: *inoffp += done; *outoffp += done; *lenp = done; + } else { + /* + * If we made no progress, there must be a good reason. + * EOF is handled explicitly above, before the loop. + */ + ASSERT3S(error, !=, 0); } zfs_exit_two(inzfsvfs, outzfsvfs, FTAG);