From 3583fa38e82cc5ca6fde84f723995aa4fdc657cd Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Thu, 12 Mar 2026 11:33:17 -0400 Subject: [PATCH] ZVOL: Restrict cloning with different properties While technically its not a problem to clone between ZVOLs with different properties, it might create expectation of new properties being applied during data move, while actually it won't happen. For copies and checksum it may mean incorrect safety expectations. For dedup, compression and special_small_blocks -- performance and space usage. This is a replica of #18180 from FS. Reviewed-by: Ameer Hamza Reviewed-by: Brian Behlendorf Signed-off-by: Alexander Motin Closes #18315 --- module/zfs/zvol.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 15ee62ca5..285b194a6 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -108,6 +108,7 @@ unsigned int zvol_request_sync = 0; struct hlist_head *zvol_htable; static list_t zvol_state_list; krwlock_t zvol_state_lock; +extern int zfs_bclone_strict_properties; extern int zfs_bclone_wait_dirty; zv_taskq_t zvol_taskqs; @@ -683,10 +684,47 @@ zvol_clone_range(zvol_state_t *zv_src, uint64_t inoff, zvol_state_t *zv_dst, goto out; } + /* + * Cloning between datasets with different properties is possible, + * but it may cause confusions when copying data between them and + * expecting new properties to apply. + */ + if (zfs_bclone_strict_properties && inos != outos && + !dmu_objset_is_snapshot(inos) && + (inos->os_checksum != outos->os_checksum || + inos->os_compress != outos->os_compress || + inos->os_copies != outos->os_copies || + inos->os_dedup_checksum != outos->os_dedup_checksum)) { + error = SET_ERROR(EXDEV); + goto out; + } + if (zv_src->zv_volblocksize != zv_dst->zv_volblocksize) { error = SET_ERROR(EINVAL); goto out; } + + /* + * Cloning between datasets with different special_small_blocks would + * bypass storage tier migration that would occur with a regular copy. + */ + if (zfs_bclone_strict_properties && inos != outos && + !dmu_objset_is_snapshot(inos) && + spa_has_special(dmu_objset_spa(inos))) { + uint64_t in_smallblk = inos->os_zpl_special_smallblock; + uint64_t out_smallblk = outos->os_zpl_special_smallblock; + if (in_smallblk != out_smallblk) { + uint64_t min_smallblk = MIN(in_smallblk, out_smallblk); + uint64_t max_smallblk = MAX(in_smallblk, out_smallblk); + if (min_smallblk < zv_src->zv_volblocksize && + (inos->os_compress != ZIO_COMPRESS_OFF || + max_smallblk >= zv_src->zv_volblocksize)) { + error = SET_ERROR(EXDEV); + goto out; + } + } + } + if (inoff >= zv_src->zv_volsize || outoff >= zv_dst->zv_volsize) { goto out; } @@ -701,6 +739,15 @@ zvol_clone_range(zvol_state_t *zv_src, uint64_t inoff, zvol_state_t *zv_dst, if (len == 0) goto out; + /* + * Callers might not be able to detect properly that we are read-only, + * so check it explicitly here. + */ + if (zv_dst->zv_flags & ZVOL_RDONLY) { + error = SET_ERROR(EROFS); + goto out; + } + /* * No overlapping if we are cloning within the same file */