1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or https://opensource.org/licenses/CDDL-1.0.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2023 by Pawel Jakub Dawidek
24#
25
26. $STF_SUITE/tests/functional/bclone/bclone.cfg
27
28export RECORDSIZE=$(zfs get -Hp -o value recordsize $TESTPOOL/$TESTFS)
29
30MINBLKSIZE1=512
31MINBLKSIZE2=1024
32
33function verify_block_cloning
34{
35	if is_linux && [[ $(linux_version) -lt $(linux_version "4.5") ]]; then
36		log_unsupported "copy_file_range not available before Linux 4.5"
37	fi
38}
39
40function verify_crossfs_block_cloning
41{
42	if is_linux && [[ $(linux_version) -lt $(linux_version "5.3") ]]; then
43		log_unsupported "copy_file_range can't copy cross-filesystem before Linux 5.3"
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=$(sha256digest $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 dsize=$1
95    typeset -r ratio=$2
96
97    if [[ $dsize -eq 0 ]]; then
98        verify_pool_prop_eq bcloneused 0
99        verify_pool_prop_eq bclonesaved 0
100        verify_pool_prop_eq bcloneratio 1.00
101    else
102        if [[ $ratio -eq 1 ]]; then
103            verify_pool_prop_eq bcloneused 0
104        else
105            verify_pool_prop_eq bcloneused $dsize
106        fi
107        verify_pool_prop_eq bclonesaved $((dsize*(ratio-1)))
108        verify_pool_prop_eq bcloneratio "${ratio}.00"
109    fi
110}
111
112# Function to test file copying and integrity check.
113function bclone_test
114{
115    typeset -r datatype=$1
116    typeset filesize=$2
117    typeset -r embedded=$3
118    typeset -r srcdir=$4
119    typeset -r dstdir=$5
120    typeset dsize
121
122    typeset -r original="${srcdir}/original"
123    typeset -r clone="${dstdir}/clone"
124
125    log_note "Testing file copy with datatype $datatype, file size $filesize, embedded $embedded"
126
127    # Create a test file with known content.
128    case $datatype in
129        random|text)
130            sync_pool $TESTPOOL
131            if [[ $datatype = "random" ]]; then
132                dd if=/dev/urandom of=$original bs=$filesize count=1 2>/dev/null
133            else
134                filesize=$(((filesize/4)*4))
135                dd if=/dev/urandom bs=$(((filesize/4)*3)) count=1 | \
136                  openssl base64 -A > $original
137            fi
138            sync_pool $TESTPOOL
139            clonefile -f $original "${clone}-tmp"
140            sync_pool $TESTPOOL
141            # It is hard to predict block sizes that will be used,
142            # so just do one clone and take it from bcloneused.
143            filesize=$(zpool get -Hp -o value bcloneused $TESTPOOL)
144            if [[ $embedded = "false" ]]; then
145                log_must test $filesize -gt 0
146            fi
147            rm -f "${clone}-tmp"
148            sync_pool $TESTPOOL
149            dsize=$filesize
150            ;;
151        hole)
152            log_must truncate_test -s $filesize -f $original
153            dsize=0
154            ;;
155        *)
156            log_fail "Unknown datatype $datatype"
157            ;;
158    esac
159    if [[ $embedded = "true" ]]; then
160        dsize=0
161    fi
162
163    typeset -r original_checksum=$(sha256digest $original)
164
165    sync_pool $TESTPOOL
166
167    # Create a first clone of the entire file.
168    clonefile -f $original "${clone}0"
169    # Try to clone the clone in the same transaction group.
170    clonefile -f "${clone}0" "${clone}2"
171
172    # Clone the original again...
173    clonefile -f $original "${clone}1"
174    # ...and overwrite it in the same transaction group.
175    clonefile -f $original "${clone}1"
176
177    # Clone the clone...
178    clonefile -f "${clone}1" "${clone}3"
179    sync_pool $TESTPOOL
180    # ...and overwrite in the new transaction group.
181    clonefile -f "${clone}1" "${clone}3"
182
183    sync_pool $TESTPOOL
184
185    # Test removal of the pending clones (before they are committed to disk).
186    clonefile -f $original "${clone}4"
187    clonefile -f "${clone}4" "${clone}5"
188    rm -f "${clone}4" "${clone}5"
189
190    # Clone into one file, but remove another file, but with the same data in
191    # the same transaction group.
192    clonefile -f $original "${clone}5"
193    sync_pool $TESTPOOL
194    clonefile -f $original "${clone}4"
195    rm -f "${clone}5"
196    test_file_integrity $original_checksum "${clone}4" $filesize
197    sync_pool $TESTPOOL
198    test_file_integrity $original_checksum "${clone}4" $filesize
199
200    clonefile -f "${clone}4" "${clone}5"
201    # Verify integrity of the cloned file before it is committed to disk.
202    test_file_integrity $original_checksum "${clone}5" $filesize
203
204    sync_pool $TESTPOOL
205
206    # Verify integrity in the new transaction group.
207    test_file_integrity $original_checksum "${clone}0" $filesize
208    test_file_integrity $original_checksum "${clone}1" $filesize
209    test_file_integrity $original_checksum "${clone}2" $filesize
210    test_file_integrity $original_checksum "${clone}3" $filesize
211    test_file_integrity $original_checksum "${clone}4" $filesize
212    test_file_integrity $original_checksum "${clone}5" $filesize
213
214    verify_pool_props $dsize 7
215
216    # Clear cache and test after fresh import.
217    log_must zpool export $TESTPOOL
218    log_must zpool import $TESTPOOL
219
220    # Cloned uncached file.
221    clonefile -f $original "${clone}6"
222    # Cloned uncached clone.
223    clonefile -f "${clone}6" "${clone}7"
224
225    # Cache the file.
226    cat $original >/dev/null
227    clonefile -f $original "${clone}8"
228    clonefile -f "${clone}8" "${clone}9"
229
230    test_file_integrity $original_checksum "${clone}6" $filesize
231    test_file_integrity $original_checksum "${clone}7" $filesize
232    test_file_integrity $original_checksum "${clone}8" $filesize
233    test_file_integrity $original_checksum "${clone}9" $filesize
234
235    sync_pool $TESTPOOL
236
237    verify_pool_props $dsize 11
238
239    log_must zpool export $TESTPOOL
240    log_must zpool import $TESTPOOL
241
242    test_file_integrity $original_checksum "${clone}0" $filesize
243    test_file_integrity $original_checksum "${clone}1" $filesize
244    test_file_integrity $original_checksum "${clone}2" $filesize
245    test_file_integrity $original_checksum "${clone}3" $filesize
246    test_file_integrity $original_checksum "${clone}4" $filesize
247    test_file_integrity $original_checksum "${clone}5" $filesize
248    test_file_integrity $original_checksum "${clone}6" $filesize
249    test_file_integrity $original_checksum "${clone}7" $filesize
250    test_file_integrity $original_checksum "${clone}8" $filesize
251    test_file_integrity $original_checksum "${clone}9" $filesize
252
253    rm -f $original
254    rm -f "${clone}1" "${clone}3" "${clone}5" "${clone}7"
255
256    sync_pool $TESTPOOL
257
258    test_file_integrity $original_checksum "${clone}0" $filesize
259    test_file_integrity $original_checksum "${clone}2" $filesize
260    test_file_integrity $original_checksum "${clone}4" $filesize
261    test_file_integrity $original_checksum "${clone}6" $filesize
262    test_file_integrity $original_checksum "${clone}8" $filesize
263    test_file_integrity $original_checksum "${clone}9" $filesize
264
265    verify_pool_props $dsize 6
266
267    rm -f "${clone}0" "${clone}2" "${clone}4" "${clone}8" "${clone}9"
268
269    sync_pool $TESTPOOL
270
271    test_file_integrity $original_checksum "${clone}6" $filesize
272
273    verify_pool_props $dsize 1
274
275    rm -f "${clone}6"
276
277    sync_pool $TESTPOOL
278
279    verify_pool_props $dsize 1
280}
281