diff --git a/tests/Makefile.am b/tests/Makefile.am
index 2e633041ab..12e9c9f9da 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -16,6 +16,7 @@ dist_scripts_test_runner_include_DATA = \
 
 scripts_runfilesdir = $(datadir)/$(PACKAGE)/runfiles
 dist_scripts_runfiles_DATA = \
+	%D%/runfiles/bclone.run \
 	%D%/runfiles/common.run \
 	%D%/runfiles/freebsd.run \
 	%D%/runfiles/linux.run \
diff --git a/tests/runfiles/bclone.run b/tests/runfiles/bclone.run
new file mode 100644
index 0000000000..3d0f545d92
--- /dev/null
+++ b/tests/runfiles/bclone.run
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source.  A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# This run file contains all of the common functional tests.  When
+# adding a new test consider also adding it to the sanity.run file
+# if the new test runs to completion in only a few seconds.
+#
+# Approximate run time: 5 hours
+#
+
+[DEFAULT]
+pre = setup
+quiet = False
+pre_user = root
+user = root
+timeout = 28800
+post_user = root
+post = cleanup
+failsafe_user = root
+failsafe = callbacks/zfs_failsafe
+outputdir = /var/tmp/test_results
+tags = ['bclone']
+
+[tests/functional/bclone]
+tests = ['bclone_crossfs_corner_cases',
+    'bclone_crossfs_data',
+    'bclone_crossfs_embedded',
+    'bclone_crossfs_hole',
+    'bclone_diffprops_all',
+    'bclone_diffprops_checksum',
+    'bclone_diffprops_compress',
+    'bclone_diffprops_copies',
+    'bclone_diffprops_recordsize',
+    'bclone_prop_sync',
+    'bclone_samefs_corner_cases',
+    'bclone_samefs_data',
+    'bclone_samefs_embedded',
+    'bclone_samefs_hole']
+tags = ['bclone']
diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run
index ef787c65c0..f94a5fba9e 100644
--- a/tests/runfiles/common.run
+++ b/tests/runfiles/common.run
@@ -53,6 +53,24 @@ tags = ['functional', 'arc']
 tests = ['atime_001_pos', 'atime_002_neg', 'root_atime_off', 'root_atime_on']
 tags = ['functional', 'atime']
 
+[tests/functional/bclone]
+tests = ['bclone_crossfs_corner_cases_limited',
+    'bclone_crossfs_data',
+    'bclone_crossfs_embedded',
+    'bclone_crossfs_hole',
+    'bclone_diffprops_all',
+    'bclone_diffprops_checksum',
+    'bclone_diffprops_compress',
+    'bclone_diffprops_copies',
+    'bclone_diffprops_recordsize',
+    'bclone_prop_sync',
+    'bclone_samefs_corner_cases_limited',
+    'bclone_samefs_data',
+    'bclone_samefs_embedded',
+    'bclone_samefs_hole']
+tags = ['functional', 'bclone']
+timeout = 7200
+
 [tests/functional/bootfs]
 tests = ['bootfs_001_pos', 'bootfs_002_neg', 'bootfs_003_pos',
     'bootfs_004_neg', 'bootfs_005_neg', 'bootfs_006_pos', 'bootfs_007_pos',
diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in
index 708b7be917..7bf4d05d54 100755
--- a/tests/test-runner/bin/zts-report.py.in
+++ b/tests/test-runner/bin/zts-report.py.in
@@ -263,13 +263,50 @@ if sys.platform.startswith('freebsd'):
         'cli_root/zpool_import/zpool_import_012_pos': ['FAIL', known_reason],
         'delegate/zfs_allow_003_pos': ['FAIL', known_reason],
         'inheritance/inherit_001_pos': ['FAIL', 11829],
-        'resilver/resilver_restart_001': ['FAIL', known_reason],
         'pool_checkpoint/checkpoint_big_rewind': ['FAIL', 12622],
         'pool_checkpoint/checkpoint_indirect': ['FAIL', 12623],
+        'resilver/resilver_restart_001': ['FAIL', known_reason],
         'snapshot/snapshot_002_pos': ['FAIL', '14831'],
     })
 elif sys.platform.startswith('linux'):
     maybe.update({
+        'bclone/bclone_crossfs_corner_cases': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_crossfs_corner_cases_limited':
+            ['SKIP', cfr_cross_reason],
+        'bclone/bclone_crossfs_data': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_crossfs_embedded': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_crossfs_hole': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_diffprops_all': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_diffprops_checksum': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_diffprops_compress': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_diffprops_copies': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_diffprops_recordsize': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_prop_sync': ['SKIP', cfr_cross_reason],
+        'bclone/bclone_samefs_corner_cases': ['SKIP', cfr_reason],
+        'bclone/bclone_samefs_corner_cases_limited': ['SKIP', cfr_reason],
+        'bclone/bclone_samefs_data': ['SKIP', cfr_reason],
+        'bclone/bclone_samefs_embedded': ['SKIP', cfr_reason],
+        'bclone/bclone_samefs_hole': ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_copyfilerange':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_copyfilerange_cross_dataset':
+            ['SKIP', cfr_cross_reason],
+        'block_cloning/block_cloning_copyfilerange_fallback':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_copyfilerange_fallback_same_txg':
+            ['SKIP', cfr_cross_reason],
+        'block_cloning/block_cloning_copyfilerange_partial':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_cross_enc_dataset':
+            ['SKIP', cfr_cross_reason],
+        'block_cloning/block_cloning_disabled_copyfilerange':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_lwb_buffer_overflow':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_replay':
+            ['SKIP', cfr_reason],
+        'block_cloning/block_cloning_replay_encrypted':
+            ['SKIP', cfr_reason],
         'cli_root/zfs_rename/zfs_rename_002_pos': ['FAIL', known_reason],
         'cli_root/zpool_reopen/zpool_reopen_003_pos': ['FAIL', known_reason],
         'fault/auto_online_002_pos': ['FAIL', 11889],
@@ -278,41 +315,21 @@ elif sys.platform.startswith('linux'):
         'fault/auto_spare_multiple': ['FAIL', 11889],
         'fault/auto_spare_shared': ['FAIL', 11889],
         'fault/decompress_fault': ['FAIL', 11889],
+        'idmap_mount/idmap_mount_001': ['SKIP', idmap_reason],
+        'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason],
+        'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
+        'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
+        'idmap_mount/idmap_mount_005': ['SKIP', idmap_reason],
         'io/io_uring': ['SKIP', 'io_uring support required'],
         'limits/filesystem_limit': ['SKIP', known_reason],
         'limits/snapshot_limit': ['SKIP', known_reason],
         'mmp/mmp_active_import': ['FAIL', known_reason],
         'mmp/mmp_exported_import': ['FAIL', known_reason],
         'mmp/mmp_inactive_import': ['FAIL', known_reason],
-        'zvol/zvol_misc/zvol_misc_snapdev': ['FAIL', 12621],
-        'zvol/zvol_misc/zvol_misc_volmode': ['FAIL', known_reason],
         'zvol/zvol_misc/zvol_misc_fua': ['SKIP', 14872],
+        'zvol/zvol_misc/zvol_misc_snapdev': ['FAIL', 12621],
         'zvol/zvol_misc/zvol_misc_trim': ['SKIP', 14872],
-        'idmap_mount/idmap_mount_001': ['SKIP', idmap_reason],
-        'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason],
-        'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason],
-        'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason],
-        'idmap_mount/idmap_mount_005': ['SKIP', idmap_reason],
-        'block_cloning/block_cloning_disabled_copyfilerange':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_copyfilerange':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_copyfilerange_partial':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_copyfilerange_fallback':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_replay':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_replay_encrypted':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_lwb_buffer_overflow':
-            ['SKIP', cfr_reason],
-        'block_cloning/block_cloning_copyfilerange_cross_dataset':
-            ['SKIP', cfr_cross_reason],
-        'block_cloning/block_cloning_copyfilerange_fallback_same_txg':
-            ['SKIP', cfr_cross_reason],
-        'block_cloning/block_cloning_cross_enc_dataset':
-            ['SKIP', cfr_cross_reason],
+        'zvol/zvol_misc/zvol_misc_volmode': ['FAIL', known_reason],
     })
 
 # Not all Github actions runners have scsi_debug module, so we may skip
diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am
index 9bdb3c2097..1b915ae98c 100644
--- a/tests/zfs-tests/cmd/Makefile.am
+++ b/tests/zfs-tests/cmd/Makefile.am
@@ -2,6 +2,7 @@ scripts_zfs_tests_bindir = $(datadir)/$(PACKAGE)/zfs-tests/bin
 
 
 scripts_zfs_tests_bin_PROGRAMS  = %D%/chg_usr_exec
+scripts_zfs_tests_bin_PROGRAMS += %D%/clonefile
 scripts_zfs_tests_bin_PROGRAMS += %D%/cp_files
 scripts_zfs_tests_bin_PROGRAMS += %D%/ctime
 scripts_zfs_tests_bin_PROGRAMS += %D%/dir_rd_update
@@ -119,7 +120,6 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/renameat2
 scripts_zfs_tests_bin_PROGRAMS += %D%/xattrtest
 scripts_zfs_tests_bin_PROGRAMS += %D%/zed_fd_spill-zedlet
 scripts_zfs_tests_bin_PROGRAMS += %D%/idmap_util
-scripts_zfs_tests_bin_PROGRAMS += %D%/clonefile
 
 %C%_idmap_util_LDADD = libspl.la
 
diff --git a/tests/zfs-tests/cmd/clonefile.c b/tests/zfs-tests/cmd/clonefile.c
index 696dc471d8..d002cd9b58 100644
--- a/tests/zfs-tests/cmd/clonefile.c
+++ b/tests/zfs-tests/cmd/clonefile.c
@@ -59,6 +59,10 @@
 #endif
 #endif /* __NR_copy_file_range */
 
+#ifdef __FreeBSD__
+#define	loff_t	off_t
+#endif
+
 ssize_t
 copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
     __attribute__((weak));
@@ -140,7 +144,7 @@ usage(void)
 	    "  FICLONERANGE:\n"
 	    "    clonefile -r <src> <dst> <soff> <doff> <len>\n"
 	    "  copy_file_range:\n"
-	    "    clonefile -f <src> <dst> <soff> <doff> <len>\n"
+	    "    clonefile -f <src> <dst> [<soff> <doff> <len | \"all\">]\n"
 	    "  FIDEDUPERANGE:\n"
 	    "    clonefile -d <src> <dst> <soff> <doff> <len>\n");
 	return (1);
@@ -179,13 +183,29 @@ main(int argc, char **argv)
 		}
 	}
 
-	if (mode == CF_MODE_NONE || (argc-optind) < 2 ||
-	    (mode != CF_MODE_CLONE && (argc-optind) < 5))
-		return (usage());
+	switch (mode) {
+		case CF_MODE_NONE:
+			return (usage());
+		case CF_MODE_CLONE:
+			if ((argc-optind) != 2)
+				return (usage());
+			break;
+		case CF_MODE_CLONERANGE:
+		case CF_MODE_DEDUPERANGE:
+			if ((argc-optind) != 5)
+				return (usage());
+			break;
+		case CF_MODE_COPYFILERANGE:
+			if ((argc-optind) != 2 && (argc-optind) != 5)
+				return (usage());
+			break;
+		default:
+			abort();
+	}
 
 	loff_t soff = 0, doff = 0;
