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
30verify_runnable "global"
31
32if [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
33  log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
34fi
35
36claim="Block cloning across encrypted datasets."
37
38log_assert $claim
39
40DS1="$TESTPOOL/encrypted1"
41DS2="$TESTPOOL/encrypted2"
42DS1_NC="$TESTPOOL/notcrypted1"
43PASSPHRASE="top_secret"
44
45function prepare_enc
46{
47    log_must zpool create -o feature@block_cloning=enabled $TESTPOOL $DISKS
48    log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
49	    "-o keyformat=passphrase -o keylocation=prompt $DS1"
50    log_must eval "echo $PASSPHRASE | zfs create -o encryption=on" \
51	    "-o keyformat=passphrase -o keylocation=prompt $DS2"
52    log_must zfs create $DS1/child1
53    log_must zfs create $DS1/child2
54    log_must zfs create $DS1_NC
55
56    log_note "Create test file"
57    # we must wait until the src file txg is written to the disk otherwise we
58    # will fallback to normal copy. See "dmu_read_l0_bps" in
59    # "zfs/module/zfs/dmu.c" and "zfs_clone_range" in
60    # "zfs/module/zfs/zfs_vnops.c"
61    log_must dd if=/dev/urandom of=/$DS1/file bs=128K count=4
62    log_must dd if=/dev/urandom of=/$DS1/child1/file bs=128K count=4
63    log_must dd if=/dev/urandom of=/$DS1_NC/file bs=128K count=4
64    log_must sync_pool $TESTPOOL
65}
66
67function cleanup_enc
68{
69	datasetexists $TESTPOOL && destroy_pool $TESTPOOL
70}
71
72function clone_and_check
73{
74    I_FILE="$1"
75    O_FILE=$2
76    I_DS=$3
77    O_DS=$4
78    SAME_BLOCKS=$5
79    # the CLONE option provides a choice between copy_file_range
80    # which should clone and a dd which is a copy no matter what
81    CLONE=$6
82    SNAPSHOT=$7
83    if [ ${#SNAPSHOT} -gt 0 ]; then
84        I_FILE=".zfs/snapshot/$SNAPSHOT/$1"
85    fi
86    if [ $CLONE ]; then
87        log_must clonefile -f "/$I_DS/$I_FILE" "/$O_DS/$O_FILE" 0 0 524288
88    else
89        log_must dd if="/$I_DS/$I_FILE" of="/$O_DS/$O_FILE" bs=128K
90    fi
91    log_must sync_pool $TESTPOOL
92
93    log_must have_same_content "/$I_DS/$I_FILE" "/$O_DS/$O_FILE"
94
95    if [ ${#SNAPSHOT} -gt 0 ]; then
96        I_DS="$I_DS@$SNAPSHOT"
97        I_FILE="$1"
98    fi
99    typeset blocks=$(get_same_blocks \
100      $I_DS $I_FILE $O_DS $O_FILE $PASSPHRASE)
101    log_must [ "$blocks" = "$SAME_BLOCKS" ]
102}
103
104log_onexit cleanup_enc
105
106prepare_enc
107
108log_note "Cloning entire file with copy_file_range across different enc" \
109    "roots, should fallback"
110# we are expecting no same block map.
111clone_and_check "file" "clone" $DS1 $DS2 "" true
112log_note "check if the file is still readable and the same after" \
113    "unmount and key unload, shouldn't fail"
114typeset hash1=$(md5digest "/$DS1/file")
115log_must zfs umount $DS1 && zfs unload-key $DS1
116typeset hash2=$(md5digest "/$DS2/clone")
117log_must [ "$hash1" = "$hash2" ]
118
119cleanup_enc
120prepare_enc
121
122log_note "Cloning entire file with copy_file_range across different child datasets"
123# clone shouldn't work because of deriving a new master key for the child
124# we are expecting no same block map.
125clone_and_check "file" "clone" $DS1 "$DS1/child1" "" true
126clone_and_check "file" "clone" "$DS1/child1" "$DS1/child2" "" true
127
128cleanup_enc
129prepare_enc
130
131log_note "Copying entire file with copy_file_range across same snapshot"
132log_must zfs snapshot -r $DS1@s1
133log_must sync_pool $TESTPOOL
134log_must rm -f "/$DS1/file"
135log_must sync_pool $TESTPOOL
136clone_and_check "file" "clone" "$DS1" "$DS1" "0 1 2 3" true "s1"
137
138cleanup_enc
139prepare_enc
140
141log_note "Copying entire file with copy_file_range across different snapshot"
142clone_and_check "file" "file" $DS1 $DS2 "" true
143log_must zfs snapshot -r $DS2@s1
144log_must sync_pool $TESTPOOL
145log_must rm -f "/$DS1/file" "/$DS2/file"
146log_must sync_pool $TESTPOOL
147clone_and_check "file" "clone" "$DS2" "$DS1" "" true "s1"
148typeset hash1=$(md5digest "/$DS1/.zfs/snapshot/s1/file")
149log_note "destroy the snapshot and check if the file is still readable and" \
150    "has the same content"
151log_must zfs destroy -r $DS2@s1
152log_must sync_pool $TESTPOOL
153typeset hash2=$(md5digest "/$DS1/file")
154log_must [ "$hash1" = "$hash2" ]
155
156cleanup_enc
157prepare_enc
158
159log_note "Copying with copy_file_range from non encrypted to encrypted"
160clone_and_check "file" "copy" $DS1_NC $DS1 "" true
161
162cleanup_enc
163prepare_enc
164
165log_note "Copying with copy_file_range from encrypted to non encrypted"
166clone_and_check "file" "copy" $DS1 $DS1_NC "" true
167
168log_must sync_pool $TESTPOOL
169
170log_pass $claim
171