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, 2016 by Delphix. All rights reserved. 29# 30 31. $STF_SUITE/include/libtest.shlib 32. $STF_SUITE/include/math.shlib 33. $STF_SUITE/tests/functional/rsend/rsend.cfg 34 35# 36# Set up test model which includes various datasets 37# 38# @final 39# @snapB 40# @init 41# | 42# ______ pclone 43# | / 44# |@psnap 45# || @final 46# ||@final @final @snapC 47# ||@snapC @snapC @snapB 48# ||@snapA @snapB @snapA 49# ||@init @init @init 50# ||| | | 51# $pool -------- $FS ------- fs1 ------- fs2 52# \ \\_____ \ | 53# vol vol \____ \ @fsnap 54# | | \ \ \ 55# @init @vsnap | ------------ fclone 56# @snapA @init \ | | 57# @final @snapB \ | @init 58# @snapC vclone @snapA 59# @final | @final 60# @init 61# @snapC 62# @final 63# 64# $1 pool name 65# 66function setup_test_model 67{ 68 typeset pool=$1 69 70 log_must zfs create -p $pool/$FS/fs1/fs2 71 72 log_must zfs snapshot $pool@psnap 73 log_must zfs clone $pool@psnap $pool/pclone 74 75 if is_global_zone ; then 76 log_must zfs create -V 16M $pool/vol 77 log_must zfs create -V 16M $pool/$FS/vol 78 79 log_must zfs snapshot $pool/$FS/vol@vsnap 80 log_must zfs clone $pool/$FS/vol@vsnap $pool/$FS/vclone 81 fi 82 83 log_must snapshot_tree $pool/$FS/fs1/fs2@fsnap 84 log_must zfs clone $pool/$FS/fs1/fs2@fsnap $pool/$FS/fs1/fclone 85 log_must zfs snapshot -r $pool@init 86 87 log_must snapshot_tree $pool@snapA 88 log_must snapshot_tree $pool@snapC 89 log_must snapshot_tree $pool/pclone@snapB 90 log_must snapshot_tree $pool/$FS@snapB 91 log_must snapshot_tree $pool/$FS@snapC 92 log_must snapshot_tree $pool/$FS/fs1@snapA 93 log_must snapshot_tree $pool/$FS/fs1@snapB 94 log_must snapshot_tree $pool/$FS/fs1@snapC 95 log_must snapshot_tree $pool/$FS/fs1/fclone@snapA 96 97 if is_global_zone ; then 98 log_must zfs snapshot $pool/vol@snapA 99 log_must zfs snapshot $pool/$FS/vol@snapB 100 log_must zfs snapshot $pool/$FS/vol@snapC 101 log_must zfs snapshot $pool/$FS/vclone@snapC 102 fi 103 104 log_must zfs snapshot -r $pool@final 105 106 return 0 107} 108 109# 110# Cleanup the BACKDIR and given pool content and all the sub datasets 111# 112# $1 pool name 113# 114function cleanup_pool 115{ 116 typeset pool=$1 117 log_must rm -rf $BACKDIR/* 118 119 if is_global_zone ; then 120 log_must zfs destroy -Rf $pool 121 else 122 typeset list=$(zfs list -H -r -t all -o name $pool) 123 for ds in $list ; do 124 if [[ $ds != $pool ]] ; then 125 if datasetexists $ds ; then 126 log_must zfs destroy -Rf $ds 127 fi 128 fi 129 done 130 fi 131 132 typeset mntpnt=$(get_prop mountpoint $pool) 133 if ! ismounted $pool ; then 134 # Make sure mountpoint directory is empty 135 if [[ -d $mntpnt ]]; then 136 log_must rm -rf $mntpnt/* 137 fi 138 139 log_must zfs mount $pool 140 fi 141 if [[ -d $mntpnt ]]; then 142 rm -rf $mntpnt/* 143 fi 144 145 return 0 146} 147 148function cleanup_pools 149{ 150 cleanup_pool $POOL2 151 destroy_pool $POOL3 152} 153 154# 155# Detect if the given two filesystems have same sub-datasets 156# 157# $1 source filesystem 158# $2 destination filesystem 159# 160function cmp_ds_subs 161{ 162 typeset src_fs=$1 163 typeset dst_fs=$2 164 165 zfs list -r -H -t all -o name $src_fs > $BACKDIR/src1 166 zfs list -r -H -t all -o name $dst_fs > $BACKDIR/dst1 167 168 eval sed -e 's:^$src_fs:PREFIX:g' < $BACKDIR/src1 > $BACKDIR/src 169 eval sed -e 's:^$dst_fs:PREFIX:g' < $BACKDIR/dst1 > $BACKDIR/dst 170 171 diff $BACKDIR/src $BACKDIR/dst 172 typeset -i ret=$? 173 174 rm -f $BACKDIR/src $BACKDIR/dst $BACKDIR/src1 $BACKDIR/dst1 175 176 return $ret 177} 178 179# 180# Compare all the directores and files in two filesystems 181# 182# $1 source filesystem 183# $2 destination filesystem 184# 185function cmp_ds_cont 186{ 187 typeset src_fs=$1 188 typeset dst_fs=$2 189 190 typeset srcdir dstdir 191 srcdir=$(get_prop mountpoint $src_fs) 192 dstdir=$(get_prop mountpoint $dst_fs) 193 194 diff -r $srcdir $dstdir > /dev/null 2>&1 195 return $? 196} 197 198# 199# Compare the given two dataset properties 200# 201# $1 dataset 1 202# $2 dataset 2 203# 204function cmp_ds_prop 205{ 206 typeset dtst1=$1 207 typeset dtst2=$2 208 209 for item in "type" "origin" "volblocksize" "aclinherit" "aclmode" \ 210 "atime" "canmount" "checksum" "compression" "copies" "devices" \ 211 "exec" "quota" "readonly" "recordsize" "reservation" "setuid" \ 212 "sharenfs" "snapdir" "version" "volsize" "xattr" "zoned" \ 213 "mountpoint"; 214 do 215 zfs get -H -o property,value,source $item $dtst1 >> \ 216 $BACKDIR/dtst1 217 zfs get -H -o property,value,source $item $dtst2 >> \ 218 $BACKDIR/dtst2 219 done 220 221 eval sed -e 's:$dtst1:PREFIX:g' < $BACKDIR/dtst1 > $BACKDIR/dtst1 222 eval sed -e 's:$dtst2:PREFIX:g' < $BACKDIR/dtst2 > $BACKDIR/dtst2 223 224 diff $BACKDIR/dtst1 $BACKDIR/dtst2 225 typeset -i ret=$? 226 227 rm -f $BACKDIR/dtst1 $BACKDIR/dtst2 228 229 return $ret 230 231} 232 233# 234# Random create directories and files 235# 236# $1 directory 237# 238function random_tree 239{ 240 typeset dir=$1 241 242 if [[ -d $dir ]]; then 243 rm -rf $dir 244 fi 245 mkdir -p $dir 246 typeset -i ret=$? 247 248 typeset -i nl nd nf 249 ((nl = RANDOM % 6 + 1)) 250 ((nd = RANDOM % 3 )) 251 ((nf = RANDOM % 5 )) 252 mktree -b $dir -l $nl -d $nd -f $nf 253 ((ret |= $?)) 254 255 return $ret 256} 257 258# 259# Put data in filesystem and take snapshot 260# 261# $1 snapshot name 262# 263function snapshot_tree 264{ 265 typeset snap=$1 266 typeset ds=${snap%%@*} 267 typeset type=$(get_prop "type" $ds) 268 269 typeset -i ret=0 270 if [[ $type == "filesystem" ]]; then 271 typeset mntpnt=$(get_prop mountpoint $ds) 272 ((ret |= $?)) 273 274 if ((ret == 0)) ; then 275 eval random_tree $mntpnt/${snap##$ds} 276 ((ret |= $?)) 277 fi 278 fi 279 280 if ((ret == 0)) ; then 281 zfs snapshot $snap 282 ((ret |= $?)) 283 fi 284 285 return $ret 286} 287 288# 289# Destroy the given snapshot and stuff 290# 291# $1 snapshot 292# 293function destroy_tree 294{ 295 typeset -i ret=0 296 typeset snap 297 for snap in "$@" ; do 298 zfs destroy $snap 299 ret=$? 300 301 typeset ds=${snap%%@*} 302 typeset type=$(get_prop "type" $ds) 303 if [[ $type == "filesystem" ]]; then 304 typeset mntpnt=$(get_prop mountpoint $ds) 305 ((ret |= $?)) 306 307 if ((ret != 0)); then 308 rm -r $mntpnt/$snap 309 ((ret |= $?)) 310 fi 311 fi 312 313 if ((ret != 0)); then 314 return $ret 315 fi 316 done 317 318 return 0 319} 320 321# 322# Get all the sub-datasets of give dataset with specific suffix 323# 324# $1 Given dataset 325# $2 Suffix 326# 327function getds_with_suffix 328{ 329 typeset ds=$1 330 typeset suffix=$2 331 332 typeset list=$(zfs list -r -H -t all -o name $ds | grep "$suffix$") 333 334 echo $list 335} 336 337# 338# Output inherited properties whitch is edited for file system 339# 340function fs_inherit_prop 341{ 342 typeset fs_prop 343 if is_global_zone ; then 344 fs_prop=$(zfs inherit 2>&1 | \ 345 awk '$2=="YES" && $3=="YES" {print $1}') 346 if ! is_te_enabled ; then 347 fs_prop=$(echo $fs_prop | grep -v "mlslabel") 348 fi 349 else 350 fs_prop=$(zfs inherit 2>&1 | \ 351 awk '$2=="YES" && $3=="YES" {print $1}'| 352 egrep -v "devices|mlslabel|sharenfs|sharesmb|zoned") 353 fi 354 355 echo $fs_prop 356} 357 358# 359# Output inherited properties for volume 360# 361function vol_inherit_prop 362{ 363 echo "checksum readonly" 364} 365 366# 367# Get the destination dataset to compare 368# 369function get_dst_ds 370{ 371 typeset srcfs=$1 372 typeset dstfs=$2 373 374 # 375 # If the srcfs is not pool 376 # 377 if ! zpool list $srcfs > /dev/null 2>&1 ; then 378 eval dstfs="$dstfs/${srcfs#*/}" 379 fi 380 381 echo $dstfs 382} 383 384# 385# Make test files 386# 387# $1 Number of files to create 388# $2 Maximum file size 389# $3 File ID offset 390# $4 File system to create the files on 391# 392function mk_files 393{ 394 nfiles=$1 395 maxsize=$2 396 file_id_offset=$3 397 fs=$4 398 399 for ((i=0; i<$nfiles; i=i+1)); do 400 dd if=/dev/urandom \ 401 of=/$fs/file-$maxsize-$((i+$file_id_offset)) \ 402 bs=$(($RANDOM * $RANDOM % $maxsize)) \ 403 count=1 >/dev/null 2>&1 || log_fail \ 404 "Failed to create /$fs/file-$maxsize-$((i+$file_id_offset))" 405 done 406 echo Created $nfiles files of random sizes up to $maxsize bytes 407} 408 409# 410# Remove test files 411# 412# $1 Number of files to remove 413# $2 Maximum file size 414# $3 File ID offset 415# $4 File system to remove the files from 416# 417function rm_files 418{ 419 nfiles=$1 420 maxsize=$2 421 file_id_offset=$3 422 fs=$4 423 424 for ((i=0; i<$nfiles; i=i+1)); do 425 rm -f /$fs/file-$maxsize-$((i+$file_id_offset)) 426 done 427 echo Removed $nfiles files of random sizes up to $maxsize bytes 428} 429 430# 431# Mess up file contents 432# 433# $1 The file path 434# 435function mess_file 436{ 437 file=$1 438 439 filesize=$(stat -c '%s' $file) 440 offset=$(($RANDOM * $RANDOM % $filesize)) 441 if (($RANDOM % 7 <= 1)); then 442 # 443 # We corrupt 2 bytes to minimize the chance that we 444 # write the same value that's already there. 445 # 446 log_must eval "dd if=/dev/random of=$file conv=notrunc " \ 447 "bs=1 count=2 oseek=$offset >/dev/null 2>&1" 448 else 449 log_must truncate -s $offset $file 450 fi 451} 452 453# 454# Diff the send/receive filesystems 455# 456# $1 The sent filesystem 457# $2 The received filesystem 458# 459function file_check 460{ 461 sendfs=$1 462 recvfs=$2 463 464 if [[ -d /$recvfs/.zfs/snapshot/a && -d \ 465 /$sendfs/.zfs/snapshot/a ]]; then 466 diff -r /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a 467 [[ $? -eq 0 ]] || log_fail "Differences found in snap a" 468 fi 469 if [[ -d /$recvfs/.zfs/snapshot/b && -d \ 470 /$sendfs/.zfs/snapshot/b ]]; then 471 diff -r /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b 472 [[ $? -eq 0 ]] || log_fail "Differences found in snap b" 473 fi 474} 475 476# 477# Resume test helper 478# 479# $1 The ZFS send command 480# $2 The filesystem where the streams are sent 481# $3 The receive filesystem 482# 483function resume_test 484{ 485 sendcmd=$1 486 streamfs=$2 487 recvfs=$3 488 489 stream_num=1 490 log_must eval "$sendcmd >/$streamfs/$stream_num" 491 492 for ((i=0; i<2; i=i+1)); do 493 mess_file /$streamfs/$stream_num 494 log_mustnot zfs recv -suv $recvfs </$streamfs/$stream_num 495 stream_num=$((stream_num+1)) 496 497 token=$(zfs get -Hp -o value receive_resume_token $recvfs) 498 log_must eval "zfs send -v -t $token >/$streamfs/$stream_num" 499 [[ -f /$streamfs/$stream_num ]] || \ 500 log_fail "NO FILE /$streamfs/$stream_num" 501 done 502 log_must zfs recv -suv $recvfs </$streamfs/$stream_num 503} 504 505# 506# Setup filesystems for the resumable send/receive tests 507# 508# $1 The "send" filesystem 509# $2 The "recv" filesystem 510# 511function test_fs_setup 512{ 513 typeset sendfs=$1 514 typeset recvfs=$2 515 typeset sendpool=${sendfs%%/*} 516 typeset recvpool=${recvfs%%/*} 517 518 datasetexists $sendfs && log_must zfs destroy -r $sendpool 519 datasetexists $recvfs && log_must zfs destroy -r $recvpool 520 521 if $(datasetexists $sendfs || zfs create -o compress=lz4 $sendfs); then 522 mk_files 1000 256 0 $sendfs & 523 mk_files 1000 131072 0 $sendfs & 524 mk_files 100 1048576 0 $sendfs & 525 mk_files 10 10485760 0 $sendfs & 526 mk_files 1 104857600 0 $sendfs & 527 log_must wait 528 log_must zfs snapshot $sendfs@a 529 530 rm_files 200 256 0 $sendfs & 531 rm_files 200 131072 0 $sendfs & 532 rm_files 20 1048576 0 $sendfs & 533 rm_files 2 10485760 0 $sendfs & 534 log_must wait 535 536 mk_files 400 256 0 $sendfs & 537 mk_files 400 131072 0 $sendfs & 538 mk_files 40 1048576 0 $sendfs & 539 mk_files 4 10485760 0 $sendfs & 540 log_must wait 541 542 log_must zfs snapshot $sendfs@b 543 log_must eval "zfs send -v $sendfs@a >/$sendpool/initial.zsend" 544 log_must eval "zfs send -v -i @a $sendfs@b " \ 545 ">/$sendpool/incremental.zsend" 546 fi 547 548 if datasetexists $streamfs; then 549 log_must zfs destroy -r $streamfs 550 fi 551 log_must zfs create -o compress=lz4 $sendpool/stream 552} 553 554# 555# Check to see if the specified features are set in a send stream. 556# The values for these features are found in uts/common/fs/zfs/sys/zfs_ioctl.h 557# 558# $1 The stream file 559# $2-$n The flags expected in the stream 560# 561function stream_has_features 562{ 563 typeset file=$1 564 shift 565 566 [[ -f $file ]] || log_fail "Couldn't find file: $file" 567 typeset flags=$(cat $file | zstreamdump | awk '/features =/ {print $3}') 568 typeset -A feature 569 feature[dedup]="1" 570 feature[dedupprops]="2" 571 feature[sa_spill]="4" 572 feature[embed_data]="10000" 573 feature[lz4]="20000" 574 feature[mooch_byteswap]="40000" 575 feature[large_blocks]="80000" 576 feature[resuming]="100000" 577 feature[redacted]="200000" 578 feature[compressed]="400000" 579 580 typeset flag known derived=0 581 for flag in "$@"; do 582 known=${feature[$flag]} 583 [[ -z $known ]] && log_fail "Unknown feature: $flag" 584 585 derived=$(echo "$flags & ${feature[$flag]} = X" | mdb | sed 's/ //g') 586 [[ $derived = $known ]] || return 1 587 done 588 589 return 0 590} 591 592# 593# Parse zstreamdump -v output. The output varies for each kind of record: 594# BEGIN records are simply output as "BEGIN" 595# END records are output as "END" 596# OBJECT records become "OBJECT <object num>" 597# FREEOBJECTS records become "FREEOBJECTS <startobj> <numobjs>" 598# FREE records become "<record type> <start> <length>" 599# WRITE records become: 600# "<record type> <compression type> <start> <logical size> <compressed size> 601# <data size>" 602# 603function parse_dump 604{ 605 sed '/^WRITE/{N;s/\n/ /;}' | grep "^[A-Z]" | awk '{ 606 if ($1 == "BEGIN" || $1 == "END") print $1 607 if ($1 == "OBJECT") print $1" "$4 608 if ($1 == "FREEOBJECTS") print $1" "$4" "$7 609 if ($1 == "FREE") print $1" "$7" "$10 610 if ($1 == "WRITE") print $1" "$15" "$18" "$21" "$24" "$27}' 611} 612 613# 614# Given a send stream, verify that the size of the stream matches what's 615# expected based on the source or target dataset. If the stream is an 616# incremental stream, subtract the size of the source snapshot before 617# comparing. This function does not currently handle incremental streams 618# that remove data. 619# 620# $1 The zstreamdump output file 621# $2 The dataset to compare against 622# This can be a source of a send or recv target (fs, not snapshot) 623# $3 The percentage below which verification is deemed a failure 624# $4 The source snapshot of an incremental send 625# 626 627function verify_stream_size 628{ 629 typeset stream=$1 630 typeset ds=$2 631 typeset percent=${3:-90} 632 typeset inc_src=$4 633 634 [[ -f $stream ]] || log_fail "No such file: $stream" 635 datasetexists $ds || log_fail "No such dataset: $ds" 636 637 typeset stream_size=$(cat $stream | zstreamdump | sed -n \ 638 's/ Total write size = \(.*\) (0x.*)/\1/p') 639 640 typeset inc_size=0 641 if [[ -n $inc_src ]]; then 642 inc_size=$(get_prop lrefer $inc_src) 643 if stream_has_features $stream compressed; then 644 inc_size=$(get_prop refer $inc_src) 645 fi 646 fi 647 648 if stream_has_features $stream compressed; then 649 ds_size=$(get_prop refer $ds) 650 else 651 ds_size=$(get_prop lrefer $ds) 652 fi 653 ds_size=$((ds_size - inc_size)) 654 655 within_percent $stream_size $ds_size $percent || log_fail \ 656 "$stream_size $ds_size differed by too much" 657} 658 659# Cleanup function for tests involving resumable send 660function resume_cleanup 661{ 662 typeset sendfs=$1 663 typeset streamfs=$2 664 665 datasetexists $sendfs && log_must zfs destroy -r $sendfs 666 datasetexists $streamfs && log_must zfs destroy -r $streamfs 667 cleanup_pool $POOL2 668 rm -f /$POOL/initial.zsend /$POOL/incremental.zsend 669} 670