-	size_t len = 0;
-	if (mode != CF_MODE_CLONE) {
+	size_t len = SSIZE_MAX;
+	if ((argc-optind) == 5) {
 		soff = strtoull(argv[optind+2], NULL, 10);
 		if (soff == ULLONG_MAX) {
 			fprintf(stderr, "invalid source offset");
@@ -196,10 +216,15 @@ main(int argc, char **argv)
 			fprintf(stderr, "invalid dest offset");
 			return (1);
 		}
-		len = strtoull(argv[optind+4], NULL, 10);
-		if (len == ULLONG_MAX) {
-			fprintf(stderr, "invalid length");
-			return (1);
+		if (mode == CF_MODE_COPYFILERANGE &&
+		    strcmp(argv[optind+4], "all") == 0) {
+			len = SSIZE_MAX;
+		} else {
+			len = strtoull(argv[optind+4], NULL, 10);
+			if (len == ULLONG_MAX) {
+				fprintf(stderr, "invalid length");
+				return (1);
+			}
 		}
 	}
 
@@ -237,13 +262,15 @@ main(int argc, char **argv)
 			abort();
 	}
 
-	off_t spos = lseek(sfd, 0, SEEK_CUR);
-	off_t slen = lseek(sfd, 0, SEEK_END);
-	off_t dpos = lseek(dfd, 0, SEEK_CUR);
-	off_t dlen = lseek(dfd, 0, SEEK_END);
+	if (!quiet) {
+		off_t spos = lseek(sfd, 0, SEEK_CUR);
+		off_t slen = lseek(sfd, 0, SEEK_END);
+		off_t dpos = lseek(dfd, 0, SEEK_CUR);
+		off_t dlen = lseek(dfd, 0, SEEK_END);
 
-	fprintf(stderr, "file offsets: src=%lu/%lu; dst=%lu/%lu\n", spos, slen,
-	    dpos, dlen);
+		fprintf(stderr, "file offsets: src=%lu/%lu; dst=%lu/%lu\n",
+		    spos, slen, dpos, dlen);
+	}
 
 	close(dfd);
 	close(sfd);
@@ -254,7 +281,8 @@ main(int argc, char **argv)
 int
 do_clone(int sfd, int dfd)
 {
-	fprintf(stderr, "using FICLONE\n");
+	if (!quiet)
+		fprintf(stderr, "using FICLONE\n");
 	int err = ioctl(dfd, CF_FICLONE, sfd);
 	if (err < 0) {
 		fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno));
@@ -266,7 +294,8 @@ do_clone(int sfd, int dfd)
 int
 do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
 {
-	fprintf(stderr, "using FICLONERANGE\n");
+	if (!quiet)
+		fprintf(stderr, "using FICLONERANGE\n");
 	cf_file_clone_range_t fcr = {
 		.src_fd = sfd,
 		.src_offset = soff,
@@ -284,12 +313,22 @@ do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
 int
 do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
 {
-	fprintf(stderr, "using copy_file_range\n");
+	if (!quiet)
+		fprintf(stderr, "using copy_file_range\n");
 	ssize_t copied = cf_copy_file_range(sfd, &soff, dfd, &doff, len, 0);
 	if (copied < 0) {
 		fprintf(stderr, "copy_file_range: %s\n", strerror(errno));
 		return (1);
 	}
+	if (len == SSIZE_MAX) {
+		struct stat sb;
+
+		if (fstat(sfd, &sb) < 0) {
+			fprintf(stderr, "fstat(sfd): %s\n", strerror(errno));
+			return (1);
+		}
+		len = sb.st_size;
+	}
 	if (copied != len) {
 		fprintf(stderr, "copy_file_range: copied less than requested: "
 		    "requested=%lu; copied=%lu\n", len, copied);
@@ -301,7 +340,8 @@ do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
 int
 do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len)
 {
-	fprintf(stderr, "using FIDEDUPERANGE\n");
+	if (!quiet)
+		fprintf(stderr, "using FIDEDUPERANGE\n");
 
 	char buf[sizeof (cf_file_dedupe_range_t)+
 	    sizeof (cf_file_dedupe_range_info_t)] = {0};
diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg
index 648f2203df..c6f74cd81a 100644
--- a/tests/zfs-tests/include/commands.cfg
+++ b/tests/zfs-tests/include/commands.cfg
@@ -98,7 +98,8 @@ export SYSTEM_FILES_COMMON='awk
     uname
     uniq
     vmstat
-    wc'
+    wc
+    xargs'
 
 export SYSTEM_FILES_FREEBSD='chflags
     compress
diff --git a/tests/zfs-tests/include/math.shlib b/tests/zfs-tests/include/math.shlib
index da1e77e5fb..2b5e60180f 100644
--- a/tests/zfs-tests/include/math.shlib
+++ b/tests/zfs-tests/include/math.shlib
@@ -123,10 +123,21 @@ function verify_ne # <a> <b> <type>
 #
 # $1 lower bound
 # $2 upper bound
+# [$3 how many]
 function random_int_between
 {
 	typeset -i min=$1
 	typeset -i max=$2
+	typeset -i count
+	typeset -i i
 
-	echo $(( (RANDOM % (max - min + 1)) + min ))
+	if [[ -z "$3" ]]; then
+		count=1
+	else
+		count=$3
+	fi
+
+	for (( i = 0; i < $count; i++ )); do
+		echo $(( (RANDOM % (max - min + 1)) + min ))
+	done
 }
diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am
index 7f5af6530e..33e97d22b6 100644
--- a/tests/zfs-tests/tests/Makefile.am
+++ b/tests/zfs-tests/tests/Makefile.am
@@ -90,6 +90,9 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \
 	functional/alloc_class/alloc_class.kshlib \
 	functional/atime/atime.cfg \
 	functional/atime/atime_common.kshlib \
+	functional/bclone/bclone.cfg \
+	functional/bclone/bclone_common.kshlib \
+	functional/bclone/bclone_corner_cases.kshlib \
 	functional/block_cloning/block_cloning.kshlib \
 	functional/cache/cache.cfg \
 	functional/cache/cache.kshlib \
@@ -438,6 +441,24 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
 	functional/atime/root_atime_on.ksh \
 	functional/atime/root_relatime_on.ksh \
 	functional/atime/setup.ksh \
+	functional/bclone/bclone_crossfs_corner_cases.ksh \
+	functional/bclone/bclone_crossfs_corner_cases_limited.ksh \
+	functional/bclone/bclone_crossfs_data.ksh \
+	functional/bclone/bclone_crossfs_embedded.ksh \
+	functional/bclone/bclone_crossfs_hole.ksh \
+	functional/bclone/bclone_diffprops_all.ksh \
+	functional/bclone/bclone_diffprops_checksum.ksh \
+	functional/bclone/bclone_diffprops_compress.ksh \
+	functional/bclone/bclone_diffprops_copies.ksh \
+	functional/bclone/bclone_diffprops_recordsize.ksh \
+	functional/bclone/bclone_prop_sync.ksh \
+	functional/bclone/bclone_samefs_corner_cases.ksh \
+	functional/bclone/bclone_samefs_corner_cases_limited.ksh \
+	functional/bclone/bclone_samefs_data.ksh \
+	functional/bclone/bclone_samefs_embedded.ksh \
+	functional/bclone/bclone_samefs_hole.ksh \
+	functional/bclone/cleanup.ksh \
+	functional/bclone/setup.ksh \
 	functional/block_cloning/cleanup.ksh \
 	functional/block_cloning/setup.ksh \
 	functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh \
diff --git a/tests/zfs-tests/tests/functional/bclone/TODO b/tests/zfs-tests/tests/functional/bclone/TODO
new file mode 100644
index 0000000000..7cd4ee898f
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/TODO
@@ -0,0 +1,4 @@
+- If dedup enabled, block_cloning uses dedup.
+- check when block cloning doesn't suppose to work
+- check block cloning between two different pools
+- block cloning from a snapshot
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone.cfg b/tests/zfs-tests/tests/functional/bclone/bclone.cfg
new file mode 100644
index 0000000000..f72d17c1be
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone.cfg
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+# TODO: We should calculate that based on ashift.
+export MINBLOCKSIZE=512
+
+export TESTSRCFS="$TESTPOOL/$TESTFS/src"
+export TESTDSTFS="$TESTPOOL/$TESTFS/dst"
+export TESTSRCDIR="$TESTDIR/src"
+export TESTDSTDIR="$TESTDIR/dst"
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_common.kshlib b/tests/zfs-tests/tests/functional/bclone/bclone_common.kshlib
new file mode 100644
index 0000000000..beba01c0ed
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_common.kshlib
@@ -0,0 +1,280 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/tests/functional/bclone/bclone.cfg
+
+export RECORDSIZE=$(zfs get -Hp -o value recordsize $TESTPOOL/$TESTFS)
+
+MINBLKSIZE1=512
+MINBLKSIZE2=1024
+
+function verify_block_cloning
+{
+	if is_linux && [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
+		log_unsupported "copy_file_range not available before Linux 4.5"
+	fi
+}
+
+function verify_crossfs_block_cloning
+{
+	if is_linux && [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
+		log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
+	fi
+}
+
+# Unused.
+function size_to_dsize
+{
+    typeset -r size=$1
+    typeset -r dir=$2
+
+    typeset -r dataset=$(df $dir | tail -1 | awk '{print $1}')
+    typeset -r recordsize=$(get_prop recordsize $dataset)
+    typeset -r copies=$(get_prop copies $dataset)
+    typeset dsize
+
+    if [[ $size -le $recordsize ]]; then
+        dsize=$(( ((size - 1) / MINBLOCKSIZE + 1) * MINBLOCKSIZE ))
+    else
+        dsize=$(( ((size - 1) / recordsize + 1) * recordsize ))
+    fi
+    dsize=$((dsize*copies))
+
+    echo $dsize
+}
+
+function test_file_integrity
+{
+    typeset -r original_checksum=$1
+    typeset -r clone=$2
+    typeset -r filesize=$3
+
+    typeset -r clone_checksum=$(sha256digest $clone)
+
+    if [[ $original_checksum != $clone_checksum ]]; then
+        log_fail "Clone $clone is corrupted with file size $filesize"
+    fi
+}
+
+function verify_pool_prop_eq
+{
+    typeset -r prop=$1
+    typeset -r expected=$2
+
+    typeset -r value=$(get_pool_prop $prop $TESTPOOL)
+    if [[ $value != $expected ]]; then
+        log_fail "Pool property $prop is incorrect: expected $expected, got $value"
+    fi
+}
+
+function verify_pool_props
+{
+    typeset -r dsize=$1
+    typeset -r ratio=$2
+
+    if [[ $dsize -eq 0 ]]; then
+        verify_pool_prop_eq bcloneused 0
+        verify_pool_prop_eq bclonesaved 0
+        verify_pool_prop_eq bcloneratio 1.00
+    else
+        if [[ $ratio -eq 1 ]]; then
+            verify_pool_prop_eq bcloneused 0
+        else
+            verify_pool_prop_eq bcloneused $dsize
+        fi
+        verify_pool_prop_eq bclonesaved $((dsize*(ratio-1)))
+        verify_pool_prop_eq bcloneratio "${ratio}.00"
+    fi
+}
+
+# Function to test file copying and integrity check.
+function bclone_test
+{
+    typeset -r datatype=$1
+    typeset filesize=$2
+    typeset -r embedded=$3
+    typeset -r srcdir=$4
+    typeset -r dstdir=$5
+    typeset dsize
+
+    typeset -r original="${srcdir}/original"
+    typeset -r clone="${dstdir}/clone"
+
+    log_note "Testing file copy with datatype $datatype, file size $filesize, embedded $embedded"
+
+    # Create a test file with known content.
+    case $datatype in
+        random|text)
+            sync_pool $TESTPOOL
+            if [[ $datatype = "random" ]]; then
+                dd if=/dev/urandom of=$original bs=$filesize count=1 2>/dev/null
+            else
+                filesize=$(((filesize/4)*4))
+                dd if=/dev/urandom bs=$(((filesize/4)*3)) count=1 | \
+                  openssl base64 -A > $original
+            fi
+            sync_pool $TESTPOOL
+            clonefile -f $original "${clone}-tmp"
+            sync_pool $TESTPOOL
+            # It is hard to predict block sizes that will be used,
+            # so just do one clone and take it from bcloneused.
+            filesize=$(zpool get -Hp -o value bcloneused $TESTPOOL)
+            if [[ $embedded = "false" ]]; then
+                log_must test $filesize -gt 0
+            fi
+            rm -f "${clone}-tmp"
+            sync_pool $TESTPOOL
+            dsize=$filesize
+            ;;
+        hole)
+            log_must truncate_test -s $filesize -f $original
+            dsize=0
+            ;;
+        *)
+            log_fail "Unknown datatype $datatype"
+            ;;
+    esac
+    if [[ $embedded = "true" ]]; then
+        dsize=0
+    fi
+
+    typeset -r original_checksum=$(sha256digest $original)
+
+    sync_pool $TESTPOOL
+
+    # Create a first clone of the entire file.
+    clonefile -f $original "${clone}0"
+    # Try to clone the clone in the same transaction group.
+    clonefile -f "${clone}0" "${clone}2"
+
+    # Clone the original again...
+    clonefile -f $original "${clone}1"
+    # ...and overwrite it in the same transaction group.
+    clonefile -f $original "${clone}1"
+
+    # Clone the clone...
+    clonefile -f "${clone}1" "${clone}3"
+    sync_pool $TESTPOOL
+    # ...and overwrite in the new transaction group.
+    clonefile -f "${clone}1" "${clone}3"
+
+    sync_pool $TESTPOOL
+
+    # Test removal of the pending clones (before they are committed to disk).
+    clonefile -f $original "${clone}4"
+    clonefile -f "${clone}4" "${clone}5"
+    rm -f "${clone}4" "${clone}5"
+
+    # Clone into one file, but remove another file, but with the same data in
+    # the same transaction group.
+    clonefile -f $original "${clone}5"
+    sync_pool $TESTPOOL
+    clonefile -f $original "${clone}4"
+    rm -f "${clone}5"
+    test_file_integrity $original_checksum "${clone}4" $filesize
+    sync_pool $TESTPOOL
+    test_file_integrity $original_checksum "${clone}4" $filesize
+
+    clonefile -f "${clone}4" "${clone}5"
+    # Verify integrity of the cloned file before it is committed to disk.
+    test_file_integrity $original_checksum "${clone}5" $filesize
+
+    sync_pool $TESTPOOL
+
+    # Verify integrity in the new transaction group.
+    test_file_integrity $original_checksum "${clone}0" $filesize
+    test_file_integrity $original_checksum "${clone}1" $filesize
+    test_file_integrity $original_checksum "${clone}2" $filesize
+    test_file_integrity $original_checksum "${clone}3" $filesize
+    test_file_integrity $original_checksum "${clone}4" $filesize
+    test_file_integrity $original_checksum "${clone}5" $filesize
+
+    verify_pool_props $dsize 7
+
+    # Clear cache and test after fresh import.
+    log_must zpool export $TESTPOOL
+    log_must zpool import $TESTPOOL
+
+    # Cloned uncached file.
+    clonefile -f $original "${clone}6"
+    # Cloned uncached clone.
+    clonefile -f "${clone}6" "${clone}7"
+
+    # Cache the file.
+    cat $original >/dev/null
+    clonefile -f $original "${clone}8"
+    clonefile -f "${clone}8" "${clone}9"
+
+    test_file_integrity $original_checksum "${clone}6" $filesize
+    test_file_integrity $original_checksum "${clone}7" $filesize
+    test_file_integrity $original_checksum "${clone}8" $filesize
+    test_file_integrity $original_checksum "${clone}9" $filesize
+
+    sync_pool $TESTPOOL
+
+    verify_pool_props $dsize 11
+
+    log_must zpool export $TESTPOOL
+    log_must zpool import $TESTPOOL
+
+    test_file_integrity $original_checksum "${clone}0" $filesize
+    test_file_integrity $original_checksum "${clone}1" $filesize
+    test_file_integrity $original_checksum "${clone}2" $filesize
+    test_file_integrity $original_checksum "${clone}3" $filesize
+    test_file_integrity $original_checksum "${clone}4" $filesize
+    test_file_integrity $original_checksum "${clone}5" $filesize
+    test_file_integrity $original_checksum "${clone}6" $filesize
+    test_file_integrity $original_checksum "${clone}7" $filesize
+    test_file_integrity $original_checksum "${clone}8" $filesize
+    test_file_integrity $original_checksum "${clone}9" $filesize
+
+    rm -f $original
+    rm -f "${clone}1" "${clone}3" "${clone}5" "${clone}7"
+
+    sync_pool $TESTPOOL
+
+    test_file_integrity $original_checksum "${clone}0" $filesize
+    test_file_integrity $original_checksum "${clone}2" $filesize
+    test_file_integrity $original_checksum "${clone}4" $filesize
+    test_file_integrity $original_checksum "${clone}6" $filesize
+    test_file_integrity $original_checksum "${clone}8" $filesize
+    test_file_integrity $original_checksum "${clone}9" $filesize
+
+    verify_pool_props $dsize 6
+
+    rm -f "${clone}0" "${clone}2" "${clone}4" "${clone}8" "${clone}9"
+
+    sync_pool $TESTPOOL
+
+    test_file_integrity $original_checksum "${clone}6" $filesize
+
+    verify_pool_props $dsize 1
+
+    rm -f "${clone}6"
+
+    sync_pool $TESTPOOL
+
+    verify_pool_props $dsize 1
+}
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_corner_cases.kshlib b/tests/zfs-tests/tests/functional/bclone/bclone_corner_cases.kshlib
new file mode 100644
index 0000000000..ddfbfc999c
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_corner_cases.kshlib
@@ -0,0 +1,315 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+function first_half_checksum
+{
+    typeset -r file=$1
+
+    dd if=$file bs=$HALFRECORDSIZE count=1 2>/dev/null | sha256digest
+}
+
+function second_half_checksum
+{
+    typeset -r file=$1
+
+    dd if=$file bs=$HALFRECORDSIZE count=1 skip=1 2>/dev/null | sha256digest
+}
+
+function bclone_corner_cases_init
+{
+    typeset -r srcdir=$1
+    typeset -r dstdir=$2
+
+    export RECORDSIZE=4096
+    export HALFRECORDSIZE=$((RECORDSIZE / 2))
+
+    export CLONE="$dstdir/clone0"
+    export ORIG0="$srcdir/orig0"
+    export ORIG1="$srcdir/orig1"
+    export ORIG2="$srcdir/orig2"
+
+    # Create source files.
+    log_must dd if=/dev/urandom of="$ORIG0" bs=$RECORDSIZE count=1
+    log_must dd if=/dev/urandom of="$ORIG1" bs=$RECORDSIZE count=1
+    log_must dd if=/dev/urandom of="$ORIG2" bs=$RECORDSIZE count=1
+
+    export FIRST_HALF_ORIG0_CHECKSUM=$(first_half_checksum $ORIG0)
+    export FIRST_HALF_ORIG1_CHECKSUM=$(first_half_checksum $ORIG1)
+    export FIRST_HALF_ORIG2_CHECKSUM=$(first_half_checksum $ORIG2)
+    export SECOND_HALF_ORIG0_CHECKSUM=$(second_half_checksum $ORIG0)
+    export SECOND_HALF_ORIG1_CHECKSUM=$(second_half_checksum $ORIG1)
+    export SECOND_HALF_ORIG2_CHECKSUM=$(second_half_checksum $ORIG2)
+    export ZEROS_CHECKSUM=$(dd if=/dev/zero bs=$HALFRECORDSIZE count=1 | sha256digest)
+    export FIRST_HALF_CHECKSUM=""
+    export SECOND_HALF_CHECKSUM=""
+}
+
+function cache_clone
+{
+    typeset -r cached=$1
+
+    case "$cached" in
+    "cached")
+        dd if=$CLONE of=/dev/null bs=$RECORDSIZE 2>/dev/null
+        ;;
+    "uncached")
+        ;;
+    *)
+        log_fail "invalid cached: $cached"
+        ;;
+    esac
+}
+
+function create_existing
+{
+    typeset -r existing=$1
+
+    case "$existing" in
+    "no")
+        ;;
+    "small empty")
+        log_must truncate_test -s $HALFRECORDSIZE -f $CLONE
+        ;;
+    "full empty")
+        log_must truncate_test -s $RECORDSIZE -f $CLONE
+        ;;
+    "small data")
+        log_must dd if=/dev/urandom of=$CLONE bs=$HALFRECORDSIZE count=1 \
+         2>/dev/null
+        ;;
+    "full data")
+        log_must dd if=/dev/urandom of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
+        ;;
+    *)
+        log_fail "invalid existing: $existing"
+        ;;
+    esac
+}
+
+function create_clone
+{
+    typeset -r clone=$1
+    typeset -r file=$2
+
+    case "$clone" in
+    "no")
+        ;;
+    "yes")
+        clonefile -f $file $CLONE
+        case "$file" in
+        $ORIG0)
+            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG0_CHECKSUM
+            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG0_CHECKSUM
+            ;;
+        $ORIG2)
+            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG2_CHECKSUM
+            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG2_CHECKSUM
+            ;;
+        *)
+            log_fail "invalid file: $file"
+            ;;
+        esac
+        ;;
+    *)
+        log_fail "invalid clone: $clone"
+        ;;
+    esac
+}
+
+function overwrite_clone
+{
+    typeset -r overwrite=$1
+
+    case "$overwrite" in
+    "no")
+        ;;
+    "free")
+        log_must truncate_test -s 0 -f $CLONE
+        log_must truncate_test -s $RECORDSIZE -f $CLONE
+        FIRST_HALF_CHECKSUM=$ZEROS_CHECKSUM
+        SECOND_HALF_CHECKSUM=$ZEROS_CHECKSUM
+        ;;
+    "full")
+        log_must dd if=$ORIG1 of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
+        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
+        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
+        ;;
+    "first half")
+        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=0 seek=0 \
+          count=1 conv=notrunc 2>/dev/null
+        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
+        ;;
+    "second half")
+        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=1 seek=1 \
+          count=1 conv=notrunc 2>/dev/null
+        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
+        ;;
+    *)
+        log_fail "invalid overwrite: $overwrite"
+        ;;
+    esac
+}
+
+function checksum_compare
+{
+    typeset -r compare=$1
+    typeset first_half_calculated_checksum second_half_calculated_checksum
+
+    case "$compare" in
+    "no")
+        ;;
+    "yes")
+        first_half_calculated_checksum=$(first_half_checksum $CLONE)
+        second_half_calculated_checksum=$(second_half_checksum $CLONE)
+
+        if [[ $first_half_calculated_checksum != $FIRST_HALF_CHECKSUM ]] || \
+           [[ $second_half_calculated_checksum != $SECOND_HALF_CHECKSUM ]]; then
+            return 1
+        fi
+        ;;
+    *)
+        log_fail "invalid compare: $compare"
+        ;;
+    esac
+}
+
+function bclone_corner_cases_test
+{
+    typeset cached existing
+    typeset first_clone first_overwrite
+    typeset read_after read_before
+    typeset second_clone second_overwrite
+    typeset -r srcdir=$1
+    typeset -r dstdir=$2
+    typeset limit=$3
+    typeset -i count=0
+
+    if [[ $srcdir != "count" ]]; then
+        if [[ -n "$limit" ]]; then
+            typeset -r total_count=$(bclone_corner_cases_test count)
+            limit=$(random_int_between 1 $total_count $((limit*2)) | sort -nu | head -n $limit | xargs)
+        fi
+        bclone_corner_cases_init $srcdir $dstdir
+    fi
+
+    #
+    # (create) / (cache) / (clone) / (overwrite) / (read) / (clone) / (overwrite) / (read) / read next txg
+    #
+    for existing in "no" "small empty" "full empty" "small data" "full data"; do
+        for cached in "uncached" "cached"; do
+            for first_clone in "no" "yes"; do
+                for first_overwrite in "no" "free" "full" "first half" "second half"; do
+                    for read_before in "no" "yes"; do
+                        for second_clone in "no" "yes"; do
+                            for second_overwrite in "no" "free" "full" "first half" "second half"; do
+                                for read_after in "no" "yes"; do
+                                    if [[ $first_clone = "no" ]] && \
+                                       [[ $second_clone = "no" ]]; then
+                                        continue
+                                    fi
+                                    if [[ $first_clone = "no" ]] && \
+                                       [[ $read_before = "yes" ]]; then
+                                        continue
+                                    fi
+                                    if [[ $second_clone = "no" ]] && \
+                                       [[ $read_before = "yes" ]] && \
+                                       [[ $read_after = "yes" ]]; then
+                                        continue
+                                    fi
+
+                                    count=$((count+1))
+
+                                    if [[ $srcdir = "count" ]]; then
+                                        # Just counting.
+                                        continue
+                                    fi
+
+                                    if [[ -n "$limit" ]]; then
+                                        if ! echo " $limit " | grep -q " $count "; then
+                                            continue
+                                        fi
+                                    fi
+
+                                    FIRST_HALF_CHECKSUM=""
+                                    SECOND_HALF_CHECKSUM=""
+
+                                    log_must zpool export $TESTPOOL
+                                    log_must zpool import $TESTPOOL
+
+                                    create_existing "$existing"
+
+                                    log_must zpool export $TESTPOOL
+                                    log_must zpool import $TESTPOOL
+
+                                    cache_clone "$cached"
+
+                                    create_clone "$first_clone" "$ORIG0"
+
+                                    overwrite_clone "$first_overwrite"
+
+                                    if checksum_compare $read_before; then
+                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
+                                    else
+                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
+                                    fi
+
+                                    create_clone "$second_clone" "$ORIG2"
+
+                                    overwrite_clone "$second_overwrite"
+
+                                    if checksum_compare $read_after; then
+                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after"
+                                    else
+                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after"
+                                    fi
+
+                                    log_must zpool export $TESTPOOL
+                                    log_must zpool import $TESTPOOL
+
+                                    if checksum_compare "yes"; then
+                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after / read_next_txg"
+                                    else
+                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after / read_next_txg"
+                                    fi
+
+                                    rm -f "$CLONE"
+                                done
+                            done
+                        done
+                    done
+                done
+            done
+        done
+    done
+
+    if [[ $srcdir = "count" ]]; then
+        echo $count
+    fi
+}
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases.ksh
new file mode 100755
index 0000000000..35188cddb0
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases.ksh
@@ -0,0 +1,45 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_corner_cases.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify various corner cases in block cloning across datasets"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set recordsize=$RECORDSIZE $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+log_must zfs set recordsize=$RECORDSIZE $TESTDSTFS
+
+bclone_corner_cases_test $TESTSRCDIR $TESTDSTDIR
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases_limited.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases_limited.ksh
new file mode 100755
index 0000000000..1fc1bbd07f
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_corner_cases_limited.ksh
@@ -0,0 +1,45 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_corner_cases.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify various corner cases in block cloning across datasets"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set recordsize=$RECORDSIZE $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+log_must zfs set recordsize=$RECORDSIZE $TESTDSTFS
+
+bclone_corner_cases_test $TESTSRCDIR $TESTDSTDIR 100
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_data.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_data.ksh
new file mode 100755
index 0000000000..e2fe25d451
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_data.ksh
@@ -0,0 +1,46 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning properly clones regular files across datasets"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+
+for filesize in 1 107 113 511 512 513 4095 4096 4097 131071 131072 131073 \
+  1048575 1048576 1048577 4194303 4194304 4194305; do
+    bclone_test random $filesize false $TESTSRCDIR $TESTDSTDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_embedded.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_embedded.ksh
new file mode 100755
index 0000000000..6a6fe1d309
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_embedded.ksh
@@ -0,0 +1,50 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning properly clones small files (with embedded blocks) across datasets"
+
+# Enable ZLE compression to make sure what is the maximum amount of data we
+# can store in BP.
+log_must zfs set compress=zle $TESTSRCFS
+log_must zfs set compress=zle $TESTDSTFS
+
+# Test BP_IS_EMBEDDED().
+# Maximum embedded payload size is 112 bytes, but the buffer is extended to
+# 512 bytes first and then compressed. 107 random bytes followed by 405 zeros
+# gives exactly 112 bytes after compression with ZLE.
+for filesize in 1 2 4 8 16 32 64 96 107; do
+    bclone_test random $filesize true $TESTSRCDIR $TESTDSTDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_hole.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_hole.ksh
new file mode 100755
index 0000000000..d4c33d6da3
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_crossfs_hole.ksh
@@ -0,0 +1,45 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning properly clones sparse files (files with holes) across datasets"
+
+# Compression doesn't matter here.
+
+# Test BP_IS_HOLE().
+for filesize in 1 511 512 513 4095 4096 4097 131071 131072 131073 \
+  1048575 1048576 1048577 4194303 4194304 4194305; do
+    bclone_test hole $filesize false $TESTSRCDIR $TESTDSTDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_all.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_all.ksh
new file mode 100755
index 0000000000..a5e7282fe6
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_all.ksh
@@ -0,0 +1,86 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning across datasets with different properties"
+
+log_must zfs set checksum=off $TESTSRCFS
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set copies=1 $TESTSRCFS
+log_must zfs set recordsize=131072 $TESTSRCFS
+log_must zfs set checksum=fletcher2 $TESTDSTFS
+log_must zfs set compress=lz4 $TESTDSTFS
+log_must zfs set copies=3 $TESTDSTFS
+log_must zfs set recordsize=8192 $TESTDSTFS
+
+FILESIZE=$(random_int_between 2 32767)
+FILESIZE=$((FILESIZE * 64))
+bclone_test text $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+
+log_must zfs set checksum=sha256 $TESTSRCFS
+log_must zfs set compress=zstd $TESTSRCFS
+log_must zfs set copies=2 $TESTSRCFS
+log_must zfs set recordsize=262144 $TESTSRCFS
+log_must zfs set checksum=off $TESTDSTFS
+log_must zfs set compress=off $TESTDSTFS
+log_must zfs set copies=1 $TESTDSTFS
+log_must zfs set recordsize=131072 $TESTDSTFS
+
+FILESIZE=$(random_int_between 2 32767)
+FILESIZE=$((FILESIZE * 64))
+bclone_test text $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+
+log_must zfs set checksum=sha512 $TESTSRCFS
+log_must zfs set compress=gzip $TESTSRCFS
+log_must zfs set copies=2 $TESTSRCFS
+log_must zfs set recordsize=512 $TESTSRCFS
+log_must zfs set checksum=fletcher4 $TESTDSTFS
+log_must zfs set compress=lzjb $TESTDSTFS
+log_must zfs set copies=3 $TESTDSTFS
+log_must zfs set recordsize=16384 $TESTDSTFS
+
+FILESIZE=$(random_int_between 2 32767)
+FILESIZE=$((FILESIZE * 64))
+bclone_test text $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+
+log_must zfs inherit checksum $TESTSRCFS
+log_must zfs inherit compress $TESTSRCFS
+log_must zfs inherit copies $TESTSRCFS
+log_must zfs inherit recordsize $TESTSRCFS
+log_must zfs inherit checksum $TESTDSTFS
+log_must zfs inherit compress $TESTDSTFS
+log_must zfs inherit copies $TESTDSTFS
+log_must zfs inherit recordsize $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_checksum.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_checksum.ksh
new file mode 100755
index 0000000000..7e064a0dfd
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_checksum.ksh
@@ -0,0 +1,62 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/include/properties.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning across datasets with different checksum properties"
+
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+
+for srcprop in "${checksum_prop_vals[@]}"; do
+    for dstprop in "${checksum_prop_vals[@]}"; do
+        if [[ $srcprop == $dstprop ]]; then
+            continue
+        fi
+        log_must zfs set checksum=$srcprop $TESTSRCFS
+        log_must zfs set checksum=$dstprop $TESTDSTFS
+        # 15*8=120, which is greater than 113, so we are sure the data won't
+        # be embedded into BP.
+        # 32767*8=262136, which is larger than a single default recordsize of
+        # 131072.
+        FILESIZE=$(random_int_between 15 32767)
+        FILESIZE=$((FILESIZE * 8))
+        bclone_test random $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+    done
+done
+
+log_must zfs inherit checksum $TESTSRCFS
+log_must zfs inherit checksum $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_compress.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_compress.ksh
new file mode 100755
index 0000000000..e1d6e59492
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_compress.ksh
@@ -0,0 +1,59 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/include/properties.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning across datasets with different compression properties"
+
+for srcprop in "${compress_prop_vals[@]}"; do
+    for dstprop in "${compress_prop_vals[@]}"; do
+        if [[ $srcprop == $dstprop ]]; then
+            continue
+        fi
+        log_must zfs set compress=$srcprop $TESTSRCFS
+        log_must zfs set compress=$dstprop $TESTDSTFS
+        # 15*8=120, which is greater than 113, so we are sure the data won't
+        # be embedded into BP.
+        # 32767*8=262136, which is larger than a single default recordsize of
+        # 131072.
+        FILESIZE=$(random_int_between 15 32767)
+        FILESIZE=$((FILESIZE * 8))
+        bclone_test text $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+    done
+done
+
+log_must zfs inherit compress $TESTSRCFS
+log_must zfs inherit compress $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_copies.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_copies.ksh
new file mode 100755
index 0000000000..ac823e1ec3
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_copies.ksh
@@ -0,0 +1,59 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/include/properties.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning across datasets with different copies properties"
+
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+
+for srcprop in "${copies_prop_vals[@]}"; do
+    for dstprop in "${copies_prop_vals[@]}"; do
+        log_must zfs set copies=$srcprop $TESTSRCFS
+        log_must zfs set copies=$dstprop $TESTDSTFS
+        # 15*8=120, which is greater than 113, so we are sure the data won't
+        # be embedded into BP.
+        # 32767*8=262136, which is larger than a single default recordsize of
+        # 131072.
+        FILESIZE=$(random_int_between 15 32767)
+        FILESIZE=$((FILESIZE * 8))
+        bclone_test random $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+    done
+done
+
+log_must zfs inherit copies $TESTSRCFS
+log_must zfs inherit copies $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_recordsize.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_recordsize.ksh
new file mode 100755
index 0000000000..d833e61231
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_diffprops_recordsize.ksh
@@ -0,0 +1,65 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/include/properties.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning across datasets with different recordsize properties"
+
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set compress=off $TESTDSTFS
+
+# recsize_prop_vals[] array contains too many entries and the tests take too
+# long. Let's use only a subset of them.
+typeset -a bclone_recsize_prop_vals=('512' '4096' '131072' '1048576')
+
+for srcprop in "${bclone_recsize_prop_vals[@]}"; do
+    for dstprop in "${bclone_recsize_prop_vals[@]}"; do
+        if [[ $srcprop == $dstprop ]]; then
+            continue
+        fi
+        log_must zfs set recordsize=$srcprop $TESTSRCFS
+        log_must zfs set recordsize=$dstprop $TESTDSTFS
+        # 2*64=128, which is greater than 113, so we are sure the data won't
+        # be embedded into BP.
+        # 32767*64=2097088, which is larger than the largest recordsize (1MB).
+        FILESIZE=$(random_int_between 2 32767)
+        FILESIZE=$((FILESIZE * 64))
+        bclone_test random $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+    done
+done
+
+log_must zfs inherit recordsize $TESTSRCFS
+log_must zfs inherit recordsize $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_prop_sync.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_prop_sync.ksh
new file mode 100755
index 0000000000..f8aa1c875c
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_prop_sync.ksh
@@ -0,0 +1,66 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/include/math.shlib
+. $STF_SUITE/include/properties.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+verify_crossfs_block_cloning
+
+log_assert "Verify block cloning with all sync property settings"
+
+log_must zfs set compress=zle $TESTSRCFS
+log_must zfs set compress=zle $TESTDSTFS
+
+for prop in "${sync_prop_vals[@]}"; do
+    log_must zfs set sync=$prop $TESTSRCFS
+    # 32767*8=262136, which is larger than a single default recordsize of
+    # 131072.
+    FILESIZE=$(random_int_between 1 32767)
+    FILESIZE=$((FILESIZE * 8))
+    bclone_test random $FILESIZE false $TESTSRCDIR $TESTSRCDIR
+done
+
+for srcprop in "${sync_prop_vals[@]}"; do
+    log_must zfs set sync=$srcprop $TESTSRCFS
+    for dstprop in "${sync_prop_vals[@]}"; do
+        log_must zfs set sync=$dstprop $TESTDSTFS
+        # 32767*8=262136, which is larger than a single default recordsize of
+        # 131072.
+        FILESIZE=$(random_int_between 1 32767)
+        FILESIZE=$((FILESIZE * 8))
+        bclone_test random $FILESIZE false $TESTSRCDIR $TESTDSTDIR
+    done
+done
+
+log_must zfs inherit sync $TESTSRCFS
+log_must zfs inherit sync $TESTDSTFS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases.ksh
new file mode 100755
index 0000000000..4aa2914da2
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases.ksh
@@ -0,0 +1,42 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_corner_cases.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+
+log_assert "Verify various corner cases in block cloning within the same dataset"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set recordsize=$RECORDSIZE $TESTSRCFS
+
+bclone_corner_cases_test $TESTSRCDIR $TESTSRCDIR
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases_limited.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases_limited.ksh
new file mode 100755
index 0000000000..b4737700eb
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_corner_cases_limited.ksh
@@ -0,0 +1,42 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_corner_cases.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+
+log_assert "Verify various corner cases in block cloning within the same dataset"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+log_must zfs set recordsize=$RECORDSIZE $TESTSRCFS
+
+bclone_corner_cases_test $TESTSRCDIR $TESTSRCDIR 100
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_samefs_data.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_data.ksh
new file mode 100755
index 0000000000..e964f7bbf6
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_data.ksh
@@ -0,0 +1,44 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+
+log_assert "Verify block cloning properly clones regular files within the same dataset"
+
+# Disable compression to make sure we won't use embedded blocks.
+log_must zfs set compress=off $TESTSRCFS
+
+for filesize in 1 107 113 511 512 513 4095 4096 4097 131071 131072 131073 \
+  1048575 1048576 1048577 4194303 4194304 4194305; do
+    bclone_test random $filesize false $TESTSRCDIR $TESTSRCDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_samefs_embedded.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_embedded.ksh
new file mode 100755
index 0000000000..df393a8780
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_embedded.ksh
@@ -0,0 +1,48 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+
+log_assert "Verify block cloning properly clones small files (with embedded blocks) within the same dataset"
+
+# Enable ZLE compression to make sure what is the maximum amount of data we
+# can store in BP.
+log_must zfs set compress=zle $TESTSRCFS
+
+# Test BP_IS_EMBEDDED().
+# Maximum embedded payload size is 112 bytes, but the buffer is extended to
+# 512 bytes first and then compressed. 107 random bytes followed by 405 zeros
+# gives exactly 112 bytes after compression with ZLE.
+for filesize in 1 2 4 8 16 32 64 96 107; do
+    bclone_test random $filesize true $TESTSRCDIR $TESTSRCDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/bclone_samefs_hole.ksh b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_hole.ksh
new file mode 100755
index 0000000000..3c6e345e6e
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/bclone_samefs_hole.ksh
@@ -0,0 +1,44 @@
+#! /bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
+
+verify_runnable "both"
+
+verify_block_cloning
+
+log_assert "Verify block cloning properly clones sparse files (files with holes) within the same dataset"
+
+# Compression doesn't matter here.
+
+# Test BP_IS_HOLE().
+for filesize in 1 511 512 513 4095 4096 4097 131071 131072 131073 \
+  1048575 1048576 1048577 4194303 4194304 4194305; do
+    bclone_test hole $filesize false $TESTSRCDIR $TESTSRCDIR
+done
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/bclone/cleanup.ksh b/tests/zfs-tests/tests/functional/bclone/cleanup.ksh
new file mode 100755
index 0000000000..df6d9c08fe
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/cleanup.ksh
@@ -0,0 +1,37 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone.cfg
+
+log_must zfs destroy $TESTSRCFS
+log_must zfs destroy $TESTDSTFS
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/bclone/setup.ksh b/tests/zfs-tests/tests/functional/bclone/setup.ksh
new file mode 100755
index 0000000000..c68719ee72
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/bclone/setup.ksh
@@ -0,0 +1,45 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or https://opensource.org/licenses/CDDL-1.0.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2023 by Pawel Jakub Dawidek
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/bclone/bclone.cfg
+
+if ! command -v clonefile > /dev/null ; then
+	log_unsupported "clonefile program required to test block cloning"
+fi
+
+DISK=${DISKS%% *}
+
+default_setup_noexit $DISK "true"
+log_must zpool set feature@block_cloning=enabled $TESTPOOL
+log_must zfs create $TESTSRCFS
+log_must zfs create $TESTDSTFS
+log_pass
diff --git a/tests/zfs-tests/tests/functional/redundancy/redundancy.kshlib b/tests/zfs-tests/tests/functional/redundancy/redundancy.kshlib
index 30818050a0..297c6a073b 100644
--- a/tests/zfs-tests/tests/functional/redundancy/redundancy.kshlib
+++ b/tests/zfs-tests/tests/functional/redundancy/redundancy.kshlib
@@ -44,28 +44,6 @@ function cleanup
 	done
 }
 
-#
-# Get random number between min and max number.
-#
-# $1 Minimal value
-# $2 Maximal value
-#
-function random
-{
-	typeset -i min=$1
-	typeset -i max=$2
-	typeset -i value
-
-	while true; do
-		((value = RANDOM % (max + 1)))
-		if ((value >= min)); then
-			break
-		fi
-	done
-
-	echo $value
-}
-
 #
 # Get the number of checksum errors for the pool.
 #