1 # vim: filetype=sh 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 http://www.opensolaris.org/os/licensing. 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 2009 Sun Microsystems, Inc. All rights reserved. 25 # Use is subject to license terms. 26 27 . ${STF_SUITE}/include/logapi.kshlib 28 29 ZFS=${ZFS:-/sbin/zfs} 30 ZPOOL=${ZPOOL:-/sbin/zpool} 31 os_name=`uname -s` 32 33 # Determine if a test has the necessary requirements to run 34 35 function test_requires 36 { 37 integer unsupported=0 38 unsupported_list="" 39 until [[ $# -eq 0 ]];do 40 var_name=$1 41 cmd=$(eval echo \$${1}) 42 if [[ ! "$cmd" != "" ]] ; then 43 print $var_name is not set 44 unsupported_list="$var_name $unsupported_list" 45 ((unsupported=unsupported+1)) 46 fi 47 shift 48 done 49 if [[ unsupported -gt 0 ]] ; then 50 log_unsupported "$unsupported_list commands are unsupported" 51 else 52 log_note "All commands are supported" 53 fi 54 } 55 56 # Determine whether a dataset is mounted 57 # 58 # $1 dataset name 59 # $2 filesystem type; optional - defaulted to zfs 60 # 61 # Return 0 if dataset is mounted; 1 if unmounted; 2 on error 62 63 function ismounted 64 { 65 typeset fstype=$2 66 [[ -z $fstype ]] && fstype=zfs 67 typeset out dir name ret 68 69 case $fstype in 70 zfs) 71 if [[ "$1" == "/"* ]] ; then 72 for out in $($ZFS mount | $AWK '{print $2}') ; do 73 [[ $1 == $out ]] && return 0 74 done 75 else 76 for out in $($ZFS mount | $AWK '{print $1}') ; do 77 [[ $1 == $out ]] && return 0 78 done 79 fi 80 ;; 81 ufs|nfs) 82 # a = device, b = "on", c = mount point", d = flags 83 $MOUNT | $GREP $fstype | while read a b c d 84 do 85 [[ "$1" == "$a" || "$1" == "$c" ]] && return 0 86 done 87 ;; 88 esac 89 90 return 1 91 } 92 93 # Return 0 if a dataset is mounted; 1 otherwise 94 # 95 # $1 dataset name 96 # $2 filesystem type; optional - defaulted to zfs 97 98 function mounted 99 { 100 ismounted $1 $2 101 (( $? == 0 )) && return 0 102 return 1 103 } 104 105 # Return 0 if a dataset is unmounted; 1 otherwise 106 # 107 # $1 dataset name 108 # $2 filesystem type; optional - defaulted to zfs 109 110 function unmounted 111 { 112 ismounted $1 $2 113 (( $? == 1 )) && return 0 114 return 1 115 } 116 117 # split line on "," 118 # 119 # $1 - line to split 120 121 function splitline 122 { 123 $ECHO $1 | $SED "s/,/ /g" 124 } 125 126 function default_setup 127 { 128 default_setup_noexit "$@" 129 130 log_pass 131 } 132 133 # 134 # Given a list of disks, setup storage pools and datasets. 135 # 136 function default_setup_noexit 137 { 138 typeset disklist=$1 139 typeset container=$2 140 typeset volume=$3 141 142 if is_global_zone; then 143 if poolexists $TESTPOOL ; then 144 destroy_pool $TESTPOOL 145 fi 146 [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL 147 log_must $ZPOOL create -f $TESTPOOL $disklist 148 else 149 reexport_pool 150 fi 151 152 $RM -rf $TESTDIR || log_unresolved Could not remove $TESTDIR 153 $MKDIR -p $TESTDIR || log_unresolved Could not create $TESTDIR 154 155 log_must $ZFS create $TESTPOOL/$TESTFS 156 log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 157 158 if [[ -n $container ]]; then 159 $RM -rf $TESTDIR1 || \ 160 log_unresolved Could not remove $TESTDIR1 161 $MKDIR -p $TESTDIR1 || \ 162 log_unresolved Could not create $TESTDIR1 163 164 log_must $ZFS create $TESTPOOL/$TESTCTR 165 log_must $ZFS set canmount=off $TESTPOOL/$TESTCTR 166 log_must $ZFS create $TESTPOOL/$TESTCTR/$TESTFS1 167 log_must $ZFS set mountpoint=$TESTDIR1 \ 168 $TESTPOOL/$TESTCTR/$TESTFS1 169 fi 170 171 if [[ -n $volume ]]; then 172 if is_global_zone ; then 173 log_must $ZFS create -V $VOLSIZE $TESTPOOL/$TESTVOL 174 else 175 log_must $ZFS create $TESTPOOL/$TESTVOL 176 fi 177 178 fi 179 } 180 181 # 182 # Given a list of disks, setup a storage pool, file system and 183 # a container. 184 # 185 function default_container_setup 186 { 187 typeset disklist=$1 188 189 default_setup "$disklist" "true" 190 } 191 192 # 193 # Given a list of disks, setup a storage pool,file system 194 # and a volume. 195 # 196 function default_volume_setup 197 { 198 typeset disklist=$1 199 200 default_setup "$disklist" "" "true" 201 } 202 203 # 204 # Given a list of disks, setup a storage pool,file system, 205 # a container and a volume. 206 # 207 function default_container_volume_setup 208 { 209 typeset disklist=$1 210 211 default_setup "$disklist" "true" "true" 212 } 213 214 # 215 # Create a snapshot on a filesystem or volume. Defaultly create a snapshot on 216 # filesystem 217 # 218 # $1 Existing filesystem or volume name. Default, $TESTFS 219 # $2 snapshot name. Default, $TESTSNAP 220 # 221 function create_snapshot 222 { 223 typeset fs_vol=${1:-$TESTFS} 224 typeset snap=${2:-$TESTSNAP} 225 226 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 227 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 228 229 if snapexists $fs_vol@$snap; then 230 log_fail "$fs_vol@$snap already exists." 231 fi 232 datasetexists $fs_vol || \ 233 log_fail "$fs_vol must exist." 234 235 log_must $ZFS snapshot $fs_vol@$snap 236 } 237 238 # 239 # Create a clone from a snapshot, default clone name is $TESTCLONE. 240 # 241 # $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default. 242 # $2 Clone name, $TESTPOOL/$TESTCLONE is default. 243 # 244 function create_clone # snapshot clone 245 { 246 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 247 typeset clone=${2:-$TESTPOOL/$TESTCLONE} 248 249 [[ -z $snap ]] && \ 250 log_fail "Snapshot name is undefined." 251 [[ -z $clone ]] && \ 252 log_fail "Clone name is undefined." 253 254 log_must $ZFS clone $snap $clone 255 } 256 257 function default_mirror_setup 258 { 259 default_mirror_setup_noexit $1 $2 $3 260 261 log_pass 262 } 263 264 # 265 # Given a pair of disks, set up a storage pool and dataset for the mirror 266 # @parameters: $1 the primary side of the mirror 267 # $2 the secondary side of the mirror 268 # @uses: ZPOOL ZFS TESTPOOL TESTFS 269 function default_mirror_setup_noexit 270 { 271 readonly func="default_mirror_setup_noexit" 272 typeset primary=$1 273 typeset secondary=$2 274 275 [[ -z $primary ]] && \ 276 log_fail "$func: No parameters passed" 277 [[ -z $secondary ]] && \ 278 log_fail "$func: No secondary partition passed" 279 [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL 280 log_must $ZPOOL create -f $TESTPOOL mirror $@ 281 log_must $ZFS create $TESTPOOL/$TESTFS 282 log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 283 } 284 285 # 286 # create a number of mirrors. 287 # We create a number($1) of 2 way mirrors using the pairs of disks named 288 # on the command line. These mirrors are *not* mounted 289 # @parameters: $1 the number of mirrors to create 290 # $... the devices to use to create the mirrors on 291 # @uses: ZPOOL ZFS TESTPOOL 292 function setup_mirrors 293 { 294 typeset -i nmirrors=$1 295 296 shift 297 while (( nmirrors > 0 )); do 298 log_must test -n "$1" -a -n "$2" 299 [[ -d /$TESTPOOL$nmirrors ]] && $RM -rf /$TESTPOOL$nmirrors 300 log_must $ZPOOL create -f $TESTPOOL$nmirrors mirror $1 $2 301 shift 2 302 (( nmirrors = nmirrors - 1 )) 303 done 304 } 305 306 # 307 # create a number of raidz pools. 308 # We create a number($1) of 2 raidz pools using the pairs of disks named 309 # on the command line. These pools are *not* mounted 310 # @parameters: $1 the number of pools to create 311 # $... the devices to use to create the pools on 312 # @uses: ZPOOL ZFS TESTPOOL 313 function setup_raidzs 314 { 315 typeset -i nraidzs=$1 316 317 shift 318 while (( nraidzs > 0 )); do 319 log_must test -n "$1" -a -n "$2" 320 [[ -d /$TESTPOOL$nraidzs ]] && $RM -rf /$TESTPOOL$nraidzs 321 log_must $ZPOOL create -f $TESTPOOL$nraidzs raidz $1 $2 322 shift 2 323 (( nraidzs = nraidzs - 1 )) 324 done 325 } 326 327 # 328 # Destroy the configured testpool mirrors. 329 # the mirrors are of the form ${TESTPOOL}{number} 330 # @uses: ZPOOL ZFS TESTPOOL 331 function destroy_mirrors 332 { 333 default_cleanup_noexit 334 335 log_pass 336 } 337 338 # 339 # Given a minimum of two disks, set up a storage pool and dataset for the raid-z 340 # $1 the list of disks 341 # 342 function default_raidz_setup 343 { 344 typeset disklist="$*" 345 set -A disks $disklist 346 347 if [[ ${#disks[*]} -lt 2 ]]; then 348 log_fail "A raid-z requires a minimum of two disks." 349 fi 350 351 [[ -d /$TESTPOOL ]] && $RM -rf /$TESTPOOL 352 log_must $ZPOOL create -f $TESTPOOL raidz $1 $2 $3 353 log_must $ZFS create $TESTPOOL/$TESTFS 354 log_must $ZFS set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 355 356 log_pass 357 } 358 359 # 360 # Common function used to cleanup storage pools and datasets. 361 # 362 # Invoked at the start of the test suite to ensure the system 363 # is in a known state, and also at the end of each set of 364 # sub-tests to ensure errors from one set of tests doesn't 365 # impact the execution of the next set. 366 367 function default_cleanup 368 { 369 default_cleanup_noexit 370 371 log_pass 372 } 373 374 function all_pools 375 { 376 cmd="$ZPOOL list -H -o name | $GREP 'testpool'" 377 eval $cmd 378 } 379 380 # 381 # Returns 0 if the system contains any pools that must not be modified by the 382 # ZFS tests. 383 # 384 function other_pools_exist 385 { 386 typeset pool_count=`$ZPOOL list -H | $GREP -v '^testpool' | $WC -l` 387 [ "$pool_count" -ne 0 ] 388 } 389 390 function default_cleanup_noexit 391 { 392 typeset exclude="" 393 typeset pool="" 394 # 395 # Destroying the pool will also destroy any 396 # filesystems it contains. 397 # 398 if is_global_zone; then 399 # Here, we loop through the pools we're allowed to 400 # destroy, only destroying them if it's safe to do 401 # so. 402 for pool in $(all_pools); do 403 if safe_to_destroy_pool $pool; then 404 destroy_pool $pool 405 fi 406 done 407 else 408 typeset fs="" 409 for fs in $($ZFS list -H -o name \ 410 | $GREP "^$ZONE_POOL/$ZONE_CTR[01234]/"); do 411 datasetexists $fs && \ 412 log_must $ZFS destroy -Rf $fs 413 done 414 415 # Need cleanup here to avoid garbage dir left. 416 for fs in $($ZFS list -H -o name \ 417 ); do 418 [[ $fs == /$ZONE_POOL ]] && continue 419 [[ -d $fs ]] && log_must $RM -rf $fs/* 420 done 421 422 # 423 # Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to 424 # the default value 425 # 426 for fs in $($ZFS list -H -o name \ 427 ); do 428 if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then 429 log_must $ZFS set reservation=none $fs 430 log_must $ZFS set recordsize=128K $fs 431 log_must $ZFS set mountpoint=/$fs $fs 432 typeset enc="" 433 enc=$(get_prop encryption $fs) 434 if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \ 435 [[ "$enc" == "off" ]]; then 436 log_must $ZFS set checksum=on $fs 437 fi 438 log_must $ZFS set compression=off $fs 439 log_must $ZFS set atime=on $fs 440 log_must $ZFS set devices=off $fs 441 log_must $ZFS set exec=on $fs 442 log_must $ZFS set setuid=on $fs 443 log_must $ZFS set readonly=off $fs 444 log_must $ZFS set snapdir=hidden $fs 445 log_must $ZFS set aclmode=groupmask $fs 446 log_must $ZFS set aclinherit=secure $fs 447 fi 448 done 449 fi 450 451 [[ -d $TESTDIR ]] && \ 452 log_must $RM -rf $TESTDIR 453 } 454 455 456 # 457 # Common function used to cleanup storage pools, file systems 458 # and containers. 459 # 460 function default_container_cleanup 461 { 462 if ! is_global_zone; then 463 reexport_pool 464 fi 465 466 ismounted $TESTPOOL/$TESTCTR/$TESTFS1 467 [[ $? -eq 0 ]] && \ 468 log_must $ZFS unmount $TESTPOOL/$TESTCTR/$TESTFS1 469 470 datasetexists $TESTPOOL/$TESTCTR/$TESTFS1 && \ 471 log_must $ZFS destroy -R $TESTPOOL/$TESTCTR/$TESTFS1 472 473 datasetexists $TESTPOOL/$TESTCTR && \ 474 log_must $ZFS destroy -Rf $TESTPOOL/$TESTCTR 475 476 [[ -e $TESTDIR1 ]] && \ 477 log_must $RM -rf $TESTDIR1 > /dev/null 2>&1 478 479 default_cleanup 480 } 481 482 # 483 # Common function used to cleanup snapshot of file system or volume. Default to 484 # delete the file system's snapshot 485 # 486 # $1 snapshot name 487 # 488 function destroy_snapshot 489 { 490 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 491 492 if ! snapexists $snap; then 493 log_fail "'$snap' does not existed." 494 fi 495 496 # 497 # For the sake of the value which come from 'get_prop' is not equal 498 # to the really mountpoint when the snapshot is unmounted. So, firstly 499 # check and make sure this snapshot's been mounted in current system. 500 # 501 typeset mtpt="" 502 if ismounted $snap; then 503 mtpt=$(get_prop mountpoint $snap) 504 (( $? != 0 )) && \ 505 log_fail "get_prop mountpoint $snap failed." 506 fi 507 508 log_must $ZFS destroy $snap 509 [[ $mtpt != "" && -d $mtpt ]] && \ 510 log_must $RM -rf $mtpt 511 } 512 513 # 514 # Common function used to cleanup clone. 515 # 516 # $1 clone name 517 # 518 function destroy_clone 519 { 520 typeset clone=${1:-$TESTPOOL/$TESTCLONE} 521 522 if ! datasetexists $clone; then 523 log_fail "'$clone' does not existed." 524 fi 525 526 # With the same reason in destroy_snapshot 527 typeset mtpt="" 528 if ismounted $clone; then 529 mtpt=$(get_prop mountpoint $clone) 530 (( $? != 0 )) && \ 531 log_fail "get_prop mountpoint $clone failed." 532 fi 533 534 log_must $ZFS destroy $clone 535 [[ $mtpt != "" && -d $mtpt ]] && \ 536 log_must $RM -rf $mtpt 537 } 538 539 # Return 0 if a snapshot exists; $? otherwise 540 # 541 # $1 - snapshot name 542 543 function snapexists 544 { 545 $ZFS list -H -t snapshot "$1" > /dev/null 2>&1 546 return $? 547 } 548 549 # 550 # Set a property to a certain value on a dataset. 551 # Sets a property of the dataset to the value as passed in. 552 # @param: 553 # $1 dataset who's property is being set 554 # $2 property to set 555 # $3 value to set property to 556 # @return: 557 # 0 if the property could be set. 558 # non-zero otherwise. 559 # @use: ZFS 560 # 561 function dataset_setprop 562 { 563 typeset fn=dataset_setprop 564 565 if (( $# < 3 )); then 566 log_note "$fn: Insufficient parameters (need 3, had $#)" 567 return 1 568 fi 569 typeset output= 570 output=$($ZFS set $2=$3 $1 2>&1) 571 typeset rv=$? 572 if (( rv != 0 )); then 573 log_note "Setting property on $1 failed." 574 log_note "property $2=$3" 575 log_note "Return Code: $rv" 576 log_note "Output: $output" 577 return $rv 578 fi 579 return 0 580 } 581 582 # 583 # Assign suite defined dataset properties. 584 # This function is used to apply the suite's defined default set of 585 # properties to a dataset. 586 # @parameters: $1 dataset to use 587 # @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP 588 # @returns: 589 # 0 if the dataset has been altered. 590 # 1 if no pool name was passed in. 591 # 2 if the dataset could not be found. 592 # 3 if the dataset could not have it's properties set. 593 # 594 function dataset_set_defaultproperties 595 { 596 typeset dataset="$1" 597 598 [[ -z $dataset ]] && return 1 599 600 typeset confset= 601 typeset -i found=0 602 for confset in $($ZFS list); do 603 if [[ $dataset = $confset ]]; then 604 found=1 605 break 606 fi 607 done 608 [[ $found -eq 0 ]] && return 2 609 if [[ -n $COMPRESSION_PROP ]]; then 610 dataset_setprop $dataset compression $COMPRESSION_PROP || \ 611 return 3 612 log_note "Compression set to '$COMPRESSION_PROP' on $dataset" 613 fi 614 if [[ -n $CHECKSUM_PROP && $WRAPPER != *"crypto"* ]]; then 615 dataset_setprop $dataset checksum $CHECKSUM_PROP || \ 616 return 3 617 log_note "Checksum set to '$CHECKSUM_PROP' on $dataset" 618 fi 619 return 0 620 } 621 622 # 623 # Check a numeric assertion 624 # @parameter: $@ the assertion to check 625 # @output: big loud notice if assertion failed 626 # @use: log_fail 627 # 628 function assert 629 { 630 (( $@ )) || log_fail $@ 631 } 632 633 function wipe_partition_table #<whole_disk_name> [<whole_disk_name> ...] 634 { 635 while [[ -n $* ]]; do 636 typeset diskname=$1 637 [ ! -e $diskname ] && log_fail "ERROR: $diskname doesn't exist" 638 if gpart list ${diskname#/dev/} >/dev/null 2>&1; then 639 wait_for 5 1 $GPART destroy -F $diskname 640 else 641 log_note "No GPT partitions detected on $diskname" 642 fi 643 log_must $GPART create -s gpt $diskname 644 shift 645 done 646 } 647 648 # 649 # Given a slice, size and disk, this function 650 # formats the slice to the specified size. 651 # Size should be specified with units as per 652 # the `format` command requirements eg. 100mb 3gb 653 # 654 function set_partition #<slice_num> <slice_start> <size_plus_units> <whole_disk_name> 655 { 656 typeset -i slicenum=$1 657 typeset start=$2 658 typeset size=$3 659 typeset disk=$4 660 set -A devmap a b c d e f g h 661 [[ -z $slicenum || -z $size || -z $disk ]] && \ 662 log_fail "The slice, size or disk name is unspecified." 663 664 size=`$ECHO $size| sed s/mb/M/` 665 size=`$ECHO $size| sed s/m/M/` 666 size=`$ECHO $size| sed s/gb/G/` 667 size=`$ECHO $size| sed s/g/G/` 668 [[ -n $start ]] && start="-b $start" 669 log_must $GPART add -t efi $start -s $size -i $slicenum $disk 670 return 0 671 } 672 673 function get_disk_size #<disk> 674 { 675 typeset disk=$1 676 diskinfo $disk | awk '{print $3}' 677 } 678 679 function get_available_disk_size #<disk> 680 { 681 typeset disk=$1 682 raw_size=`get_disk_size $disk` 683 (( available_size = raw_size * 95 / 100 )) 684 echo $available_size 685 } 686 687 # 688 # Get the end cyl of the given slice 689 # #TODO: fix this to be GPT-compatible if we want to use the SMI WRAPPER. This 690 # function is not necessary on FreeBSD 691 # 692 function get_endslice #<disk> <slice> 693 { 694 log_fail "get_endslice has not been updated for GPT partitions" 695 } 696 697 # 698 # Get the first LBA that is beyond the end of the given partition 699 function get_partition_end #<disk> <partition_index> 700 { 701 typeset disk=$1 702 typeset partition_index=$2 703 export partition_index 704 $GPART show $disk | $AWK \ 705 '/^[ \t]/ && $3 ~ ENVIRON["partition_index"] {print $1 + $2}' 706 } 707 708 709 # 710 # Given a size,disk and total number of partitions, this function formats the 711 # disk partitions from 0 to the total partition number with the same specified 712 # size. 713 # 714 function partition_disk #<part_size> <whole_disk_name> <total_parts> 715 { 716 typeset -i i=1 717 typeset part_size=$1 718 typeset disk_name=$2 719 typeset total_parts=$3 720 typeset cyl 721 722 wipe_partition_table $disk_name 723 while (( i <= $total_parts )); do 724 set_partition $i "" $part_size $disk_name 725 (( i = i+1 )) 726 done 727 } 728 729 function size_of_file # fname 730 { 731 typeset fname=$1 732 sz=`stat -f '%z' $fname` 733 [[ -z "$sz" ]] && log_fail "stat($fname) failed" 734 $ECHO $sz 735 return 0 736 } 737 738 # 739 # This function continues to write to a filenum number of files into dirnum 740 # number of directories until either $FILE_WRITE returns an error or the 741 # maximum number of files per directory have been written. 742 # 743 # Usage: 744 # fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data] 745 # 746 # Return value: 0 on success 747 # non 0 on error 748 # 749 # Where : 750 # destdir: is the directory where everything is to be created under 751 # dirnum: the maximum number of subdirectories to use, -1 no limit 752 # filenum: the maximum number of files per subdirectory 753 # blocksz: number of bytes per block 754 # num_writes: number of blocks to write 755 # data: the data that will be written 756 # 757 # E.g. 758 # file_fs /testdir 20 25 1024 256 0 759 # 760 # Note: blocksz * num_writes equals the size of the testfile 761 # 762 function fill_fs # destdir dirnum filenum blocksz num_writes data 763 { 764 typeset destdir=${1:-$TESTDIR} 765 typeset -i dirnum=${2:-50} 766 typeset -i filenum=${3:-50} 767 typeset -i blocksz=${4:-8192} 768 typeset -i num_writes=${5:-10240} 769 typeset -i data=${6:-0} 770 771 typeset -i retval=0 772 typeset -i dn=0 # current dir number 773 typeset -i fn=0 # current file number 774 while (( retval == 0 )); do 775 (( dirnum >= 0 && dn >= dirnum )) && break 776 typeset curdir=$destdir/$dn 777 log_must $MKDIR -p $curdir 778 for (( fn = 0; $fn < $filenum && $retval == 0; fn++ )); do 779 log_cmd $FILE_WRITE -o create -f $curdir/$TESTFILE.$fn \ 780 -b $blocksz -c $num_writes -d $data 781 retval=$? 782 done 783 (( dn = dn + 1 )) 784 done 785 return $retval 786 } 787 788 # 789 # Simple function to get the specified property. If unable to 790 # get the property then exits. 791 # 792 # Note property is in 'parsable' format (-p) 793 # 794 function get_prop # property dataset 795 { 796 typeset prop_val 797 typeset prop=$1 798 typeset dataset=$2 799 800 prop_val=$($ZFS get -pH -o value $prop $dataset 2>/dev/null) 801 if [[ $? -ne 0 ]]; then 802 log_note "Unable to get $prop property for dataset $dataset" 803 return 1 804 fi 805 806 $ECHO $prop_val 807 return 0 808 } 809 810 # 811 # Simple function to return the lesser of two values. 812 # 813 function min 814 { 815 typeset first_arg=$1 816 typeset second_arg=$2 817 818 if (( first_arg < second_arg )); then 819 $ECHO $first_arg 820 else 821 $ECHO $second_arg 822 fi 823 return 0 824 } 825 826 # 827 # Simple function to get the specified property of pool. If unable to 828 # get the property then exits. 829 # 830 function get_pool_prop # property pool 831 { 832 typeset prop_val 833 typeset prop=$1 834 typeset pool=$2 835 836 if poolexists $pool ; then 837 prop_val=$($ZPOOL get $prop $pool 2>/dev/null | $TAIL -1 | \ 838 $AWK '{print $3}') 839 if [[ $? -ne 0 ]]; then 840 log_note "Unable to get $prop property for pool " \ 841 "$pool" 842 return 1 843 fi 844 else 845 log_note "Pool $pool not exists." 846 return 1 847 fi 848 849 $ECHO $prop_val 850 return 0 851 } 852 853 # Return 0 if a pool exists; $? otherwise 854 # 855 # $1 - pool name 856 857 function poolexists 858 { 859 typeset pool=$1 860 861 if [[ -z $pool ]]; then 862 log_note "No pool name given." 863 return 1 864 fi 865 866 $ZPOOL list -H "$pool" > /dev/null 2>&1 867 return $? 868 } 869 870 # Return 0 if all the specified datasets exist; $? otherwise 871 # 872 # $1-n dataset name 873 function datasetexists 874 { 875 if (( $# == 0 )); then 876 log_note "No dataset name given." 877 return 1 878 fi 879 880 while (( $# > 0 )); do 881 $ZFS list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 || \ 882 return $? 883 shift 884 done 885 886 return 0 887 } 888 889 # return 0 if none of the specified datasets exists, otherwise return 1. 890 # 891 # $1-n dataset name 892 function datasetnonexists 893 { 894 if (( $# == 0 )); then 895 log_note "No dataset name given." 896 return 1 897 fi 898 899 while (( $# > 0 )); do 900 $ZFS list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 && \ 901 return 1 902 shift 903 done 904 905 return 0 906 } 907 908 # 909 # Given a mountpoint, or a dataset name, determine if it is shared. 910 # 911 # Returns 0 if shared, 1 otherwise. 912 # 913 function is_shared 914 { 915 typeset fs=$1 916 typeset mtpt 917 918 if [[ $fs != "/"* ]] ; then 919 if datasetnonexists "$fs" ; then 920 return 1 921 else 922 mtpt=$(get_prop mountpoint "$fs") 923 case $mtpt in 924 none|legacy|-) return 1 925 ;; 926 *) fs=$mtpt 927 ;; 928 esac 929 fi 930 fi 931 932 for mtpt in `$SHARE | $AWK '{print $2}'` ; do 933 if [[ $mtpt == $fs ]] ; then 934 return 0 935 fi 936 done 937 938 typeset stat=$($SVCS -H -o STA nfs/server:default) 939 if [[ $stat != "ON" ]]; then 940 log_note "Current nfs/server status: $stat" 941 fi 942 943 return 1 944 } 945 946 # 947 # Given a mountpoint, determine if it is not shared. 948 # 949 # Returns 0 if not shared, 1 otherwise. 950 # 951 function not_shared 952 { 953 typeset fs=$1 954 955 is_shared $fs 956 if (( $? == 0)); then 957 return 1 958 fi 959 960 return 0 961 } 962 963 # 964 # Helper function to unshare a mountpoint. 965 # 966 function unshare_fs #fs 967 { 968 typeset fs=$1 969 970 is_shared $fs 971 if (( $? == 0 )); then 972 log_must $ZFS unshare $fs 973 fi 974 975 return 0 976 } 977 978 # 979 # Check NFS server status and trigger it online. 980 # 981 function setup_nfs_server 982 { 983 # Cannot share directory in non-global zone. 984 # 985 if ! is_global_zone; then 986 log_note "Cannot trigger NFS server by sharing in LZ." 987 return 988 fi 989 990 typeset nfs_fmri="svc:/network/nfs/server:default" 991 if [[ $($SVCS -Ho STA $nfs_fmri) != "ON" ]]; then 992 # 993 # Only really sharing operation can enable NFS server 994 # to online permanently. 995 # 996 typeset dummy=$TMPDIR/dummy 997 998 if [[ -d $dummy ]]; then 999 log_must $RM -rf $dummy 1000 fi 1001 1002 log_must $MKDIR $dummy 1003 log_must $SHARE $dummy 1004 1005 # 1006 # Waiting for fmri's status to be the final status. 1007 # Otherwise, in transition, an asterisk (*) is appended for 1008 # instances, unshare will reverse status to 'DIS' again. 1009 # 1010 # Waiting for 1's at least. 1011 # 1012 log_must $SLEEP 1 1013 timeout=10 1014 while [[ timeout -ne 0 && $($SVCS -Ho STA $nfs_fmri) == *'*' ]] 1015 do 1016 log_must $SLEEP 1 1017 1018 (( timeout -= 1 )) 1019 done 1020 1021 log_must $UNSHARE $dummy 1022 log_must $RM -rf $dummy 1023 fi 1024 1025 log_note "Current NFS status: '$($SVCS -Ho STA,FMRI $nfs_fmri)'" 1026 } 1027 1028 # 1029 # To verify whether calling process is in global zone 1030 # 1031 # Return 0 if in global zone, 1 in non-global zone 1032 # 1033 function is_global_zone 1034 { 1035 typeset cur_zone=$($ZONENAME 2>/dev/null) 1036 1037 # Zones are not supported on FreeBSD. 1038 if [[ $os_name == "FreeBSD" ]]; then 1039 return 0 1040 fi 1041 1042 if [[ $cur_zone != "global" ]]; then 1043 return 1 1044 fi 1045 return 0 1046 } 1047 1048 # 1049 # Verify whether test is permit to run from 1050 # global zone, local zone, or both 1051 # 1052 # $1 zone limit, could be "global", "local", or "both"(no limit) 1053 # 1054 # Return 0 if permit, otherwise exit with log_unsupported 1055 # 1056 function verify_runnable # zone limit 1057 { 1058 typeset limit=$1 1059 1060 [[ -z $limit ]] && return 0 1061 1062 if is_global_zone ; then 1063 case $limit in 1064 global|both) 1065 break 1066 ;; 1067 local) log_unsupported "Test is unable to run from \ 1068 global zone." 1069 break 1070 ;; 1071 *) log_note "Warning: unknown limit $limit - use both." 1072 ;; 1073 esac 1074 else 1075 case $limit in 1076 local|both) 1077 break 1078 ;; 1079 global) log_unsupported "Test is unable to run from \ 1080 local zone." 1081 break 1082 ;; 1083 *) log_note "Warning: unknown limit $limit - use both." 1084 ;; 1085 esac 1086 1087 reexport_pool 1088 fi 1089 1090 return 0 1091 } 1092 1093 # Return 0 if create successfully or the pool exists; $? otherwise 1094 # Note: In local zones, this function should return 0 silently. 1095 # 1096 # $1 - pool name 1097 # $2-n - [keyword] devs_list 1098 1099 function create_pool #pool devs_list 1100 { 1101 typeset pool=${1%%/*} 1102 1103 shift 1104 1105 if [[ -z $pool ]]; then 1106 log_note "Missing pool name." 1107 return 1 1108 fi 1109 1110 if poolexists $pool ; then 1111 destroy_pool $pool 1112 fi 1113 1114 if is_global_zone ; then 1115 [[ -d /$pool ]] && $RM -rf /$pool 1116 log_must $ZPOOL create -f $pool $@ 1117 fi 1118 1119 return 0 1120 } 1121 1122 # Return 0 if destroy successfully or the pool exists; $? otherwise 1123 # Note: In local zones, this function should return 0 silently. 1124 # 1125 # $1 - pool name 1126 # Destroy pool with the given parameters. 1127 1128 function destroy_pool #pool 1129 { 1130 typeset pool=${1%%/*} 1131 typeset mtpt 1132 1133 if [[ -z $pool ]]; then 1134 log_note "No pool name given." 1135 return 1 1136 fi 1137 1138 if is_global_zone ; then 1139 if poolexists "$pool" ; then 1140 mtpt=$(get_prop mountpoint "$pool") 1141 log_must $ZPOOL destroy -f $pool 1142 1143 [[ -d $mtpt ]] && \ 1144 log_must $RM -rf $mtpt 1145 else 1146 log_note "Pool $pool does not exist, skipping destroy." 1147 return 1 1148 fi 1149 fi 1150 1151 return 0 1152 } 1153 1154 # 1155 # Create file vdevs. 1156 # By default this generates sparse vdevs 10GB in size, for performance. 1157 # 1158 function create_vdevs # vdevs 1159 { 1160 typeset vdsize=10G 1161 1162 [ -n "$VDEV_SIZE" ] && vdsize=$VDEV_SIZE 1163 rm -f $@ || return 1 1164 truncate -s $vdsize $@ 1165 } 1166 1167 # 1168 # Firstly, create a pool with 5 datasets. Then, create a single zone and 1169 # export the 5 datasets to it. In addition, we also add a ZFS filesystem 1170 # and a zvol device to the zone. 1171 # 1172 # $1 zone name 1173 # $2 zone root directory prefix 1174 # $3 zone ip 1175 # 1176 function zfs_zones_setup #zone_name zone_root zone_ip 1177 { 1178 typeset zone_name=${1:-$(hostname)-z} 1179 typeset zone_root=${2:-"/zone_root"} 1180 typeset zone_ip=${3:-"10.1.1.10"} 1181 typeset prefix_ctr=$ZONE_CTR 1182 typeset pool_name=$ZONE_POOL 1183 typeset -i cntctr=5 1184 typeset -i i=0 1185 1186 # Create pool and 5 container within it 1187 # 1188 [[ -d /$pool_name ]] && $RM -rf /$pool_name 1189 log_must $ZPOOL create -f $pool_name $DISKS 1190 while (( i < cntctr )); do 1191 log_must $ZFS create $pool_name/$prefix_ctr$i 1192 (( i += 1 )) 1193 done 1194 1195 # create a zvol 1196 log_must $ZFS create -V 1g $pool_name/zone_zvol 1197 1198 # 1199 # If current system support slog, add slog device for pool 1200 # 1201 typeset sdevs="$TMPDIR/sdev1 $TMPDIR/sdev2" 1202 log_must create_vdevs $sdevs 1203 log_must $ZPOOL add $pool_name log mirror $sdevs 1204 1205 # this isn't supported just yet. 1206 # Create a filesystem. In order to add this to 1207 # the zone, it must have it's mountpoint set to 'legacy' 1208 # log_must $ZFS create $pool_name/zfs_filesystem 1209 # log_must $ZFS set mountpoint=legacy $pool_name/zfs_filesystem 1210 1211 [[ -d $zone_root ]] && \ 1212 log_must $RM -rf $zone_root/$zone_name 1213 [[ ! -d $zone_root ]] && \ 1214 log_must $MKDIR -p -m 0700 $zone_root/$zone_name 1215 1216 # Create zone configure file and configure the zone 1217 # 1218 typeset zone_conf=$TMPDIR/zone_conf.${TESTCASE_ID} 1219 $ECHO "create" > $zone_conf 1220 $ECHO "set zonepath=$zone_root/$zone_name" >> $zone_conf 1221 $ECHO "set autoboot=true" >> $zone_conf 1222 i=0 1223 while (( i < cntctr )); do 1224 $ECHO "add dataset" >> $zone_conf 1225 $ECHO "set name=$pool_name/$prefix_ctr$i" >> \ 1226 $zone_conf 1227 $ECHO "end" >> $zone_conf 1228 (( i += 1 )) 1229 done 1230 1231 # add our zvol to the zone 1232 $ECHO "add device" >> $zone_conf 1233 $ECHO "set match=/dev/zvol/$pool_name/zone_zvol" >> $zone_conf 1234 $ECHO "end" >> $zone_conf 1235 1236 # add a corresponding zvol to the zone 1237 $ECHO "add device" >> $zone_conf 1238 $ECHO "set match=/dev/zvol/$pool_name/zone_zvol" >> $zone_conf 1239 $ECHO "end" >> $zone_conf 1240 1241 # once it's supported, we'll add our filesystem to the zone 1242 # $ECHO "add fs" >> $zone_conf 1243 # $ECHO "set type=zfs" >> $zone_conf 1244 # $ECHO "set special=$pool_name/zfs_filesystem" >> $zone_conf 1245 # $ECHO "set dir=/export/zfs_filesystem" >> $zone_conf 1246 # $ECHO "end" >> $zone_conf 1247 1248 $ECHO "verify" >> $zone_conf 1249 $ECHO "commit" >> $zone_conf 1250 log_must $ZONECFG -z $zone_name -f $zone_conf 1251 log_must $RM -f $zone_conf 1252 1253 # Install the zone 1254 $ZONEADM -z $zone_name install 1255 if (( $? == 0 )); then 1256 log_note "SUCCESS: $ZONEADM -z $zone_name install" 1257 else 1258 log_fail "FAIL: $ZONEADM -z $zone_name install" 1259 fi 1260 1261 # Install sysidcfg file 1262 # 1263 typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg 1264 $ECHO "system_locale=C" > $sysidcfg 1265 $ECHO "terminal=dtterm" >> $sysidcfg 1266 $ECHO "network_interface=primary {" >> $sysidcfg 1267 $ECHO "hostname=$zone_name" >> $sysidcfg 1268 $ECHO "}" >> $sysidcfg 1269 $ECHO "name_service=NONE" >> $sysidcfg 1270 $ECHO "root_password=mo791xfZ/SFiw" >> $sysidcfg 1271 $ECHO "security_policy=NONE" >> $sysidcfg 1272 $ECHO "timezone=US/Eastern" >> $sysidcfg 1273 1274 # Boot this zone 1275 log_must $ZONEADM -z $zone_name boot 1276 } 1277 1278 # 1279 # Reexport TESTPOOL & TESTPOOL(1-4) 1280 # 1281 function reexport_pool 1282 { 1283 typeset -i cntctr=5 1284 typeset -i i=0 1285 1286 while (( i < cntctr )); do 1287 if (( i == 0 )); then 1288 TESTPOOL=$ZONE_POOL/$ZONE_CTR$i 1289 if ! ismounted $TESTPOOL; then 1290 log_must $ZFS mount $TESTPOOL 1291 fi 1292 else 1293 eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i 1294 if eval ! ismounted \$TESTPOOL$i; then 1295 log_must eval $ZFS mount \$TESTPOOL$i 1296 fi 1297 fi 1298 (( i += 1 )) 1299 done 1300 } 1301 1302 # 1303 # Wait for something to return true, checked by the caller. 1304 # 1305 function wait_for_checked # timeout dt <method> [args...] 1306 { 1307 typeset timeout=$1 1308 typeset dt=$2 1309 shift; shift 1310 typeset -i start=$(date '+%s') 1311 typeset -i endtime 1312 1313 log_note "Waiting $timeout seconds (checked every $dt seconds) for: $*" 1314 ((endtime = start + timeout)) 1315 while :; do 1316 $* 1317 [ $? -eq 0 ] && return 1318 curtime=$(date '+%s') 1319 [ $curtime -gt $endtime ] && return 1 1320 sleep $dt 1321 done 1322 return 0 1323 } 1324 1325 # 1326 # Wait for something to return true. 1327 # 1328 function wait_for # timeout dt <method> [args...] 1329 { 1330 typeset timeout=$1 1331 typeset dt=$2 1332 shift; shift 1333 1334 wait_for_checked $timeout $dt $* || \ 1335 log_fail "ERROR: Timed out waiting for: $*" 1336 } 1337 1338 # 1339 # Verify a given disk is online or offline 1340 # 1341 # Return 0 is pool/disk matches expected state, 1 otherwise 1342 # stateexpr is a regex like ONLINE or REMOVED|UNAVAIL 1343 # 1344 function check_state # pool disk stateexpr 1345 { 1346 typeset pool=$1 1347 typeset disk=${2#/dev/} 1348 disk=${disk#/dev/} 1349 disk=${disk#/dev/} 1350 typeset stateexpr=$3 1351 1352 $ZPOOL status -v $pool | grep "$disk" \ 1353 | egrep -i "$stateexpr" > /dev/null 2>&1 1354 1355 return $? 1356 } 1357 1358 # 1359 # Wait for a given disk to leave a state 1360 # 1361 function wait_for_state_exit 1362 { 1363 typeset pool=$1 1364 typeset disk=$2 1365 typeset state=$3 1366 1367 while check_state "$pool" "$disk" "$state"; do 1368 $SLEEP 1 1369 done 1370 } 1371 1372 # 1373 # Wait for a given disk to enter a state 1374 # 1375 function wait_for_state_enter 1376 { 1377 typeset -i timeout=$1 1378 typeset pool=$2 1379 typeset disk=$3 1380 typeset state=$4 1381 1382 log_note "Waiting up to $timeout seconds for $disk to become $state ..." 1383 for ((; $timeout > 0; timeout=$timeout-1)); do 1384 check_state $pool "$disk" "$state" 1385 [ $? -eq 0 ] && return 1386 $SLEEP 1 1387 done 1388 log_must $ZPOOL status $pool 1389 log_fail "ERROR: Disk $disk not marked as $state in $pool" 1390 } 1391 1392 # 1393 # Get the mountpoint of snapshot 1394 # as its mountpoint 1395 # 1396 function snapshot_mountpoint 1397 { 1398 typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 1399 1400 if [[ $dataset != *@* ]]; then 1401 log_fail "Error name of snapshot '$dataset'." 1402 fi 1403 1404 typeset fs=${dataset%@*} 1405 typeset snap=${dataset#*@} 1406 1407 if [[ -z $fs || -z $snap ]]; then 1408 log_fail "Error name of snapshot '$dataset'." 1409 fi 1410 1411 $ECHO $(get_prop mountpoint $fs)/$(get_snapdir_name)/$snap 1412 } 1413 1414 function pool_maps_intact # pool 1415 { 1416 typeset pool="$1" 1417 1418 if ! $ZDB -bcv $pool; then 1419 return 1 1420 fi 1421 return 0 1422 } 1423 1424 function filesys_has_zil # filesystem 1425 { 1426 typeset filesys="$1" 1427 1428 if ! $ZDB -ivv $filesys | $GREP "ZIL header"; then 1429 return 1 1430 fi 1431 return 0 1432 } 1433 1434 # 1435 # Given a pool and file system, this function will verify the file system 1436 # using the zdb internal tool. Note that the pool is exported and imported 1437 # to ensure it has consistent state. 1438 # 1439 function verify_filesys # pool filesystem dir 1440 { 1441 typeset pool="$1" 1442 typeset filesys="$2" 1443 typeset zdbout="$TMPDIR/zdbout.${TESTCASE_ID}" 1444 1445 shift 1446 shift 1447 typeset dirs=$@ 1448 typeset search_path="" 1449 1450 log_note "Calling $ZDB to verify filesystem '$filesys'" 1451 log_must $ZPOOL export $pool 1452 1453 if [[ -n $dirs ]] ; then 1454 for dir in $dirs ; do 1455 search_path="$search_path -d $dir" 1456 done 1457 fi 1458 1459 log_must $ZPOOL import $search_path $pool 1460 1461 $ZDB -cudi $filesys > $zdbout 2>&1 1462 if [[ $? != 0 ]]; then 1463 log_note "Output: $ZDB -cudi $filesys" 1464 $CAT $zdbout 1465 log_fail "$ZDB detected errors with: '$filesys'" 1466 fi 1467 1468 log_must $RM -rf $zdbout 1469 } 1470 1471 # 1472 # Given a pool, and this function list all disks in the pool 1473 # 1474 function get_disklist # pool 1475 { 1476 typeset disklist="" 1477 1478 disklist=$($ZPOOL iostat -v $1 | $NAWK '(NR >4 ) {print $1}' | \ 1479 $GREP -v "\-\-\-\-\-" | \ 1480 $EGREP -v -e "^(mirror|raidz1|raidz2|spare|log|cache)$" ) 1481 1482 $ECHO $disklist 1483 } 1484 1485 # 1486 # Destroy all existing metadevices and state database 1487 # 1488 function destroy_metas 1489 { 1490 typeset metad 1491 1492 for metad in $($METASTAT -p | $AWK '{print $1}'); do 1493 log_must $METACLEAR -rf $metad 1494 done 1495 1496 for metad in $($METADB | $CUT -f6 | $GREP dev | $UNIQ); do 1497 log_must $METADB -fd $metad 1498 done 1499 } 1500 1501 # /** 1502 # This function kills a given list of processes after a time period. We use 1503 # this in the stress tests instead of STF_TIMEOUT so that we can have processes 1504 # run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT 1505 # would be listed as FAIL, which we don't want : we're happy with stress tests 1506 # running for a certain amount of time, then finishing. 1507 # 1508 # @param $1 the time in seconds after which we should terminate these processes 1509 # @param $2..$n the processes we wish to terminate. 1510 # */ 1511 function stress_timeout 1512 { 1513 typeset -i TIMEOUT=$1 1514 shift 1515 typeset cpids="$@" 1516 1517 log_note "Waiting for child processes($cpids). " \ 1518 "It could last dozens of minutes, please be patient ..." 1519 log_must $SLEEP $TIMEOUT 1520 1521 log_note "Killing child processes after ${TIMEOUT} stress timeout." 1522 typeset pid 1523 for pid in $cpids; do 1524 $PS -p $pid > /dev/null 2>&1 1525 if (( $? == 0 )); then 1526 log_must $KILL -USR1 $pid 1527 fi 1528 done 1529 } 1530 1531 # 1532 # Check whether current OS support a specified feature or not 1533 # 1534 # return 0 if current OS version is in unsupported list, 1 otherwise 1535 # 1536 # $1 unsupported target OS versions 1537 # 1538 function check_version # <OS version> 1539 { 1540 typeset unsupported_vers="$@" 1541 typeset ver 1542 typeset cur_ver=`$UNAME -r` 1543 1544 for ver in $unsupported_vers; do 1545 [[ "$cur_ver" == "$ver" ]] && return 0 1546 done 1547 1548 return 1 1549 } 1550 1551 # 1552 # Verify a given hotspare disk is inuse or avail 1553 # 1554 # Return 0 is pool/disk matches expected state, 1 otherwise 1555 # 1556 function check_hotspare_state # pool disk state{inuse,avail} 1557 { 1558 typeset pool=$1 1559 typeset disk=${2#/dev/} 1560 disk=${disk#/dev/} 1561 disk=${disk#/dev/} 1562 typeset state=$3 1563 1564 cur_state=$(get_device_state $pool $disk "spares") 1565 1566 if [[ $state != ${cur_state} ]]; then 1567 return 1 1568 fi 1569 return 0 1570 } 1571 1572 # 1573 # Verify a given slog disk is inuse or avail 1574 # 1575 # Return 0 is pool/disk matches expected state, 1 otherwise 1576 # 1577 function check_slog_state # pool disk state{online,offline,unavail} 1578 { 1579 typeset pool=$1 1580 typeset disk=${2#/dev/} 1581 disk=${disk#/dev/} 1582 disk=${disk#/dev/} 1583 typeset state=$3 1584 1585 cur_state=$(get_device_state $pool $disk "logs") 1586 1587 if [[ $state != ${cur_state} ]]; then 1588 return 1 1589 fi 1590 return 0 1591 } 1592 1593 # 1594 # Verify a given vdev disk is inuse or avail 1595 # 1596 # Return 0 is pool/disk matches expected state, 1 otherwise 1597 # 1598 function check_vdev_state # pool disk state{online,offline,unavail} 1599 { 1600 typeset pool=$1 1601 typeset disk=${2#/dev/} 1602 disk=${disk#/dev/} 1603 disk=${disk#/dev/} 1604 typeset state=$3 1605 1606 if [[ $WRAPPER == *"smi"* ]]; then 1607 $ECHO $disk | $EGREP "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1 1608 if (( $? == 0 )); then 1609 disk=${disk}s2 1610 fi 1611 fi 1612 1613 cur_state=$(get_device_state $pool $disk) 1614 1615 if [[ $state != ${cur_state} ]]; then 1616 return 1 1617 fi 1618 return 0 1619 } 1620 1621 # 1622 # Check the output of 'zpool status -v <pool>', 1623 # and to see if the content of <token> contain the <keyword> specified. 1624 # 1625 # Return 0 is contain, 1 otherwise 1626 # 1627 function check_pool_status # pool token keyword 1628 { 1629 typeset pool=$1 1630 typeset token=$2 1631 typeset keyword=$3 1632 1633 $ZPOOL status -v "$pool" 2>/dev/null | \ 1634 $NAWK -v token="$token:" '($1==token) {print $0}' | \ 1635 $GREP -i "$keyword" >/dev/null 2>&1 1636 1637 return $? 1638 } 1639 1640 function vdev_pool_error_count 1641 { 1642 typeset errs=$1 1643 if [ -z "$2" ]; then 1644 test $errs -gt 0; ret=$? 1645 else 1646 test $errs -eq $2; ret=$? 1647 fi 1648 log_debug "vdev_pool_error_count: errs='$errs' \$2='$2' ret='$ret'" 1649 return $ret 1650 } 1651 1652 # 1653 # Generate a pool status error file suitable for pool_errors_from_file. 1654 # If the pool is healthy, returns 0. Otherwise, the caller must handle the 1655 # returned temporarily file appropriately. 1656 # 1657 function pool_error_file # <pool> 1658 { 1659 typeset pool="$1" 1660 1661 typeset tmpfile=$TMPDIR/pool_status.${TESTCASE_ID} 1662 $ZPOOL status -x $pool > ${tmpfile} 1663 echo $tmpfile 1664 } 1665 1666 # 1667 # Evaluates <file> counting the number of errors. If vdev specified, only 1668 # that vdev's errors are counted. Returns the total number. <file> will be 1669 # deleted on exit. 1670 # 1671 function pool_errors_from_file # <file> [vdev] 1672 { 1673 typeset file=$1 1674 shift 1675 typeset checkvdev="$2" 1676 1677 typeset line 1678 typeset -i fetchbegin=1 1679 typeset -i errnum=0 1680 typeset -i c_read=0 1681 typeset -i c_write=0 1682 typeset -i c_cksum=0 1683 1684 cat ${file} | $EGREP -v "pool:" | while read line; do 1685 if (( $fetchbegin != 0 )); then 1686 $ECHO $line | $GREP "NAME" >/dev/null 2>&1 1687 (( $? == 0 )) && (( fetchbegin = 0 )) 1688 continue 1689 fi 1690 1691 if [[ -n $checkvdev ]]; then 1692 $ECHO $line | $GREP $checkvdev >/dev/null 2>&1 1693 (( $? != 0 )) && continue 1694 c_read=`$ECHO $line | $AWK '{print $3}'` 1695 c_write=`$ECHO $line | $AWK '{print $4}'` 1696 c_cksum=`$ECHO $line | $AWK '{print $5}'` 1697 if [ $c_read != 0 ] || [ $c_write != 0 ] || \ 1698 [ $c_cksum != 0 ] 1699 then 1700 (( errnum = errnum + 1 )) 1701 fi 1702 break 1703 fi 1704 1705 c_read=`$ECHO $line | $AWK '{print $3}'` 1706 c_write=`$ECHO $line | $AWK '{print $4}'` 1707 c_cksum=`$ECHO $line | $AWK '{print $5}'` 1708 if [ $c_read != 0 ] || [ $c_write != 0 ] || \ 1709 [ $c_cksum != 0 ] 1710 then 1711 (( errnum = errnum + 1 )) 1712 fi 1713 done 1714 1715 rm -f $file 1716 echo $errnum 1717 } 1718 1719 # 1720 # Returns whether the vdev has the given number of errors. 1721 # If the number is unspecified, any non-zero number returns true. 1722 # 1723 function vdev_has_errors # pool vdev [errors] 1724 { 1725 typeset pool=$1 1726 typeset vdev=$2 1727 typeset tmpfile=$(pool_error_file $pool) 1728 log_note "Original pool status:" 1729 cat $tmpfile 1730 1731 typeset -i errs=$(pool_errors_from_file $tmpfile $vdev) 1732 vdev_pool_error_count $errs $3 1733 } 1734 1735 # 1736 # Returns whether the pool has the given number of errors. 1737 # If the number is unspecified, any non-zero number returns true. 1738 # 1739 function pool_has_errors # pool [errors] 1740 { 1741 typeset pool=$1 1742 typeset tmpfile=$(pool_error_file $pool) 1743 log_note "Original pool status:" 1744 cat $tmpfile 1745 1746 typeset -i errs=$(pool_errors_from_file $tmpfile) 1747 vdev_pool_error_count $errs $2 1748 } 1749 1750 # 1751 # Returns whether clearing $pool at $vdev (if given) succeeds. 1752 # 1753 function pool_clear_succeeds 1754 { 1755 typeset pool="$1" 1756 typeset vdev=$2 1757 1758 $ZPOOL clear $pool $vdev 1759 ! pool_has_errors $pool 1760 } 1761 1762 # 1763 # Return whether the pool is healthy 1764 # 1765 function is_pool_healthy # pool 1766 { 1767 typeset pool=$1 1768 1769 typeset healthy_output="pool '$pool' is healthy" 1770 typeset real_output=$($ZPOOL status -x $pool) 1771 1772 if [[ "$real_output" == "$healthy_output" ]]; then 1773 return 0 1774 else 1775 typeset -i ret 1776 $ZPOOL status -x $pool | $GREP "state:" | \ 1777 $GREP "FAULTED" >/dev/null 2>&1 1778 ret=$? 1779 (( $ret == 0 )) && return 1 1780 typeset l_scan 1781 typeset errnum 1782 l_scan=$($ZPOOL status -x $pool | $GREP "scan:") 1783 l_scan=${l_scan##*"with"} 1784 errnum=$($ECHO $l_scan | $AWK '{print $1}') 1785 if [ "$errnum" != "0" ]; then 1786 return 1 1787 else 1788 return 0 1789 fi 1790 fi 1791 } 1792 1793 # 1794 # These 5 following functions are instance of check_pool_status() 1795 # is_pool_resilvering - to check if the pool is resilver in progress 1796 # is_pool_resilvered - to check if the pool is resilver completed 1797 # is_pool_scrubbing - to check if the pool is scrub in progress 1798 # is_pool_scrubbed - to check if the pool is scrub completed 1799 # is_pool_scrub_stopped - to check if the pool is scrub stopped 1800 # 1801 function is_pool_resilvering #pool 1802 { 1803 check_pool_status "$1" "scan" "resilver in progress" 1804 return $? 1805 } 1806 1807 function is_pool_resilvered #pool 1808 { 1809 check_pool_status "$1" "scan" "resilvered" 1810 return $? 1811 } 1812 1813 function resilver_happened # pool 1814 { 1815 typeset pool=$1 1816 is_pool_resilvering "$pool" || is_pool_resilvered "$pool" 1817 } 1818 1819 function is_pool_scrubbing #pool 1820 { 1821 check_pool_status "$1" "scan" "scrub in progress" 1822 return $? 1823 } 1824 1825 function is_pool_scrubbed #pool 1826 { 1827 check_pool_status "$1" "scan" "scrub repaired" 1828 return $? 1829 } 1830 1831 function is_pool_scrub_stopped #pool 1832 { 1833 check_pool_status "$1" "scan" "scrub canceled" 1834 return $? 1835 } 1836 1837 function is_pool_state # pool state 1838 { 1839 check_pool_status "$1" "state" "$2" 1840 return $? 1841 } 1842 1843 # 1844 # Erase the partition tables and destroy any zfs labels 1845 # 1846 function cleanup_devices #vdevs 1847 { 1848 for device in $@; do 1849 # Labelclear must happen first, otherwise it may interfere 1850 # with the teardown/setup of GPT labels. 1851 $ZPOOL labelclear -f $device 1852 # Only wipe partition tables for arguments that are disks, 1853 # as opposed to slices (which are valid arguments here). 1854 if geom disk list | grep -qx "Geom name: ${device#/dev/}"; then 1855 wipe_partition_table $device 1856 fi 1857 done 1858 return 0 1859 } 1860 1861 # 1862 # Verify the rsh connectivity to each remote host in RHOSTS. 1863 # 1864 # Return 0 if remote host is accessible; otherwise 1. 1865 # $1 remote host name 1866 # $2 username 1867 # 1868 function verify_rsh_connect #rhost, username 1869 { 1870 typeset rhost=$1 1871 typeset username=$2 1872 typeset rsh_cmd="$RSH -n" 1873 typeset cur_user= 1874 1875 $GETENT hosts $rhost >/dev/null 2>&1 1876 if (( $? != 0 )); then 1877 log_note "$rhost cannot be found from" \ 1878 "administrative database." 1879 return 1 1880 fi 1881 1882 $PING $rhost 3 >/dev/null 2>&1 1883 if (( $? != 0 )); then 1884 log_note "$rhost is not reachable." 1885 return 1 1886 fi 1887 1888 if (( ${#username} != 0 )); then 1889 rsh_cmd="$rsh_cmd -l $username" 1890 cur_user="given user \"$username\"" 1891 else 1892 cur_user="current user \"`$LOGNAME`\"" 1893 fi 1894 1895 if ! $rsh_cmd $rhost $TRUE; then 1896 log_note "$RSH to $rhost is not accessible" \ 1897 "with $cur_user." 1898 return 1 1899 fi 1900 1901 return 0 1902 } 1903 1904 # 1905 # Verify the remote host connection via rsh after rebooting 1906 # $1 remote host 1907 # 1908 function verify_remote 1909 { 1910 rhost=$1 1911 1912 # 1913 # The following loop waits for the remote system rebooting. 1914 # Each iteration will wait for 150 seconds. there are 1915 # total 5 iterations, so the total timeout value will 1916 # be 12.5 minutes for the system rebooting. This number 1917 # is an approxiate number. 1918 # 1919 typeset -i count=0 1920 while ! verify_rsh_connect $rhost; do 1921 sleep 150 1922 (( count = count + 1 )) 1923 if (( count > 5 )); then 1924 return 1 1925 fi 1926 done 1927 return 0 1928 } 1929 1930 # 1931 # Replacement function for /usr/bin/rsh. This function will include 1932 # the /usr/bin/rsh and meanwhile return the execution status of the 1933 # last command. 1934 # 1935 # $1 usrname passing down to -l option of /usr/bin/rsh 1936 # $2 remote machine hostname 1937 # $3... command string 1938 # 1939 1940 function rsh_status 1941 { 1942 typeset ruser=$1 1943 typeset rhost=$2 1944 typeset -i ret=0 1945 typeset cmd_str="" 1946 typeset rsh_str="" 1947 1948 shift; shift 1949 cmd_str="$@" 1950 1951 err_file=$TMPDIR/${rhost}.${TESTCASE_ID}.err 1952 if (( ${#ruser} == 0 )); then 1953 rsh_str="$RSH -n" 1954 else 1955 rsh_str="$RSH -n -l $ruser" 1956 fi 1957 1958 $rsh_str $rhost /usr/local/bin/ksh93 -c "'$cmd_str; \ 1959 print -u 2 \"status=\$?\"'" \ 1960 >/dev/null 2>$err_file 1961 ret=$? 1962 if (( $ret != 0 )); then 1963 $CAT $err_file 1964 $RM -f $std_file $err_file 1965 log_fail "$RSH itself failed with exit code $ret..." 1966 fi 1967 1968 ret=$($GREP -v 'print -u 2' $err_file | $GREP 'status=' | \ 1969 $CUT -d= -f2) 1970 (( $ret != 0 )) && $CAT $err_file >&2 1971 1972 $RM -f $err_file >/dev/null 2>&1 1973 return $ret 1974 } 1975 1976 # 1977 # Get the SUNWstc-fs-zfs package installation path in a remote host 1978 # $1 remote host name 1979 # 1980 function get_remote_pkgpath 1981 { 1982 typeset rhost=$1 1983 typeset pkgpath="" 1984 1985 pkgpath=$($RSH -n $rhost "$PKGINFO -l SUNWstc-fs-zfs | $GREP BASEDIR: |\ 1986 $CUT -d: -f2") 1987 1988 $ECHO $pkgpath 1989 } 1990 1991 #/** 1992 # A function to find and locate free disks on a system or from given 1993 # disks as the parameter. Since the conversion to ATF, this function is 1994 # superfluous; it is assumed that the user will supply an accurate list of 1995 # disks to use. So we just return the arguments. 1996 # 1997 # $@ given disks to find which are free 1998 # 1999 # @return a string containing the list of available disks 2000 #*/ 2001 function find_disks 2002 { 2003 (( first=0 )) 2004 for disk in $@; do 2005 [[ $first == 1 ]] && echo -n " " 2006 (( first=1 )) 2007 case $disk in 2008 /dev/*) echo -n "$disk" ;; 2009 *) echo -n "/dev/$disk" ;; 2010 esac 2011 done 2012 } 2013 2014 # A function to set convenience variables for disks. 2015 function set_disks 2016 { 2017 set -A disk_array $(find_disks $DISKS) 2018 [[ -z "$DISK_ARRAY_LIMIT" ]] && typeset -i DISK_ARRAY_LIMIT=5 2019 2020 export DISK="" 2021 typeset -i i=0 2022 while (( i < ${#disk_array[*]} && i <= $DISK_ARRAY_LIMIT )); do 2023 export DISK${i}="${disk_array[$i]}" 2024 DISKSARRAY="$DISKSARRAY ${disk_array[$i]}" 2025 (( i = i + 1 )) 2026 done 2027 export DISK_ARRAY_NUM=$i 2028 export DISKSARRAY 2029 export disk=$DISK0 2030 } 2031 2032 # 2033 # Add specified user to specified group 2034 # 2035 # $1 group name 2036 # $2 user name 2037 # 2038 function add_user #<group_name> <user_name> 2039 { 2040 typeset gname=$1 2041 typeset uname=$2 2042 2043 if (( ${#gname} == 0 || ${#uname} == 0 )); then 2044 log_fail "group name or user name are not defined." 2045 fi 2046 2047 # Check to see if the user exists. 2048 $ID $uname > /dev/null 2>&1 && return 0 2049 2050 # Assign 1000 as the base uid 2051 typeset -i uid=1000 2052 while true; do 2053 typeset -i ret 2054 $USERADD -u $uid -g $gname -d /var/tmp/$uname -m $uname 2055 ret=$? 2056 case $ret in 2057 0) return 0 ;; 2058 # The uid is not unique 2059 65) ((uid += 1)) ;; 2060 *) return 1 ;; 2061 esac 2062 if [[ $uid == 65000 ]]; then 2063 log_fail "No user id available under 65000 for $uname" 2064 fi 2065 done 2066 2067 return 0 2068 } 2069 2070 # 2071 # Delete the specified user. 2072 # 2073 # $1 login name 2074 # 2075 function del_user #<logname> 2076 { 2077 typeset user=$1 2078 2079 if (( ${#user} == 0 )); then 2080 log_fail "login name is necessary." 2081 fi 2082 2083 if $ID $user > /dev/null 2>&1; then 2084 log_must $USERDEL $user 2085 fi 2086 2087 return 0 2088 } 2089 2090 # 2091 # Select valid gid and create specified group. 2092 # 2093 # $1 group name 2094 # 2095 function add_group #<group_name> 2096 { 2097 typeset group=$1 2098 2099 if (( ${#group} == 0 )); then 2100 log_fail "group name is necessary." 2101 fi 2102 2103 # See if the group already exists. 2104 $GROUPSHOW $group >/dev/null 2>&1 2105 [[ $? == 0 ]] && return 0 2106 2107 # Assign 100 as the base gid 2108 typeset -i gid=100 2109 while true; do 2110 $GROUPADD -g $gid $group > /dev/null 2>&1 2111 typeset -i ret=$? 2112 case $ret in 2113 0) return 0 ;; 2114 # The gid is not unique 2115 65) ((gid += 1)) ;; 2116 *) return 1 ;; 2117 esac 2118 if [[ $gid == 65000 ]]; then 2119 log_fail "No user id available under 65000 for $group" 2120 fi 2121 done 2122 } 2123 2124 # 2125 # Delete the specified group. 2126 # 2127 # $1 group name 2128 # 2129 function del_group #<group_name> 2130 { 2131 typeset grp=$1 2132 if (( ${#grp} == 0 )); then 2133 log_fail "group name is necessary." 2134 fi 2135 2136 $GROUPDEL -n $grp > /dev/null 2>&1 2137 typeset -i ret=$? 2138 case $ret in 2139 # Group does not exist, or was deleted successfully. 2140 0|6|65) return 0 ;; 2141 # Name already exists as a group name 2142 9) log_must $GROUPDEL $grp ;; 2143 *) return 1 ;; 2144 esac 2145 2146 return 0 2147 } 2148 2149 # 2150 # This function will return true if it's safe to destroy the pool passed 2151 # as argument 1. It checks for pools based on zvols and files, and also 2152 # files contained in a pool that may have a different mountpoint. 2153 # 2154 function safe_to_destroy_pool { # $1 the pool name 2155 2156 typeset pool="" 2157 typeset DONT_DESTROY="" 2158 2159 # We check that by deleting the $1 pool, we're not 2160 # going to pull the rug out from other pools. Do this 2161 # by looking at all other pools, ensuring that they 2162 # aren't built from files or zvols contained in this pool. 2163 2164 for pool in $($ZPOOL list -H -o name) 2165 do 2166 ALTMOUNTPOOL="" 2167 2168 # this is a list of the top-level directories in each of the files 2169 # that make up the path to the files the pool is based on 2170 FILEPOOL=$($ZPOOL status -v $pool | $GREP /$1/ | \ 2171 $AWK '{print $1}') 2172 2173 # this is a list of the zvols that make up the pool 2174 ZVOLPOOL=$($ZPOOL status -v $pool | $GREP "/dev/zvol/$1$" | \ 2175 $AWK '{print $1}') 2176 2177 # also want to determine if it's a file-based pool using an 2178 # alternate mountpoint... 2179 POOL_FILE_DIRS=$($ZPOOL status -v $pool | \ 2180 $GREP / | $AWK '{print $1}' | \ 2181 $AWK -F/ '{print $2}' | $GREP -v "dev") 2182 2183 for pooldir in $POOL_FILE_DIRS 2184 do 2185 OUTPUT=$($ZFS list -H -r -o mountpoint $1 | \ 2186 $GREP "${pooldir}$" | $AWK '{print $1}') 2187 2188 ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}" 2189 done 2190 2191 2192 if [ ! -z "$ZVOLPOOL" ] 2193 then 2194 DONT_DESTROY="true" 2195 log_note "Pool $pool is built from $ZVOLPOOL on $1" 2196 fi 2197 2198 if [ ! -z "$FILEPOOL" ] 2199 then 2200 DONT_DESTROY="true" 2201 log_note "Pool $pool is built from $FILEPOOL on $1" 2202 fi 2203 2204 if [ ! -z "$ALTMOUNTPOOL" ] 2205 then 2206 DONT_DESTROY="true" 2207 log_note "Pool $pool is built from $ALTMOUNTPOOL on $1" 2208 fi 2209 done 2210 2211 if [ -z "${DONT_DESTROY}" ] 2212 then 2213 return 0 2214 else 2215 log_note "Warning: it is not safe to destroy $1!" 2216 return 1 2217 fi 2218 } 2219 2220 # 2221 # Get IP address of hostname 2222 # $1 hostname 2223 # 2224 function getipbyhost 2225 { 2226 typeset ip 2227 ip=`$ARP $1 2>/dev/null | $AWK -F\) '{print $1}' \ 2228 | $AWK -F\( '{print $2}'` 2229 $ECHO $ip 2230 } 2231 2232 # 2233 # Setup iSCSI initiator to target 2234 # $1 target hostname 2235 # 2236 function iscsi_isetup 2237 { 2238 # check svc:/network/iscsi_initiator:default state, try to enable it 2239 # if the state is not ON 2240 typeset ISCSII_FMRI="svc:/network/iscsi_initiator:default" 2241 if [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) ]]; then 2242 log_must $SVCADM enable $ISCSII_FMRI 2243 2244 typeset -i retry=20 2245 while [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) && \ 2246 ( $retry -ne 0 ) ]] 2247 do 2248 (( retry = retry - 1 )) 2249 $SLEEP 1 2250 done 2251 2252 if [[ "ON" != $($SVCS -H -o sta $ISCSII_FMRI) ]]; then 2253 log_fail "$ISCSII_FMRI service can not be enabled!" 2254 fi 2255 fi 2256 2257 log_must $ISCSIADM add discovery-address $(getipbyhost $1) 2258 log_must $ISCSIADM modify discovery --sendtargets enable 2259 log_must $DEVFSADM -i iscsi 2260 } 2261 2262 # 2263 # Check whether iscsi parameter is set as remote 2264 # 2265 # return 0 if iscsi is set as remote, otherwise 1 2266 # 2267 function check_iscsi_remote 2268 { 2269 if [[ $iscsi == "remote" ]] ; then 2270 return 0 2271 else 2272 return 1 2273 fi 2274 } 2275 2276 # 2277 # Check if a volume is a valide iscsi target 2278 # $1 volume name 2279 # return 0 if suceeds, otherwise, return 1 2280 # 2281 function is_iscsi_target 2282 { 2283 typeset dataset=$1 2284 typeset target targets 2285 2286 [[ -z $dataset ]] && return 1 2287 2288 targets=$($ISCSITADM list target | $GREP "Target:" | $AWK '{print $2}') 2289 [[ -z $targets ]] && return 1 2290 2291 for target in $targets; do 2292 [[ $dataset == $target ]] && return 0 2293 done 2294 2295 return 1 2296 } 2297 2298 # 2299 # Get the iSCSI name of a target 2300 # $1 target name 2301 # 2302 function iscsi_name 2303 { 2304 typeset target=$1 2305 typeset name 2306 2307 [[ -z $target ]] && log_fail "No parameter." 2308 2309 if ! is_iscsi_target $target ; then 2310 log_fail "Not a target." 2311 fi 2312 2313 name=$($ISCSITADM list target $target | $GREP "iSCSI Name:" \ 2314 | $AWK '{print $2}') 2315 2316 return $name 2317 } 2318 2319 # 2320 # check svc:/system/iscsitgt:default state, try to enable it if the state 2321 # is not ON 2322 # 2323 function iscsitgt_setup 2324 { 2325 log_must $RM -f $ISCSITGTFILE 2326 if [[ "ON" == $($SVCS -H -o sta $ISCSITGT_FMRI) ]]; then 2327 log_note "iscsitgt is already enabled" 2328 return 2329 fi 2330 2331 log_must $SVCADM enable -t $ISCSITGT_FMRI 2332 2333 typeset -i retry=20 2334 while [[ "ON" != $($SVCS -H -o sta $ISCSITGT_FMRI) && \ 2335 ( $retry -ne 0 ) ]] 2336 do 2337 $SLEEP 1 2338 (( retry = retry - 1 )) 2339 done 2340 2341 if [[ "ON" != $($SVCS -H -o sta $ISCSITGT_FMRI) ]]; then 2342 log_fail "$ISCSITGT_FMRI service can not be enabled!" 2343 fi 2344 2345 log_must $TOUCH $ISCSITGTFILE 2346 } 2347 2348 # 2349 # set DISABLED state of svc:/system/iscsitgt:default 2350 # which is the most suiteable state if $ISCSITGTFILE exists 2351 # 2352 function iscsitgt_cleanup 2353 { 2354 if [[ -e $ISCSITGTFILE ]]; then 2355 log_must $SVCADM disable $ISCSITGT_FMRI 2356 log_must $RM -f $ISCSITGTFILE 2357 fi 2358 } 2359 2360 # 2361 # Close iSCSI initiator to target 2362 # $1 target hostname 2363 # 2364 function iscsi_iclose 2365 { 2366 log_must $ISCSIADM modify discovery --sendtargets disable 2367 log_must $ISCSIADM remove discovery-address $(getipbyhost $1) 2368 $DEVFSADM -Cv 2369 } 2370 2371 # 2372 # Get the available ZFS compression options 2373 # $1 option type zfs_set|zfs_compress 2374 # 2375 function get_compress_opts 2376 { 2377 typeset COMPRESS_OPTS 2378 typeset GZIP_OPTS="gzip gzip-1 gzip-2 gzip-3 gzip-4 gzip-5 \ 2379 gzip-6 gzip-7 gzip-8 gzip-9" 2380 2381 if [[ $1 == "zfs_compress" ]] ; then 2382 COMPRESS_OPTS="on lzjb" 2383 elif [[ $1 == "zfs_set" ]] ; then 2384 COMPRESS_OPTS="on off lzjb" 2385 fi 2386 typeset valid_opts="$COMPRESS_OPTS" 2387 $ZFS get 2>&1 | $GREP gzip >/dev/null 2>&1 2388 if [[ $? -eq 0 ]]; then 2389 valid_opts="$valid_opts $GZIP_OPTS" 2390 fi 2391 $ECHO "$valid_opts" 2392 } 2393 2394 # 2395 # Check the subcommand/option is supported 2396 # 2397 function check_opt_support #command, option 2398 { 2399 typeset command=$1 2400 typeset option=$2 2401 2402 if [[ -z $command ]]; then 2403 return 0 2404 elif [[ -z $option ]]; then 2405 eval "$ZFS 2>&1 | $GREP '$command' > /dev/null 2>&1" 2406 else 2407 eval "$ZFS $command 2>&1 | $GREP -- '$option' | \ 2408 $GREP -v -- 'User-defined' > /dev/null 2>&1" 2409 fi 2410 return $? 2411 } 2412 2413 # 2414 # Check the zpool subcommand/option is supported 2415 # 2416 function check_zpool_opt_support #command, option 2417 { 2418 typeset command=$1 2419 typeset option=$2 2420 2421 if [[ -z $command ]]; then 2422 return 0 2423 elif [[ -z $option ]]; then 2424 eval "$ZPOOL 2>&1 | $GREP '$command' > /dev/null 2>&1" 2425 else 2426 eval "$ZPOOL $command 2>&1 | $GREP -- '$option' > /dev/null 2>&1" 2427 fi 2428 return $? 2429 } 2430 2431 # 2432 # Verify zfs operation with -p option work as expected 2433 # $1 operation, value could be create, clone or rename 2434 # $2 dataset type, value could be fs or vol 2435 # $3 dataset name 2436 # $4 new dataset name 2437 # 2438 function verify_opt_p_ops 2439 { 2440 typeset ops=$1 2441 typeset datatype=$2 2442 typeset dataset=$3 2443 typeset newdataset=$4 2444 2445 if [[ $datatype != "fs" && $datatype != "vol" ]]; then 2446 log_fail "$datatype is not supported." 2447 fi 2448 2449 # check parameters accordingly 2450 case $ops in 2451 create) 2452 newdataset=$dataset 2453 dataset="" 2454 if [[ $datatype == "vol" ]]; then 2455 ops="create -V $VOLSIZE" 2456 fi 2457 ;; 2458 clone) 2459 if [[ -z $newdataset ]]; then 2460 log_fail "newdataset should not be empty" \ 2461 "when ops is $ops." 2462 fi 2463 log_must datasetexists $dataset 2464 log_must snapexists $dataset 2465 ;; 2466 rename) 2467 if [[ -z $newdataset ]]; then 2468 log_fail "newdataset should not be empty" \ 2469 "when ops is $ops." 2470 fi 2471 log_must datasetexists $dataset 2472 log_mustnot snapexists $dataset 2473 ;; 2474 *) 2475 log_fail "$ops is not supported." 2476 ;; 2477 esac 2478 2479 # make sure the upper level filesystem does not exist 2480 if datasetexists ${newdataset%/*} ; then 2481 log_must $ZFS destroy -rRf ${newdataset%/*} 2482 fi 2483 2484 # without -p option, operation will fail 2485 log_mustnot $ZFS $ops $dataset $newdataset 2486 log_mustnot datasetexists $newdataset ${newdataset%/*} 2487 2488 # with -p option, operation should succeed 2489 log_must $ZFS $ops -p $dataset $newdataset 2490 if ! datasetexists $newdataset ; then 2491 log_fail "-p option does not work for $ops" 2492 fi 2493 2494 # when $ops is create or clone, redo the operation still return zero 2495 if [[ $ops != "rename" ]]; then 2496 log_must $ZFS $ops -p $dataset $newdataset 2497 fi 2498 2499 return 0 2500 } 2501 2502 function get_disk_guid 2503 { 2504 typeset diskname=$1 2505 lastcwd=$(pwd) 2506 cd /dev 2507 guid=$($ZDB -l ${diskname} | ${AWK} '/^ guid:/ {print $2}' | head -1) 2508 cd $lastcwd 2509 echo $guid 2510 } 2511 2512 # 2513 # Get cachefile for a pool. 2514 # Prints the cache file, if there is one. 2515 # Returns 0 for a default zpool.cache, 1 for an explicit one, and 2 for none. 2516 # 2517 function cachefile_for_pool 2518 { 2519 typeset pool=$1 2520 2521 cachefile=$(get_pool_prop cachefile $pool) 2522 [[ $? != 0 ]] && return 1 2523 2524 case "$cachefile" in 2525 none) ret=2 ;; 2526 "-") 2527 ret=2 2528 for dir in /boot/zfs /etc/zfs; do 2529 if [[ -f "${dir}/zpool.cache" ]]; then 2530 cachefile="${dir}/zpool.cache" 2531 ret=0 2532 break 2533 fi 2534 done 2535 ;; 2536 *) ret=1; 2537 esac 2538 [[ $ret -eq 0 || $ret -eq 1 ]] && print "$cachefile" 2539 return $ret 2540 } 2541 2542 # 2543 # Assert that the pool is in the appropriate cachefile. 2544 # 2545 function assert_pool_in_cachefile 2546 { 2547 typeset pool=$1 2548 2549 cachefile=$(cachefile_for_pool $pool) 2550 [ $? -ne 0 ] && log_fail "ERROR: Cachefile not created for '$pool'?" 2551 log_must test -e "${cachefile}" 2552 log_must zdb -U ${cachefile} -C ${pool} 2553 } 2554 2555 # 2556 # Get the zdb options given the cachefile state of the pool. 2557 # 2558 function zdb_cachefile_opts 2559 { 2560 typeset pool=$1 2561 typeset vdevdir=$2 2562 typeset opts 2563 2564 if poolexists "$pool"; then 2565 cachefile=$(cachefile_for_pool $pool) 2566 typeset -i ret=$? 2567 case $ret in 2568 0) opts="-C" ;; 2569 1) opts="-U $cachefile -C" ;; 2570 2) opts="-eC" ;; 2571 *) log_fail "Unknown return '$ret'" ;; 2572 esac 2573 else 2574 opts="-eC" 2575 [[ -n "$vdevdir" ]] && opts="$opts -p $vdevdir" 2576 fi 2577 echo "$opts" 2578 } 2579 2580 # 2581 # Get configuration of pool 2582 # $1 pool name 2583 # $2 config name 2584 # 2585 function get_config 2586 { 2587 typeset pool=$1 2588 typeset config=$2 2589 typeset vdevdir=$3 2590 typeset alt_root 2591 typeset zdb_opts 2592 2593 zdb_opts=$(zdb_cachefile_opts $pool $vdevdir) 2594 value=$($ZDB $zdb_opts $pool | $GREP "$config:" | $AWK -F: '{print $2}') 2595 if [[ -n $value ]] ; then 2596 value=${value#'} 2597 value=${value%'} 2598 else 2599 return 1 2600 fi 2601 echo $value 2602 2603 return 0 2604 } 2605 2606 # 2607 # Privated function. Random select one of items from arguments. 2608 # 2609 # $1 count 2610 # $2-n string 2611 # 2612 function _random_get 2613 { 2614 typeset cnt=$1 2615 shift 2616 2617 typeset str="$@" 2618 typeset -i ind 2619 ((ind = RANDOM % cnt + 1)) 2620 2621 typeset ret=$($ECHO "$str" | $CUT -f $ind -d ' ') 2622 $ECHO $ret 2623 } 2624 2625 # 2626 # Random select one of item from arguments which include NONE string 2627 # 2628 function random_get_with_non 2629 { 2630 typeset -i cnt=$# 2631 ((cnt =+ 1)) 2632 2633 _random_get "$cnt" "$@" 2634 } 2635 2636 # 2637 # Random select one of item from arguments which doesn't include NONE string 2638 # 2639 function random_get 2640 { 2641 _random_get "$#" "$@" 2642 } 2643 2644 # 2645 # The function will generate a dataset name with specific length 2646 # $1, the length of the name 2647 # $2, the base string to construct the name 2648 # 2649 function gen_dataset_name 2650 { 2651 typeset -i len=$1 2652 typeset basestr="$2" 2653 typeset -i baselen=${#basestr} 2654 typeset -i iter=0 2655 typeset l_name="" 2656 2657 if (( len % baselen == 0 )); then 2658 (( iter = len / baselen )) 2659 else 2660 (( iter = len / baselen + 1 )) 2661 fi 2662 while (( iter > 0 )); do 2663 l_name="${l_name}$basestr" 2664 2665 (( iter -= 1 )) 2666 done 2667 2668 $ECHO $l_name 2669 } 2670 2671 # 2672 # Ensure that a given path has been synced, not just ZIL committed. 2673 # 2674 # XXX On FreeBSD, the sync(8) command (via $SYNC) calls zfs_sync() which just 2675 # does a zil_commit(), as opposed to a txg_wait_synced(). For things that 2676 # require writing to their final destination (e.g. for intentional 2677 # corruption purposes), zil_commit() is not good enough. 2678 # 2679 function force_sync_path # path 2680 { 2681 typeset path="$1" 2682 2683 log_must $ZPOOL export $TESTPOOL 2684 log_must $ZPOOL import -d $path $TESTPOOL 2685 } 2686 2687 # 2688 # Get cksum tuple of dataset 2689 # $1 dataset name 2690 # 2691 # zdb output is like below 2692 # " Dataset pool/fs [ZPL], ID 978, cr_txg 2277, 19.0K, 5 objects, 2693 # rootbp [L0 DMU objset] 400L/200P DVA[0]=<0:1880c00:200> 2694 # DVA[1]=<0:341880c00:200> fletcher4 lzjb LE contiguous birth=2292 fill=5 2695 # cksum=989930ccf:4014fe00c83:da5e388e58b4:1f7332052252ac " 2696 # 2697 function datasetcksum 2698 { 2699 typeset cksum 2700 $SYNC 2701 cksum=$($ZDB -vvv $1 | $GREP "^Dataset $1 \[" | $GREP "cksum" \ 2702 | $AWK -F= '{print $6}') 2703 $ECHO $cksum 2704 } 2705 2706 # 2707 # Get cksum of file 2708 # #1 file path 2709 # 2710 function checksum 2711 { 2712 typeset cksum 2713 cksum=$($CKSUM $1 | $AWK '{print $1}') 2714 $ECHO $cksum 2715 } 2716 2717 # 2718 # Get the given disk/slice state from the specific field of the pool 2719 # 2720 function get_device_state #pool disk field("", "spares","logs") 2721 { 2722 typeset pool=$1 2723 typeset disk=${2#/dev/} 2724 disk=${disk#/dev/} 2725 disk=${disk#/dev/} 2726 typeset field=${3:-$pool} 2727 2728 state=$($ZPOOL status -v "$pool" 2>/dev/null | \ 2729 $NAWK -v device=$disk -v pool=$pool -v field=$field \ 2730 'BEGIN {startconfig=0; startfield=0; } 2731 /config:/ {startconfig=1} 2732 (startconfig==1)&&($1==field) {startfield=1; next;} 2733 (startfield==1)&&($1==device) {print $2; exit;} 2734 (startfield==1)&&(NF>=3)&&($(NF-1)=="was")&&($NF==device) {print $2; exit;} 2735 (startfield==1)&&($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}') 2736 print $state 2737 } 2738 2739 2740 # 2741 # print the given directory filesystem type 2742 # 2743 # $1 directory name 2744 # 2745 function get_fstype 2746 { 2747 typeset dir=$1 2748 2749 if [[ -z $dir ]]; then 2750 log_fail "Usage: get_fstype <directory>" 2751 fi 2752 2753 $DF -T $dir | $AWK '{print $2}' 2754 } 2755 2756 # 2757 # Given a disk, label it to VTOC regardless what label was on the disk 2758 # $1 disk 2759 # 2760 function labelvtoc 2761 { 2762 typeset disk=$1 2763 if [[ -z $disk ]]; then 2764 log_fail "The disk name is unspecified." 2765 fi 2766 typeset label_file=$TMPDIR/labelvtoc.${TESTCASE_ID} 2767 typeset arch=$($UNAME -p) 2768 2769 if [[ $arch == "i386" ]]; then 2770 $ECHO "label" > $label_file 2771 $ECHO "0" >> $label_file 2772 $ECHO "" >> $label_file 2773 $ECHO "q" >> $label_file 2774 $ECHO "q" >> $label_file 2775 2776 $FDISK -B $disk >/dev/null 2>&1 2777 # wait a while for fdisk finishes 2778 $SLEEP 60 2779 elif [[ $arch == "sparc" ]]; then 2780 $ECHO "label" > $label_file 2781 $ECHO "0" >> $label_file 2782 $ECHO "" >> $label_file 2783 $ECHO "" >> $label_file 2784 $ECHO "" >> $label_file 2785 $ECHO "q" >> $label_file 2786 else 2787 log_fail "unknown arch type" 2788 fi 2789 2790 $FORMAT -e -s -d $disk -f $label_file 2791 typeset -i ret_val=$? 2792 $RM -f $label_file 2793 # 2794 # wait the format to finish 2795 # 2796 $SLEEP 60 2797 if (( ret_val != 0 )); then 2798 log_fail "unable to label $disk as VTOC." 2799 fi 2800 2801 return 0 2802 } 2803 2804 # 2805 # Detect if the given filesystem property is supported in this release 2806 # 2807 # 0 Yes, it is supported 2808 # !0 No, it is not supported 2809 # 2810 function fs_prop_exist 2811 { 2812 typeset prop=$1 2813 2814 if [[ -z $prop ]]; then 2815 log_fail "Usage: fs_prop_exist <property>" 2816 2817 return 1 2818 fi 2819 2820 # 2821 # If the property is shortened column name, 2822 # convert it to the standard name 2823 # 2824 case $prop in 2825 avail) prop=available ;; 2826 refer) prop=referenced ;; 2827 volblock) prop=volblocksize ;; 2828 compress) prop=compression ;; 2829 rdonly) prop=readonly ;; 2830 recsize) prop=recordsize ;; 2831 reserv) prop=reservation ;; 2832 refreserv) prop=refreservation ;; 2833 esac 2834 2835 # 2836 # The zfs get output looks like the following 2837 # 2838 2839 # 2840 # The following properties are supported: 2841 # 2842 # PROPERTY EDIT INHERIT VALUES 2843 # 2844 # available NO NO <size> 2845 # compressratio NO NO <1.00x or higher if compressed> 2846 # creation NO NO <date> 2847 # ... ... 2848 # zoned YES YES on | off 2849 # 2850 # Sizes are specified in bytes with standard units such as K, M, G, etc. 2851 # 2852 2853 # 2854 # Start to extract property from the first blank line after 'PROPERTY' 2855 # and stop at the next blank line 2856 # 2857 $ZFS get 2>&1 | \ 2858 $AWK '/PROPERTY/ {start=1; next} 2859 /Sizes/ {start=0} 2860 start==1 {print $1}' | \ 2861 $GREP -w "$prop" > /dev/null 2>&1 2862 2863 return $? 2864 } 2865 2866 # 2867 # Detect if the given pool property is supported in this release 2868 # 2869 # 0 Yes, it is supported 2870 # !0 No, it is not supported 2871 # 2872 function pool_prop_exist 2873 { 2874 typeset prop=$1 2875 if [[ -z $prop ]]; then 2876 log_fail "Usage: pool_prop_exist <property>" 2877 2878 return 1 2879 fi 2880 # 2881 # If the property is shortened column name, 2882 # convert it to the standard name 2883 # 2884 case $prop in 2885 avail) prop=available ;; 2886 cap) prop=capacity ;; 2887 replace) prop=autoreplace ;; 2888 esac 2889 2890 # 2891 # The zpool get output looks like the following 2892 # 2893 2894 # usage: 2895 # get <"all" | property[,...]> <pool> ... 2896 # 2897 # the following properties are supported: 2898 # 2899 # PROPERTY EDIT VALUES 2900 # 2901 # available NO <size> 2902 # capacity NO <size> 2903 # guid NO <guid> 2904 # health NO <state> 2905 # size NO <size> 2906 # used NO <size> 2907 # altroot YES <path> 2908 # autoreplace YES on | off 2909 # bootfs YES <filesystem> 2910 # cachefile YES <file> | none 2911 # delegation YES on | off 2912 # failmode YES wait | continue | panic 2913 # version YES <version> 2914 2915 $ZPOOL get 2>&1 | \ 2916 $AWK '/PROPERTY/ {start=1; next} 2917 start==1 {print $1}' | \ 2918 $GREP -w "$prop" > /dev/null 2>&1 2919 2920 return $? 2921 } 2922 2923 # 2924 # check if the system was installed as zfsroot or not 2925 # return: 0 ture, otherwise false 2926 # 2927 function is_zfsroot 2928 { 2929 $DF -T / | $GREP -q zfs 2930 } 2931 2932 # 2933 # get the root filesystem name if it's zfsroot system. 2934 # 2935 # return: root filesystem name 2936 function get_rootfs 2937 { 2938 typeset rootfs="" 2939 rootfs=$($MOUNT | $AWK '$3 == "\/" && $4~/zfs/ {print $1}') 2940 if [[ -z "$rootfs" ]]; then 2941 log_fail "Can not get rootfs" 2942 fi 2943 $ZFS list $rootfs > /dev/null 2>&1 2944 if (( $? == 0 )); then 2945 $ECHO $rootfs 2946 else 2947 log_fail "This is not a zfsroot system." 2948 fi 2949 } 2950 2951 # 2952 # get the rootfs's pool name 2953 # return: 2954 # rootpool name 2955 # 2956 function get_rootpool 2957 { 2958 typeset rootfs="" 2959 typeset rootpool="" 2960 rootfs=$(get_rootfs) 2961 rootpool=`$ECHO $rootfs | awk -F\/ '{print $1}'` 2962 echo $rootpool 2963 } 2964 2965 # 2966 # Get the sub string from specified source string 2967 # 2968 # $1 source string 2969 # $2 start position. Count from 1 2970 # $3 offset 2971 # 2972 function get_substr #src_str pos offset 2973 { 2974 typeset pos offset 2975 2976 $ECHO $1 | \ 2977 $NAWK -v pos=$2 -v offset=$3 '{print substr($0, pos, offset)}' 2978 } 2979 2980 # 2981 # Get the directory path of given device 2982 # 2983 function get_device_dir #device 2984 { 2985 typeset device=$1 2986 2987 $ECHO "/dev" 2988 } 2989 2990 # 2991 # Get the package name 2992 # 2993 function get_package_name 2994 { 2995 typeset dirpath=${1:-$STC_NAME} 2996 2997 print "SUNWstc-${dirpath}" | /usr/bin/sed -e "s/\//-/g" 2998 } 2999 3000 # 3001 # Get the word numbers from a string separated by white space 3002 # 3003 function get_word_count 3004 { 3005 $ECHO $1 | $WC -w 3006 } 3007 3008 # 3009 # To verify if the require numbers of disks is given 3010 # 3011 function verify_disk_count 3012 { 3013 typeset -i min=${2:-1} 3014 3015 typeset -i count=$(get_word_count "$1") 3016 3017 if (( count < min )); then 3018 atf_skip "A minimum of $min disks is required to run." \ 3019 " You specified $count disk(s)" 3020 fi 3021 } 3022 3023 # 3024 # Verify that vfs.zfs.vol.recursive is set, so pools can be created using zvols 3025 # as backing stores. 3026 # 3027 function verify_zvol_recursive 3028 { 3029 if [ "`sysctl -n vfs.zfs.vol.recursive`" -ne 1 ]; then 3030 atf_skip "Recursive ZVOLs not enabled" 3031 fi 3032 } 3033 3034 # 3035 # bsdmap disk/slice number to a device path 3036 # 3037 function bsddevmap 3038 { 3039 typeset arg=$1 3040 echo $arg | egrep "*s[0-9]$" > /dev/null 2>&1 3041 if [ $? -eq 0 ] 3042 then 3043 n=`echo $arg| wc -c` 3044 set -A map a b c d e f g h i j 3045 s=`echo $arg | cut -c $((n-1))` 3046 arg=${arg%s[0-9]}${map[$s]} 3047 fi 3048 echo $arg 3049 } 3050 3051 # 3052 # Get the name of the snapshots directory. Traditionally .zfs/snapshots 3053 # 3054 function get_snapdir_name 3055 { 3056 echo ".zfs/snapshot" 3057 } 3058 3059 # 3060 # Unmount all ZFS filesystems except for those that are in the KEEP variable 3061 # 3062 function unmount_all_safe 3063 { 3064 echo $(all_pools) | \ 3065 $XARGS -n 1 $ZFS list -H -o name -t all -r | \ 3066 $XARGS -n 1 $ZFS unmount 3067 } 3068 3069 # 3070 # Return the highest pool version that this OS can create 3071 # 3072 function get_zpool_version 3073 { 3074 # We assume output from zpool upgrade -v of the form: 3075 # 3076 # This system is currently running ZFS version 2. 3077 # . 3078 # . 3079 typeset ZPOOL_VERSION=$($ZPOOL upgrade -v | $HEAD -1 | \ 3080 $AWK '{print $NF}' | $SED -e 's/\.//g') 3081 # Starting with version 5000, the output format changes to: 3082 # This system supports ZFS pool feature flags. 3083 # . 3084 # . 3085 if [[ $ZPOOL_VERSION = "flags" ]]; then 3086 ZPOOL_VERSION=5000 3087 fi 3088 echo $ZPOOL_VERSION 3089 } 3090 3091 # Ensures that zfsd is running, starting it if necessary. Every test that 3092 # interacts with zfsd must call this at startup. This is intended primarily 3093 # to eliminate interference from outside the test suite. 3094 function ensure_zfsd_running 3095 { 3096 if ! service zfsd status > /dev/null 2>&1; then 3097 service zfsd start || service zfsd onestart 3098 service zfsd status > /dev/null 2>&1 || 3099 log_unsupported "Test requires zfsd" 3100 fi 3101 } 3102 3103 # Temporarily stops ZFSD, because it can interfere with some tests. If this 3104 # function is used, then restart_zfsd _must_ be called in the cleanup routine. 3105 function stop_zfsd 3106 { 3107 $RM -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests 3108 if [[ -n "$ZFSD" && -x "$ZFSD" ]]; then 3109 if /etc/rc.d/zfsd status > /dev/null; then 3110 log_note "Stopping zfsd" 3111 $TOUCH $TMPDIR/.zfsd_enabled_during_stf_zfs_tests 3112 /etc/rc.d/zfsd stop || /etc/rc.d/zfsd onestop 3113 fi 3114 fi 3115 } 3116 3117 # Restarts zfsd after it has been stopped by stop_zfsd. Intelligently restarts 3118 # only iff zfsd was running at the time stop_zfsd was called. 3119 function restart_zfsd 3120 { 3121 if [[ -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests ]]; then 3122 log_note "Restarting zfsd" 3123 /etc/rc.d/zfsd start || /etc/rc.d/zfsd onestart 3124 fi 3125 $RM -f $TMPDIR/.zfsd_enabled_during_stf_zfs_tests 3126 } 3127 3128 # 3129 # Using the given <vdev>, obtain the value of the property <propname> for 3130 # the given <tvd> identified by numeric id. 3131 # 3132 function get_tvd_prop # vdev tvd propname 3133 { 3134 typeset vdev=$1 3135 typeset -i tvd=$2 3136 typeset propname=$3 3137 3138 $ZDB -l $vdev | $AWK -v tvd=$tvd -v prop="${propname}:" ' 3139 BEGIN { start = 0; } 3140 /^ id:/ && ($2==tvd) { start = 1; next; } 3141 (start==0) { next; } 3142 /^ [a-z]+/ && ($1==prop) { print $2; exit; } 3143 /^ children/ { exit; } 3144 ' 3145 } 3146 3147 # 3148 # Convert a DVA into a physical block address. Prints number of blocks. 3149 # This takes the usual printed form, in which offsets are left shifted so 3150 # they represent bytes rather than the native sector count. 3151 # 3152 function dva_to_block_addr # dva 3153 { 3154 typeset dva=$1 3155 3156 typeset offcol=$(echo $dva | cut -f2 -d:) 3157 typeset -i offset="0x${offcol}" 3158 # First add 4MB to skip the boot blocks and first two vdev labels, 3159 # then convert to 512 byte blocks (for use with dd). Note that this 3160 # differs from simply adding 8192 blocks, since the input offset is 3161 # given in bytes and has the actual ashift baked in. 3162 (( offset += 4*1024*1024 )) 3163 (( offset >>= 9 )) 3164 echo "$offset" 3165 } 3166 3167 # 3168 # Convert a RAIDZ DVA into a physical block address. This has the same 3169 # output as dva_to_block_addr (number of blocks from beginning of device), but 3170 # is more complicated due to RAIDZ. ashift is normally always 9, but RAIDZ 3171 # uses the actual tvd ashift instead. Furthermore, the number of vdevs changes 3172 # the actual block for each device. 3173 # 3174 function raidz_dva_to_block_addr # dva ncols ashift 3175 { 3176 typeset dva=$1 3177 typeset -i ncols=$2 3178 typeset -i ashift=$3 3179 3180 typeset -i offset=0x$(echo $dva | cut -f2 -d:) 3181 (( offset >>= ashift )) 3182 3183 typeset -i ioff=$(( (offset + ncols - 1) / ncols )) 3184 3185 # Now add the front 4MB and return. 3186 (( ioff += ( 4194304 >> $ashift ) )) 3187 echo "$ioff" 3188 } 3189 3190 # 3191 # Return the vdevs for the given toplevel vdev number. 3192 # Child vdevs will only be included if they are ONLINE. Output format: 3193 # 3194 # <toplevel vdev type> <nchildren> <child1>[:<child2> ...] 3195 # 3196 # Valid toplevel vdev types are mirror, raidz[1-3], leaf (which can be a 3197 # disk or a file). Note that 'nchildren' can be larger than the number of 3198 # returned children; it represents the number of children regardless of how 3199 # many are actually online. 3200 # 3201 function vdevs_for_tvd # pool tvd 3202 { 3203 typeset pool=$1 3204 typeset -i tvd=$2 3205 3206 $ZPOOL status $pool | $AWK -v want_tvd=$tvd ' 3207 BEGIN { 3208 start = 0; tvd = -1; lvd = -1; 3209 type = "UNKNOWN"; disks = ""; disk = ""; 3210 nchildren = 0; 3211 } 3212 /NAME.*STATE/ { start = 1; next; } 3213 (start==0) { next; } 3214 3215 (tvd > want_tvd) { exit; } 3216 END { print type " " nchildren " " disks; } 3217 3218 length(disk) > 0 { 3219 if (length(disks) > 0) { disks = disks " "; } 3220 if (substr(disk, 0, 1) == "/") { 3221 disks = disks disk; 3222 } else { 3223 disks = disks "/dev/" disk; 3224 } 3225 disk = ""; 3226 } 3227 3228 /^\t(spares|logs)/ { tvd = want_tvd + 1; next; } 3229 /^\t (mirror|raidz[1-3])-[0-9]+/ { 3230 tvd += 1; 3231 (tvd == want_tvd) && type = substr($1, 0, 6); 3232 next; 3233 } 3234 /^\t [\/A-Za-z]+/ { 3235 tvd += 1; 3236 if (tvd == want_tvd) { 3237 (( nchildren += 1 )) 3238 type = "leaf"; 3239 ($2 == "ONLINE") && disk = $1; 3240 } 3241 next; 3242 } 3243 3244 (tvd < want_tvd) { next; } 3245 3246 /^\t spare-[0-9]+/ { next; } 3247 /^\t [\/A-Za-z]+/ { 3248 (( nchildren += 1 )) 3249 ($2 == "ONLINE") && disk = $1; 3250 next; 3251 } 3252 3253 /^\t [\/A-Za-z]+/ { 3254 (( nchildren += 1 )) 3255 ($2 == "ONLINE") && disk = $1; 3256 next; 3257 } 3258 ' 3259 } 3260 3261 # 3262 # Get a vdev path, ashift & offset for a given pool/dataset and DVA. 3263 # If desired, can also select the toplevel vdev child number. 3264 # 3265 function dva_to_vdev_ashift_off # pool/dataset dva [leaf_vdev_num] 3266 { 3267 typeset poollike=$1 3268 typeset dva=$2 3269 typeset -i leaf_vdev_num=$3 3270 3271 # vdevs are normally 0-indexed while arguments are 1-indexed. 3272 (( leaf_vdev_num += 1 )) 3273 3274 # Strip any child datasets or snapshots. 3275 pool=$(echo $poollike | sed -e 's,[/@].*,,g') 3276 tvd=$(echo $dva | cut -d: -f1) 3277 3278 set -- $(vdevs_for_tvd $pool $tvd) 3279 log_debug "vdevs_for_tvd: $* <EOM>" 3280 tvd_type=$1; shift 3281 nchildren=$1; shift 3282 3283 lvd=$(eval echo \$$leaf_vdev_num) 3284 log_debug "type='$tvd_type' children='$nchildren' lvd='$lvd' dva='$dva'" 3285 case $tvd_type in 3286 raidz*) 3287 ashift=$(get_tvd_prop $lvd $tvd ashift) 3288 log_debug "raidz: ashift='${ashift}'" 3289 off=$(raidz_dva_to_block_addr $dva $nchildren $ashift) 3290 ;; 3291 *) 3292 ashift=9 3293 off=$(dva_to_block_addr $dva) 3294 ;; 3295 esac 3296 echo "${lvd}:${ashift}:${off}" 3297 } 3298 3299 # 3300 # Get the DVA for the specified dataset's given filepath. 3301 # 3302 function file_dva # dataset filepath [level] [offset] [dva_num] 3303 { 3304 typeset dataset=$1 3305 typeset filepath=$2 3306 typeset -i level=$3 3307 typeset -i offset=$4 3308 typeset -i dva_num=$5 3309 3310 typeset -li blksz=0 3311 typeset -li blknum=0 3312 typeset -li startoff 3313 typeset -li inode 3314 3315 eval `$STAT -s "$filepath"` 3316 inode="$st_ino" 3317 3318 # The inner match is for 'DVA[0]=<0:1b412600:200>', in which the 3319 # text surrounding the actual DVA is a fixed size with 8 characters 3320 # before it and 1 after. 3321 $ZDB -P -vvvvv "$dataset/" $inode | \ 3322 $AWK -v level=${level} -v dva_num=${dva_num} ' 3323 BEGIN { stage = 0; } 3324 (stage == 0) && ($1=="Object") { stage = 1; next; } 3325 3326 (stage == 1) { 3327 print $3 " " $4; 3328 stage = 2; next; 3329 } 3330 3331 (stage == 2) && /^Indirect blocks/ { stage=3; next; } 3332 (stage < 3) { next; } 3333 3334 match($2, /L[0-9]/) { 3335 if (substr($2, RSTART+1, RLENGTH-1) != level) { next; } 3336 } 3337 match($3, /DVA\[.*>/) { 3338 dva = substr($3, RSTART+8, RLENGTH-9); 3339 if (substr($3, RSTART+4, 1) == dva_num) { 3340 print $1 " " dva; 3341 } 3342 } 3343 ' | \ 3344 while read line; do 3345 log_debug "params='$blksz/$blknum/$startoff' line='$line'" 3346 if (( blksz == 0 )); then 3347 typeset -i iblksz=$(echo $line | cut -d " " -f1) 3348 typeset -i dblksz=$(echo $line | cut -d " " -f2) 3349 3350 # Calculate the actual desired block starting offset. 3351 if (( level > 0 )); then 3352 typeset -i nbps_per_level 3353 typeset -i indsz 3354 typeset -i i=0 3355 3356 (( nbps_per_level = iblksz / 128 )) 3357 (( blksz = dblksz )) 3358 for (( i = 0; $i < $level; i++ )); do 3359 (( blksz *= nbps_per_level )) 3360 done 3361 else 3362 blksz=$dblksz 3363 fi 3364 3365 (( blknum = offset / blksz )) 3366 (( startoff = blknum * blksz )) 3367 continue 3368 fi 3369 3370 typeset lineoffstr=$(echo $line | cut -d " " -f1) 3371 typeset -i lineoff=$(printf "%d" "0x${lineoffstr}") 3372 typeset dva="$(echo $line | cut -d " " -f2)" 3373 log_debug "str='$lineoffstr' lineoff='$lineoff' dva='$dva'" 3374 if [[ -n "$dva" ]] && (( lineoff == startoff )); then 3375 echo $line | cut -d " " -f2 3376 return 0 3377 fi 3378 done 3379 return 1 3380 } 3381 3382 # 3383 # Corrupt the given dataset's filepath file. This will obtain the first 3384 # level 0 block's DVA and scribble random bits on it. 3385 # 3386 function corrupt_file # dataset filepath [leaf_vdev_num] 3387 { 3388 typeset dataset=$1 3389 typeset filepath=$2 3390 typeset -i leaf_vdev_num="$3" 3391 3392 dva=$(file_dva $dataset $filepath) 3393 [ $? -ne 0 ] && log_fail "ERROR: Can't find file $filepath on $dataset" 3394 3395 vdoff=$(dva_to_vdev_ashift_off $dataset $dva $leaf_vdev_num) 3396 vdev=$(echo $vdoff | cut -d: -f1) 3397 ashift=$(echo $vdoff | cut -d: -f2) 3398 off=$(echo $vdoff | cut -d: -f3) 3399 blocksize=$(( 1 << $ashift )) 3400 3401 log_note "Corrupting ${dataset}'s $filepath on $vdev at DVA $dva with ashift $ashift" 3402 log_must $DD if=/dev/urandom bs=$blocksize of=$vdev seek=$off count=1 conv=notrunc 3403 } 3404 3405 # 3406 # Given a number of files, this function will iterate through 3407 # the loop creating the specified number of files, whose names 3408 # will start with <basename>. 3409 # 3410 # The <data> argument is special: it can be "ITER", in which case 3411 # the -d argument will be the value of the current iteration. It 3412 # can be 0, in which case it will always be 0. Otherwise, it will 3413 # always be the given value. 3414 # 3415 # If <snapbase> is specified, a snapshot will be taken using the 3416 # argument as the snapshot basename. 3417 # 3418 function populate_dir # basename num_files write_count blocksz data snapbase 3419 { 3420 typeset basename=$1 3421 typeset -i num_files=$2 3422 typeset -i write_count=$3 3423 typeset -i blocksz=$4 3424 typeset -i i 3425 typeset data=$5 3426 typeset snapbase="$6" 3427 3428 log_note "populate_dir: data='$data'" 3429 for (( i = 0; i < num_files; i++ )); do 3430 case "$data" in 3431 0) d=0 ;; 3432 ITER) d=$i ;; 3433 *) d=$data ;; 3434 esac 3435 3436 log_must $FILE_WRITE -o create -c $write_count \ 3437 -f ${basename}.$i -b $blocksz -d $d 3438 3439 [ -n "$snapbase" ] && log_must $ZFS snapshot ${snapbase}.${i} 3440 done 3441 } 3442 3443 # Reap all children registered in $child_pids. 3444 function reap_children 3445 { 3446 [ -z "$child_pids" ] && return 3447 for wait_pid in $child_pids; do 3448 log_must $KILL $wait_pid 3449 done 3450 child_pids="" 3451 } 3452 3453 # Busy a path. Expects to be reaped via reap_children. Tries to run as 3454 # long and slowly as possible. [num] is taken as a hint; if such a file 3455 # already exists a different one will be chosen. 3456 function busy_path # <path> [num] 3457 { 3458 typeset busypath=$1 3459 typeset -i num=$2 3460 3461 while :; do 3462 busyfile="$busypath/busyfile.${num}" 3463 [ ! -f "$busyfile" ] && break 3464 done 3465 3466 cmd="$DD if=/dev/urandom of=$busyfile bs=512" 3467 ( cd $busypath && $cmd ) & 3468 typeset pid=$! 3469 $SLEEP 1 3470 log_must $PS -p $pid 3471 child_pids="$child_pids $pid" 3472 } 3473