diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 618eeb934017..b68202d84924 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -34,6 +34,15 @@ tags = ['functional', 'acl', 'posix-sa'] tests = ['atime_003_pos', 'root_relatime_on'] tags = ['functional', 'atime'] +[tests/functional/block_cloning:Linux] +tests = ['block_cloning_copyfilerange', 'block_cloning_copyfilerange_partial', + 'block_cloning_ficlone', 'block_cloning_ficlonerange', + 'block_cloning_ficlonerange_partial', + 'block_cloning_disabled_copyfilerange', 'block_cloning_disabled_ficlone', + 'block_cloning_disabled_ficlonerange', + 'block_cloning_copyfilerange_cross_dataset'] +tags = ['functional', 'block_cloning'] + [tests/functional/chattr:Linux] tests = ['chattr_001_pos', 'chattr_002_neg'] tags = ['functional', 'chattr'] diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in index cf438e0e6495..c9a2b4179aec 100755 --- a/tests/test-runner/bin/zts-report.py.in +++ b/tests/test-runner/bin/zts-report.py.in @@ -134,6 +134,12 @@ ci_reason = 'CI runner doesn\'t have all requirements' # idmap_reason = 'Idmapped mount needs kernel 5.12+' +# +# copy_file_range() is not supported by all kernels +# +cfr_reason = 'Kernel copy_file_range support required' +cfr_cross_reason = 'copy_file_range(2) cross-filesystem needs kernel 5.3+' + # # These tests are known to fail, thus we use this list to prevent these # failures from failing the job as a whole; only unexpected failures @@ -288,6 +294,14 @@ elif sys.platform.startswith('linux'): '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_cross_dataset': + ['SKIP', cfr_cross_reason], }) diff --git a/tests/zfs-tests/cmd/.gitignore b/tests/zfs-tests/cmd/.gitignore index f68f58072818..5f53b687191a 100644 --- a/tests/zfs-tests/cmd/.gitignore +++ b/tests/zfs-tests/cmd/.gitignore @@ -1,6 +1,7 @@ /badsend /btree_test /chg_usr_exec +/clonefile /devname2devid /dir_rd_update /draid diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am index 066abb6ce3b5..9bdb3c209756 100644 --- a/tests/zfs-tests/cmd/Makefile.am +++ b/tests/zfs-tests/cmd/Makefile.am @@ -119,6 +119,7 @@ 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 new file mode 100644 index 000000000000..a7e7277ae411 --- /dev/null +++ b/tests/zfs-tests/cmd/clonefile.c @@ -0,0 +1,333 @@ +/* + * SPDX-License-Identifier: MIT + * + * Copyright (c) 2023, Rob Norris + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This program is to test the availability and behaviour of copy_file_range, + * FICLONE, FICLONERANGE and FIDEDUPERANGE in the Linux kernel. It should + * compile and run even if these features aren't exposed through the libc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __NR_copy_file_range +#if defined(__x86_64__) +#define __NR_copy_file_range (326) +#elif defined(__i386__) +#define __NR_copy_file_range (377) +#elif defined(__s390__) +#define __NR_copy_file_range (375) +#elif defined(__arm__) +#define __NR_copy_file_range (391) +#elif defined(__aarch64__) +#define __NR_copy_file_range (285) +#elif defined(__powerpc__) +#define __NR_copy_file_range (379) +#else +#error "no definition of __NR_copy_file_range for this platform" +#endif +#endif /* __NR_copy_file_range */ + +ssize_t +copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int) + __attribute__((weak)); + +static inline ssize_t +cf_copy_file_range(int sfd, loff_t *soff, int dfd, loff_t *doff, + size_t len, unsigned int flags) +{ + if (copy_file_range) + return (copy_file_range(sfd, soff, dfd, doff, len, flags)); + return ( + syscall(__NR_copy_file_range, sfd, soff, dfd, doff, len, flags)); +} + +/* Define missing FICLONE */ +#ifdef FICLONE +#define CF_FICLONE FICLONE +#else +#define CF_FICLONE _IOW(0x94, 9, int) +#endif + +/* Define missing FICLONERANGE and support structs */ +#ifdef FICLONERANGE +#define CF_FICLONERANGE FICLONERANGE +typedef struct file_clone_range cf_file_clone_range_t; +#else +typedef struct { + int64_t src_fd; + uint64_t src_offset; + uint64_t src_length; + uint64_t dest_offset; +} cf_file_clone_range_t; +#define CF_FICLONERANGE _IOW(0x94, 13, cf_file_clone_range_t) +#endif + +/* Define missing FIDEDUPERANGE and support structs */ +#ifdef FIDEDUPERANGE +#define CF_FIDEDUPERANGE FIDEDUPERANGE +#define CF_FILE_DEDUPE_RANGE_SAME FILE_DEDUPE_RANGE_SAME +#define CF_FILE_DEDUPE_RANGE_DIFFERS FILE_DEDUPE_RANGE_DIFFERS +typedef struct file_dedupe_range_info cf_file_dedupe_range_info_t; +typedef struct file_dedupe_range cf_file_dedupe_range_t; +#else +typedef struct { + int64_t dest_fd; + uint64_t dest_offset; + uint64_t bytes_deduped; + int32_t status; + uint32_t reserved; +} cf_file_dedupe_range_info_t; +typedef struct { + uint64_t src_offset; + uint64_t src_length; + uint16_t dest_count; + uint16_t reserved1; + uint32_t reserved2; + cf_file_dedupe_range_info_t info[0]; +} cf_file_dedupe_range_t; +#define CF_FIDEDUPERANGE _IOWR(0x94, 54, cf_file_dedupe_range_t) +#define CF_FILE_DEDUPE_RANGE_SAME (0) +#define CF_FILE_DEDUPE_RANGE_DIFFERS (1) +#endif + +typedef enum { + CF_MODE_NONE, + CF_MODE_CLONE, + CF_MODE_CLONERANGE, + CF_MODE_COPYFILERANGE, + CF_MODE_DEDUPERANGE, +} cf_mode_t; + +static int +usage(void) +{ + printf( + "usage:\n" + " FICLONE:\n" + " clonefile -c \n" + " FICLONERANGE:\n" + " clonefile -r \n" + " copy_file_range:\n" + " clonefile -f \n" + " FIDEDUPERANGE:\n" + " clonefile -d \n"); + return (1); +} + +int do_clone(int sfd, int dfd); +int 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); +int do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len); + +int quiet = 0; + +int +main(int argc, char **argv) +{ + cf_mode_t mode = CF_MODE_NONE; + + char c; + while ((c = getopt(argc, argv, "crfdq")) != -1) { + switch (c) { + case 'c': + mode = CF_MODE_CLONE; + break; + case 'r': + mode = CF_MODE_CLONERANGE; + break; + case 'f': + mode = CF_MODE_COPYFILERANGE; + break; + case 'd': + mode = CF_MODE_DEDUPERANGE; + break; + case 'q': + quiet = 1; + break; + } + } + + if (mode == CF_MODE_NONE || (argc-optind) < 2 || + (mode != CF_MODE_CLONE && (argc-optind) < 5)) + return (usage()); + + loff_t soff = 0, doff = 0; + size_t len = 0; + if (mode != CF_MODE_CLONE) { + soff = strtoull(argv[optind+2], NULL, 10); + if (soff == ULLONG_MAX) { + fprintf(stderr, "invalid source offset"); + return (1); + } + doff = strtoull(argv[optind+3], NULL, 10); + if (doff == ULLONG_MAX) { + fprintf(stderr, "invalid dest offset"); + return (1); + } + len = strtoull(argv[optind+4], NULL, 10); + if (len == ULLONG_MAX) { + fprintf(stderr, "invalid length"); + return (1); + } + } + + int sfd = open(argv[optind], O_RDONLY); + if (sfd < 0) { + fprintf(stderr, "open: %s: %s\n", + argv[optind], strerror(errno)); + return (1); + } + + int dfd = open(argv[optind+1], O_WRONLY|O_CREAT, + S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (sfd < 0) { + fprintf(stderr, "open: %s: %s\n", + argv[optind+1], strerror(errno)); + close(sfd); + return (1); + } + + int err; + switch (mode) { + case CF_MODE_CLONE: + err = do_clone(sfd, dfd); + break; + case CF_MODE_CLONERANGE: + err = do_clonerange(sfd, dfd, soff, doff, len); + break; + case CF_MODE_COPYFILERANGE: + err = do_copyfilerange(sfd, dfd, soff, doff, len); + break; + case CF_MODE_DEDUPERANGE: + err = do_deduperange(sfd, dfd, soff, doff, len); + break; + default: + 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); + + fprintf(stderr, "file offsets: src=%lu/%lu; dst=%lu/%lu\n", spos, slen, + dpos, dlen); + + close(dfd); + close(sfd); + + return (err == 0 ? 0 : 1); +} + +int +do_clone(int sfd, int dfd) +{ + fprintf(stderr, "using FICLONE\n"); + int err = ioctl(dfd, CF_FICLONE, sfd); + if (err < 0) { + fprintf(stderr, "ioctl(FICLONE): %s\n", strerror(errno)); + return (err); + } + return (0); +} + +int +do_clonerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len) +{ + fprintf(stderr, "using FICLONERANGE\n"); + cf_file_clone_range_t fcr = { + .src_fd = sfd, + .src_offset = soff, + .src_length = len, + .dest_offset = doff, + }; + int err = ioctl(dfd, CF_FICLONERANGE, &fcr); + if (err < 0) { + fprintf(stderr, "ioctl(FICLONERANGE): %s\n", strerror(errno)); + return (err); + } + return (0); +} + +int +do_copyfilerange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len) +{ + 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 (copied != len) { + fprintf(stderr, "copy_file_range: copied less than requested: " + "requested=%lu; copied=%lu\n", len, copied); + return (1); + } + return (0); +} + +int +do_deduperange(int sfd, int dfd, loff_t soff, loff_t doff, size_t len) +{ + fprintf(stderr, "using FIDEDUPERANGE\n"); + + char buf[sizeof (cf_file_dedupe_range_t)+ + sizeof (cf_file_dedupe_range_info_t)] = {0}; + cf_file_dedupe_range_t *fdr = (cf_file_dedupe_range_t *)&buf[0]; + cf_file_dedupe_range_info_t *fdri = + (cf_file_dedupe_range_info_t *) + &buf[sizeof (cf_file_dedupe_range_t)]; + + fdr->src_offset = soff; + fdr->src_length = len; + fdr->dest_count = 1; + + fdri->dest_fd = dfd; + fdri->dest_offset = doff; + + int err = ioctl(sfd, CF_FIDEDUPERANGE, fdr); + if (err != 0) + fprintf(stderr, "ioctl(FIDEDUPERANGE): %s\n", strerror(errno)); + + if (fdri->status < 0) { + fprintf(stderr, "dedup failed: %s\n", strerror(-fdri->status)); + err = -1; + } else if (fdri->status == CF_FILE_DEDUPE_RANGE_DIFFERS) { + fprintf(stderr, "dedup failed: range differs\n"); + err = -1; + } + + return (err); +} diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index b3cfe149ffa7..fa545e06bbf3 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -182,6 +182,7 @@ export ZFS_FILES='zdb export ZFSTEST_FILES='badsend btree_test chg_usr_exec + clonefile devname2devid dir_rd_update draid diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index ff65dc1ac2b0..0819cb6b576e 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -90,6 +90,7 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/alloc_class/alloc_class.kshlib \ functional/atime/atime.cfg \ functional/atime/atime_common.kshlib \ + functional/block_cloning/block_cloning.kshlib \ functional/cache/cache.cfg \ functional/cache/cache.kshlib \ functional/cachefile/cachefile.cfg \ @@ -437,6 +438,17 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/atime/root_atime_on.ksh \ functional/atime/root_relatime_on.ksh \ functional/atime/setup.ksh \ + functional/block_cloning/cleanup.ksh \ + functional/block_cloning/setup.ksh \ + functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh \ + functional/block_cloning/block_cloning_copyfilerange.ksh \ + functional/block_cloning/block_cloning_copyfilerange_partial.ksh \ + functional/block_cloning/block_cloning_disabled_copyfilerange.ksh \ + functional/block_cloning/block_cloning_disabled_ficlone.ksh \ + functional/block_cloning/block_cloning_disabled_ficlonerange.ksh \ + functional/block_cloning/block_cloning_ficlone.ksh \ + functional/block_cloning/block_cloning_ficlonerange.ksh \ + functional/block_cloning/block_cloning_ficlonerange_partial.ksh \ functional/bootfs/bootfs_001_pos.ksh \ functional/bootfs/bootfs_002_neg.ksh \ functional/bootfs/bootfs_003_pos.ksh \ diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning.kshlib b/tests/zfs-tests/tests/functional/block_cloning/block_cloning.kshlib new file mode 100644 index 000000000000..9998e5a87bfe --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning.kshlib @@ -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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib + +function have_same_content +{ + typeset hash1=$(cat $1 | md5sum) + typeset hash2=$(cat $2 | md5sum) + + log_must [ "$hash1" = "$hash2" ] +} + +function unique_blocks +{ + typeset zdbout=${TMPDIR:-$TEST_BASE_DIR}/zdbout.$$ + zdb -vvvvv $1 -O $2 | \ + awk '/ L0 / { print ++l " " $3 " " $7 }' > $zdbout.a + zdb -vvvvv $3 -O $4 | \ + awk '/ L0 / { print ++l " " $3 " " $7 }' > $zdbout.b + echo $(sort $zdbout.a $zdbout.b | uniq -d | cut -f1 -d' ') +} + diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange.ksh new file mode 100755 index 000000000000..9adcbfcd88a1 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange.ksh @@ -0,0 +1,60 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then + log_unsupported "copy_file_range not available before Linux 4.5" +fi + +claim="The copy_file_range syscall can clone whole files." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "1 2 3 4" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_cross_dataset.ksh new file mode 100755 index 000000000000..07e089e89ceb --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_cross_dataset.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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +if [[ $(linux_version) -lt $(linux_version "5.3") ]]; then + log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3" +fi + +claim="The copy_file_range syscall can clone across datasets." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must zfs create $TESTPOOL/$TESTFS1 +log_must zfs create $TESTPOOL/$TESTFS2 + +log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS1/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must \ + clonefile -f /$TESTPOOL/$TESTFS1/file1 /$TESTPOOL/$TESTFS2/file2 0 0 524288 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/$TESTFS1/file1 /$TESTPOOL/$TESTFS2/file2 + +typeset blocks=$(unique_blocks \ + $TESTPOOL/$TESTFS1 file1 $TESTPOOL/$TESTFS2 file2) +log_must [ "$blocks" = "1 2 3 4" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_partial.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_partial.ksh new file mode 100755 index 000000000000..ecac62b20350 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_copyfilerange_partial.ksh @@ -0,0 +1,68 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then + log_unsupported "copy_file_range not available before Linux 4.5" +fi + +claim="The copy_file_range syscall can clone parts of a file." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must dd if=/$TESTPOOL/file1 of=/$TESTPOOL/file2 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "" ] + +log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 131072 131072 262144 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "2 3" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_copyfilerange.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_copyfilerange.ksh new file mode 100755 index 000000000000..30b155a140c4 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_copyfilerange.ksh @@ -0,0 +1,60 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +if [[ $(linux_version) -lt $(linux_version "4.5") ]]; then + log_unsupported "copy_file_range not available before Linux 4.5" +fi + +claim="The copy_file_range syscall copies files when block cloning is disabled." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must clonefile -f /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlone.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlone.ksh new file mode 100755 index 000000000000..10a2715ea253 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlone.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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +claim="The FICLONE ioctl fails when block cloning is disabled." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_mustnot clonefile -c /$TESTPOOL/file1 /$TESTPOOL/file2 + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlonerange.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlonerange.ksh new file mode 100755 index 000000000000..e8461e6d3c38 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_disabled_ficlonerange.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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +claim="The FICLONERANGE ioctl fails when block cloning is disabled." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=disabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_mustnot clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288 + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlone.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlone.ksh new file mode 100755 index 000000000000..d13a39229870 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlone.ksh @@ -0,0 +1,56 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +claim="The FICLONE ioctl can clone files." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must clonefile -c /$TESTPOOL/file1 /$TESTPOOL/file2 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "1 2 3 4" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange.ksh new file mode 100755 index 000000000000..6556050c4352 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange.ksh @@ -0,0 +1,56 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +claim="The FICLONERANGE ioctl can clone whole files." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 0 0 524288 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "1 2 3 4" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange_partial.ksh b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange_partial.ksh new file mode 100755 index 000000000000..37a3511a26d5 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/block_cloning_ficlonerange_partial.ksh @@ -0,0 +1,64 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +claim="The FICLONERANGE ioctl can clone parts of a file." + +log_assert $claim + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_onexit cleanup + +log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS + +log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must dd if=/$TESTPOOL/file1 of=/$TESTPOOL/file2 bs=128K count=4 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "" ] + +log_must clonefile -r /$TESTPOOL/file1 /$TESTPOOL/file2 131072 131072 262144 +log_must sync_pool $TESTPOOL + +log_must have_same_content /$TESTPOOL/file1 /$TESTPOOL/file2 + +typeset blocks=$(unique_blocks $TESTPOOL file1 $TESTPOOL file2) +log_must [ "$blocks" = "2 3" ] + +log_pass $claim diff --git a/tests/zfs-tests/tests/functional/block_cloning/cleanup.ksh b/tests/zfs-tests/tests/functional/block_cloning/cleanup.ksh new file mode 100755 index 000000000000..7ac13adb6325 --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/cleanup.ksh @@ -0,0 +1,34 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +verify_runnable "global" + +default_cleanup_noexit + +log_pass diff --git a/tests/zfs-tests/tests/functional/block_cloning/setup.ksh b/tests/zfs-tests/tests/functional/block_cloning/setup.ksh new file mode 100755 index 000000000000..512f5a0644df --- /dev/null +++ b/tests/zfs-tests/tests/functional/block_cloning/setup.ksh @@ -0,0 +1,36 @@ +#!/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, Klara Inc. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib + +if ! command -v clonefile > /dev/null ; then + log_unsupported "clonefile program required to test block cloning" +fi + +verify_runnable "global" + +log_pass