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