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 http://www.opensolaris.org/os/licensing. 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 2009 Sun Microsystems, Inc. All rights reserved. 24# Use is subject to license terms. 25# 26 27# 28# Copyright (c) 2013, 2018 by Delphix. All rights reserved. 29# Copyright (c) 2020 by Datto Inc. All rights reserved. 30# 31 32. $STF_SUITE/include/libtest.shlib 33. $STF_SUITE/include/math.shlib 34. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib 35. $STF_SUITE/tests/functional/rsend/rsend.cfg 36 37# 38# Set up test model which includes various datasets 39# 40# @final 41# @snapB 42# @init 43# | 44# ______ pclone 45# | / 46# |@psnap 47# || @final 48# ||@final @final @snapC 49# ||@snapC @snapC @snapB 50# ||@snapA @snapB @snapA 51# ||@init @init @init 52# ||| | | 53# $pool -------- $FS ------- fs1 ------- fs2 54# \ \\_____ \ | 55# vol vol \____ \ @fsnap 56# | | \ \ \ 57# @init @vsnap | ------------ fclone 58# @snapA @init \ | | 59# @final @snapB \ | @init 60# @snapC vclone @snapA 61# @final | @final 62# @init 63# @snapC 64# @final 65# 66# $1 pool name 67# 68function setup_test_model 69{ 70 typeset pool=$1 71 72 log_must zfs create -p $pool/$FS/fs1/fs2 73 74 log_must zfs snapshot $pool@psnap 75 log_must zfs clone $pool@psnap $pool/pclone 76 77 if is_global_zone ; then 78 log_must zfs create -V 16M $pool/vol 79 log_must zfs create -V 16M $pool/$FS/vol 80 block_device_wait 81 82 log_must zfs snapshot $pool/$FS/vol@vsnap 83 log_must zfs clone $pool/$FS/vol@vsnap $pool/$FS/vclone 84 block_device_wait 85 fi 86 87 log_must snapshot_tree $pool/$FS/fs1/fs2@fsnap 88 log_must zfs clone $pool/$FS/fs1/fs2@fsnap $pool/$FS/fs1/fclone 89 log_must zfs snapshot -r $pool@init 90 91 log_must snapshot_tree $pool@snapA 92 log_must snapshot_tree $pool@snapC 93 log_must snapshot_tree $pool/pclone@snapB 94 log_must snapshot_tree $pool/$FS@snapB 95 log_must snapshot_tree $pool/$FS@snapC 96 log_must snapshot_tree $pool/$FS/fs1@snapA 97 log_must snapshot_tree $pool/$FS/fs1@snapB 98 log_must snapshot_tree $pool/$FS/fs1@snapC 99 log_must snapshot_tree $pool/$FS/fs1/fclone@snapA 100 101 if is_global_zone ; then 102 log_must zfs snapshot $pool/vol@snapA 103 log_must zfs snapshot $pool/$FS/vol@snapB 104 log_must zfs snapshot $pool/$FS/vol@snapC 105 log_must zfs snapshot $pool/$FS/vclone@snapC 106 fi 107 108 log_must zfs snapshot -r $pool@final 109 110 return 0 111} 112 113# 114# Cleanup the BACKDIR and given pool content and all the sub datasets 115# 116# $1 pool name 117# 118function cleanup_pool 119{ 120 typeset pool=$1 121 log_must rm -rf $BACKDIR/* 122 123 if is_global_zone ; then 124 log_must_busy zfs destroy -Rf $pool 125 else 126 typeset list=$(zfs list -H -r -t all -o name $pool) 127 for ds in $list ; do 128 if [[ $ds != $pool ]] ; then 129 if datasetexists $ds ; then 130 log_must_busy zfs destroy -Rf $ds 131 fi 132 fi 133 done 134 fi 135 136 typeset mntpnt=$(get_prop mountpoint $pool) 137 if ! ismounted $pool ; then 138 # Make sure mountpoint directory is empty 139 if [[ -d $mntpnt ]]; then 140 log_must rm -rf $mntpnt/* 141 fi 142 143 log_must zfs mount $pool 144 fi 145 if [[ -d $mntpnt ]]; then 146 rm -rf $mntpnt/* 147 fi 148 149 return 0 150} 151 152function cleanup_pools 153{ 154 cleanup_pool $POOL2 155 destroy_pool $POOL3 156} 157 158function cmp_md5s { 159 typeset file1=$1 160 typeset file2=$2 161 162 typeset sum1=$(md5digest $file1) 163 typeset sum2=$(md5digest $file2) 164 test "$sum1" = "$sum2" 165} 166 167# 168# Detect if the given two filesystems have same sub-datasets 169# 170# $1 source filesystem 171# $2 destination filesystem 172# 173function cmp_ds_subs 174{ 175 typeset src_fs=$1 176 typeset dst_fs=$2 177 178 zfs list -r -H -t all -o name $src_fs > $BACKDIR/src1 179 zfs list -r -H -t all -o name $dst_fs > $BACKDIR/dst1 180 181 eval sed -e 's:^$src_fs:PREFIX:g' < $BACKDIR/src1 > $BACKDIR/src 182 eval sed -e 's:^$dst_fs:PREFIX:g' < $BACKDIR/dst1 > $BACKDIR/dst 183 184 diff $BACKDIR/src $BACKDIR/dst 185 typeset -i ret=$? 186 187 rm -f $BACKDIR/src $BACKDIR/dst $BACKDIR/src1 $BACKDIR/dst1 188 189 return $ret 190} 191 192# 193# Compare all the directories and files in two filesystems 194# 195# $1 source filesystem 196# $2 destination filesystem 197# 198function cmp_ds_cont 199{ 200 typeset src_fs=$1 201 typeset dst_fs=$2 202 203 typeset srcdir dstdir 204 srcdir=$(get_prop mountpoint $src_fs) 205 dstdir=$(get_prop mountpoint $dst_fs) 206 207 diff -r $srcdir $dstdir > /dev/null 2>&1 208 return $? 209} 210 211# 212# Compare the given two dataset properties 213# 214# $1 dataset 1 215# $2 dataset 2 216# 217function cmp_ds_prop 218{ 219 typeset dtst1=$1 220 typeset dtst2=$2 221 typeset -a props=("type" "origin" "volblocksize" "acltype" "dnodesize" \ 222 "atime" "canmount" "checksum" "compression" "copies" "devices" \ 223 "exec" "quota" "readonly" "recordsize" "reservation" "setuid" \ 224 "snapdir" "version" "volsize" "xattr" "mountpoint"); 225 if is_freebsd; then 226 props+=("jailed") 227 else 228 props+=("zoned") 229 fi 230 231 for prop in $props; 232 do 233 zfs get -H -o property,value,source $prop $dtst1 >> \ 234 $BACKDIR/dtst1 235 zfs get -H -o property,value,source $prop $dtst2 >> \ 236 $BACKDIR/dtst2 237 done 238 239 eval sed -e 's:$dtst1:PREFIX:g' < $BACKDIR/dtst1 > $BACKDIR/dtst1 240 eval sed -e 's:$dtst2:PREFIX:g' < $BACKDIR/dtst2 > $BACKDIR/dtst2 241 242 diff $BACKDIR/dtst1 $BACKDIR/dtst2 243 typeset -i ret=$? 244 245 rm -f $BACKDIR/dtst1 $BACKDIR/dtst2 246 247 return $ret 248 249} 250 251# 252# Random create directories and files 253# 254# $1 directory 255# 256function random_tree 257{ 258 typeset dir=$1 259 260 if [[ -d $dir ]]; then 261 rm -rf $dir 262 fi 263 mkdir -p $dir 264 typeset -i ret=$? 265 266 typeset -i nl nd nf 267 ((nl = RANDOM % 6 + 1)) 268 ((nd = RANDOM % 3 )) 269 ((nf = RANDOM % 5 )) 270 mktree -b $dir -l $nl -d $nd -f $nf 271 ((ret |= $?)) 272 273 return $ret 274} 275 276# 277# Put data in filesystem and take snapshot 278# 279# $1 snapshot name 280# 281function snapshot_tree 282{ 283 typeset snap=$1 284 typeset ds=${snap%%@*} 285 typeset type=$(get_prop "type" $ds) 286 287 typeset -i ret=0 288 if [[ $type == "filesystem" ]]; then 289 typeset mntpnt=$(get_prop mountpoint $ds) 290 ((ret |= $?)) 291 292 if ((ret == 0)) ; then 293 eval random_tree $mntpnt/${snap##$ds} 294 ((ret |= $?)) 295 fi 296 fi 297 298 if ((ret == 0)) ; then 299 zfs snapshot $snap 300 ((ret |= $?)) 301 fi 302 303 return $ret 304} 305 306# 307# Destroy the given snapshot and stuff 308# 309# $1 snapshot 310# 311function destroy_tree 312{ 313 typeset -i ret=0 314 typeset snap 315 for snap in "$@" ; do 316 log_must_busy zfs destroy $snap 317 318 typeset ds=${snap%%@*} 319 typeset type=$(get_prop "type" $ds) 320 if [[ $type == "filesystem" ]]; then 321 typeset mntpnt=$(get_prop mountpoint $ds) 322 if [[ -n $mntpnt ]]; then 323 rm -rf $mntpnt/$snap 324 fi 325 fi 326 done 327 328 return 0 329} 330 331# 332# Get all the sub-datasets of give dataset with specific suffix 333# 334# $1 Given dataset 335# $2 Suffix 336# 337function getds_with_suffix 338{ 339 typeset ds=$1 340 typeset suffix=$2 341 342 typeset list=$(zfs list -r -H -t all -o name $ds | grep "$suffix$") 343 344 echo $list 345} 346 347# 348# Output inherited properties which is edited for file system 349# 350function fs_inherit_prop 351{ 352 typeset fs_prop 353 if is_global_zone ; then 354 fs_prop=$(zfs inherit 2>&1 | \ 355 awk '$2=="YES" && $3=="YES" {print $1}') 356 if ! is_te_enabled ; then 357 fs_prop=$(echo $fs_prop | grep -v "mlslabel") 358 fi 359 else 360 fs_prop=$(zfs inherit 2>&1 | \ 361 awk '$2=="YES" && $3=="YES" {print $1}'| 362 egrep -v "devices|mlslabel|sharenfs|sharesmb|zoned") 363 fi 364 365 echo $fs_prop 366} 367 368# 369# Output inherited properties for volume 370# 371function vol_inherit_prop 372{ 373 echo "checksum readonly" 374} 375 376# 377# Get the destination dataset to compare 378# 379function get_dst_ds 380{ 381 typeset srcfs=$1 382 typeset dstfs=$2 383 384 # 385 # If the srcfs is not pool 386 # 387 if ! zpool list $srcfs > /dev/null 2>&1 ; then 388 eval dstfs="$dstfs/${srcfs#*/}" 389 fi 390 391 echo $dstfs 392} 393 394# 395# Make test files 396# 397# $1 Number of files to create 398# $2 Maximum file size 399# $3 File ID offset 400# $4 File system to create the files on 401# 402function mk_files 403{ 404 nfiles=$1 405 maxsize=$2 406 file_id_offset=$3 407 fs=$4 408 bs=512 409 410 for ((i=0; i<$nfiles; i=i+1)); do 411 file_name="/$fs/file-$maxsize-$((i+$file_id_offset))" 412 file_size=$((($RANDOM * $RANDOM % ($maxsize - 1)) + 1)) 413 414 # 415 # Create an interesting mix of files which contain both 416 # data blocks and holes for more realistic test coverage. 417 # Half the files are created as sparse then partially filled, 418 # the other half is dense then a hole is punched in the file. 419 # 420 if [ $((RANDOM % 2)) -eq 0 ]; then 421 truncate -s $file_size $file_name || \ 422 log_fail "Failed to create $file_name" 423 dd if=/dev/urandom of=$file_name \ 424 bs=$bs count=$(($file_size / 2 / $bs)) \ 425 seek=$(($RANDOM % (($file_size / 2 / $bs) + 1))) \ 426 conv=notrunc >/dev/null 2>&1 || \ 427 log_fail "Failed to create $file_name" 428 else 429 dd if=/dev/urandom of=$file_name \ 430 bs=$file_size count=1 >/dev/null 2>&1 || \ 431 log_fail "Failed to create $file_name" 432 dd if=/dev/zero of=$file_name \ 433 bs=$bs count=$(($file_size / 2 / $bs)) \ 434 seek=$(($RANDOM % (($file_size / 2 / $bs) + 1))) \ 435 conv=notrunc >/dev/null 2>&1 || \ 436 log_fail "Failed to create $file_name" 437 fi 438 done 439 echo Created $nfiles files of random sizes up to $maxsize bytes 440} 441 442# 443# Remove test files 444# 445# $1 Number of files to remove 446# $2 Maximum file size 447# $3 File ID offset 448# $4 File system to remove the files from 449# 450function rm_files 451{ 452 nfiles=$1 453 maxsize=$2 454 file_id_offset=$3 455 fs=$4 456 457 for ((i=0; i<$nfiles; i=i+1)); do 458 rm -f /$fs/file-$maxsize-$((i+$file_id_offset)) 459 done 460 echo Removed $nfiles files of random sizes up to $maxsize bytes 461} 462 463# 464# Simulate a random set of operations which could be reasonably expected 465# to occur on an average filesystem. 466# 467# $1 Number of files to modify 468# $2 Maximum file size 469# $3 File system to modify the file on 470# $4 Enabled xattrs (optional) 471# 472function churn_files 473{ 474 nfiles=$1 475 maxsize=$2 476 fs=$3 477 xattrs=${4:-1} 478 479 # 480 # Remove roughly half of the files in order to make it more 481 # likely that a dnode will be reallocated. 482 # 483 for ((i=0; i<$nfiles; i=i+1)); do 484 file_name="/$fs/file-$i" 485 486 if [[ -e $file_name ]]; then 487 if [ $((RANDOM % 2)) -eq 0 ]; then 488 rm $file_name || \ 489 log_fail "Failed to remove $file_name" 490 fi 491 fi 492 done 493 494 # 495 # Remount the filesystem to simulate normal usage. This resets 496 # the last allocated object id allowing for new objects to be 497 # reallocated in the locations of previously freed objects. 498 # 499 log_must zfs unmount $fs 500 log_must zfs mount $fs 501 502 for i in {0..$nfiles}; do 503 file_name="/$fs/file-$i" 504 file_size=$((($RANDOM * $RANDOM % ($maxsize - 1)) + 1)) 505 506 # 507 # When the file exists modify it in one of five ways to 508 # simulate normal usage: 509 # - (20%) Remove and set and extended attribute on the file 510 # - (20%) Overwrite the existing file 511 # - (20%) Truncate the existing file to a random length 512 # - (20%) Truncate the existing file to zero length 513 # - (20%) Remove the file 514 # 515 # Otherwise create the missing file. 20% of the created 516 # files will be small and use embedded block pointers, the 517 # remainder with have random sizes up to the maximum size. 518 # Three extended attributes are attached to all of the files. 519 # 520 if [[ -e $file_name ]]; then 521 value=$((RANDOM % 5)) 522 if [ $value -eq 0 -a $xattrs -ne 0 ]; then 523 attrname="testattr$((RANDOM % 3))" 524 attrlen="$(((RANDOM % 1000) + 1))" 525 attrvalue="$(random_string VALID_NAME_CHAR \ 526 $attrlen)" 527 rm_xattr $attrname $file_name || \ 528 log_fail "Failed to remove $attrname" 529 set_xattr $attrname "$attrvalue" $file_name || \ 530 log_fail "Failed to set $attrname" 531 elif [ $value -eq 1 ]; then 532 dd if=/dev/urandom of=$file_name \ 533 bs=$file_size count=1 >/dev/null 2>&1 || \ 534 log_fail "Failed to overwrite $file_name" 535 elif [ $value -eq 2 ]; then 536 truncate -s $file_size $file_name || \ 537 log_fail "Failed to truncate $file_name" 538 elif [ $value -eq 3 ]; then 539 truncate -s 0 $file_name || \ 540 log_fail "Failed to truncate $file_name" 541 else 542 rm $file_name || \ 543 log_fail "Failed to remove $file_name" 544 fi 545 else 546 if [ $((RANDOM % 5)) -eq 0 ]; then 547 file_size=$((($RANDOM % 64) + 1)) 548 fi 549 550 dd if=/dev/urandom of=$file_name \ 551 bs=$file_size count=1 >/dev/null 2>&1 || \ 552 log_fail "Failed to create $file_name" 553 554 if [ $xattrs -ne 0 ]; then 555 for j in {0..2}; do 556 attrname="testattr$j" 557 attrlen="$(((RANDOM % 1000) + 1))" 558 attrvalue="$(random_string \ 559 VALID_NAME_CHAR $attrlen)" 560 set_xattr $attrname \ 561 "$attrvalue" $file_name || \ 562 log_fail "Failed to set $attrname" 563 done 564 fi 565 fi 566 done 567 568 return 0 569} 570 571# 572# Mess up a send file's contents 573# 574# $1 The send file path 575# 576function mess_send_file 577{ 578 file=$1 579 580 filesize=$(stat_size $file) 581 582 offset=$(($RANDOM * $RANDOM % $filesize)) 583 584 # The random offset might truncate the send stream to be 585 # smaller than the DRR_BEGIN record. If this happens, then 586 # the receiving system won't have enough info to create the 587 # partial dataset at all. We use zstreamdump to check for 588 # this and retry in this case. 589 nr_begins=$(head -c $offset $file | zstreamdump | \ 590 grep DRR_BEGIN | awk '{ print $5 }') 591 while [ "$nr_begins" -eq 0 ]; do 592 offset=$(($RANDOM * $RANDOM % $filesize)) 593 nr_begins=$(head -c $offset $file | zstreamdump | \ 594 grep DRR_BEGIN | awk '{ print $5 }') 595 done 596 597 if (($RANDOM % 7 <= 1)); then 598 # 599 # We corrupt 2 bytes to minimize the chance that we 600 # write the same value that's already there. 601 # 602 log_must eval "dd if=/dev/urandom of=$file conv=notrunc " \ 603 "bs=1 count=2 seek=$offset >/dev/null 2>&1" 604 else 605 log_must truncate -s $offset $file 606 fi 607} 608 609# 610# Diff the send/receive filesystems 611# 612# $1 The sent filesystem 613# $2 The received filesystem 614# 615function file_check 616{ 617 sendfs=$1 618 recvfs=$2 619 620 if [[ -d /$recvfs/.zfs/snapshot/a && -d \ 621 /$sendfs/.zfs/snapshot/a ]]; then 622 diff -r /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a 623 [[ $? -eq 0 ]] || log_fail "Differences found in snap a" 624 fi 625 if [[ -d /$recvfs/.zfs/snapshot/b && -d \ 626 /$sendfs/.zfs/snapshot/b ]]; then 627 diff -r /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b 628 [[ $? -eq 0 ]] || log_fail "Differences found in snap b" 629 fi 630} 631 632# 633# Resume test helper 634# 635# $1 The ZFS send command 636# $2 The filesystem where the streams are sent 637# $3 The receive filesystem 638# $4 Test dry-run (optional) 639# 640function resume_test 641{ 642 typeset sendcmd=$1 643 typeset streamfs=$2 644 typeset recvfs=$3 645 typeset dryrun=${4:-1} 646 647 stream_num=1 648 log_must eval "$sendcmd >/$streamfs/$stream_num" 649 650 for ((i=0; i<2; i=i+1)); do 651 mess_send_file /$streamfs/$stream_num 652 log_mustnot zfs recv -suv $recvfs </$streamfs/$stream_num 653 stream_num=$((stream_num+1)) 654 655 token=$(zfs get -Hp -o value receive_resume_token $recvfs) 656 657 # Do a dry-run 658 [ $dryrun -ne 0 ] && \ 659 log_must eval "zfs send -nvt $token > /dev/null" 660 661 log_must eval "zfs send -t $token >/$streamfs/$stream_num" 662 [[ -f /$streamfs/$stream_num ]] || \ 663 log_fail "NO FILE /$streamfs/$stream_num" 664 done 665 log_must zfs recv -suv $recvfs </$streamfs/$stream_num 666} 667 668function get_resume_token 669{ 670 sendcmd=$1 671 streamfs=$2 672 recvfs=$3 673 674 log_must eval "$sendcmd > /$streamfs/1" 675 mess_send_file /$streamfs/1 676 log_mustnot zfs recv -suv $recvfs < /$streamfs/1 2>&1 677 token=$(zfs get -Hp -o value receive_resume_token $recvfs) 678 echo "$token" > /$streamfs/resume_token 679 680 return 0 681} 682 683# 684# Setup filesystems for the resumable send/receive tests 685# 686# $1 The "send" filesystem 687# $2 The "recv" filesystem 688# 689function test_fs_setup 690{ 691 typeset sendfs=$1 692 typeset recvfs=$2 693 typeset streamfs=$3 694 typeset sendpool=${sendfs%%/*} 695 typeset recvpool=${recvfs%%/*} 696 697 datasetexists $sendfs && log_must_busy zfs destroy -r $sendpool 698 datasetexists $recvfs && log_must_busy zfs destroy -r $recvpool 699 datasetexists $streamfs && log_must_busy zfs destroy -r $streamfs 700 701 if datasetexists $sendfs || zfs create -o compress=lz4 $sendfs; then 702 mk_files 1000 256 0 $sendfs & 703 mk_files 1000 131072 0 $sendfs & 704 mk_files 100 1048576 0 $sendfs & 705 mk_files 10 10485760 0 $sendfs & 706 mk_files 1 104857600 0 $sendfs & 707 log_must wait 708 log_must zfs snapshot $sendfs@a 709 710 rm_files 200 256 0 $sendfs & 711 rm_files 200 131072 0 $sendfs & 712 rm_files 20 1048576 0 $sendfs & 713 rm_files 2 10485760 0 $sendfs & 714 log_must wait 715 716 mk_files 400 256 0 $sendfs & 717 mk_files 400 131072 0 $sendfs & 718 mk_files 40 1048576 0 $sendfs & 719 mk_files 4 10485760 0 $sendfs & 720 log_must wait 721 722 log_must zfs snapshot $sendfs@b 723 log_must eval "zfs send -v $sendfs@a >/$sendpool/initial.zsend" 724 log_must eval "zfs send -v -i @a $sendfs@b " \ 725 ">/$sendpool/incremental.zsend" 726 fi 727 728 log_must zfs create -o compress=lz4 $streamfs 729} 730 731# 732# Check to see if the specified features are set in a send stream. 733# The values for these features are found in include/sys/zfs_ioctl.h 734# 735# $1 The stream file 736# $2-$n The flags expected in the stream 737# 738function stream_has_features 739{ 740 typeset file=$1 741 shift 742 743 [[ -f $file ]] || log_fail "Couldn't find file: $file" 744 typeset flags=$(cat $file | zstreamdump | \ 745 awk '/features =/ {features = $3} END {print features}') 746 typeset -A feature 747 feature[dedup]="1" 748 feature[dedupprops]="2" 749 feature[sa_spill]="4" 750 feature[embed_data]="10000" 751 feature[lz4]="20000" 752 feature[mooch_byteswap]="40000" 753 feature[large_blocks]="80000" 754 feature[resuming]="100000" 755 feature[redacted]="200000" 756 feature[compressed]="400000" 757 758 typeset flag known derived=0 759 for flag in "$@"; do 760 known=${feature[$flag]} 761 [[ -z $known ]] && log_fail "Unknown feature: $flag" 762 763 derived=$(printf "%x" $((0x${flags} & 0x${feature[$flag]}))) 764 [[ $derived = $known ]] || return 1 765 done 766 767 return 0 768} 769 770# 771# Given a send stream, verify that the size of the stream matches what's 772# expected based on the source or target dataset. If the stream is an 773# incremental stream, subtract the size of the source snapshot before 774# comparing. This function does not currently handle incremental streams 775# that remove data. 776# 777# $1 The zstreamdump output file 778# $2 The dataset to compare against 779# This can be a source of a send or recv target (fs, not snapshot) 780# $3 The percentage below which verification is deemed a failure 781# $4 The source snapshot of an incremental send 782# 783 784function verify_stream_size 785{ 786 typeset stream=$1 787 typeset ds=$2 788 typeset percent=${3:-90} 789 typeset inc_src=$4 790 791 [[ -f $stream ]] || log_fail "No such file: $stream" 792 datasetexists $ds || log_fail "No such dataset: $ds" 793 794 typeset stream_size=$(cat $stream | zstreamdump | sed -n \ 795 's/ Total payload size = \(.*\) (0x.*)/\1/p') 796 797 typeset inc_size=0 798 if [[ -n $inc_src ]]; then 799 inc_size=$(get_prop lrefer $inc_src) 800 if stream_has_features $stream compressed; then 801 inc_size=$(get_prop refer $inc_src) 802 fi 803 fi 804 805 if stream_has_features $stream compressed; then 806 ds_size=$(get_prop refer $ds) 807 else 808 ds_size=$(get_prop lrefer $ds) 809 fi 810 ds_size=$((ds_size - inc_size)) 811 812 within_percent $stream_size $ds_size $percent || log_fail \ 813 "$stream_size $ds_size differed by too much" 814} 815 816# Cleanup function for tests involving resumable send 817function resume_cleanup 818{ 819 typeset sendfs=$1 820 typeset streamfs=$2 821 typeset sendpool=${sendfs%%/*} 822 823 datasetexists $sendfs && log_must_busy zfs destroy -r $sendfs 824 datasetexists $streamfs && log_must_busy zfs destroy -r $streamfs 825 cleanup_pool $POOL2 826 rm -f /$sendpool/initial.zsend /$sendpool/incremental.zsend 827} 828 829# Randomly set the property to one of the enumerated values. 830function rand_set_prop 831{ 832 typeset dtst=$1 833 typeset prop=$2 834 shift 2 835 typeset value=$(random_get $@) 836 837 log_must eval "zfs set $prop='$value' $dtst" 838} 839 840# Generate a recursive checksum of a filesystem which includes the file 841# contents and any associated extended attributes. 842function recursive_cksum 843{ 844 case "$(uname)" in 845 FreeBSD) 846 find $1 -type f -exec sh -c 'sha256 -q {}; lsextattr -q \ 847 system {} | sha256 -q; lsextattr -q user {} | sha256 -q' \ 848 \; | sort | sha256 -q 849 ;; 850 *) 851 find $1 -type f -exec sh -c 'sha256sum {}; getfattr \ 852 --absolute-names --only-values -d {} | sha256sum' \; | \ 853 sort -k 2 | awk '{ print $1 }' | sha256sum | \ 854 awk '{ print $1 }' 855 ;; 856 esac 857} 858