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