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 by Pawel Jakub Dawidek
25#
26
27. $STF_SUITE/include/libtest.shlib
28. $STF_SUITE/include/math.shlib
29. $STF_SUITE/tests/functional/bclone/bclone_common.kshlib
30
31function first_half_checksum
32{
33    typeset -r file=$1
34
35    dd if=$file bs=$HALFRECORDSIZE count=1 2>/dev/null | sha256digest
36}
37
38function second_half_checksum
39{
40    typeset -r file=$1
41
42    dd if=$file bs=$HALFRECORDSIZE count=1 skip=1 2>/dev/null | sha256digest
43}
44
45function bclone_corner_cases_init
46{
47    typeset -r srcdir=$1
48    typeset -r dstdir=$2
49
50    export RECORDSIZE=4096
51    export HALFRECORDSIZE=$((RECORDSIZE / 2))
52
53    export CLONE="$dstdir/clone0"
54    export ORIG0="$srcdir/orig0"
55    export ORIG1="$srcdir/orig1"
56    export ORIG2="$srcdir/orig2"
57
58    # Create source files.
59    log_must dd if=/dev/urandom of="$ORIG0" bs=$RECORDSIZE count=1
60    log_must dd if=/dev/urandom of="$ORIG1" bs=$RECORDSIZE count=1
61    log_must dd if=/dev/urandom of="$ORIG2" bs=$RECORDSIZE count=1
62
63    export FIRST_HALF_ORIG0_CHECKSUM=$(first_half_checksum $ORIG0)
64    export FIRST_HALF_ORIG1_CHECKSUM=$(first_half_checksum $ORIG1)
65    export FIRST_HALF_ORIG2_CHECKSUM=$(first_half_checksum $ORIG2)
66    export SECOND_HALF_ORIG0_CHECKSUM=$(second_half_checksum $ORIG0)
67    export SECOND_HALF_ORIG1_CHECKSUM=$(second_half_checksum $ORIG1)
68    export SECOND_HALF_ORIG2_CHECKSUM=$(second_half_checksum $ORIG2)
69    export ZEROS_CHECKSUM=$(dd if=/dev/zero bs=$HALFRECORDSIZE count=1 | sha256digest)
70    export FIRST_HALF_CHECKSUM=""
71    export SECOND_HALF_CHECKSUM=""
72}
73
74function cache_clone
75{
76    typeset -r cached=$1
77
78    case "$cached" in
79    "cached")
80        dd if=$CLONE of=/dev/null bs=$RECORDSIZE 2>/dev/null
81        ;;
82    "uncached")
83        ;;
84    *)
85        log_fail "invalid cached: $cached"
86        ;;
87    esac
88}
89
90function create_existing
91{
92    typeset -r existing=$1
93
94    case "$existing" in
95    "no")
96        ;;
97    "small empty")
98        log_must truncate_test -s $HALFRECORDSIZE -f $CLONE
99        ;;
100    "full empty")
101        log_must truncate_test -s $RECORDSIZE -f $CLONE
102        ;;
103    "small data")
104        log_must dd if=/dev/urandom of=$CLONE bs=$HALFRECORDSIZE count=1 \
105         2>/dev/null
106        ;;
107    "full data")
108        log_must dd if=/dev/urandom of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
109        ;;
110    *)
111        log_fail "invalid existing: $existing"
112        ;;
113    esac
114}
115
116function create_clone
117{
118    typeset -r clone=$1
119    typeset -r file=$2
120
121    case "$clone" in
122    "no")
123        ;;
124    "yes")
125        clonefile -f $file $CLONE
126        case "$file" in
127        $ORIG0)
128            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG0_CHECKSUM
129            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG0_CHECKSUM
130            ;;
131        $ORIG2)
132            FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG2_CHECKSUM
133            SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG2_CHECKSUM
134            ;;
135        *)
136            log_fail "invalid file: $file"
137            ;;
138        esac
139        ;;
140    *)
141        log_fail "invalid clone: $clone"
142        ;;
143    esac
144}
145
146function overwrite_clone
147{
148    typeset -r overwrite=$1
149
150    case "$overwrite" in
151    "no")
152        ;;
153    "free")
154        log_must truncate_test -s 0 -f $CLONE
155        log_must truncate_test -s $RECORDSIZE -f $CLONE
156        FIRST_HALF_CHECKSUM=$ZEROS_CHECKSUM
157        SECOND_HALF_CHECKSUM=$ZEROS_CHECKSUM
158        ;;
159    "full")
160        log_must dd if=$ORIG1 of=$CLONE bs=$RECORDSIZE count=1 2>/dev/null
161        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
162        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
163        ;;
164    "first half")
165        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=0 seek=0 \
166          count=1 conv=notrunc 2>/dev/null
167        FIRST_HALF_CHECKSUM=$FIRST_HALF_ORIG1_CHECKSUM
168        ;;
169    "second half")
170        log_must dd if=$ORIG1 of=$CLONE bs=$HALFRECORDSIZE skip=1 seek=1 \
171          count=1 conv=notrunc 2>/dev/null
172        SECOND_HALF_CHECKSUM=$SECOND_HALF_ORIG1_CHECKSUM
173        ;;
174    *)
175        log_fail "invalid overwrite: $overwrite"
176        ;;
177    esac
178}
179
180function checksum_compare
181{
182    typeset -r compare=$1
183    typeset first_half_calculated_checksum second_half_calculated_checksum
184
185    case "$compare" in
186    "no")
187        ;;
188    "yes")
189        first_half_calculated_checksum=$(first_half_checksum $CLONE)
190        second_half_calculated_checksum=$(second_half_checksum $CLONE)
191
192        if [[ $first_half_calculated_checksum != $FIRST_HALF_CHECKSUM ]] || \
193           [[ $second_half_calculated_checksum != $SECOND_HALF_CHECKSUM ]]; then
194            return 1
195        fi
196        ;;
197    *)
198        log_fail "invalid compare: $compare"
199        ;;
200    esac
201}
202
203function bclone_corner_cases_test
204{
205    typeset cached existing
206    typeset first_clone first_overwrite
207    typeset read_after read_before
208    typeset second_clone second_overwrite
209    typeset -r srcdir=$1
210    typeset -r dstdir=$2
211    typeset limit=$3
212    typeset -i count=0
213
214    if [[ $srcdir != "count" ]]; then
215        if [[ -n "$limit" ]]; then
216            typeset -r total_count=$(bclone_corner_cases_test count)
217            limit=$(random_int_between 1 $total_count $((limit*2)) | sort -nu | head -n $limit | xargs)
218        fi
219        bclone_corner_cases_init $srcdir $dstdir
220    fi
221
222    #
223    # (create) / (cache) / (clone) / (overwrite) / (read) / (clone) / (overwrite) / (read) / read next txg
224    #
225    for existing in "no" "small empty" "full empty" "small data" "full data"; do
226        for cached in "uncached" "cached"; do
227            for first_clone in "no" "yes"; do
228                for first_overwrite in "no" "free" "full" "first half" "second half"; do
229                    for read_before in "no" "yes"; do
230                        for second_clone in "no" "yes"; do
231                            for second_overwrite in "no" "free" "full" "first half" "second half"; do
232                                for read_after in "no" "yes"; do
233                                    if [[ $first_clone = "no" ]] && \
234                                       [[ $second_clone = "no" ]]; then
235                                        continue
236                                    fi
237                                    if [[ $first_clone = "no" ]] && \
238                                       [[ $read_before = "yes" ]]; then
239                                        continue
240                                    fi
241                                    if [[ $second_clone = "no" ]] && \
242                                       [[ $read_before = "yes" ]] && \
243                                       [[ $read_after = "yes" ]]; then
244                                        continue
245                                    fi
246
247                                    count=$((count+1))
248
249                                    if [[ $srcdir = "count" ]]; then
250                                        # Just counting.
251                                        continue
252                                    fi
253
254                                    if [[ -n "$limit" ]]; then
255                                        if ! echo " $limit " | grep -q " $count "; then
256                                            continue
257                                        fi
258                                    fi
259
260                                    FIRST_HALF_CHECKSUM=""
261                                    SECOND_HALF_CHECKSUM=""
262
263                                    log_must zpool export $TESTPOOL
264                                    log_must zpool import $TESTPOOL
265
266                                    create_existing "$existing"
267
268                                    log_must zpool export $TESTPOOL
269                                    log_must zpool import $TESTPOOL
270
271                                    cache_clone "$cached"
272
273                                    create_clone "$first_clone" "$ORIG0"
274
275                                    overwrite_clone "$first_overwrite"
276
277                                    if checksum_compare $read_before; then
278                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
279                                    else
280                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before"
281                                    fi
282
283                                    create_clone "$second_clone" "$ORIG2"
284
285                                    overwrite_clone "$second_overwrite"
286
287                                    if checksum_compare $read_after; then
288                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after"
289                                    else
290                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after"
291                                    fi
292
293                                    log_must zpool export $TESTPOOL
294                                    log_must zpool import $TESTPOOL
295
296                                    if checksum_compare "yes"; then
297                                        log_note "existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after / read_next_txg"
298                                    else
299                                        log_fail "FAIL: existing: $existing / cached: $cached / first_clone: $first_clone / first_overwrite: $first_overwrite / read_before: $read_before / second_clone: $second_clone / read_after: $read_after / read_next_txg"
300                                    fi
301
302                                    rm -f "$CLONE"
303                                done
304                            done
305                        done
306                    done
307                done
308            done
309        done
310    done
311
312    if [[ $srcdir = "count" ]]; then
313        echo $count
314    fi
315}
316