xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/tests/functional/bclone/bclone_common.kshlib (revision b1c1ee4429fcca8f69873a8be66184e68e1b19d7)
1# SPDX-License-Identifier: CDDL-1.0
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 by Pawel Jakub Dawidek
25#
26
27. $STF_SUITE/tests/functional/bclone/bclone.cfg
28
29export RECORDSIZE=$(zfs get -Hp -o value recordsize $TESTPOOL/$TESTFS)
30
31MINBLKSIZE1=512
32MINBLKSIZE2=1024
33
34function verify_crossfs_block_cloning
35{
36	if is_linux && [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
37		log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
38	fi
39
40	# Cross dataset block cloning only supported on FreeBSD 14+
41	# https://github.com/freebsd/freebsd-src/commit/969071be938c
42        if is_freebsd && [ $(freebsd_version) -lt $(freebsd_version 14.0) ] ; then
43               log_unsupported "Cloning across datasets not supported in $(uname -r)"
44        fi
45}
46
47# Unused.
48function size_to_dsize
49{
50    typeset -r size=$1
51    typeset -r dir=$2
52
53    typeset -r dataset=$(df $dir | tail -1 | awk '{print $1}')
54    typeset -r recordsize=$(get_prop recordsize $dataset)
55    typeset -r copies=$(get_prop copies $dataset)
56    typeset dsize
57
58    if [[ $size -le $recordsize ]]; then
59        dsize=$(( ((size - 1) / MINBLOCKSIZE + 1) * MINBLOCKSIZE ))
60    else
61        dsize=$(( ((size - 1) / recordsize + 1) * recordsize ))
62    fi
63    dsize=$((dsize*copies))
64
65    echo $dsize
66}
67
68function test_file_integrity
69{
70    typeset -r original_checksum=$1
71    typeset -r clone=$2
72    typeset -r filesize=$3
73
74    typeset -r clone_checksum=$(xxh128digest $clone)
75
76    if [[ $original_checksum != $clone_checksum ]]; then
77        log_fail "Clone $clone is corrupted with file size $filesize"
78    fi
79}
80
81function verify_pool_prop_eq
82{
83    typeset -r prop=$1
84    typeset -r expected=$2
85
86    typeset -r value=$(get_pool_prop $prop $TESTPOOL)
87    if [[ $value != $expected ]]; then
88        log_fail "Pool property $prop is incorrect: expected $expected, got $value"
89    fi
90}
91
92function verify_pool_props
93{
94    typeset -r oused=$1
95    typeset -r osaved=$2
96    typeset dsize=$3
97    typeset ratio=$4
98
99    if [[ $dsize -eq 0 ]]; then
100        ratio=1
101    elif [[ $ratio -eq 1 ]]; then
102        dsize=0
103    fi
104    verify_pool_prop_eq bcloneused $(($oused+$dsize))
105    verify_pool_prop_eq bclonesaved $(($osaved+dsize*(ratio-1)))
106    if [[ $oused -eq 0 ]]; then
107        verify_pool_prop_eq bcloneratio "${ratio}.00"
108    fi
109}
110
111# Function to test file copying and integrity check.
112function bclone_test
113{
114    typeset -r datatype=$1
115    typeset filesize=$2
116    typeset -r embedded=$3
117    typeset -r srcdir=$4
118    typeset -r dstdir=$5
119    typeset dsize
120    typeset oused
121    typeset osaved
122
123    typeset -r original="${srcdir}/original"
124    typeset -r clone="${dstdir}/clone"
125
126    log_note "Testing file copy with datatype $datatype, file size $filesize, embedded $embedded"
127
128    # Save current block cloning stats for later use.
129    sync_pool $TESTPOOL
130    oused=$(get_pool_prop bcloneused $TESTPOOL)
131    osaved=$(get_pool_prop bclonesaved $TESTPOOL)
132
133    # Create a test file with known content.
134    case $datatype in
135        random|text)
136            if [[ $datatype = "random" ]]; then
137                dd if=/dev/urandom of=$original bs=$filesize count=1 2>/dev/null
138            else
139                filesize=$(((filesize/4)*4))
140                dd if=/dev/urandom bs=$(((filesize/4)*3)) count=1 | \
141                  openssl base64 -A > $original
142            fi
143            sync_pool $TESTPOOL
144            clonefile -f $original "${clone}-tmp"
145            sync_pool $TESTPOOL
146            # It is hard to predict block sizes that will be used,
147            # so just do one clone and take it from bcloneused.
148            dsize=$(get_pool_prop bcloneused $TESTPOOL)
149            dsize=$(($dsize-$oused))
150            if [[ $embedded = "false" ]]; then
151                log_must test $dsize -gt 0
152            fi
153            rm -f "${clone}-tmp"
154            sync_pool $TESTPOOL
155            ;;
156        hole)
157            log_must truncate_test -s $filesize -f $original
158            dsize=0
159            ;;
160        *)
161            log_fail "Unknown datatype $datatype"
162            ;;
163    esac
164    if [[ $embedded = "true" ]]; then
165        dsize=0
166    fi
167
168    typeset -r original_checksum=$(xxh128digest $original)
169
170    sync_pool $TESTPOOL
171
172    # Create a first clone of the entire file.
173    clonefile -f $original "${clone}0"
174    # Try to clone the clone in the same transaction group.
175    clonefile -f "${clone}0" "${clone}2"
176
177    # Clone the original again...
178    clonefile -f $original "${clone}1"
179    # ...and overwrite it in the same transaction group.
180    clonefile -f $original "${clone}1"
181
182    # Clone the clone...
183    clonefile -f "${clone}1" "${clone}3"
184    sync_pool $TESTPOOL
185    # ...and overwrite in the new transaction group.
186    clonefile -f "${clone}1" "${clone}3"
187
188    sync_pool $TESTPOOL
189
190    # Test removal of the pending clones (before they are committed to disk).
191    clonefile -f $original "${clone}4"
192    clonefile -f "${clone}4" "${clone}5"
193    rm -f "${clone}4" "${clone}5"
194
195    # Clone into one file, but remove another file, but with the same data in
196    # the same transaction group.
197    clonefile -f $original "${clone}5"
198    sync_pool $TESTPOOL
199    clonefile -f $original "${clone}4"
200    rm -f "${clone}5"
201    test_file_integrity $original_checksum "${clone}4" $filesize
202    sync_pool $TESTPOOL
203    test_file_integrity $original_checksum "${clone}4" $filesize
204
205    clonefile -f "${clone}4" "${clone}5"
206    # Verify integrity of the cloned file before it is committed to disk.
207    test_file_integrity $original_checksum "${clone}5" $filesize
208
209    sync_pool $TESTPOOL
210
211    # Verify integrity in the new transaction group.
212    test_file_integrity $original_checksum "${clone}0" $filesize
213    test_file_integrity $original_checksum "${clone}1" $filesize
214    test_file_integrity $original_checksum "${clone}2" $filesize
215    test_file_integrity $original_checksum "${clone}3" $filesize
216    test_file_integrity $original_checksum "${clone}4" $filesize
217    test_file_integrity $original_checksum "${clone}5" $filesize
218
219    verify_pool_props $oused $osaved $dsize 7
220
221    # Clear cache and test after fresh import.
222    log_must zpool export $TESTPOOL
223    log_must zpool import $TESTPOOL
224
225    # Cloned uncached file.
226    clonefile -f $original "${clone}6"
227    # Cloned uncached clone.
228    clonefile -f "${clone}6" "${clone}7"
229
230    # Cache the file.
231    cat $original >/dev/null
232    clonefile -f $original "${clone}8"
233    clonefile -f "${clone}8" "${clone}9"
234
235    test_file_integrity $original_checksum "${clone}6" $filesize
236    test_file_integrity $original_checksum "${clone}7" $filesize
237    test_file_integrity $original_checksum "${clone}8" $filesize
238    test_file_integrity $original_checksum "${clone}9" $filesize
239
240    sync_pool $TESTPOOL
241
242    verify_pool_props $oused $osaved $dsize 11
243
244    log_must zpool export $TESTPOOL
245    log_must zpool import $TESTPOOL
246
247    test_file_integrity $original_checksum "${clone}0" $filesize
248    test_file_integrity $original_checksum "${clone}1" $filesize
249    test_file_integrity $original_checksum "${clone}2" $filesize
250    test_file_integrity $original_checksum "${clone}3" $filesize
251    test_file_integrity $original_checksum "${clone}4" $filesize
252    test_file_integrity $original_checksum "${clone}5" $filesize
253    test_file_integrity $original_checksum "${clone}6" $filesize
254    test_file_integrity $original_checksum "${clone}7" $filesize
255    test_file_integrity $original_checksum "${clone}8" $filesize
256    test_file_integrity $original_checksum "${clone}9" $filesize
257
258    rm -f $original
259    rm -f "${clone}1" "${clone}3" "${clone}5" "${clone}7"
260
261    sync_pool $TESTPOOL
262
263    test_file_integrity $original_checksum "${clone}0" $filesize
264    test_file_integrity $original_checksum "${clone}2" $filesize
265    test_file_integrity $original_checksum "${clone}4" $filesize
266    test_file_integrity $original_checksum "${clone}6" $filesize
267    test_file_integrity $original_checksum "${clone}8" $filesize
268    test_file_integrity $original_checksum "${clone}9" $filesize
269
270    verify_pool_props $oused $osaved $dsize 6
271
272    rm -f "${clone}0" "${clone}2" "${clone}4" "${clone}8" "${clone}9"
273
274    sync_pool $TESTPOOL
275
276    test_file_integrity $original_checksum "${clone}6" $filesize
277
278    verify_pool_props $oused $osaved $dsize 1
279
280    rm -f "${clone}6"
281
282    sync_pool $TESTPOOL
283
284    verify_pool_props $oused $osaved $dsize 1
285}
286