xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/tests/functional/bclone/bclone_corner_cases.kshlib (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1#! /bin/ksh -p
2# SPDX-License-Identifier: CDDL-1.0
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or https://opensource.org/licenses/CDDL-1.0.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright (c) 2023 by Pawel Jakub Dawidek
26#
27
28. $STF_SUITE/include/libtest.shlib
29. $STF_SUITE/include/math.shlib
30. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
31
32function first_half_checksum
33{
34    typeset -r file=$1
35
36    dd if=$file bs=$HALFRECORDSIZE count=1 2>/dev/null | xxh128digest
37}
38
39function second_half_checksum
40{
41    typeset -r file=$1
42
43    dd if=$file bs=$HALFRECORDSIZE count=1 skip=1 2>/dev/null | xxh128digest
44}
45
46function bclone_corner_cases_init
47{
48    typeset -r srcdir=$1
49    typeset -r dstdir=$2
50
51    export RECORDSIZE=4096
52    export HALFRECORDSIZE=$((RECORDSIZE / 2))
53
54    export CLONE="$dstdir/clone0"
55    export ORIG0="$srcdir/orig0"
56    export ORIG1="$srcdir/orig1"
57    export ORIG2="$srcdir/orig2"
58
59    # Create source files.
60    log_must dd if=/dev/urandom of="$ORIG0" bs=$RECORDSIZE count=1
61    log_must dd if=/dev/urandom of="$ORIG1" bs=$RECORDSIZE count=1
62    log_must dd if=/dev/urandom of="$ORIG2" bs=$RECORDSIZE count=1
63
64    export FIRST_HALF_ORIG0_CHECKSUM=$(first_half_checksum $ORIG0)
65    export FIRST_HALF_ORIG1_CHECKSUM=$(first_half_checksum $ORIG1)
66    export FIRST_HALF_ORIG2_CHECKSUM=$(first_half_checksum $ORIG2)
67    export SECOND_HALF_ORIG0_CHECKSUM=$(second_half_checksum $ORIG0)
68    export SECOND_HALF_ORIG1_CHECKSUM=$(second_half_checksum $ORIG1)
69    export SECOND_HALF_ORIG2_CHECKSUM=$(second_half_checksum $ORIG2)
70    export ZEROS_CHECKSUM=$(dd if=/dev/zero bs=$HALFRECORDSIZE count=1 2>/dev/null | xxh128digest)
71    export FIRST_HALF_CHECKSUM=""
72    export SECOND_HALF_CHECKSUM=""
73}
74
75function cache_clone
76{
77    typeset -r cached=$1
78
79    case "$cached" in
80    "cached")
81        dd if=$CLONE of=/dev/null bs=$RECORDSIZE 2>/dev/null
82        ;;
83    "uncached")
84        ;;
85    *)
86        log_fail "invalid cached: $cached"
87        ;;
88    esac
89}
90
91function create_existing
92{
93    typeset -r existing=$1
94
95    case "$existing" in
96    "no")
97        ;;
98    "small empty")
99        log_must truncate_test -s $HALFRECORDSIZE -f $CLONE
100        ;;
101    "full empty")
102        log_must truncate_test -s $RECORDSIZE -f $CLONE
103        ;;
104    "small data")
105        log_must dd if=/dev/urandom of=$CLONE bs=$HALFRECORDSIZE count=1 \
106         2>/dev/null
107        ;;
108    "full data")
109        log_must dd if=/dev/urandom of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
110        ;;
111    *)
112        log_fail "invalid existing: $existing"
113        ;;
114    esac
115}
116
117function create_clone
118{
119    typeset -r clone=$1
120    typeset -r file=$2
121
122    case "$clone" in
123    "no")
124        ;;
125    "yes")
126        clonefile -f $file $CLONE
127        case "$file" in
128        $ORIG0)
129            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG0_CHECKSUM
130            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG0_CHECKSUM
131            ;;
132        $ORIG2)
133            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG2_CHECKSUM
134            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG2_CHECKSUM
135            ;;
136        *)
137            log_fail "invalid file: $file"
138            ;;
139        esac
140        ;;
141    *)
142        log_fail "invalid clone: $clone"
143        ;;
144    esac
145}
146
147function overwrite_clone
148{
149    typeset -r overwrite=$1
150
151    case "$overwrite" in
152    "no")
153        ;;
154    "free")
155        log_must truncate_test -s 0 -f $CLONE
156        log_must truncate_test -s $RECORDSIZE -f $CLONE
157        FIRST_HALF_CHECKSUM=$ZEROS_CHECKSUM
158        SECOND_HALF_CHECKSUM=$ZEROS_CHECKSUM
159        ;;
160    "full")
161        log_must dd if=$ORIG1 of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
162        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
163        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
164        ;;
165    "first half")
166        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=0 seek=0 \
167          count=1 conv=notrunc 2>/dev/null
168        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
169        ;;
170    "second half")
171        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=1 seek=1 \
172          count=1 conv=notrunc 2>/dev/null
173        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
174        ;;
175    *)
176        log_fail "invalid overwrite: $overwrite"
177        ;;
178    esac
179}
180
181function checksum_compare
182{
183    typeset -r compare=$1
184    typeset first_half_calculated_checksum second_half_calculated_checksum
185
186    case "$compare" in
187    "no")
188        ;;
189    "yes")
190        first_half_calculated_checksum=$(first_half_checksum $CLONE)
191        second_half_calculated_checksum=$(second_half_checksum $CLONE)
192
193        if [[ $first_half_calculated_checksum != $FIRST_HALF_CHECKSUM ]] || \
194           [[ $second_half_calculated_checksum != $SECOND_HALF_CHECKSUM ]]; then
195            return 1
196        fi
197        ;;
198    *)
199        log_fail "invalid compare: $compare"
200        ;;
201    esac
202}
203
204function bclone_corner_cases_test
205{
206    typeset cached existing
207    typeset first_clone first_overwrite
208    typeset read_after read_before
209    typeset second_clone second_overwrite
210    typeset -r srcdir=$1
211    typeset -r dstdir=$2
212    typeset limit=$3
213    typeset -i count=0
214    typeset oused
215    typeset osaved
216
217    if [[ $srcdir != "count" ]]; then
218        if [[ -n "$limit" ]]; then
219            typeset -r total_count=$(bclone_corner_cases_test count)
220            limit=$(random_int_between 1 $total_count $((limit*2)) | sort -nu | head -n $limit | xargs)
221        fi
222        bclone_corner_cases_init $srcdir $dstdir
223
224        # Save current block cloning stats for later use.
225        sync_pool $TESTPOOL
226        oused=$(get_pool_prop bcloneused $TESTPOOL)
227        osaved=$(get_pool_prop bclonesaved $TESTPOOL)
228    fi
229
230    #
231    # (create) / (cache) / (clone) / (overwrite) / (read) / (clone) / (overwrite) / (read) / read next txg
232    #
233    for existing in "no" "small empty" "full empty" "small data" "full data"; do
234        for cached in "uncached" "cached"; do
235            for first_clone in "no" "yes"; do
236                for first_overwrite in "no" "free" "full" "first half" "second half"; do
237                    for read_before in "no" "yes"; do
238                        for second_clone in "no" "yes"; do
239                            for second_overwrite in "no" "free" "full" "first half" "second half"; do
240                                for read_after in "no" "yes"; do
241                                    if [[ $first_clone = "no" ]] && \
242                                       [[ $second_clone = "no" ]]; then
243                                        continue
244                                    fi
245                                    if [[ $first_clone = "no" ]] && \
246                                       [[ $read_before = "yes" ]]; then
247                                        continue
248                                    fi
249                                    if [[ $second_clone = "no" ]] && \
250                                       [[ $read_before = "yes" ]] && \
251                                       [[ $read_after = "yes" ]]; then
252                                        continue
253                                    fi
254
255                                    count=$((count+1))
256
257                                    if [[ $srcdir = "count" ]]; then
258                                        # Just counting.
259                                        continue
260                                    fi
261
262                                    if [[ -n "$limit" ]]; then
263                                        if ! echo " $limit " | grep -q " $count "; then
264                                            continue
265                                        fi
266                                    fi
267
268                                    FIRST_HALF_CHECKSUM=""
269                                    SECOND_HALF_CHECKSUM=""
270
271                                    log_must zpool export $TESTPOOL
272                                    log_must zpool import $TESTPOOL
273
274                                    create_existing "$existing"
275
276                                    log_must zpool export $TESTPOOL
277                                    log_must zpool import $TESTPOOL
278
279                                    cache_clone "$cached"
280
281                                    create_clone "$first_clone" "$ORIG0"
282
283                                    overwrite_clone "$first_overwrite"
284
285                                    if checksum_compare $read_before; then
286                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
287                                    else
288                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
289                                    fi
290
291                                    create_clone "$second_clone" "$ORIG2"
292
293                                    overwrite_clone "$second_overwrite"
294
295                                    if checksum_compare $read_after; then
296                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / second_overwrite: $second_overwrite / read_after: $read_after"
297                                    else
298                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / second_overwrite: $second_overwrite / read_after: $read_after"
299                                    fi
300
301                                    log_must zpool export $TESTPOOL
302                                    log_must zpool import $TESTPOOL
303
304                                    if checksum_compare "yes"; then
305                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / second_overwrite: $second_overwrite / read_after: $read_after / read_next_txg"
306                                    else
307                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / second_overwrite: $second_overwrite / read_after: $read_after / read_next_txg"
308                                    fi
309
310                                    rm -f "$CLONE"
311                                    sync_pool $TESTPOOL
312                                    verify_pool_prop_eq bcloneused $oused
313                                    verify_pool_prop_eq bclonesaved $osaved
314                                done
315                            done
316                        done
317                    done
318                done
319            done
320        done
321    done
322
323    if [[ $srcdir = "count" ]]; then
324        echo $count
325    fi
326}
327