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 (c) 2009, Sun Microsystems Inc. All rights reserved. 24# Copyright (c) 2012, 2020, Delphix. All rights reserved. 25# Copyright (c) 2017, Tim Chase. All rights reserved. 26# Copyright (c) 2017, Nexenta Systems Inc. All rights reserved. 27# Copyright (c) 2017, Lawrence Livermore National Security LLC. 28# Copyright (c) 2017, Datto Inc. All rights reserved. 29# Copyright (c) 2017, Open-E Inc. All rights reserved. 30# Copyright (c) 2021, The FreeBSD Foundation. 31# Use is subject to license terms. 32# 33 34. ${STF_TOOLS}/include/logapi.shlib 35. ${STF_SUITE}/include/math.shlib 36. ${STF_SUITE}/include/blkdev.shlib 37 38. ${STF_SUITE}/include/tunables.cfg 39 40# 41# Apply constrained path when available. This is required since the 42# PATH may have been modified by sudo's secure_path behavior. 43# 44if [ -n "$STF_PATH" ]; then 45 export PATH="$STF_PATH" 46fi 47 48# 49# Generic dot version comparison function 50# 51# Returns success when version $1 is greater than or equal to $2. 52# 53function compare_version_gte 54{ 55 if [[ "$(printf "$1\n$2" | sort -V | tail -n1)" == "$1" ]]; then 56 return 0 57 else 58 return 1 59 fi 60} 61 62# Linux kernel version comparison function 63# 64# $1 Linux version ("4.10", "2.6.32") or blank for installed Linux version 65# 66# Used for comparison: if [ $(linux_version) -ge $(linux_version "2.6.32") ] 67# 68function linux_version 69{ 70 typeset ver="$1" 71 72 [[ -z "$ver" ]] && ver=$(uname -r | grep -Eo "^[0-9]+\.[0-9]+\.[0-9]+") 73 74 typeset version=$(echo $ver | cut -d '.' -f 1) 75 typeset major=$(echo $ver | cut -d '.' -f 2) 76 typeset minor=$(echo $ver | cut -d '.' -f 3) 77 78 [[ -z "$version" ]] && version=0 79 [[ -z "$major" ]] && major=0 80 [[ -z "$minor" ]] && minor=0 81 82 echo $((version * 10000 + major * 100 + minor)) 83} 84 85# Determine if this is a Linux test system 86# 87# Return 0 if platform Linux, 1 if otherwise 88 89function is_linux 90{ 91 if [[ $(uname -o) == "GNU/Linux" ]]; then 92 return 0 93 else 94 return 1 95 fi 96} 97 98# Determine if this is an illumos test system 99# 100# Return 0 if platform illumos, 1 if otherwise 101function is_illumos 102{ 103 if [[ $(uname -o) == "illumos" ]]; then 104 return 0 105 else 106 return 1 107 fi 108} 109 110# Determine if this is a FreeBSD test system 111# 112# Return 0 if platform FreeBSD, 1 if otherwise 113 114function is_freebsd 115{ 116 if [[ $(uname -o) == "FreeBSD" ]]; then 117 return 0 118 else 119 return 1 120 fi 121} 122 123# Determine if this is a DilOS test system 124# 125# Return 0 if platform DilOS, 1 if otherwise 126 127function is_dilos 128{ 129 typeset ID="" 130 [[ -f /etc/os-release ]] && . /etc/os-release 131 if [[ $ID == "dilos" ]]; then 132 return 0 133 else 134 return 1 135 fi 136} 137 138# Determine if this is a 32-bit system 139# 140# Return 0 if platform is 32-bit, 1 if otherwise 141 142function is_32bit 143{ 144 if [[ $(getconf LONG_BIT) == "32" ]]; then 145 return 0 146 else 147 return 1 148 fi 149} 150 151# Determine if kmemleak is enabled 152# 153# Return 0 if kmemleak is enabled, 1 if otherwise 154 155function is_kmemleak 156{ 157 if is_linux && [[ -e /sys/kernel/debug/kmemleak ]]; then 158 return 0 159 else 160 return 1 161 fi 162} 163 164# Determine whether a dataset is mounted 165# 166# $1 dataset name 167# $2 filesystem type; optional - defaulted to zfs 168# 169# Return 0 if dataset is mounted; 1 if unmounted; 2 on error 170 171function ismounted 172{ 173 typeset fstype=$2 174 [[ -z $fstype ]] && fstype=zfs 175 typeset out dir name ret 176 177 case $fstype in 178 zfs) 179 if [[ "$1" == "/"* ]] ; then 180 for out in $(zfs mount | awk '{print $2}'); do 181 [[ $1 == $out ]] && return 0 182 done 183 else 184 for out in $(zfs mount | awk '{print $1}'); do 185 [[ $1 == $out ]] && return 0 186 done 187 fi 188 ;; 189 ufs|nfs) 190 if is_freebsd; then 191 mount -pt $fstype | while read dev dir _t _flags; do 192 [[ "$1" == "$dev" || "$1" == "$dir" ]] && return 0 193 done 194 else 195 out=$(df -F $fstype $1 2>/dev/null) 196 ret=$? 197 (($ret != 0)) && return $ret 198 199 dir=${out%%\(*} 200 dir=${dir%% *} 201 name=${out##*\(} 202 name=${name%%\)*} 203 name=${name%% *} 204 205 [[ "$1" == "$dir" || "$1" == "$name" ]] && return 0 206 fi 207 ;; 208 ext*) 209 out=$(df -t $fstype $1 2>/dev/null) 210 return $? 211 ;; 212 zvol) 213 if [[ -L "$ZVOL_DEVDIR/$1" ]]; then 214 link=$(readlink -f $ZVOL_DEVDIR/$1) 215 [[ -n "$link" ]] && \ 216 mount | grep -q "^$link" && \ 217 return 0 218 fi 219 ;; 220 esac 221 222 return 1 223} 224 225# Return 0 if a dataset is mounted; 1 otherwise 226# 227# $1 dataset name 228# $2 filesystem type; optional - defaulted to zfs 229 230function mounted 231{ 232 ismounted $1 $2 233 (($? == 0)) && return 0 234 return 1 235} 236 237# Return 0 if a dataset is unmounted; 1 otherwise 238# 239# $1 dataset name 240# $2 filesystem type; optional - defaulted to zfs 241 242function unmounted 243{ 244 ismounted $1 $2 245 (($? == 1)) && return 0 246 return 1 247} 248 249# split line on "," 250# 251# $1 - line to split 252 253function splitline 254{ 255 echo $1 | tr ',' ' ' 256} 257 258function default_setup 259{ 260 default_setup_noexit "$@" 261 262 log_pass 263} 264 265function default_setup_no_mountpoint 266{ 267 default_setup_noexit "$1" "$2" "$3" "yes" 268 269 log_pass 270} 271 272# 273# Given a list of disks, setup storage pools and datasets. 274# 275function default_setup_noexit 276{ 277 typeset disklist=$1 278 typeset container=$2 279 typeset volume=$3 280 typeset no_mountpoint=$4 281 log_note begin default_setup_noexit 282 283 if is_global_zone; then 284 if poolexists $TESTPOOL ; then 285 destroy_pool $TESTPOOL 286 fi 287 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 288 log_must zpool create -f $TESTPOOL $disklist 289 else 290 reexport_pool 291 fi 292 293 rm -rf $TESTDIR || log_unresolved Could not remove $TESTDIR 294 mkdir -p $TESTDIR || log_unresolved Could not create $TESTDIR 295 296 log_must zfs create $TESTPOOL/$TESTFS 297 if [[ -z $no_mountpoint ]]; then 298 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 299 fi 300 301 if [[ -n $container ]]; then 302 rm -rf $TESTDIR1 || \ 303 log_unresolved Could not remove $TESTDIR1 304 mkdir -p $TESTDIR1 || \ 305 log_unresolved Could not create $TESTDIR1 306 307 log_must zfs create $TESTPOOL/$TESTCTR 308 log_must zfs set canmount=off $TESTPOOL/$TESTCTR 309 log_must zfs create $TESTPOOL/$TESTCTR/$TESTFS1 310 if [[ -z $no_mountpoint ]]; then 311 log_must zfs set mountpoint=$TESTDIR1 \ 312 $TESTPOOL/$TESTCTR/$TESTFS1 313 fi 314 fi 315 316 if [[ -n $volume ]]; then 317 if is_global_zone ; then 318 log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL 319 block_device_wait 320 else 321 log_must zfs create $TESTPOOL/$TESTVOL 322 fi 323 fi 324} 325 326# 327# Given a list of disks, setup a storage pool, file system and 328# a container. 329# 330function default_container_setup 331{ 332 typeset disklist=$1 333 334 default_setup "$disklist" "true" 335} 336 337# 338# Given a list of disks, setup a storage pool,file system 339# and a volume. 340# 341function default_volume_setup 342{ 343 typeset disklist=$1 344 345 default_setup "$disklist" "" "true" 346} 347 348# 349# Given a list of disks, setup a storage pool,file system, 350# a container and a volume. 351# 352function default_container_volume_setup 353{ 354 typeset disklist=$1 355 356 default_setup "$disklist" "true" "true" 357} 358 359# 360# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on 361# filesystem 362# 363# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS 364# $2 snapshot name. Default, $TESTSNAP 365# 366function create_snapshot 367{ 368 typeset fs_vol=${1:-$TESTPOOL/$TESTFS} 369 typeset snap=${2:-$TESTSNAP} 370 371 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 372 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 373 374 if snapexists $fs_vol@$snap; then 375 log_fail "$fs_vol@$snap already exists." 376 fi 377 datasetexists $fs_vol || \ 378 log_fail "$fs_vol must exist." 379 380 log_must zfs snapshot $fs_vol@$snap 381} 382 383# 384# Create a clone from a snapshot, default clone name is $TESTCLONE. 385# 386# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default. 387# $2 Clone name, $TESTPOOL/$TESTCLONE is default. 388# 389function create_clone # snapshot clone 390{ 391 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 392 typeset clone=${2:-$TESTPOOL/$TESTCLONE} 393 394 [[ -z $snap ]] && \ 395 log_fail "Snapshot name is undefined." 396 [[ -z $clone ]] && \ 397 log_fail "Clone name is undefined." 398 399 log_must zfs clone $snap $clone 400} 401 402# 403# Create a bookmark of the given snapshot. Defaultly create a bookmark on 404# filesystem. 405# 406# $1 Existing filesystem or volume name. Default, $TESTFS 407# $2 Existing snapshot name. Default, $TESTSNAP 408# $3 bookmark name. Default, $TESTBKMARK 409# 410function create_bookmark 411{ 412 typeset fs_vol=${1:-$TESTFS} 413 typeset snap=${2:-$TESTSNAP} 414 typeset bkmark=${3:-$TESTBKMARK} 415 416 [[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined." 417 [[ -z $snap ]] && log_fail "Snapshot's name is undefined." 418 [[ -z $bkmark ]] && log_fail "Bookmark's name is undefined." 419 420 if bkmarkexists $fs_vol#$bkmark; then 421 log_fail "$fs_vol#$bkmark already exists." 422 fi 423 datasetexists $fs_vol || \ 424 log_fail "$fs_vol must exist." 425 snapexists $fs_vol@$snap || \ 426 log_fail "$fs_vol@$snap must exist." 427 428 log_must zfs bookmark $fs_vol@$snap $fs_vol#$bkmark 429} 430 431# 432# Create a temporary clone result of an interrupted resumable 'zfs receive' 433# $1 Destination filesystem name. Must not exist, will be created as the result 434# of this function along with its %recv temporary clone 435# $2 Source filesystem name. Must not exist, will be created and destroyed 436# 437function create_recv_clone 438{ 439 typeset recvfs="$1" 440 typeset sendfs="${2:-$TESTPOOL/create_recv_clone}" 441 typeset snap="$sendfs@snap1" 442 typeset incr="$sendfs@snap2" 443 typeset mountpoint="$TESTDIR/create_recv_clone" 444 typeset sendfile="$TESTDIR/create_recv_clone.zsnap" 445 446 [[ -z $recvfs ]] && log_fail "Recv filesystem's name is undefined." 447 448 datasetexists $recvfs && log_fail "Recv filesystem must not exist." 449 datasetexists $sendfs && log_fail "Send filesystem must not exist." 450 451 log_must zfs create -o mountpoint="$mountpoint" $sendfs 452 log_must zfs snapshot $snap 453 log_must eval "zfs send $snap | zfs recv -u $recvfs" 454 log_must mkfile 1m "$mountpoint/data" 455 log_must zfs snapshot $incr 456 log_must eval "zfs send -i $snap $incr | dd bs=10K count=1 \ 457 iflag=fullblock > $sendfile" 458 log_mustnot eval "zfs recv -su $recvfs < $sendfile" 459 destroy_dataset "$sendfs" "-r" 460 log_must rm -f "$sendfile" 461 462 if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then 463 log_fail "Error creating temporary $recvfs/%recv clone" 464 fi 465} 466 467function default_mirror_setup 468{ 469 default_mirror_setup_noexit $1 $2 $3 470 471 log_pass 472} 473 474# 475# Given a pair of disks, set up a storage pool and dataset for the mirror 476# @parameters: $1 the primary side of the mirror 477# $2 the secondary side of the mirror 478# @uses: ZPOOL ZFS TESTPOOL TESTFS 479function default_mirror_setup_noexit 480{ 481 readonly func="default_mirror_setup_noexit" 482 typeset primary=$1 483 typeset secondary=$2 484 485 [[ -z $primary ]] && \ 486 log_fail "$func: No parameters passed" 487 [[ -z $secondary ]] && \ 488 log_fail "$func: No secondary partition passed" 489 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 490 log_must zpool create -f $TESTPOOL mirror $@ 491 log_must zfs create $TESTPOOL/$TESTFS 492 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 493} 494 495# 496# create a number of mirrors. 497# We create a number($1) of 2 way mirrors using the pairs of disks named 498# on the command line. These mirrors are *not* mounted 499# @parameters: $1 the number of mirrors to create 500# $... the devices to use to create the mirrors on 501# @uses: ZPOOL ZFS TESTPOOL 502function setup_mirrors 503{ 504 typeset -i nmirrors=$1 505 506 shift 507 while ((nmirrors > 0)); do 508 log_must test -n "$1" -a -n "$2" 509 [[ -d /$TESTPOOL$nmirrors ]] && rm -rf /$TESTPOOL$nmirrors 510 log_must zpool create -f $TESTPOOL$nmirrors mirror $1 $2 511 shift 2 512 ((nmirrors = nmirrors - 1)) 513 done 514} 515 516# 517# create a number of raidz pools. 518# We create a number($1) of 2 raidz pools using the pairs of disks named 519# on the command line. These pools are *not* mounted 520# @parameters: $1 the number of pools to create 521# $... the devices to use to create the pools on 522# @uses: ZPOOL ZFS TESTPOOL 523function setup_raidzs 524{ 525 typeset -i nraidzs=$1 526 527 shift 528 while ((nraidzs > 0)); do 529 log_must test -n "$1" -a -n "$2" 530 [[ -d /$TESTPOOL$nraidzs ]] && rm -rf /$TESTPOOL$nraidzs 531 log_must zpool create -f $TESTPOOL$nraidzs raidz $1 $2 532 shift 2 533 ((nraidzs = nraidzs - 1)) 534 done 535} 536 537# 538# Destroy the configured testpool mirrors. 539# the mirrors are of the form ${TESTPOOL}{number} 540# @uses: ZPOOL ZFS TESTPOOL 541function destroy_mirrors 542{ 543 default_cleanup_noexit 544 545 log_pass 546} 547 548# 549# Given a minimum of two disks, set up a storage pool and dataset for the raid-z 550# $1 the list of disks 551# 552function default_raidz_setup 553{ 554 typeset disklist="$*" 555 disks=(${disklist[*]}) 556 557 if [[ ${#disks[*]} -lt 2 ]]; then 558 log_fail "A raid-z requires a minimum of two disks." 559 fi 560 561 [[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL 562 log_must zpool create -f $TESTPOOL raidz $disklist 563 log_must zfs create $TESTPOOL/$TESTFS 564 log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS 565 566 log_pass 567} 568 569# 570# Common function used to cleanup storage pools and datasets. 571# 572# Invoked at the start of the test suite to ensure the system 573# is in a known state, and also at the end of each set of 574# sub-tests to ensure errors from one set of tests doesn't 575# impact the execution of the next set. 576 577function default_cleanup 578{ 579 default_cleanup_noexit 580 581 log_pass 582} 583 584# 585# Utility function used to list all available pool names. 586# 587# NOTE: $KEEP is a variable containing pool names, separated by a newline 588# character, that must be excluded from the returned list. 589# 590function get_all_pools 591{ 592 zpool list -H -o name | grep -Fvx "$KEEP" | grep -v "$NO_POOLS" 593} 594 595function default_cleanup_noexit 596{ 597 typeset pool="" 598 # 599 # Destroying the pool will also destroy any 600 # filesystems it contains. 601 # 602 if is_global_zone; then 603 zfs unmount -a > /dev/null 2>&1 604 ALL_POOLS=$(get_all_pools) 605 # Here, we loop through the pools we're allowed to 606 # destroy, only destroying them if it's safe to do 607 # so. 608 while [ ! -z ${ALL_POOLS} ] 609 do 610 for pool in ${ALL_POOLS} 611 do 612 if safe_to_destroy_pool $pool ; 613 then 614 destroy_pool $pool 615 fi 616 done 617 ALL_POOLS=$(get_all_pools) 618 done 619 620 zfs mount -a 621 else 622 typeset fs="" 623 for fs in $(zfs list -H -o name \ 624 | grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do 625 destroy_dataset "$fs" "-Rf" 626 done 627 628 # Need cleanup here to avoid garbage dir left. 629 for fs in $(zfs list -H -o name); do 630 [[ $fs == /$ZONE_POOL ]] && continue 631 [[ -d $fs ]] && log_must rm -rf $fs/* 632 done 633 634 # 635 # Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to 636 # the default value 637 # 638 for fs in $(zfs list -H -o name); do 639 if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then 640 log_must zfs set reservation=none $fs 641 log_must zfs set recordsize=128K $fs 642 log_must zfs set mountpoint=/$fs $fs 643 typeset enc="" 644 enc=$(get_prop encryption $fs) 645 if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \ 646 [[ "$enc" == "off" ]]; then 647 log_must zfs set checksum=on $fs 648 fi 649 log_must zfs set compression=off $fs 650 log_must zfs set atime=on $fs 651 log_must zfs set devices=off $fs 652 log_must zfs set exec=on $fs 653 log_must zfs set setuid=on $fs 654 log_must zfs set readonly=off $fs 655 log_must zfs set snapdir=hidden $fs 656 log_must zfs set aclmode=groupmask $fs 657 log_must zfs set aclinherit=secure $fs 658 fi 659 done 660 fi 661 662 [[ -d $TESTDIR ]] && \ 663 log_must rm -rf $TESTDIR 664 665 disk1=${DISKS%% *} 666 if is_mpath_device $disk1; then 667 delete_partitions 668 fi 669 670 rm -f $TEST_BASE_DIR/{err,out} 671} 672 673 674# 675# Common function used to cleanup storage pools, file systems 676# and containers. 677# 678function default_container_cleanup 679{ 680 if ! is_global_zone; then 681 reexport_pool 682 fi 683 684 ismounted $TESTPOOL/$TESTCTR/$TESTFS1 685 [[ $? -eq 0 ]] && \ 686 log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1 687 688 destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R" 689 destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf" 690 691 [[ -e $TESTDIR1 ]] && \ 692 log_must rm -rf $TESTDIR1 > /dev/null 2>&1 693 694 default_cleanup 695} 696 697# 698# Common function used to cleanup snapshot of file system or volume. Default to 699# delete the file system's snapshot 700# 701# $1 snapshot name 702# 703function destroy_snapshot 704{ 705 typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 706 707 if ! snapexists $snap; then 708 log_fail "'$snap' does not exist." 709 fi 710 711 # 712 # For the sake of the value which come from 'get_prop' is not equal 713 # to the really mountpoint when the snapshot is unmounted. So, firstly 714 # check and make sure this snapshot's been mounted in current system. 715 # 716 typeset mtpt="" 717 if ismounted $snap; then 718 mtpt=$(get_prop mountpoint $snap) 719 (($? != 0)) && \ 720 log_fail "get_prop mountpoint $snap failed." 721 fi 722 723 destroy_dataset "$snap" 724 [[ $mtpt != "" && -d $mtpt ]] && \ 725 log_must rm -rf $mtpt 726} 727 728# 729# Common function used to cleanup clone. 730# 731# $1 clone name 732# 733function destroy_clone 734{ 735 typeset clone=${1:-$TESTPOOL/$TESTCLONE} 736 737 if ! datasetexists $clone; then 738 log_fail "'$clone' does not existed." 739 fi 740 741 # With the same reason in destroy_snapshot 742 typeset mtpt="" 743 if ismounted $clone; then 744 mtpt=$(get_prop mountpoint $clone) 745 (($? != 0)) && \ 746 log_fail "get_prop mountpoint $clone failed." 747 fi 748 749 destroy_dataset "$clone" 750 [[ $mtpt != "" && -d $mtpt ]] && \ 751 log_must rm -rf $mtpt 752} 753 754# 755# Common function used to cleanup bookmark of file system or volume. Default 756# to delete the file system's bookmark. 757# 758# $1 bookmark name 759# 760function destroy_bookmark 761{ 762 typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK} 763 764 if ! bkmarkexists $bkmark; then 765 log_fail "'$bkmarkp' does not existed." 766 fi 767 768 destroy_dataset "$bkmark" 769} 770 771# Return 0 if a snapshot exists; $? otherwise 772# 773# $1 - snapshot name 774 775function snapexists 776{ 777 zfs list -H -t snapshot "$1" > /dev/null 2>&1 778 return $? 779} 780 781# 782# Return 0 if a bookmark exists; $? otherwise 783# 784# $1 - bookmark name 785# 786function bkmarkexists 787{ 788 zfs list -H -t bookmark "$1" > /dev/null 2>&1 789 return $? 790} 791 792# 793# Return 0 if a hold exists; $? otherwise 794# 795# $1 - hold tag 796# $2 - snapshot name 797# 798function holdexists 799{ 800 zfs holds "$2" | awk '{ print $2 }' | grep "$1" > /dev/null 2>&1 801 return $? 802} 803 804# 805# Set a property to a certain value on a dataset. 806# Sets a property of the dataset to the value as passed in. 807# @param: 808# $1 dataset who's property is being set 809# $2 property to set 810# $3 value to set property to 811# @return: 812# 0 if the property could be set. 813# non-zero otherwise. 814# @use: ZFS 815# 816function dataset_setprop 817{ 818 typeset fn=dataset_setprop 819 820 if (($# < 3)); then 821 log_note "$fn: Insufficient parameters (need 3, had $#)" 822 return 1 823 fi 824 typeset output= 825 output=$(zfs set $2=$3 $1 2>&1) 826 typeset rv=$? 827 if ((rv != 0)); then 828 log_note "Setting property on $1 failed." 829 log_note "property $2=$3" 830 log_note "Return Code: $rv" 831 log_note "Output: $output" 832 return $rv 833 fi 834 return 0 835} 836 837# 838# Assign suite defined dataset properties. 839# This function is used to apply the suite's defined default set of 840# properties to a dataset. 841# @parameters: $1 dataset to use 842# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP 843# @returns: 844# 0 if the dataset has been altered. 845# 1 if no pool name was passed in. 846# 2 if the dataset could not be found. 847# 3 if the dataset could not have it's properties set. 848# 849function dataset_set_defaultproperties 850{ 851 typeset dataset="$1" 852 853 [[ -z $dataset ]] && return 1 854 855 typeset confset= 856 typeset -i found=0 857 for confset in $(zfs list); do 858 if [[ $dataset = $confset ]]; then 859 found=1 860 break 861 fi 862 done 863 [[ $found -eq 0 ]] && return 2 864 if [[ -n $COMPRESSION_PROP ]]; then 865 dataset_setprop $dataset compression $COMPRESSION_PROP || \ 866 return 3 867 log_note "Compression set to '$COMPRESSION_PROP' on $dataset" 868 fi 869 if [[ -n $CHECKSUM_PROP ]]; then 870 dataset_setprop $dataset checksum $CHECKSUM_PROP || \ 871 return 3 872 log_note "Checksum set to '$CHECKSUM_PROP' on $dataset" 873 fi 874 return 0 875} 876 877# 878# Check a numeric assertion 879# @parameter: $@ the assertion to check 880# @output: big loud notice if assertion failed 881# @use: log_fail 882# 883function assert 884{ 885 (($@)) || log_fail "$@" 886} 887 888# 889# Function to format partition size of a disk 890# Given a disk cxtxdx reduces all partitions 891# to 0 size 892# 893function zero_partitions #<whole_disk_name> 894{ 895 typeset diskname=$1 896 typeset i 897 898 if is_freebsd; then 899 gpart destroy -F $diskname 900 elif is_linux; then 901 DSK=$DEV_DSKDIR/$diskname 902 DSK=$(echo $DSK | sed -e "s|//|/|g") 903 log_must parted $DSK -s -- mklabel gpt 904 blockdev --rereadpt $DSK 2>/dev/null 905 block_device_wait 906 else 907 for i in 0 1 3 4 5 6 7 908 do 909 log_must set_partition $i "" 0mb $diskname 910 done 911 fi 912 913 return 0 914} 915 916# 917# Given a slice, size and disk, this function 918# formats the slice to the specified size. 919# Size should be specified with units as per 920# the `format` command requirements eg. 100mb 3gb 921# 922# NOTE: This entire interface is problematic for the Linux parted utility 923# which requires the end of the partition to be specified. It would be 924# best to retire this interface and replace it with something more flexible. 925# At the moment a best effort is made. 926# 927# arguments: <slice_num> <slice_start> <size_plus_units> <whole_disk_name> 928function set_partition 929{ 930 typeset -i slicenum=$1 931 typeset start=$2 932 typeset size=$3 933 typeset disk=${4#$DEV_DSKDIR/} 934 disk=${disk#$DEV_RDSKDIR/} 935 936 case "$(uname)" in 937 Linux) 938 if [[ -z $size || -z $disk ]]; then 939 log_fail "The size or disk name is unspecified." 940 fi 941 disk=$DEV_DSKDIR/$disk 942 typeset size_mb=${size%%[mMgG]} 943 944 size_mb=${size_mb%%[mMgG][bB]} 945 if [[ ${size:1:1} == 'g' ]]; then 946 ((size_mb = size_mb * 1024)) 947 fi 948 949 # Create GPT partition table when setting slice 0 or 950 # when the device doesn't already contain a GPT label. 951 parted $disk -s -- print 1 >/dev/null 952 typeset ret_val=$? 953 if [[ $slicenum -eq 0 || $ret_val -ne 0 ]]; then 954 parted $disk -s -- mklabel gpt 955 if [[ $? -ne 0 ]]; then 956 log_note "Failed to create GPT partition table on $disk" 957 return 1 958 fi 959 fi 960 961 # When no start is given align on the first cylinder. 962 if [[ -z "$start" ]]; then 963 start=1 964 fi 965 966 # Determine the cylinder size for the device and using 967 # that calculate the end offset in cylinders. 968 typeset -i cly_size_kb=0 969 cly_size_kb=$(parted -m $disk -s -- \ 970 unit cyl print | head -3 | tail -1 | \ 971 awk -F '[:k.]' '{print $4}') 972 ((end = (size_mb * 1024 / cly_size_kb) + start)) 973 974 parted $disk -s -- \ 975 mkpart part$slicenum ${start}cyl ${end}cyl 976 typeset ret_val=$? 977 if [[ $ret_val -ne 0 ]]; then 978 log_note "Failed to create partition $slicenum on $disk" 979 return 1 980 fi 981 982 blockdev --rereadpt $disk 2>/dev/null 983 block_device_wait $disk 984 ;; 985 FreeBSD) 986 if [[ -z $size || -z $disk ]]; then 987 log_fail "The size or disk name is unspecified." 988 fi 989 disk=$DEV_DSKDIR/$disk 990 991 if [[ $slicenum -eq 0 ]] || ! gpart show $disk >/dev/null 2>&1; then 992 gpart destroy -F $disk >/dev/null 2>&1 993 gpart create -s GPT $disk 994 if [[ $? -ne 0 ]]; then 995 log_note "Failed to create GPT partition table on $disk" 996 return 1 997 fi 998 fi 999 1000 typeset index=$((slicenum + 1)) 1001 1002 if [[ -n $start ]]; then 1003 start="-b $start" 1004 fi 1005 gpart add -t freebsd-zfs $start -s $size -i $index $disk 1006 if [[ $ret_val -ne 0 ]]; then 1007 log_note "Failed to create partition $slicenum on $disk" 1008 return 1 1009 fi 1010 1011 block_device_wait $disk 1012 ;; 1013 *) 1014 if [[ -z $slicenum || -z $size || -z $disk ]]; then 1015 log_fail "The slice, size or disk name is unspecified." 1016 fi 1017 1018 typeset format_file=/var/tmp/format_in.$$ 1019 1020 echo "partition" >$format_file 1021 echo "$slicenum" >> $format_file 1022 echo "" >> $format_file 1023 echo "" >> $format_file 1024 echo "$start" >> $format_file 1025 echo "$size" >> $format_file 1026 echo "label" >> $format_file 1027 echo "" >> $format_file 1028 echo "q" >> $format_file 1029 echo "q" >> $format_file 1030 1031 format -e -s -d $disk -f $format_file 1032 typeset ret_val=$? 1033 rm -f $format_file 1034 ;; 1035 esac 1036 1037 if [[ $ret_val -ne 0 ]]; then 1038 log_note "Unable to format $disk slice $slicenum to $size" 1039 return 1 1040 fi 1041 return 0 1042} 1043 1044# 1045# Delete all partitions on all disks - this is specifically for the use of multipath 1046# devices which currently can only be used in the test suite as raw/un-partitioned 1047# devices (ie a zpool cannot be created on a whole mpath device that has partitions) 1048# 1049function delete_partitions 1050{ 1051 typeset disk 1052 1053 if [[ -z $DISKSARRAY ]]; then 1054 DISKSARRAY=$DISKS 1055 fi 1056 1057 if is_linux; then 1058 typeset -i part 1059 for disk in $DISKSARRAY; do 1060 for (( part = 1; part < MAX_PARTITIONS; part++ )); do 1061 typeset partition=${disk}${SLICE_PREFIX}${part} 1062 parted $DEV_DSKDIR/$disk -s rm $part > /dev/null 2>&1 1063 if lsblk | grep -qF ${partition}; then 1064 log_fail "Partition ${partition} not deleted" 1065 else 1066 log_note "Partition ${partition} deleted" 1067 fi 1068 done 1069 done 1070 elif is_freebsd; then 1071 for disk in $DISKSARRAY; do 1072 if gpart destroy -F $disk; then 1073 log_note "Partitions for ${disk} deleted" 1074 else 1075 log_fail "Partitions for ${disk} not deleted" 1076 fi 1077 done 1078 fi 1079} 1080 1081# 1082# Get the end cyl of the given slice 1083# 1084function get_endslice #<disk> <slice> 1085{ 1086 typeset disk=$1 1087 typeset slice=$2 1088 if [[ -z $disk || -z $slice ]] ; then 1089 log_fail "The disk name or slice number is unspecified." 1090 fi 1091 1092 case "$(uname)" in 1093 Linux) 1094 endcyl=$(parted -s $DEV_DSKDIR/$disk -- unit cyl print | \ 1095 awk "/part${slice}/"' {sub(/cyl/, "", $3); print $3}') 1096 ((endcyl = (endcyl + 1))) 1097 ;; 1098 FreeBSD) 1099 disk=${disk#/dev/zvol/} 1100 disk=${disk%p*} 1101 slice=$((slice + 1)) 1102 endcyl=$(gpart show $disk | \ 1103 awk -v slice=$slice '$3 == slice { print $1 + $2 }') 1104 ;; 1105 *) 1106 disk=${disk#/dev/dsk/} 1107 disk=${disk#/dev/rdsk/} 1108 disk=${disk%s*} 1109 1110 typeset -i ratio=0 1111 ratio=$(prtvtoc /dev/rdsk/${disk}s2 | \ 1112 grep "sectors\/cylinder" | \ 1113 awk '{print $2}') 1114 1115 if ((ratio == 0)); then 1116 return 1117 fi 1118 1119 typeset -i endcyl=$(prtvtoc -h /dev/rdsk/${disk}s2 | 1120 nawk -v token="$slice" '{if ($1==token) print $6}') 1121 1122 ((endcyl = (endcyl + 1) / ratio)) 1123 ;; 1124 esac 1125 1126 echo $endcyl 1127} 1128 1129 1130# 1131# Given a size,disk and total slice number, this function formats the 1132# disk slices from 0 to the total slice number with the same specified 1133# size. 1134# 1135function partition_disk #<slice_size> <whole_disk_name> <total_slices> 1136{ 1137 typeset -i i=0 1138 typeset slice_size=$1 1139 typeset disk_name=$2 1140 typeset total_slices=$3 1141 typeset cyl 1142 1143 zero_partitions $disk_name 1144 while ((i < $total_slices)); do 1145 if ! is_linux; then 1146 if ((i == 2)); then 1147 ((i = i + 1)) 1148 continue 1149 fi 1150 fi 1151 log_must set_partition $i "$cyl" $slice_size $disk_name 1152 cyl=$(get_endslice $disk_name $i) 1153 ((i = i+1)) 1154 done 1155} 1156 1157# 1158# This function continues to write to a filenum number of files into dirnum 1159# number of directories until either file_write returns an error or the 1160# maximum number of files per directory have been written. 1161# 1162# Usage: 1163# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data] 1164# 1165# Return value: 0 on success 1166# non 0 on error 1167# 1168# Where : 1169# destdir: is the directory where everything is to be created under 1170# dirnum: the maximum number of subdirectories to use, -1 no limit 1171# filenum: the maximum number of files per subdirectory 1172# bytes: number of bytes to write 1173# num_writes: number of types to write out bytes 1174# data: the data that will be written 1175# 1176# E.g. 1177# fill_fs /testdir 20 25 1024 256 0 1178# 1179# Note: bytes * num_writes equals the size of the testfile 1180# 1181function fill_fs # destdir dirnum filenum bytes num_writes data 1182{ 1183 typeset destdir=${1:-$TESTDIR} 1184 typeset -i dirnum=${2:-50} 1185 typeset -i filenum=${3:-50} 1186 typeset -i bytes=${4:-8192} 1187 typeset -i num_writes=${5:-10240} 1188 typeset data=${6:-0} 1189 1190 mkdir -p $destdir/{1..$dirnum} 1191 for f in $destdir/{1..$dirnum}/$TESTFILE{1..$filenum}; do 1192 file_write -o create -f $f -b $bytes -c $num_writes -d $data \ 1193 || return $? 1194 done 1195 return 0 1196} 1197 1198# 1199# Simple function to get the specified property. If unable to 1200# get the property then exits. 1201# 1202# Note property is in 'parsable' format (-p) 1203# 1204function get_prop # property dataset 1205{ 1206 typeset prop_val 1207 typeset prop=$1 1208 typeset dataset=$2 1209 1210 prop_val=$(zfs get -pH -o value $prop $dataset 2>/dev/null) 1211 if [[ $? -ne 0 ]]; then 1212 log_note "Unable to get $prop property for dataset " \ 1213 "$dataset" 1214 return 1 1215 fi 1216 1217 echo "$prop_val" 1218 return 0 1219} 1220 1221# 1222# Simple function to get the specified property of pool. If unable to 1223# get the property then exits. 1224# 1225# Note property is in 'parsable' format (-p) 1226# 1227function get_pool_prop # property pool 1228{ 1229 typeset prop_val 1230 typeset prop=$1 1231 typeset pool=$2 1232 1233 if poolexists $pool ; then 1234 prop_val=$(zpool get -pH $prop $pool 2>/dev/null | tail -1 | \ 1235 awk '{print $3}') 1236 if [[ $? -ne 0 ]]; then 1237 log_note "Unable to get $prop property for pool " \ 1238 "$pool" 1239 return 1 1240 fi 1241 else 1242 log_note "Pool $pool not exists." 1243 return 1 1244 fi 1245 1246 echo "$prop_val" 1247 return 0 1248} 1249 1250# Return 0 if a pool exists; $? otherwise 1251# 1252# $1 - pool name 1253 1254function poolexists 1255{ 1256 typeset pool=$1 1257 1258 if [[ -z $pool ]]; then 1259 log_note "No pool name given." 1260 return 1 1261 fi 1262 1263 zpool get name "$pool" > /dev/null 2>&1 1264 return $? 1265} 1266 1267# Return 0 if all the specified datasets exist; $? otherwise 1268# 1269# $1-n dataset name 1270function datasetexists 1271{ 1272 if (($# == 0)); then 1273 log_note "No dataset name given." 1274 return 1 1275 fi 1276 1277 while (($# > 0)); do 1278 zfs get name $1 > /dev/null 2>&1 || \ 1279 return $? 1280 shift 1281 done 1282 1283 return 0 1284} 1285 1286# return 0 if none of the specified datasets exists, otherwise return 1. 1287# 1288# $1-n dataset name 1289function datasetnonexists 1290{ 1291 if (($# == 0)); then 1292 log_note "No dataset name given." 1293 return 1 1294 fi 1295 1296 while (($# > 0)); do 1297 zfs list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 \ 1298 && return 1 1299 shift 1300 done 1301 1302 return 0 1303} 1304 1305function is_shared_freebsd 1306{ 1307 typeset fs=$1 1308 1309 pgrep -q mountd && showmount -E | grep -qx $fs 1310} 1311 1312function is_shared_illumos 1313{ 1314 typeset fs=$1 1315 typeset mtpt 1316 1317 for mtpt in `share | awk '{print $2}'` ; do 1318 if [[ $mtpt == $fs ]] ; then 1319 return 0 1320 fi 1321 done 1322 1323 typeset stat=$(svcs -H -o STA nfs/server:default) 1324 if [[ $stat != "ON" ]]; then 1325 log_note "Current nfs/server status: $stat" 1326 fi 1327 1328 return 1 1329} 1330 1331function is_shared_linux 1332{ 1333 typeset fs=$1 1334 typeset mtpt 1335 1336 for mtpt in `share | awk '{print $1}'` ; do 1337 if [[ $mtpt == $fs ]] ; then 1338 return 0 1339 fi 1340 done 1341 return 1 1342} 1343 1344# 1345# Given a mountpoint, or a dataset name, determine if it is shared via NFS. 1346# 1347# Returns 0 if shared, 1 otherwise. 1348# 1349function is_shared 1350{ 1351 typeset fs=$1 1352 typeset mtpt 1353 1354 if [[ $fs != "/"* ]] ; then 1355 if datasetnonexists "$fs" ; then 1356 return 1 1357 else 1358 mtpt=$(get_prop mountpoint "$fs") 1359 case $mtpt in 1360 none|legacy|-) return 1 1361 ;; 1362 *) fs=$mtpt 1363 ;; 1364 esac 1365 fi 1366 fi 1367 1368 case $(uname) in 1369 FreeBSD) is_shared_freebsd "$fs" ;; 1370 Linux) is_shared_linux "$fs" ;; 1371 *) is_shared_illumos "$fs" ;; 1372 esac 1373} 1374 1375function is_exported_illumos 1376{ 1377 typeset fs=$1 1378 typeset mtpt 1379 1380 for mtpt in `awk '{print $1}' /etc/dfs/sharetab` ; do 1381 if [[ $mtpt == $fs ]] ; then 1382 return 0 1383 fi 1384 done 1385 1386 return 1 1387} 1388 1389function is_exported_freebsd 1390{ 1391 typeset fs=$1 1392 typeset mtpt 1393 1394 for mtpt in `awk '{print $1}' /etc/zfs/exports` ; do 1395 if [[ $mtpt == $fs ]] ; then 1396 return 0 1397 fi 1398 done 1399 1400 return 1 1401} 1402 1403function is_exported_linux 1404{ 1405 typeset fs=$1 1406 typeset mtpt 1407 1408 for mtpt in `awk '{print $1}' /etc/exports.d/zfs.exports` ; do 1409 if [[ $mtpt == $fs ]] ; then 1410 return 0 1411 fi 1412 done 1413 1414 return 1 1415} 1416 1417# 1418# Given a mountpoint, or a dataset name, determine if it is exported via 1419# the os-specific NFS exports file. 1420# 1421# Returns 0 if exported, 1 otherwise. 1422# 1423function is_exported 1424{ 1425 typeset fs=$1 1426 typeset mtpt 1427 1428 if [[ $fs != "/"* ]] ; then 1429 if datasetnonexists "$fs" ; then 1430 return 1 1431 else 1432 mtpt=$(get_prop mountpoint "$fs") 1433 case $mtpt in 1434 none|legacy|-) return 1 1435 ;; 1436 *) fs=$mtpt 1437 ;; 1438 esac 1439 fi 1440 fi 1441 1442 case $(uname) in 1443 FreeBSD) is_exported_freebsd "$fs" ;; 1444 Linux) is_exported_linux "$fs" ;; 1445 *) is_exported_illumos "$fs" ;; 1446 esac 1447} 1448 1449# 1450# Given a dataset name determine if it is shared via SMB. 1451# 1452# Returns 0 if shared, 1 otherwise. 1453# 1454function is_shared_smb 1455{ 1456 typeset fs=$1 1457 typeset mtpt 1458 1459 if datasetnonexists "$fs" ; then 1460 return 1 1461 else 1462 fs=$(echo $fs | tr / _) 1463 fi 1464 1465 if is_linux; then 1466 for mtpt in `net usershare list | awk '{print $1}'` ; do 1467 if [[ $mtpt == $fs ]] ; then 1468 return 0 1469 fi 1470 done 1471 return 1 1472 else 1473 log_note "Currently unsupported by the test framework" 1474 return 1 1475 fi 1476} 1477 1478# 1479# Given a mountpoint, determine if it is not shared via NFS. 1480# 1481# Returns 0 if not shared, 1 otherwise. 1482# 1483function not_shared 1484{ 1485 typeset fs=$1 1486 1487 is_shared $fs 1488 if (($? == 0)); then 1489 return 1 1490 fi 1491 1492 return 0 1493} 1494 1495# 1496# Given a dataset determine if it is not shared via SMB. 1497# 1498# Returns 0 if not shared, 1 otherwise. 1499# 1500function not_shared_smb 1501{ 1502 typeset fs=$1 1503 1504 is_shared_smb $fs 1505 if (($? == 0)); then 1506 return 1 1507 fi 1508 1509 return 0 1510} 1511 1512# 1513# Helper function to unshare a mountpoint. 1514# 1515function unshare_fs #fs 1516{ 1517 typeset fs=$1 1518 1519 is_shared $fs || is_shared_smb $fs 1520 if (($? == 0)); then 1521 zfs unshare $fs || log_fail "zfs unshare $fs failed" 1522 fi 1523 1524 return 0 1525} 1526 1527# 1528# Helper function to share a NFS mountpoint. 1529# 1530function share_nfs #fs 1531{ 1532 typeset fs=$1 1533 1534 if is_linux; then 1535 is_shared $fs 1536 if (($? != 0)); then 1537 log_must share "*:$fs" 1538 fi 1539 else 1540 is_shared $fs 1541 if (($? != 0)); then 1542 log_must share -F nfs $fs 1543 fi 1544 fi 1545 1546 return 0 1547} 1548 1549# 1550# Helper function to unshare a NFS mountpoint. 1551# 1552function unshare_nfs #fs 1553{ 1554 typeset fs=$1 1555 1556 if is_linux; then 1557 is_shared $fs 1558 if (($? == 0)); then 1559 log_must unshare -u "*:$fs" 1560 fi 1561 else 1562 is_shared $fs 1563 if (($? == 0)); then 1564 log_must unshare -F nfs $fs 1565 fi 1566 fi 1567 1568 return 0 1569} 1570 1571# 1572# Helper function to show NFS shares. 1573# 1574function showshares_nfs 1575{ 1576 if is_linux; then 1577 share -v 1578 else 1579 share -F nfs 1580 fi 1581 1582 return 0 1583} 1584 1585# 1586# Helper function to show SMB shares. 1587# 1588function showshares_smb 1589{ 1590 if is_linux; then 1591 net usershare list 1592 else 1593 share -F smb 1594 fi 1595 1596 return 0 1597} 1598 1599function check_nfs 1600{ 1601 if is_linux; then 1602 share -s 1603 elif is_freebsd; then 1604 showmount -e 1605 else 1606 log_unsupported "Unknown platform" 1607 fi 1608 1609 if [[ $? -ne 0 ]]; then 1610 log_unsupported "The NFS utilities are not installed" 1611 fi 1612} 1613 1614# 1615# Check NFS server status and trigger it online. 1616# 1617function setup_nfs_server 1618{ 1619 # Cannot share directory in non-global zone. 1620 # 1621 if ! is_global_zone; then 1622 log_note "Cannot trigger NFS server by sharing in LZ." 1623 return 1624 fi 1625 1626 if is_linux; then 1627 # 1628 # Re-synchronize /var/lib/nfs/etab with /etc/exports and 1629 # /etc/exports.d./* to provide a clean test environment. 1630 # 1631 log_must share -r 1632 1633 log_note "NFS server must be started prior to running ZTS." 1634 return 1635 elif is_freebsd; then 1636 kill -s HUP $(cat /var/run/mountd.pid) 1637 1638 log_note "NFS server must be started prior to running ZTS." 1639 return 1640 fi 1641 1642 typeset nfs_fmri="svc:/network/nfs/server:default" 1643 if [[ $(svcs -Ho STA $nfs_fmri) != "ON" ]]; then 1644 # 1645 # Only really sharing operation can enable NFS server 1646 # to online permanently. 1647 # 1648 typeset dummy=/tmp/dummy 1649 1650 if [[ -d $dummy ]]; then 1651 log_must rm -rf $dummy 1652 fi 1653 1654 log_must mkdir $dummy 1655 log_must share $dummy 1656 1657 # 1658 # Waiting for fmri's status to be the final status. 1659 # Otherwise, in transition, an asterisk (*) is appended for 1660 # instances, unshare will reverse status to 'DIS' again. 1661 # 1662 # Waiting for 1's at least. 1663 # 1664 log_must sleep 1 1665 timeout=10 1666 while [[ timeout -ne 0 && $(svcs -Ho STA $nfs_fmri) == *'*' ]] 1667 do 1668 log_must sleep 1 1669 1670 ((timeout -= 1)) 1671 done 1672 1673 log_must unshare $dummy 1674 log_must rm -rf $dummy 1675 fi 1676 1677 log_note "Current NFS status: '$(svcs -Ho STA,FMRI $nfs_fmri)'" 1678} 1679 1680# 1681# To verify whether calling process is in global zone 1682# 1683# Return 0 if in global zone, 1 in non-global zone 1684# 1685function is_global_zone 1686{ 1687 if is_linux || is_freebsd; then 1688 return 0 1689 else 1690 typeset cur_zone=$(zonename 2>/dev/null) 1691 if [[ $cur_zone != "global" ]]; then 1692 return 1 1693 fi 1694 return 0 1695 fi 1696} 1697 1698# 1699# Verify whether test is permitted to run from 1700# global zone, local zone, or both 1701# 1702# $1 zone limit, could be "global", "local", or "both"(no limit) 1703# 1704# Return 0 if permitted, otherwise exit with log_unsupported 1705# 1706function verify_runnable # zone limit 1707{ 1708 typeset limit=$1 1709 1710 [[ -z $limit ]] && return 0 1711 1712 if is_global_zone ; then 1713 case $limit in 1714 global|both) 1715 ;; 1716 local) log_unsupported "Test is unable to run from "\ 1717 "global zone." 1718 ;; 1719 *) log_note "Warning: unknown limit $limit - " \ 1720 "use both." 1721 ;; 1722 esac 1723 else 1724 case $limit in 1725 local|both) 1726 ;; 1727 global) log_unsupported "Test is unable to run from "\ 1728 "local zone." 1729 ;; 1730 *) log_note "Warning: unknown limit $limit - " \ 1731 "use both." 1732 ;; 1733 esac 1734 1735 reexport_pool 1736 fi 1737 1738 return 0 1739} 1740 1741# Return 0 if create successfully or the pool exists; $? otherwise 1742# Note: In local zones, this function should return 0 silently. 1743# 1744# $1 - pool name 1745# $2-n - [keyword] devs_list 1746 1747function create_pool #pool devs_list 1748{ 1749 typeset pool=${1%%/*} 1750 1751 shift 1752 1753 if [[ -z $pool ]]; then 1754 log_note "Missing pool name." 1755 return 1 1756 fi 1757 1758 if poolexists $pool ; then 1759 destroy_pool $pool 1760 fi 1761 1762 if is_global_zone ; then 1763 [[ -d /$pool ]] && rm -rf /$pool 1764 log_must zpool create -f $pool $@ 1765 fi 1766 1767 return 0 1768} 1769 1770# Return 0 if destroy successfully or the pool exists; $? otherwise 1771# Note: In local zones, this function should return 0 silently. 1772# 1773# $1 - pool name 1774# Destroy pool with the given parameters. 1775 1776function destroy_pool #pool 1777{ 1778 typeset pool=${1%%/*} 1779 typeset mtpt 1780 1781 if [[ -z $pool ]]; then 1782 log_note "No pool name given." 1783 return 1 1784 fi 1785 1786 if is_global_zone ; then 1787 if poolexists "$pool" ; then 1788 mtpt=$(get_prop mountpoint "$pool") 1789 1790 # At times, syseventd/udev activity can cause attempts 1791 # to destroy a pool to fail with EBUSY. We retry a few 1792 # times allowing failures before requiring the destroy 1793 # to succeed. 1794 log_must_busy zpool destroy -f $pool 1795 1796 [[ -d $mtpt ]] && \ 1797 log_must rm -rf $mtpt 1798 else 1799 log_note "Pool does not exist. ($pool)" 1800 return 1 1801 fi 1802 fi 1803 1804 return 0 1805} 1806 1807# Return 0 if created successfully; $? otherwise 1808# 1809# $1 - dataset name 1810# $2-n - dataset options 1811 1812function create_dataset #dataset dataset_options 1813{ 1814 typeset dataset=$1 1815 1816 shift 1817 1818 if [[ -z $dataset ]]; then 1819 log_note "Missing dataset name." 1820 return 1 1821 fi 1822 1823 if datasetexists $dataset ; then 1824 destroy_dataset $dataset 1825 fi 1826 1827 log_must zfs create $@ $dataset 1828 1829 return 0 1830} 1831 1832# Return 0 if destroy successfully or the dataset exists; $? otherwise 1833# Note: In local zones, this function should return 0 silently. 1834# 1835# $1 - dataset name 1836# $2 - custom arguments for zfs destroy 1837# Destroy dataset with the given parameters. 1838 1839function destroy_dataset #dataset #args 1840{ 1841 typeset dataset=$1 1842 typeset mtpt 1843 typeset args=${2:-""} 1844 1845 if [[ -z $dataset ]]; then 1846 log_note "No dataset name given." 1847 return 1 1848 fi 1849 1850 if is_global_zone ; then 1851 if datasetexists "$dataset" ; then 1852 mtpt=$(get_prop mountpoint "$dataset") 1853 log_must_busy zfs destroy $args $dataset 1854 1855 [[ -d $mtpt ]] && \ 1856 log_must rm -rf $mtpt 1857 else 1858 log_note "Dataset does not exist. ($dataset)" 1859 return 1 1860 fi 1861 fi 1862 1863 return 0 1864} 1865 1866# 1867# Firstly, create a pool with 5 datasets. Then, create a single zone and 1868# export the 5 datasets to it. In addition, we also add a ZFS filesystem 1869# and a zvol device to the zone. 1870# 1871# $1 zone name 1872# $2 zone root directory prefix 1873# $3 zone ip 1874# 1875function zfs_zones_setup #zone_name zone_root zone_ip 1876{ 1877 typeset zone_name=${1:-$(hostname)-z} 1878 typeset zone_root=${2:-"/zone_root"} 1879 typeset zone_ip=${3:-"10.1.1.10"} 1880 typeset prefix_ctr=$ZONE_CTR 1881 typeset pool_name=$ZONE_POOL 1882 typeset -i cntctr=5 1883 typeset -i i=0 1884 1885 # Create pool and 5 container within it 1886 # 1887 [[ -d /$pool_name ]] && rm -rf /$pool_name 1888 log_must zpool create -f $pool_name $DISKS 1889 while ((i < cntctr)); do 1890 log_must zfs create $pool_name/$prefix_ctr$i 1891 ((i += 1)) 1892 done 1893 1894 # create a zvol 1895 log_must zfs create -V 1g $pool_name/zone_zvol 1896 block_device_wait 1897 1898 # 1899 # Add slog device for pool 1900 # 1901 typeset sdevs="$TEST_BASE_DIR/sdev1 $TEST_BASE_DIR/sdev2" 1902 log_must mkfile $MINVDEVSIZE $sdevs 1903 log_must zpool add $pool_name log mirror $sdevs 1904 1905 # this isn't supported just yet. 1906 # Create a filesystem. In order to add this to 1907 # the zone, it must have it's mountpoint set to 'legacy' 1908 # log_must zfs create $pool_name/zfs_filesystem 1909 # log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem 1910 1911 [[ -d $zone_root ]] && \ 1912 log_must rm -rf $zone_root/$zone_name 1913 [[ ! -d $zone_root ]] && \ 1914 log_must mkdir -p -m 0700 $zone_root/$zone_name 1915 1916 # Create zone configure file and configure the zone 1917 # 1918 typeset zone_conf=/tmp/zone_conf.$$ 1919 echo "create" > $zone_conf 1920 echo "set zonepath=$zone_root/$zone_name" >> $zone_conf 1921 echo "set autoboot=true" >> $zone_conf 1922 i=0 1923 while ((i < cntctr)); do 1924 echo "add dataset" >> $zone_conf 1925 echo "set name=$pool_name/$prefix_ctr$i" >> \ 1926 $zone_conf 1927 echo "end" >> $zone_conf 1928 ((i += 1)) 1929 done 1930 1931 # add our zvol to the zone 1932 echo "add device" >> $zone_conf 1933 echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf 1934 echo "end" >> $zone_conf 1935 1936 # add a corresponding zvol rdsk to the zone 1937 echo "add device" >> $zone_conf 1938 echo "set match=$ZVOL_RDEVDIR/$pool_name/zone_zvol" >> $zone_conf 1939 echo "end" >> $zone_conf 1940 1941 # once it's supported, we'll add our filesystem to the zone 1942 # echo "add fs" >> $zone_conf 1943 # echo "set type=zfs" >> $zone_conf 1944 # echo "set special=$pool_name/zfs_filesystem" >> $zone_conf 1945 # echo "set dir=/export/zfs_filesystem" >> $zone_conf 1946 # echo "end" >> $zone_conf 1947 1948 echo "verify" >> $zone_conf 1949 echo "commit" >> $zone_conf 1950 log_must zonecfg -z $zone_name -f $zone_conf 1951 log_must rm -f $zone_conf 1952 1953 # Install the zone 1954 zoneadm -z $zone_name install 1955 if (($? == 0)); then 1956 log_note "SUCCESS: zoneadm -z $zone_name install" 1957 else 1958 log_fail "FAIL: zoneadm -z $zone_name install" 1959 fi 1960 1961 # Install sysidcfg file 1962 # 1963 typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg 1964 echo "system_locale=C" > $sysidcfg 1965 echo "terminal=dtterm" >> $sysidcfg 1966 echo "network_interface=primary {" >> $sysidcfg 1967 echo "hostname=$zone_name" >> $sysidcfg 1968 echo "}" >> $sysidcfg 1969 echo "name_service=NONE" >> $sysidcfg 1970 echo "root_password=mo791xfZ/SFiw" >> $sysidcfg 1971 echo "security_policy=NONE" >> $sysidcfg 1972 echo "timezone=US/Eastern" >> $sysidcfg 1973 1974 # Boot this zone 1975 log_must zoneadm -z $zone_name boot 1976} 1977 1978# 1979# Reexport TESTPOOL & TESTPOOL(1-4) 1980# 1981function reexport_pool 1982{ 1983 typeset -i cntctr=5 1984 typeset -i i=0 1985 1986 while ((i < cntctr)); do 1987 if ((i == 0)); then 1988 TESTPOOL=$ZONE_POOL/$ZONE_CTR$i 1989 if ! ismounted $TESTPOOL; then 1990 log_must zfs mount $TESTPOOL 1991 fi 1992 else 1993 eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i 1994 if eval ! ismounted \$TESTPOOL$i; then 1995 log_must eval zfs mount \$TESTPOOL$i 1996 fi 1997 fi 1998 ((i += 1)) 1999 done 2000} 2001 2002# 2003# Verify a given disk or pool state 2004# 2005# Return 0 is pool/disk matches expected state, 1 otherwise 2006# 2007function check_state # pool disk state{online,offline,degraded} 2008{ 2009 typeset pool=$1 2010 typeset disk=${2#$DEV_DSKDIR/} 2011 typeset state=$3 2012 2013 [[ -z $pool ]] || [[ -z $state ]] \ 2014 && log_fail "Arguments invalid or missing" 2015 2016 if [[ -z $disk ]]; then 2017 #check pool state only 2018 zpool get -H -o value health $pool \ 2019 | grep -i "$state" > /dev/null 2>&1 2020 else 2021 zpool status -v $pool | grep "$disk" \ 2022 | grep -i "$state" > /dev/null 2>&1 2023 fi 2024 2025 return $? 2026} 2027 2028# 2029# Get the mountpoint of snapshot 2030# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap> 2031# as its mountpoint 2032# 2033function snapshot_mountpoint 2034{ 2035 typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 2036 2037 if [[ $dataset != *@* ]]; then 2038 log_fail "Error name of snapshot '$dataset'." 2039 fi 2040 2041 typeset fs=${dataset%@*} 2042 typeset snap=${dataset#*@} 2043 2044 if [[ -z $fs || -z $snap ]]; then 2045 log_fail "Error name of snapshot '$dataset'." 2046 fi 2047 2048 echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap 2049} 2050 2051# 2052# Given a device and 'ashift' value verify it's correctly set on every label 2053# 2054function verify_ashift # device ashift 2055{ 2056 typeset device="$1" 2057 typeset ashift="$2" 2058 2059 zdb -e -lll $device | awk -v ashift=$ashift '/ashift: / { 2060 if (ashift != $2) 2061 exit 1; 2062 else 2063 count++; 2064 } END { 2065 if (count != 4) 2066 exit 1; 2067 else 2068 exit 0; 2069 }' 2070 2071 return $? 2072} 2073 2074# 2075# Given a pool and file system, this function will verify the file system 2076# using the zdb internal tool. Note that the pool is exported and imported 2077# to ensure it has consistent state. 2078# 2079function verify_filesys # pool filesystem dir 2080{ 2081 typeset pool="$1" 2082 typeset filesys="$2" 2083 typeset zdbout="/tmp/zdbout.$$" 2084 2085 shift 2086 shift 2087 typeset dirs=$@ 2088 typeset search_path="" 2089 2090 log_note "Calling zdb to verify filesystem '$filesys'" 2091 zfs unmount -a > /dev/null 2>&1 2092 log_must zpool export $pool 2093 2094 if [[ -n $dirs ]] ; then 2095 for dir in $dirs ; do 2096 search_path="$search_path -d $dir" 2097 done 2098 fi 2099 2100 log_must zpool import $search_path $pool 2101 2102 zdb -cudi $filesys > $zdbout 2>&1 2103 if [[ $? != 0 ]]; then 2104 log_note "Output: zdb -cudi $filesys" 2105 cat $zdbout 2106 log_fail "zdb detected errors with: '$filesys'" 2107 fi 2108 2109 log_must zfs mount -a 2110 log_must rm -rf $zdbout 2111} 2112 2113# 2114# Given a pool issue a scrub and verify that no checksum errors are reported. 2115# 2116function verify_pool 2117{ 2118 typeset pool=${1:-$TESTPOOL} 2119 2120 log_must zpool scrub $pool 2121 log_must wait_scrubbed $pool 2122 2123 typeset -i cksum=$(zpool status $pool | awk ' 2124 !NF { isvdev = 0 } 2125 isvdev { errors += $NF } 2126 /CKSUM$/ { isvdev = 1 } 2127 END { print errors } 2128 ') 2129 if [[ $cksum != 0 ]]; then 2130 log_must zpool status -v 2131 log_fail "Unexpected CKSUM errors found on $pool ($cksum)" 2132 fi 2133} 2134 2135# 2136# Given a pool, and this function list all disks in the pool 2137# 2138function get_disklist # pool 2139{ 2140 typeset disklist="" 2141 2142 disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \ 2143 grep -v "\-\-\-\-\-" | \ 2144 egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$") 2145 2146 echo $disklist 2147} 2148 2149# 2150# Given a pool, and this function list all disks in the pool with their full 2151# path (like "/dev/sda" instead of "sda"). 2152# 2153function get_disklist_fullpath # pool 2154{ 2155 args="-P $1" 2156 get_disklist $args 2157} 2158 2159 2160 2161# /** 2162# This function kills a given list of processes after a time period. We use 2163# this in the stress tests instead of STF_TIMEOUT so that we can have processes 2164# run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT 2165# would be listed as FAIL, which we don't want : we're happy with stress tests 2166# running for a certain amount of time, then finishing. 2167# 2168# @param $1 the time in seconds after which we should terminate these processes 2169# @param $2..$n the processes we wish to terminate. 2170# */ 2171function stress_timeout 2172{ 2173 typeset -i TIMEOUT=$1 2174 shift 2175 typeset cpids="$@" 2176 2177 log_note "Waiting for child processes($cpids). " \ 2178 "It could last dozens of minutes, please be patient ..." 2179 log_must sleep $TIMEOUT 2180 2181 log_note "Killing child processes after ${TIMEOUT} stress timeout." 2182 typeset pid 2183 for pid in $cpids; do 2184 ps -p $pid > /dev/null 2>&1 2185 if (($? == 0)); then 2186 log_must kill -USR1 $pid 2187 fi 2188 done 2189} 2190 2191# 2192# Verify a given hotspare disk is inuse or avail 2193# 2194# Return 0 is pool/disk matches expected state, 1 otherwise 2195# 2196function check_hotspare_state # pool disk state{inuse,avail} 2197{ 2198 typeset pool=$1 2199 typeset disk=${2#$DEV_DSKDIR/} 2200 typeset state=$3 2201 2202 cur_state=$(get_device_state $pool $disk "spares") 2203 2204 if [[ $state != ${cur_state} ]]; then 2205 return 1 2206 fi 2207 return 0 2208} 2209 2210# 2211# Wait until a hotspare transitions to a given state or times out. 2212# 2213# Return 0 when pool/disk matches expected state, 1 on timeout. 2214# 2215function wait_hotspare_state # pool disk state timeout 2216{ 2217 typeset pool=$1 2218 typeset disk=${2#*$DEV_DSKDIR/} 2219 typeset state=$3 2220 typeset timeout=${4:-60} 2221 typeset -i i=0 2222 2223 while [[ $i -lt $timeout ]]; do 2224 if check_hotspare_state $pool $disk $state; then 2225 return 0 2226 fi 2227 2228 i=$((i+1)) 2229 sleep 1 2230 done 2231 2232 return 1 2233} 2234 2235# 2236# Verify a given slog disk is inuse or avail 2237# 2238# Return 0 is pool/disk matches expected state, 1 otherwise 2239# 2240function check_slog_state # pool disk state{online,offline,unavail} 2241{ 2242 typeset pool=$1 2243 typeset disk=${2#$DEV_DSKDIR/} 2244 typeset state=$3 2245 2246 cur_state=$(get_device_state $pool $disk "logs") 2247 2248 if [[ $state != ${cur_state} ]]; then 2249 return 1 2250 fi 2251 return 0 2252} 2253 2254# 2255# Verify a given vdev disk is inuse or avail 2256# 2257# Return 0 is pool/disk matches expected state, 1 otherwise 2258# 2259function check_vdev_state # pool disk state{online,offline,unavail} 2260{ 2261 typeset pool=$1 2262 typeset disk=${2#*$DEV_DSKDIR/} 2263 typeset state=$3 2264 2265 cur_state=$(get_device_state $pool $disk) 2266 2267 if [[ $state != ${cur_state} ]]; then 2268 return 1 2269 fi 2270 return 0 2271} 2272 2273# 2274# Wait until a vdev transitions to a given state or times out. 2275# 2276# Return 0 when pool/disk matches expected state, 1 on timeout. 2277# 2278function wait_vdev_state # pool disk state timeout 2279{ 2280 typeset pool=$1 2281 typeset disk=${2#*$DEV_DSKDIR/} 2282 typeset state=$3 2283 typeset timeout=${4:-60} 2284 typeset -i i=0 2285 2286 while [[ $i -lt $timeout ]]; do 2287 if check_vdev_state $pool $disk $state; then 2288 return 0 2289 fi 2290 2291 i=$((i+1)) 2292 sleep 1 2293 done 2294 2295 return 1 2296} 2297 2298# 2299# Check the output of 'zpool status -v <pool>', 2300# and to see if the content of <token> contain the <keyword> specified. 2301# 2302# Return 0 is contain, 1 otherwise 2303# 2304function check_pool_status # pool token keyword <verbose> 2305{ 2306 typeset pool=$1 2307 typeset token=$2 2308 typeset keyword=$3 2309 typeset verbose=${4:-false} 2310 2311 scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" ' 2312 ($1==token) {print $0}') 2313 if [[ $verbose == true ]]; then 2314 log_note $scan 2315 fi 2316 echo $scan | egrep -i "$keyword" > /dev/null 2>&1 2317 2318 return $? 2319} 2320 2321# 2322# The following functions are instance of check_pool_status() 2323# is_pool_resilvering - to check if the pool resilver is in progress 2324# is_pool_resilvered - to check if the pool resilver is completed 2325# is_pool_scrubbing - to check if the pool scrub is in progress 2326# is_pool_scrubbed - to check if the pool scrub is completed 2327# is_pool_scrub_stopped - to check if the pool scrub is stopped 2328# is_pool_scrub_paused - to check if the pool scrub has paused 2329# is_pool_removing - to check if the pool removing is a vdev 2330# is_pool_removed - to check if the pool remove is completed 2331# is_pool_discarding - to check if the pool checkpoint is being discarded 2332# 2333function is_pool_resilvering #pool <verbose> 2334{ 2335 check_pool_status "$1" "scan" \ 2336 "resilver[ ()0-9A-Za-z:_-]* in progress since" $2 2337 return $? 2338} 2339 2340function is_pool_resilvered #pool <verbose> 2341{ 2342 check_pool_status "$1" "scan" "resilvered " $2 2343 return $? 2344} 2345 2346function is_pool_scrubbing #pool <verbose> 2347{ 2348 check_pool_status "$1" "scan" "scrub in progress since " $2 2349 return $? 2350} 2351 2352function is_pool_scrubbed #pool <verbose> 2353{ 2354 check_pool_status "$1" "scan" "scrub repaired" $2 2355 return $? 2356} 2357 2358function is_pool_scrub_stopped #pool <verbose> 2359{ 2360 check_pool_status "$1" "scan" "scrub canceled" $2 2361 return $? 2362} 2363 2364function is_pool_scrub_paused #pool <verbose> 2365{ 2366 check_pool_status "$1" "scan" "scrub paused since " $2 2367 return $? 2368} 2369 2370function is_pool_removing #pool 2371{ 2372 check_pool_status "$1" "remove" "in progress since " 2373 return $? 2374} 2375 2376function is_pool_removed #pool 2377{ 2378 check_pool_status "$1" "remove" "completed on" 2379 return $? 2380} 2381 2382function is_pool_discarding #pool 2383{ 2384 check_pool_status "$1" "checkpoint" "discarding" 2385 return $? 2386} 2387 2388function wait_for_degraded 2389{ 2390 typeset pool=$1 2391 typeset timeout=${2:-30} 2392 typeset t0=$SECONDS 2393 2394 while :; do 2395 [[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break 2396 log_note "$pool is not yet degraded." 2397 sleep 1 2398 if ((SECONDS - t0 > $timeout)); then 2399 log_note "$pool not degraded after $timeout seconds." 2400 return 1 2401 fi 2402 done 2403 2404 return 0 2405} 2406 2407# 2408# Use create_pool()/destroy_pool() to clean up the information in 2409# in the given disk to avoid slice overlapping. 2410# 2411function cleanup_devices #vdevs 2412{ 2413 typeset pool="foopool$$" 2414 2415 for vdev in $@; do 2416 zero_partitions $vdev 2417 done 2418 2419 poolexists $pool && destroy_pool $pool 2420 create_pool $pool $@ 2421 destroy_pool $pool 2422 2423 return 0 2424} 2425 2426#/** 2427# A function to find and locate free disks on a system or from given 2428# disks as the parameter. It works by locating disks that are in use 2429# as swap devices and dump devices, and also disks listed in /etc/vfstab 2430# 2431# $@ given disks to find which are free, default is all disks in 2432# the test system 2433# 2434# @return a string containing the list of available disks 2435#*/ 2436function find_disks 2437{ 2438 # Trust provided list, no attempt is made to locate unused devices. 2439 if is_linux || is_freebsd; then 2440 echo "$@" 2441 return 2442 fi 2443 2444 2445 sfi=/tmp/swaplist.$$ 2446 dmpi=/tmp/dumpdev.$$ 2447 max_finddisksnum=${MAX_FINDDISKSNUM:-6} 2448 2449 swap -l > $sfi 2450 dumpadm > $dmpi 2>/dev/null 2451 2452# write an awk script that can process the output of format 2453# to produce a list of disks we know about. Note that we have 2454# to escape "$2" so that the shell doesn't interpret it while 2455# we're creating the awk script. 2456# ------------------- 2457 cat > /tmp/find_disks.awk <<EOF 2458#!/bin/nawk -f 2459 BEGIN { FS="."; } 2460 2461 /^Specify disk/{ 2462 searchdisks=0; 2463 } 2464 2465 { 2466 if (searchdisks && \$2 !~ "^$"){ 2467 split(\$2,arr," "); 2468 print arr[1]; 2469 } 2470 } 2471 2472 /^AVAILABLE DISK SELECTIONS:/{ 2473 searchdisks=1; 2474 } 2475EOF 2476#--------------------- 2477 2478 chmod 755 /tmp/find_disks.awk 2479 disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)} 2480 rm /tmp/find_disks.awk 2481 2482 unused="" 2483 for disk in $disks; do 2484 # Check for mounted 2485 grep "${disk}[sp]" /etc/mnttab >/dev/null 2486 (($? == 0)) && continue 2487 # Check for swap 2488 grep "${disk}[sp]" $sfi >/dev/null 2489 (($? == 0)) && continue 2490 # check for dump device 2491 grep "${disk}[sp]" $dmpi >/dev/null 2492 (($? == 0)) && continue 2493 # check to see if this disk hasn't been explicitly excluded 2494 # by a user-set environment variable 2495 echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null 2496 (($? == 0)) && continue 2497 unused_candidates="$unused_candidates $disk" 2498 done 2499 rm $sfi 2500 rm $dmpi 2501 2502# now just check to see if those disks do actually exist 2503# by looking for a device pointing to the first slice in 2504# each case. limit the number to max_finddisksnum 2505 count=0 2506 for disk in $unused_candidates; do 2507 if is_disk_device $DEV_DSKDIR/${disk}s0 && \ 2508 [ $count -lt $max_finddisksnum ]; then 2509 unused="$unused $disk" 2510 # do not impose limit if $@ is provided 2511 [[ -z $@ ]] && ((count = count + 1)) 2512 fi 2513 done 2514 2515# finally, return our disk list 2516 echo $unused 2517} 2518 2519function add_user_freebsd #<group_name> <user_name> <basedir> 2520{ 2521 typeset group=$1 2522 typeset user=$2 2523 typeset basedir=$3 2524 2525 # Check to see if the user exists. 2526 if id $user > /dev/null 2>&1; then 2527 return 0 2528 fi 2529 2530 # Assign 1000 as the base uid 2531 typeset -i uid=1000 2532 while true; do 2533 typeset -i ret 2534 pw useradd -u $uid -g $group -d $basedir/$user -m -n $user 2535 ret=$? 2536 case $ret in 2537 0) break ;; 2538 # The uid is not unique 2539 65) ((uid += 1)) ;; 2540 *) return 1 ;; 2541 esac 2542 if [[ $uid == 65000 ]]; then 2543 log_fail "No user id available under 65000 for $user" 2544 fi 2545 done 2546 2547 # Silence MOTD 2548 touch $basedir/$user/.hushlogin 2549 2550 return 0 2551} 2552 2553# 2554# Delete the specified user. 2555# 2556# $1 login name 2557# 2558function del_user_freebsd #<logname> 2559{ 2560 typeset user=$1 2561 2562 if id $user > /dev/null 2>&1; then 2563 log_must pw userdel $user 2564 fi 2565 2566 return 0 2567} 2568 2569# 2570# Select valid gid and create specified group. 2571# 2572# $1 group name 2573# 2574function add_group_freebsd #<group_name> 2575{ 2576 typeset group=$1 2577 2578 # See if the group already exists. 2579 if pw groupshow $group >/dev/null 2>&1; then 2580 return 0 2581 fi 2582 2583 # Assign 1000 as the base gid 2584 typeset -i gid=1000 2585 while true; do 2586 pw groupadd -g $gid -n $group > /dev/null 2>&1 2587 typeset -i ret=$? 2588 case $ret in 2589 0) return 0 ;; 2590 # The gid is not unique 2591 65) ((gid += 1)) ;; 2592 *) return 1 ;; 2593 esac 2594 if [[ $gid == 65000 ]]; then 2595 log_fail "No user id available under 65000 for $group" 2596 fi 2597 done 2598} 2599 2600# 2601# Delete the specified group. 2602# 2603# $1 group name 2604# 2605function del_group_freebsd #<group_name> 2606{ 2607 typeset group=$1 2608 2609 pw groupdel -n $group > /dev/null 2>&1 2610 typeset -i ret=$? 2611 case $ret in 2612 # Group does not exist, or was deleted successfully. 2613 0|6|65) return 0 ;; 2614 # Name already exists as a group name 2615 9) log_must pw groupdel $group ;; 2616 *) return 1 ;; 2617 esac 2618 2619 return 0 2620} 2621 2622function add_user_illumos #<group_name> <user_name> <basedir> 2623{ 2624 typeset group=$1 2625 typeset user=$2 2626 typeset basedir=$3 2627 2628 log_must useradd -g $group -d $basedir/$user -m $user 2629 2630 return 0 2631} 2632 2633function del_user_illumos #<user_name> 2634{ 2635 typeset user=$1 2636 2637 if id $user > /dev/null 2>&1; then 2638 log_must_retry "currently used" 6 userdel $user 2639 fi 2640 2641 return 0 2642} 2643 2644function add_group_illumos #<group_name> 2645{ 2646 typeset group=$1 2647 2648 typeset -i gid=100 2649 while true; do 2650 groupadd -g $gid $group > /dev/null 2>&1 2651 typeset -i ret=$? 2652 case $ret in 2653 0) return 0 ;; 2654 # The gid is not unique 2655 4) ((gid += 1)) ;; 2656 *) return 1 ;; 2657 esac 2658 done 2659} 2660 2661function del_group_illumos #<group_name> 2662{ 2663 typeset group=$1 2664 2665 groupmod -n $grp $grp > /dev/null 2>&1 2666 typeset -i ret=$? 2667 case $ret in 2668 # Group does not exist. 2669 6) return 0 ;; 2670 # Name already exists as a group name 2671 9) log_must groupdel $grp ;; 2672 *) return 1 ;; 2673 esac 2674} 2675 2676function add_user_linux #<group_name> <user_name> <basedir> 2677{ 2678 typeset group=$1 2679 typeset user=$2 2680 typeset basedir=$3 2681 2682 log_must useradd -g $group -d $basedir/$user -m $user 2683 2684 # Add new users to the same group and the command line utils. 2685 # This allows them to be run out of the original users home 2686 # directory as long as it permissioned to be group readable. 2687 cmd_group=$(stat --format="%G" $(which zfs)) 2688 log_must usermod -a -G $cmd_group $user 2689 2690 return 0 2691} 2692 2693function del_user_linux #<user_name> 2694{ 2695 typeset user=$1 2696 2697 if id $user > /dev/null 2>&1; then 2698 log_must_retry "currently used" 6 userdel $user 2699 fi 2700 2701 return 0 2702} 2703 2704function add_group_linux #<group_name> 2705{ 2706 typeset group=$1 2707 2708 # Assign 100 as the base gid, a larger value is selected for 2709 # Linux because for many distributions 1000 and under are reserved. 2710 while true; do 2711 groupadd $group > /dev/null 2>&1 2712 typeset -i ret=$? 2713 case $ret in 2714 0) return 0 ;; 2715 *) return 1 ;; 2716 esac 2717 done 2718} 2719 2720function del_group_linux #<group_name> 2721{ 2722 typeset group=$1 2723 2724 getent group $group > /dev/null 2>&1 2725 typeset -i ret=$? 2726 case $ret in 2727 # Group does not exist. 2728 2) return 0 ;; 2729 # Name already exists as a group name 2730 0) log_must groupdel $group ;; 2731 *) return 1 ;; 2732 esac 2733 2734 return 0 2735} 2736 2737# 2738# Add specified user to specified group 2739# 2740# $1 group name 2741# $2 user name 2742# $3 base of the homedir (optional) 2743# 2744function add_user #<group_name> <user_name> <basedir> 2745{ 2746 typeset group=$1 2747 typeset user=$2 2748 typeset basedir=${3:-"/var/tmp"} 2749 2750 if ((${#group} == 0 || ${#user} == 0)); then 2751 log_fail "group name or user name are not defined." 2752 fi 2753 2754 case $(uname) in 2755 FreeBSD) 2756 add_user_freebsd "$group" "$user" "$basedir" 2757 ;; 2758 Linux) 2759 add_user_linux "$group" "$user" "$basedir" 2760 ;; 2761 *) 2762 add_user_illumos "$group" "$user" "$basedir" 2763 ;; 2764 esac 2765 2766 return 0 2767} 2768 2769# 2770# Delete the specified user. 2771# 2772# $1 login name 2773# $2 base of the homedir (optional) 2774# 2775function del_user #<logname> <basedir> 2776{ 2777 typeset user=$1 2778 typeset basedir=${2:-"/var/tmp"} 2779 2780 if ((${#user} == 0)); then 2781 log_fail "login name is necessary." 2782 fi 2783 2784 case $(uname) in 2785 FreeBSD) 2786 del_user_freebsd "$user" 2787 ;; 2788 Linux) 2789 del_user_linux "$user" 2790 ;; 2791 *) 2792 del_user_illumos "$user" 2793 ;; 2794 esac 2795 2796 [[ -d $basedir/$user ]] && rm -fr $basedir/$user 2797 2798 return 0 2799} 2800 2801# 2802# Select valid gid and create specified group. 2803# 2804# $1 group name 2805# 2806function add_group #<group_name> 2807{ 2808 typeset group=$1 2809 2810 if ((${#group} == 0)); then 2811 log_fail "group name is necessary." 2812 fi 2813 2814 case $(uname) in 2815 FreeBSD) 2816 add_group_freebsd "$group" 2817 ;; 2818 Linux) 2819 add_group_linux "$group" 2820 ;; 2821 *) 2822 add_group_illumos "$group" 2823 ;; 2824 esac 2825 2826 return 0 2827} 2828 2829# 2830# Delete the specified group. 2831# 2832# $1 group name 2833# 2834function del_group #<group_name> 2835{ 2836 typeset group=$1 2837 2838 if ((${#group} == 0)); then 2839 log_fail "group name is necessary." 2840 fi 2841 2842 case $(uname) in 2843 FreeBSD) 2844 del_group_freebsd "$group" 2845 ;; 2846 Linux) 2847 del_group_linux "$group" 2848 ;; 2849 *) 2850 del_group_illumos "$group" 2851 ;; 2852 esac 2853 2854 return 0 2855} 2856 2857# 2858# This function will return true if it's safe to destroy the pool passed 2859# as argument 1. It checks for pools based on zvols and files, and also 2860# files contained in a pool that may have a different mountpoint. 2861# 2862function safe_to_destroy_pool { # $1 the pool name 2863 2864 typeset pool="" 2865 typeset DONT_DESTROY="" 2866 2867 # We check that by deleting the $1 pool, we're not 2868 # going to pull the rug out from other pools. Do this 2869 # by looking at all other pools, ensuring that they 2870 # aren't built from files or zvols contained in this pool. 2871 2872 for pool in $(zpool list -H -o name) 2873 do 2874 ALTMOUNTPOOL="" 2875 2876 # this is a list of the top-level directories in each of the 2877 # files that make up the path to the files the pool is based on 2878 FILEPOOL=$(zpool status -v $pool | grep /$1/ | \ 2879 awk '{print $1}') 2880 2881 # this is a list of the zvols that make up the pool 2882 ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \ 2883 | awk '{print $1}') 2884 2885 # also want to determine if it's a file-based pool using an 2886 # alternate mountpoint... 2887 POOL_FILE_DIRS=$(zpool status -v $pool | \ 2888 grep / | awk '{print $1}' | \ 2889 awk -F/ '{print $2}' | grep -v "dev") 2890 2891 for pooldir in $POOL_FILE_DIRS 2892 do 2893 OUTPUT=$(zfs list -H -r -o mountpoint $1 | \ 2894 grep "${pooldir}$" | awk '{print $1}') 2895 2896 ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}" 2897 done 2898 2899 2900 if [ ! -z "$ZVOLPOOL" ] 2901 then 2902 DONT_DESTROY="true" 2903 log_note "Pool $pool is built from $ZVOLPOOL on $1" 2904 fi 2905 2906 if [ ! -z "$FILEPOOL" ] 2907 then 2908 DONT_DESTROY="true" 2909 log_note "Pool $pool is built from $FILEPOOL on $1" 2910 fi 2911 2912 if [ ! -z "$ALTMOUNTPOOL" ] 2913 then 2914 DONT_DESTROY="true" 2915 log_note "Pool $pool is built from $ALTMOUNTPOOL on $1" 2916 fi 2917 done 2918 2919 if [ -z "${DONT_DESTROY}" ] 2920 then 2921 return 0 2922 else 2923 log_note "Warning: it is not safe to destroy $1!" 2924 return 1 2925 fi 2926} 2927 2928# 2929# Verify zfs operation with -p option work as expected 2930# $1 operation, value could be create, clone or rename 2931# $2 dataset type, value could be fs or vol 2932# $3 dataset name 2933# $4 new dataset name 2934# 2935function verify_opt_p_ops 2936{ 2937 typeset ops=$1 2938 typeset datatype=$2 2939 typeset dataset=$3 2940 typeset newdataset=$4 2941 2942 if [[ $datatype != "fs" && $datatype != "vol" ]]; then 2943 log_fail "$datatype is not supported." 2944 fi 2945 2946 # check parameters accordingly 2947 case $ops in 2948 create) 2949 newdataset=$dataset 2950 dataset="" 2951 if [[ $datatype == "vol" ]]; then 2952 ops="create -V $VOLSIZE" 2953 fi 2954 ;; 2955 clone) 2956 if [[ -z $newdataset ]]; then 2957 log_fail "newdataset should not be empty" \ 2958 "when ops is $ops." 2959 fi 2960 log_must datasetexists $dataset 2961 log_must snapexists $dataset 2962 ;; 2963 rename) 2964 if [[ -z $newdataset ]]; then 2965 log_fail "newdataset should not be empty" \ 2966 "when ops is $ops." 2967 fi 2968 log_must datasetexists $dataset 2969 ;; 2970 *) 2971 log_fail "$ops is not supported." 2972 ;; 2973 esac 2974 2975 # make sure the upper level filesystem does not exist 2976 destroy_dataset "${newdataset%/*}" "-rRf" 2977 2978 # without -p option, operation will fail 2979 log_mustnot zfs $ops $dataset $newdataset 2980 log_mustnot datasetexists $newdataset ${newdataset%/*} 2981 2982 # with -p option, operation should succeed 2983 log_must zfs $ops -p $dataset $newdataset 2984 block_device_wait 2985 2986 if ! datasetexists $newdataset ; then 2987 log_fail "-p option does not work for $ops" 2988 fi 2989 2990 # when $ops is create or clone, redo the operation still return zero 2991 if [[ $ops != "rename" ]]; then 2992 log_must zfs $ops -p $dataset $newdataset 2993 fi 2994 2995 return 0 2996} 2997 2998# 2999# Get configuration of pool 3000# $1 pool name 3001# $2 config name 3002# 3003function get_config 3004{ 3005 typeset pool=$1 3006 typeset config=$2 3007 typeset alt_root 3008 3009 if ! poolexists "$pool" ; then 3010 return 1 3011 fi 3012 alt_root=$(zpool list -H $pool | awk '{print $NF}') 3013 if [[ $alt_root == "-" ]]; then 3014 value=$(zdb -C $pool | grep "$config:" | awk -F: \ 3015 '{print $2}') 3016 else 3017 value=$(zdb -e $pool | grep "$config:" | awk -F: \ 3018 '{print $2}') 3019 fi 3020 if [[ -n $value ]] ; then 3021 value=${value#'} 3022 value=${value%'} 3023 fi 3024 echo $value 3025 3026 return 0 3027} 3028 3029# 3030# Privated function. Random select one of items from arguments. 3031# 3032# $1 count 3033# $2-n string 3034# 3035function _random_get 3036{ 3037 typeset cnt=$1 3038 shift 3039 3040 typeset str="$@" 3041 typeset -i ind 3042 ((ind = RANDOM % cnt + 1)) 3043 3044 typeset ret=$(echo "$str" | cut -f $ind -d ' ') 3045 echo $ret 3046} 3047 3048# 3049# Random select one of item from arguments which include NONE string 3050# 3051function random_get_with_non 3052{ 3053 typeset -i cnt=$# 3054 ((cnt =+ 1)) 3055 3056 _random_get "$cnt" "$@" 3057} 3058 3059# 3060# Random select one of item from arguments which doesn't include NONE string 3061# 3062function random_get 3063{ 3064 _random_get "$#" "$@" 3065} 3066 3067# 3068# The function will generate a dataset name with specific length 3069# $1, the length of the name 3070# $2, the base string to construct the name 3071# 3072function gen_dataset_name 3073{ 3074 typeset -i len=$1 3075 typeset basestr="$2" 3076 typeset -i baselen=${#basestr} 3077 typeset -i iter=0 3078 typeset l_name="" 3079 3080 if ((len % baselen == 0)); then 3081 ((iter = len / baselen)) 3082 else 3083 ((iter = len / baselen + 1)) 3084 fi 3085 while ((iter > 0)); do 3086 l_name="${l_name}$basestr" 3087 3088 ((iter -= 1)) 3089 done 3090 3091 echo $l_name 3092} 3093 3094# 3095# Get cksum tuple of dataset 3096# $1 dataset name 3097# 3098# sample zdb output: 3099# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp 3100# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4 3101# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P 3102# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744 3103function datasetcksum 3104{ 3105 typeset cksum 3106 sync 3107 sync_all_pools 3108 cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \ 3109 | awk -F= '{print $7}') 3110 echo $cksum 3111} 3112 3113# 3114# Get cksum of file 3115# #1 file path 3116# 3117function checksum 3118{ 3119 typeset cksum 3120 cksum=$(cksum $1 | awk '{print $1}') 3121 echo $cksum 3122} 3123 3124# 3125# Get the given disk/slice state from the specific field of the pool 3126# 3127function get_device_state #pool disk field("", "spares","logs") 3128{ 3129 typeset pool=$1 3130 typeset disk=${2#$DEV_DSKDIR/} 3131 typeset field=${3:-$pool} 3132 3133 state=$(zpool status -v "$pool" 2>/dev/null | \ 3134 nawk -v device=$disk -v pool=$pool -v field=$field \ 3135 'BEGIN {startconfig=0; startfield=0; } 3136 /config:/ {startconfig=1} 3137 (startconfig==1) && ($1==field) {startfield=1; next;} 3138 (startfield==1) && ($1==device) {print $2; exit;} 3139 (startfield==1) && 3140 ($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}') 3141 echo $state 3142} 3143 3144 3145# 3146# print the given directory filesystem type 3147# 3148# $1 directory name 3149# 3150function get_fstype 3151{ 3152 typeset dir=$1 3153 3154 if [[ -z $dir ]]; then 3155 log_fail "Usage: get_fstype <directory>" 3156 fi 3157 3158 # 3159 # $ df -n / 3160 # / : ufs 3161 # 3162 df -n $dir | awk '{print $3}' 3163} 3164 3165# 3166# Given a disk, label it to VTOC regardless what label was on the disk 3167# $1 disk 3168# 3169function labelvtoc 3170{ 3171 typeset disk=$1 3172 if [[ -z $disk ]]; then 3173 log_fail "The disk name is unspecified." 3174 fi 3175 typeset label_file=/var/tmp/labelvtoc.$$ 3176 typeset arch=$(uname -p) 3177 3178 if is_linux || is_freebsd; then 3179 log_note "Currently unsupported by the test framework" 3180 return 1 3181 fi 3182 3183 if [[ $arch == "i386" ]]; then 3184 echo "label" > $label_file 3185 echo "0" >> $label_file 3186 echo "" >> $label_file 3187 echo "q" >> $label_file 3188 echo "q" >> $label_file 3189 3190 fdisk -B $disk >/dev/null 2>&1 3191 # wait a while for fdisk finishes 3192 sleep 60 3193 elif [[ $arch == "sparc" ]]; then 3194 echo "label" > $label_file 3195 echo "0" >> $label_file 3196 echo "" >> $label_file 3197 echo "" >> $label_file 3198 echo "" >> $label_file 3199 echo "q" >> $label_file 3200 else 3201 log_fail "unknown arch type" 3202 fi 3203 3204 format -e -s -d $disk -f $label_file 3205 typeset -i ret_val=$? 3206 rm -f $label_file 3207 # 3208 # wait the format to finish 3209 # 3210 sleep 60 3211 if ((ret_val != 0)); then 3212 log_fail "unable to label $disk as VTOC." 3213 fi 3214 3215 return 0 3216} 3217 3218# 3219# check if the system was installed as zfsroot or not 3220# return: 0 if zfsroot, non-zero if not 3221# 3222function is_zfsroot 3223{ 3224 df -n / | grep zfs > /dev/null 2>&1 3225 return $? 3226} 3227 3228# 3229# get the root filesystem name if it's zfsroot system. 3230# 3231# return: root filesystem name 3232function get_rootfs 3233{ 3234 typeset rootfs="" 3235 3236 if is_freebsd; then 3237 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3238 elif ! is_linux; then 3239 rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \ 3240 /etc/mnttab) 3241 fi 3242 if [[ -z "$rootfs" ]]; then 3243 log_fail "Can not get rootfs" 3244 fi 3245 zfs list $rootfs > /dev/null 2>&1 3246 if (($? == 0)); then 3247 echo $rootfs 3248 else 3249 log_fail "This is not a zfsroot system." 3250 fi 3251} 3252 3253# 3254# get the rootfs's pool name 3255# return: 3256# rootpool name 3257# 3258function get_rootpool 3259{ 3260 typeset rootfs="" 3261 typeset rootpool="" 3262 3263 if is_freebsd; then 3264 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3265 elif ! is_linux; then 3266 rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \ 3267 /etc/mnttab) 3268 fi 3269 if [[ -z "$rootfs" ]]; then 3270 log_fail "Can not get rootpool" 3271 fi 3272 zfs list $rootfs > /dev/null 2>&1 3273 if (($? == 0)); then 3274 echo ${rootfs%%/*} 3275 else 3276 log_fail "This is not a zfsroot system." 3277 fi 3278} 3279 3280# 3281# Get the word numbers from a string separated by white space 3282# 3283function get_word_count 3284{ 3285 echo $1 | wc -w 3286} 3287 3288# 3289# To verify if the require numbers of disks is given 3290# 3291function verify_disk_count 3292{ 3293 typeset -i min=${2:-1} 3294 3295 typeset -i count=$(get_word_count "$1") 3296 3297 if ((count < min)); then 3298 log_untested "A minimum of $min disks is required to run." \ 3299 " You specified $count disk(s)" 3300 fi 3301} 3302 3303function ds_is_volume 3304{ 3305 typeset type=$(get_prop type $1) 3306 [[ $type = "volume" ]] && return 0 3307 return 1 3308} 3309 3310function ds_is_filesystem 3311{ 3312 typeset type=$(get_prop type $1) 3313 [[ $type = "filesystem" ]] && return 0 3314 return 1 3315} 3316 3317function ds_is_snapshot 3318{ 3319 typeset type=$(get_prop type $1) 3320 [[ $type = "snapshot" ]] && return 0 3321 return 1 3322} 3323 3324# 3325# Check if Trusted Extensions are installed and enabled 3326# 3327function is_te_enabled 3328{ 3329 svcs -H -o state labeld 2>/dev/null | grep "enabled" 3330 if (($? != 0)); then 3331 return 1 3332 else 3333 return 0 3334 fi 3335} 3336 3337# Utility function to determine if a system has multiple cpus. 3338function is_mp 3339{ 3340 if is_linux; then 3341 (($(nproc) > 1)) 3342 elif is_freebsd; then 3343 sysctl -n kern.smp.cpus 3344 else 3345 (($(psrinfo | wc -l) > 1)) 3346 fi 3347 3348 return $? 3349} 3350 3351function get_cpu_freq 3352{ 3353 if is_linux; then 3354 lscpu | awk '/CPU MHz/ { print $3 }' 3355 elif is_freebsd; then 3356 sysctl -n hw.clockrate 3357 else 3358 psrinfo -v 0 | awk '/processor operates at/ {print $6}' 3359 fi 3360} 3361 3362# Run the given command as the user provided. 3363function user_run 3364{ 3365 typeset user=$1 3366 shift 3367 3368 log_note "user: $user" 3369 log_note "cmd: $*" 3370 3371 typeset out=$TEST_BASE_DIR/out 3372 typeset err=$TEST_BASE_DIR/err 3373 3374 sudo -Eu $user env PATH="$PATH" ksh <<<"$*" >$out 2>$err 3375 typeset res=$? 3376 log_note "out: $(<$out)" 3377 log_note "err: $(<$err)" 3378 return $res 3379} 3380 3381# 3382# Check if the pool contains the specified vdevs 3383# 3384# $1 pool 3385# $2..n <vdev> ... 3386# 3387# Return 0 if the vdevs are contained in the pool, 1 if any of the specified 3388# vdevs is not in the pool, and 2 if pool name is missing. 3389# 3390function vdevs_in_pool 3391{ 3392 typeset pool=$1 3393 typeset vdev 3394 3395 if [[ -z $pool ]]; then 3396 log_note "Missing pool name." 3397 return 2 3398 fi 3399 3400 shift 3401 3402 # We could use 'zpool list' to only get the vdevs of the pool but we 3403 # can't reference a mirror/raidz vdev using its ID (i.e mirror-0), 3404 # therefore we use the 'zpool status' output. 3405 typeset tmpfile=$(mktemp) 3406 zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile 3407 for vdev in $@; do 3408 grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1 3409 [[ $? -ne 0 ]] && return 1 3410 done 3411 3412 rm -f $tmpfile 3413 3414 return 0; 3415} 3416 3417function get_max 3418{ 3419 typeset -l i max=$1 3420 shift 3421 3422 for i in "$@"; do 3423 max=$((max > i ? max : i)) 3424 done 3425 3426 echo $max 3427} 3428 3429function get_min 3430{ 3431 typeset -l i min=$1 3432 shift 3433 3434 for i in "$@"; do 3435 min=$((min < i ? min : i)) 3436 done 3437 3438 echo $min 3439} 3440 3441# Write data that can be compressed into a directory 3442function write_compressible 3443{ 3444 typeset dir=$1 3445 typeset megs=$2 3446 typeset nfiles=${3:-1} 3447 typeset bs=${4:-1024k} 3448 typeset fname=${5:-file} 3449 3450 [[ -d $dir ]] || log_fail "No directory: $dir" 3451 3452 # Under Linux fio is not currently used since its behavior can 3453 # differ significantly across versions. This includes missing 3454 # command line options and cases where the --buffer_compress_* 3455 # options fail to behave as expected. 3456 if is_linux; then 3457 typeset file_bytes=$(to_bytes $megs) 3458 typeset bs_bytes=4096 3459 typeset blocks=$(($file_bytes / $bs_bytes)) 3460 3461 for (( i = 0; i < $nfiles; i++ )); do 3462 truncate -s $file_bytes $dir/$fname.$i 3463 3464 # Write every third block to get 66% compression. 3465 for (( j = 0; j < $blocks; j += 3 )); do 3466 dd if=/dev/urandom of=$dir/$fname.$i \ 3467 seek=$j bs=$bs_bytes count=1 \ 3468 conv=notrunc >/dev/null 2>&1 3469 done 3470 done 3471 else 3472 log_must eval "fio \ 3473 --name=job \ 3474 --fallocate=0 \ 3475 --minimal \ 3476 --randrepeat=0 \ 3477 --buffer_compress_percentage=66 \ 3478 --buffer_compress_chunk=4096 \ 3479 --directory=$dir \ 3480 --numjobs=$nfiles \ 3481 --nrfiles=$nfiles \ 3482 --rw=write \ 3483 --bs=$bs \ 3484 --filesize=$megs \ 3485 --filename_format='$fname.\$jobnum' >/dev/null" 3486 fi 3487} 3488 3489function get_objnum 3490{ 3491 typeset pathname=$1 3492 typeset objnum 3493 3494 [[ -e $pathname ]] || log_fail "No such file or directory: $pathname" 3495 if is_freebsd; then 3496 objnum=$(stat -f "%i" $pathname) 3497 else 3498 objnum=$(stat -c %i $pathname) 3499 fi 3500 echo $objnum 3501} 3502 3503# 3504# Sync data to the pool 3505# 3506# $1 pool name 3507# $2 boolean to force uberblock (and config including zpool cache file) update 3508# 3509function sync_pool #pool <force> 3510{ 3511 typeset pool=${1:-$TESTPOOL} 3512 typeset force=${2:-false} 3513 3514 if [[ $force == true ]]; then 3515 log_must zpool sync -f $pool 3516 else 3517 log_must zpool sync $pool 3518 fi 3519 3520 return 0 3521} 3522 3523# 3524# Sync all pools 3525# 3526# $1 boolean to force uberblock (and config including zpool cache file) update 3527# 3528function sync_all_pools #<force> 3529{ 3530 typeset force=${1:-false} 3531 3532 if [[ $force == true ]]; then 3533 log_must zpool sync -f 3534 else 3535 log_must zpool sync 3536 fi 3537 3538 return 0 3539} 3540 3541# 3542# Wait for zpool 'freeing' property drops to zero. 3543# 3544# $1 pool name 3545# 3546function wait_freeing #pool 3547{ 3548 typeset pool=${1:-$TESTPOOL} 3549 while true; do 3550 [[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break 3551 log_must sleep 1 3552 done 3553} 3554 3555# 3556# Wait for every device replace operation to complete 3557# 3558# $1 pool name 3559# 3560function wait_replacing #pool 3561{ 3562 typeset pool=${1:-$TESTPOOL} 3563 while true; do 3564 [[ "" == "$(zpool status $pool | 3565 awk '/replacing-[0-9]+/ {print $1}')" ]] && break 3566 log_must sleep 1 3567 done 3568} 3569 3570# 3571# Wait for a pool to be scrubbed 3572# 3573# $1 pool name 3574# 3575function wait_scrubbed 3576{ 3577 typeset pool=${1:-$TESTPOOL} 3578 while ! is_pool_scrubbed $pool ; do 3579 sleep 1 3580 done 3581} 3582 3583# Backup the zed.rc in our test directory so that we can edit it for our test. 3584# 3585# Returns: Backup file name. You will need to pass this to zed_rc_restore(). 3586function zed_rc_backup 3587{ 3588 zedrc_backup="$(mktemp)" 3589 cp $ZEDLET_DIR/zed.rc $zedrc_backup 3590 echo $zedrc_backup 3591} 3592 3593function zed_rc_restore 3594{ 3595 mv $1 $ZEDLET_DIR/zed.rc 3596} 3597 3598# 3599# Setup custom environment for the ZED. 3600# 3601# $@ Optional list of zedlets to run under zed. 3602function zed_setup 3603{ 3604 if ! is_linux; then 3605 log_unsupported "No zed on $(uname)" 3606 fi 3607 3608 if [[ ! -d $ZEDLET_DIR ]]; then 3609 log_must mkdir $ZEDLET_DIR 3610 fi 3611 3612 if [[ ! -e $VDEVID_CONF ]]; then 3613 log_must touch $VDEVID_CONF 3614 fi 3615 3616 if [[ -e $VDEVID_CONF_ETC ]]; then 3617 log_fail "Must not have $VDEVID_CONF_ETC file present on system" 3618 fi 3619 EXTRA_ZEDLETS=$@ 3620 3621 # Create a symlink for /etc/zfs/vdev_id.conf file. 3622 log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC 3623 3624 # Setup minimal ZED configuration. Individual test cases should 3625 # add additional ZEDLETs as needed for their specific test. 3626 log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR 3627 log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR 3628 3629 # Scripts must only be user writable. 3630 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3631 saved_umask=$(umask) 3632 log_must umask 0022 3633 for i in $EXTRA_ZEDLETS ; do 3634 log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR 3635 done 3636 log_must umask $saved_umask 3637 fi 3638 3639 # Customize the zed.rc file to enable the full debug log. 3640 log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc 3641 echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc 3642 3643} 3644 3645# 3646# Cleanup custom ZED environment. 3647# 3648# $@ Optional list of zedlets to remove from our test zed.d directory. 3649function zed_cleanup 3650{ 3651 if ! is_linux; then 3652 return 3653 fi 3654 EXTRA_ZEDLETS=$@ 3655 3656 log_must rm -f ${ZEDLET_DIR}/zed.rc 3657 log_must rm -f ${ZEDLET_DIR}/zed-functions.sh 3658 log_must rm -f ${ZEDLET_DIR}/all-syslog.sh 3659 log_must rm -f ${ZEDLET_DIR}/all-debug.sh 3660 log_must rm -f ${ZEDLET_DIR}/state 3661 3662 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3663 for i in $EXTRA_ZEDLETS ; do 3664 log_must rm -f ${ZEDLET_DIR}/$i 3665 done 3666 fi 3667 log_must rm -f $ZED_LOG 3668 log_must rm -f $ZED_DEBUG_LOG 3669 log_must rm -f $VDEVID_CONF_ETC 3670 log_must rm -f $VDEVID_CONF 3671 rmdir $ZEDLET_DIR 3672} 3673 3674# 3675# Check if ZED is currently running, if not start ZED. 3676# 3677function zed_start 3678{ 3679 if ! is_linux; then 3680 return 3681 fi 3682 3683 # ZEDLET_DIR=/var/tmp/zed 3684 if [[ ! -d $ZEDLET_DIR ]]; then 3685 log_must mkdir $ZEDLET_DIR 3686 fi 3687 3688 # Verify the ZED is not already running. 3689 pgrep -x zed > /dev/null 3690 if (($? == 0)); then 3691 log_note "ZED already running" 3692 else 3693 log_note "Starting ZED" 3694 # run ZED in the background and redirect foreground logging 3695 # output to $ZED_LOG. 3696 log_must truncate -s 0 $ZED_DEBUG_LOG 3697 log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \ 3698 "-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &" 3699 fi 3700 3701 return 0 3702} 3703 3704# 3705# Kill ZED process 3706# 3707function zed_stop 3708{ 3709 if ! is_linux; then 3710 return 3711 fi 3712 3713 log_note "Stopping ZED" 3714 while true; do 3715 zedpids="$(pgrep -x zed)" 3716 [ "$?" -ne 0 ] && break 3717 3718 log_must kill $zedpids 3719 sleep 1 3720 done 3721 return 0 3722} 3723 3724# 3725# Drain all zevents 3726# 3727function zed_events_drain 3728{ 3729 while [ $(zpool events -H | wc -l) -ne 0 ]; do 3730 sleep 1 3731 zpool events -c >/dev/null 3732 done 3733} 3734 3735# Set a variable in zed.rc to something, un-commenting it in the process. 3736# 3737# $1 variable 3738# $2 value 3739function zed_rc_set 3740{ 3741 var="$1" 3742 val="$2" 3743 # Remove the line 3744 cmd="'/$var/d'" 3745 eval sed -i $cmd $ZEDLET_DIR/zed.rc 3746 3747 # Add it at the end 3748 echo "$var=$val" >> $ZEDLET_DIR/zed.rc 3749} 3750 3751 3752# 3753# Check is provided device is being active used as a swap device. 3754# 3755function is_swap_inuse 3756{ 3757 typeset device=$1 3758 3759 if [[ -z $device ]] ; then 3760 log_note "No device specified." 3761 return 1 3762 fi 3763 3764 if is_linux; then 3765 swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1 3766 elif is_freebsd; then 3767 swapctl -l | grep -w $device 3768 else 3769 swap -l | grep -w $device > /dev/null 2>&1 3770 fi 3771 3772 return $? 3773} 3774 3775# 3776# Setup a swap device using the provided device. 3777# 3778function swap_setup 3779{ 3780 typeset swapdev=$1 3781 3782 if is_linux; then 3783 log_must eval "mkswap $swapdev > /dev/null 2>&1" 3784 log_must swapon $swapdev 3785 elif is_freebsd; then 3786 log_must swapctl -a $swapdev 3787 else 3788 log_must swap -a $swapdev 3789 fi 3790 3791 return 0 3792} 3793 3794# 3795# Cleanup a swap device on the provided device. 3796# 3797function swap_cleanup 3798{ 3799 typeset swapdev=$1 3800 3801 if is_swap_inuse $swapdev; then 3802 if is_linux; then 3803 log_must swapoff $swapdev 3804 elif is_freebsd; then 3805 log_must swapoff $swapdev 3806 else 3807 log_must swap -d $swapdev 3808 fi 3809 fi 3810 3811 return 0 3812} 3813 3814# 3815# Set a global system tunable (64-bit value) 3816# 3817# $1 tunable name (use a NAME defined in tunables.cfg) 3818# $2 tunable values 3819# 3820function set_tunable64 3821{ 3822 set_tunable_impl "$1" "$2" Z 3823} 3824 3825# 3826# Set a global system tunable (32-bit value) 3827# 3828# $1 tunable name (use a NAME defined in tunables.cfg) 3829# $2 tunable values 3830# 3831function set_tunable32 3832{ 3833 set_tunable_impl "$1" "$2" W 3834} 3835 3836function set_tunable_impl 3837{ 3838 typeset name="$1" 3839 typeset value="$2" 3840 typeset mdb_cmd="$3" 3841 typeset module="${4:-zfs}" 3842 3843 eval "typeset tunable=\$$name" 3844 case "$tunable" in 3845 UNSUPPORTED) 3846 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3847 ;; 3848 "") 3849 log_fail "Tunable '$name' must be added to tunables.cfg" 3850 ;; 3851 *) 3852 ;; 3853 esac 3854 3855 [[ -z "$value" ]] && return 1 3856 [[ -z "$mdb_cmd" ]] && return 1 3857 3858 case "$(uname)" in 3859 Linux) 3860 typeset zfs_tunables="/sys/module/$module/parameters" 3861 [[ -w "$zfs_tunables/$tunable" ]] || return 1 3862 cat >"$zfs_tunables/$tunable" <<<"$value" 3863 return $? 3864 ;; 3865 FreeBSD) 3866 sysctl vfs.zfs.$tunable=$value 3867 return "$?" 3868 ;; 3869 SunOS) 3870 [[ "$module" -eq "zfs" ]] || return 1 3871 echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw 3872 return $? 3873 ;; 3874 esac 3875} 3876 3877# 3878# Get a global system tunable 3879# 3880# $1 tunable name (use a NAME defined in tunables.cfg) 3881# 3882function get_tunable 3883{ 3884 get_tunable_impl "$1" 3885} 3886 3887function get_tunable_impl 3888{ 3889 typeset name="$1" 3890 typeset module="${2:-zfs}" 3891 3892 eval "typeset tunable=\$$name" 3893 case "$tunable" in 3894 UNSUPPORTED) 3895 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3896 ;; 3897 "") 3898 log_fail "Tunable '$name' must be added to tunables.cfg" 3899 ;; 3900 *) 3901 ;; 3902 esac 3903 3904 case "$(uname)" in 3905 Linux) 3906 typeset zfs_tunables="/sys/module/$module/parameters" 3907 [[ -f "$zfs_tunables/$tunable" ]] || return 1 3908 cat $zfs_tunables/$tunable 3909 return $? 3910 ;; 3911 FreeBSD) 3912 sysctl -n vfs.zfs.$tunable 3913 ;; 3914 SunOS) 3915 [[ "$module" -eq "zfs" ]] || return 1 3916 ;; 3917 esac 3918 3919 return 1 3920} 3921 3922# 3923# Prints the current time in seconds since UNIX Epoch. 3924# 3925function current_epoch 3926{ 3927 printf '%(%s)T' 3928} 3929 3930# 3931# Get decimal value of global uint32_t variable using mdb. 3932# 3933function mdb_get_uint32 3934{ 3935 typeset variable=$1 3936 typeset value 3937 3938 value=$(mdb -k -e "$variable/X | ::eval .=U") 3939 if [[ $? -ne 0 ]]; then 3940 log_fail "Failed to get value of '$variable' from mdb." 3941 return 1 3942 fi 3943 3944 echo $value 3945 return 0 3946} 3947 3948# 3949# Set global uint32_t variable to a decimal value using mdb. 3950# 3951function mdb_set_uint32 3952{ 3953 typeset variable=$1 3954 typeset value=$2 3955 3956 mdb -kw -e "$variable/W 0t$value" > /dev/null 3957 if [[ $? -ne 0 ]]; then 3958 echo "Failed to set '$variable' to '$value' in mdb." 3959 return 1 3960 fi 3961 3962 return 0 3963} 3964 3965# 3966# Set global scalar integer variable to a hex value using mdb. 3967# Note: Target should have CTF data loaded. 3968# 3969function mdb_ctf_set_int 3970{ 3971 typeset variable=$1 3972 typeset value=$2 3973 3974 mdb -kw -e "$variable/z $value" > /dev/null 3975 if [[ $? -ne 0 ]]; then 3976 echo "Failed to set '$variable' to '$value' in mdb." 3977 return 1 3978 fi 3979 3980 return 0 3981} 3982 3983# 3984# Compute MD5 digest for given file or stdin if no file given. 3985# Note: file path must not contain spaces 3986# 3987function md5digest 3988{ 3989 typeset file=$1 3990 3991 case $(uname) in 3992 FreeBSD) 3993 md5 -q $file 3994 ;; 3995 *) 3996 md5sum -b $file | awk '{ print $1 }' 3997 ;; 3998 esac 3999} 4000 4001# 4002# Compute SHA256 digest for given file or stdin if no file given. 4003# Note: file path must not contain spaces 4004# 4005function sha256digest 4006{ 4007 typeset file=$1 4008 4009 case $(uname) in 4010 FreeBSD) 4011 sha256 -q $file 4012 ;; 4013 *) 4014 sha256sum -b $file | awk '{ print $1 }' 4015 ;; 4016 esac 4017} 4018 4019function new_fs #<args> 4020{ 4021 case $(uname) in 4022 FreeBSD) 4023 newfs "$@" 4024 ;; 4025 *) 4026 echo y | newfs -v "$@" 4027 ;; 4028 esac 4029} 4030 4031function stat_size #<path> 4032{ 4033 typeset path=$1 4034 4035 case $(uname) in 4036 FreeBSD) 4037 stat -f %z "$path" 4038 ;; 4039 *) 4040 stat -c %s "$path" 4041 ;; 4042 esac 4043} 4044 4045function stat_ctime #<path> 4046{ 4047 typeset path=$1 4048 4049 case $(uname) in 4050 FreeBSD) 4051 stat -f %c "$path" 4052 ;; 4053 *) 4054 stat -c %Z "$path" 4055 ;; 4056 esac 4057} 4058 4059function stat_crtime #<path> 4060{ 4061 typeset path=$1 4062 4063 case $(uname) in 4064 FreeBSD) 4065 stat -f %B "$path" 4066 ;; 4067 *) 4068 stat -c %W "$path" 4069 ;; 4070 esac 4071} 4072 4073function stat_generation #<path> 4074{ 4075 typeset path=$1 4076 4077 case $(uname) in 4078 Linux) 4079 getversion "${path}" 4080 ;; 4081 *) 4082 stat -f %v "${path}" 4083 ;; 4084 esac 4085} 4086 4087# Run a command as if it was being run in a TTY. 4088# 4089# Usage: 4090# 4091# faketty command 4092# 4093function faketty 4094{ 4095 if is_freebsd; then 4096 script -q /dev/null env "$@" 4097 else 4098 script --return --quiet -c "$*" /dev/null 4099 fi 4100} 4101 4102# 4103# Produce a random permutation of the integers in a given range (inclusive). 4104# 4105function range_shuffle # begin end 4106{ 4107 typeset -i begin=$1 4108 typeset -i end=$2 4109 4110 seq ${begin} ${end} | sort -R 4111} 4112 4113# 4114# Cross-platform xattr helpers 4115# 4116 4117function get_xattr # name path 4118{ 4119 typeset name=$1 4120 typeset path=$2 4121 4122 case $(uname) in 4123 FreeBSD) 4124 getextattr -qq user "${name}" "${path}" 4125 ;; 4126 *) 4127 attr -qg "${name}" "${path}" 4128 ;; 4129 esac 4130} 4131 4132function set_xattr # name value path 4133{ 4134 typeset name=$1 4135 typeset value=$2 4136 typeset path=$3 4137 4138 case $(uname) in 4139 FreeBSD) 4140 setextattr user "${name}" "${value}" "${path}" 4141 ;; 4142 *) 4143 attr -qs "${name}" -V "${value}" "${path}" 4144 ;; 4145 esac 4146} 4147 4148function set_xattr_stdin # name value 4149{ 4150 typeset name=$1 4151 typeset path=$2 4152 4153 case $(uname) in 4154 FreeBSD) 4155 setextattr -i user "${name}" "${path}" 4156 ;; 4157 *) 4158 attr -qs "${name}" "${path}" 4159 ;; 4160 esac 4161} 4162 4163function rm_xattr # name path 4164{ 4165 typeset name=$1 4166 typeset path=$2 4167 4168 case $(uname) in 4169 FreeBSD) 4170 rmextattr -q user "${name}" "${path}" 4171 ;; 4172 *) 4173 attr -qr "${name}" "${path}" 4174 ;; 4175 esac 4176} 4177 4178function ls_xattr # path 4179{ 4180 typeset path=$1 4181 4182 case $(uname) in 4183 FreeBSD) 4184 lsextattr -qq user "${path}" 4185 ;; 4186 *) 4187 attr -ql "${path}" 4188 ;; 4189 esac 4190} 4191 4192function kstat # stat flags? 4193{ 4194 typeset stat=$1 4195 typeset flags=${2-"-n"} 4196 4197 case $(uname) in 4198 FreeBSD) 4199 sysctl $flags kstat.zfs.misc.$stat 4200 ;; 4201 Linux) 4202 typeset zfs_kstat="/proc/spl/kstat/zfs/$stat" 4203 [[ -f "$zfs_kstat" ]] || return 1 4204 cat $zfs_kstat 4205 ;; 4206 *) 4207 false 4208 ;; 4209 esac 4210} 4211 4212function get_arcstat # stat 4213{ 4214 typeset stat=$1 4215 4216 case $(uname) in 4217 FreeBSD) 4218 kstat arcstats.$stat 4219 ;; 4220 Linux) 4221 kstat arcstats | awk "/$stat/ { print \$3 }" 4222 ;; 4223 *) 4224 false 4225 ;; 4226 esac 4227} 4228 4229function punch_hole # offset length file 4230{ 4231 typeset offset=$1 4232 typeset length=$2 4233 typeset file=$3 4234 4235 case $(uname) in 4236 FreeBSD) 4237 truncate -d -o $offset -l $length "$file" 4238 ;; 4239 Linux) 4240 fallocate --punch-hole --offset $offset --length $length "$file" 4241 ;; 4242 *) 4243 false 4244 ;; 4245 esac 4246} 4247 4248# 4249# Wait for the specified arcstat to reach non-zero quiescence. 4250# If echo is 1 echo the value after reaching quiescence, otherwise 4251# if echo is 0 print the arcstat we are waiting on. 4252# 4253function arcstat_quiescence # stat echo 4254{ 4255 typeset stat=$1 4256 typeset echo=$2 4257 typeset do_once=true 4258 4259 if [[ $echo -eq 0 ]]; then 4260 echo "Waiting for arcstat $1 quiescence." 4261 fi 4262 4263 while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do 4264 typeset stat1=$(get_arcstat $stat) 4265 sleep 2 4266 typeset stat2=$(get_arcstat $stat) 4267 do_once=false 4268 done 4269 4270 if [[ $echo -eq 1 ]]; then 4271 echo $stat2 4272 fi 4273} 4274 4275function arcstat_quiescence_noecho # stat 4276{ 4277 typeset stat=$1 4278 arcstat_quiescence $stat 0 4279} 4280 4281function arcstat_quiescence_echo # stat 4282{ 4283 typeset stat=$1 4284 arcstat_quiescence $stat 1 4285} 4286 4287# 4288# Given an array of pids, wait until all processes 4289# have completed and check their return status. 4290# 4291function wait_for_children #children 4292{ 4293 rv=0 4294 children=("$@") 4295 for child in "${children[@]}" 4296 do 4297 child_exit=0 4298 wait ${child} || child_exit=$? 4299 if [ $child_exit -ne 0 ]; then 4300 echo "child ${child} failed with ${child_exit}" 4301 rv=1 4302 fi 4303 done 4304 return $rv 4305} 4306