1#!/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or https://opensource.org/licenses/CDDL-1.0.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22
23#
24# Copyright (c) 2023, Kay Pedersen <mail@mkwg.de>
25#
26
27. $STF_SUITE/include/libtest.shlib
28. $STF_SUITE/tests/functional/block_cloning/block_cloning.kshlib
29. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
30
31verify_runnable "global"
32
33verify_crossfs_block_cloning
34
35claim="Block cloning across encrypted datasets."
36
37log_assert $claim
38
39DS1="$TESTPOOL/encrypted1"
40DS2="$TESTPOOL/encrypted2"
41DS1_NC="$TESTPOOL/notcrypted1"
42PASSPHRASE="top_secret"
43
44function prepare_enc
45{
46    log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
47    log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
48	    "-o keyformat=passphrase -o keylocation=prompt $DS1"
49    log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
50	    "-o keyformat=passphrase -o keylocation=prompt $DS2"
51    log_must zfs create $DS1/child1
52    log_must zfs create $DS1/child2
53    log_must zfs create $DS1_NC
54
55    log_note "Create test file"
56    # we must wait until the src file txg is written to the disk otherwise we
57    # will fallback to normal copy. See "dmu_read_l0_bps" in
58    # "zfs/module/zfs/dmu.c" and "zfs_clone_range" in
59    # "zfs/module/zfs/zfs_vnops.c"
60    log_must dd if=/dev/urandom of=/$DS1/file bs=128K count=4
61    log_must dd if=/dev/urandom of=/$DS1/child1/file bs=128K count=4
62    log_must dd if=/dev/urandom of=/$DS1_NC/file bs=128K count=4
63    log_must sync_pool $TESTPOOL
64}
65
66function cleanup_enc
67{
68	datasetexists $TESTPOOL && destroy_pool $TESTPOOL
69}
70
71function clone_and_check
72{
73    I_FILE="$1"
74    O_FILE=$2
75    I_DS=$3
76    O_DS=$4
77    SAME_BLOCKS=$5
78    # the CLONE option provides a choice between copy_file_range
79    # which should clone and a dd which is a copy no matter what
80    CLONE=$6
81    SNAPSHOT=$7
82    if [ ${#SNAPSHOT} -gt 0 ]; then
83        I_FILE=".zfs/snapshot/$SNAPSHOT/$1"
84    fi
85    if [ $CLONE ]; then
86        log_must clonefile -f "/$I_DS/$I_FILE" "/$O_DS/$O_FILE" 0 0 524288
87    else
88        log_must dd if="/$I_DS/$I_FILE" of="/$O_DS/$O_FILE" bs=128K
89    fi
90    log_must sync_pool $TESTPOOL
91
92    log_must have_same_content "/$I_DS/$I_FILE" "/$O_DS/$O_FILE"
93
94    if [ ${#SNAPSHOT} -gt 0 ]; then
95        I_DS="$I_DS@$SNAPSHOT"
96        I_FILE="$1"
97    fi
98    typeset blocks=$(get_same_blocks \
99      $I_DS $I_FILE $O_DS $O_FILE $PASSPHRASE)
100    log_must [ "$blocks" = "$SAME_BLOCKS" ]
101}
102
103log_onexit cleanup_enc
104
105prepare_enc
106
107log_note "Cloning entire file with copy_file_range across different enc" \
108    "roots, should fallback"
109# we are expecting no same block map.
110clone_and_check "file" "clone" $DS1 $DS2 "" true
111log_note "check if the file is still readable and the same after" \
112    "unmount and key unload, shouldn't fail"
113typeset hash1=$(md5digest "/$DS1/file")
114log_must zfs umount $DS1 && zfs unload-key $DS1
115typeset hash2=$(md5digest "/$DS2/clone")
116log_must [ "$hash1" = "$hash2" ]
117
118cleanup_enc
119prepare_enc
120
121log_note "Cloning entire file with copy_file_range across different child datasets"
122# clone shouldn't work because of deriving a new master key for the child
123# we are expecting no same block map.
124clone_and_check "file" "clone" $DS1 "$DS1/child1" "" true
125clone_and_check "file" "clone" "$DS1/child1" "$DS1/child2" "" true
126
127cleanup_enc
128prepare_enc
129
130log_note "Copying entire file with copy_file_range across same snapshot"
131log_must zfs snapshot -r $DS1@s1
132log_must sync_pool $TESTPOOL
133log_must rm -f "/$DS1/file"
134log_must sync_pool $TESTPOOL
135clone_and_check "file" "clone" "$DS1" "$DS1" "0 1 2 3" true "s1"
136
137cleanup_enc
138prepare_enc
139
140log_note "Copying entire file with copy_file_range across different snapshot"
141clone_and_check "file" "file" $DS1 $DS2 "" true
142log_must zfs snapshot -r $DS2@s1
143log_must sync_pool $TESTPOOL
144log_must rm -f "/$DS1/file" "/$DS2/file"
145log_must sync_pool $TESTPOOL
146clone_and_check "file" "clone" "$DS2" "$DS1" "" true "s1"
147typeset hash1=$(md5digest "/$DS1/.zfs/snapshot/s1/file")
148log_note "destroy the snapshot and check if the file is still readable and" \
149    "has the same content"
150log_must zfs destroy -r $DS2@s1
151log_must sync_pool $TESTPOOL
152typeset hash2=$(md5digest "/$DS1/file")
153log_must [ "$hash1" = "$hash2" ]
154
155cleanup_enc
156prepare_enc
157
158log_note "Copying with copy_file_range from non encrypted to encrypted"
159clone_and_check "file" "copy" $DS1_NC $DS1 "" true
160
161cleanup_enc
162prepare_enc
163
164log_note "Copying with copy_file_range from encrypted to non encrypted"
165clone_and_check "file" "copy" $DS1 $DS1_NC "" true
166
167log_must sync_pool $TESTPOOL
168
169log_pass $claim
170