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 # If current system support slog, add slog device for pool 1900 # 1901 if verify_slog_support ; then 1902 typeset sdevs="$TEST_BASE_DIR/sdev1 $TEST_BASE_DIR/sdev2" 1903 log_must mkfile $MINVDEVSIZE $sdevs 1904 log_must zpool add $pool_name log mirror $sdevs 1905 fi 1906 1907 # this isn't supported just yet. 1908 # Create a filesystem. In order to add this to 1909 # the zone, it must have it's mountpoint set to 'legacy' 1910 # log_must zfs create $pool_name/zfs_filesystem 1911 # log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem 1912 1913 [[ -d $zone_root ]] && \ 1914 log_must rm -rf $zone_root/$zone_name 1915 [[ ! -d $zone_root ]] && \ 1916 log_must mkdir -p -m 0700 $zone_root/$zone_name 1917 1918 # Create zone configure file and configure the zone 1919 # 1920 typeset zone_conf=/tmp/zone_conf.$$ 1921 echo "create" > $zone_conf 1922 echo "set zonepath=$zone_root/$zone_name" >> $zone_conf 1923 echo "set autoboot=true" >> $zone_conf 1924 i=0 1925 while ((i < cntctr)); do 1926 echo "add dataset" >> $zone_conf 1927 echo "set name=$pool_name/$prefix_ctr$i" >> \ 1928 $zone_conf 1929 echo "end" >> $zone_conf 1930 ((i += 1)) 1931 done 1932 1933 # add our zvol to the zone 1934 echo "add device" >> $zone_conf 1935 echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf 1936 echo "end" >> $zone_conf 1937 1938 # add a corresponding zvol rdsk to the zone 1939 echo "add device" >> $zone_conf 1940 echo "set match=$ZVOL_RDEVDIR/$pool_name/zone_zvol" >> $zone_conf 1941 echo "end" >> $zone_conf 1942 1943 # once it's supported, we'll add our filesystem to the zone 1944 # echo "add fs" >> $zone_conf 1945 # echo "set type=zfs" >> $zone_conf 1946 # echo "set special=$pool_name/zfs_filesystem" >> $zone_conf 1947 # echo "set dir=/export/zfs_filesystem" >> $zone_conf 1948 # echo "end" >> $zone_conf 1949 1950 echo "verify" >> $zone_conf 1951 echo "commit" >> $zone_conf 1952 log_must zonecfg -z $zone_name -f $zone_conf 1953 log_must rm -f $zone_conf 1954 1955 # Install the zone 1956 zoneadm -z $zone_name install 1957 if (($? == 0)); then 1958 log_note "SUCCESS: zoneadm -z $zone_name install" 1959 else 1960 log_fail "FAIL: zoneadm -z $zone_name install" 1961 fi 1962 1963 # Install sysidcfg file 1964 # 1965 typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg 1966 echo "system_locale=C" > $sysidcfg 1967 echo "terminal=dtterm" >> $sysidcfg 1968 echo "network_interface=primary {" >> $sysidcfg 1969 echo "hostname=$zone_name" >> $sysidcfg 1970 echo "}" >> $sysidcfg 1971 echo "name_service=NONE" >> $sysidcfg 1972 echo "root_password=mo791xfZ/SFiw" >> $sysidcfg 1973 echo "security_policy=NONE" >> $sysidcfg 1974 echo "timezone=US/Eastern" >> $sysidcfg 1975 1976 # Boot this zone 1977 log_must zoneadm -z $zone_name boot 1978} 1979 1980# 1981# Reexport TESTPOOL & TESTPOOL(1-4) 1982# 1983function reexport_pool 1984{ 1985 typeset -i cntctr=5 1986 typeset -i i=0 1987 1988 while ((i < cntctr)); do 1989 if ((i == 0)); then 1990 TESTPOOL=$ZONE_POOL/$ZONE_CTR$i 1991 if ! ismounted $TESTPOOL; then 1992 log_must zfs mount $TESTPOOL 1993 fi 1994 else 1995 eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i 1996 if eval ! ismounted \$TESTPOOL$i; then 1997 log_must eval zfs mount \$TESTPOOL$i 1998 fi 1999 fi 2000 ((i += 1)) 2001 done 2002} 2003 2004# 2005# Verify a given disk or pool state 2006# 2007# Return 0 is pool/disk matches expected state, 1 otherwise 2008# 2009function check_state # pool disk state{online,offline,degraded} 2010{ 2011 typeset pool=$1 2012 typeset disk=${2#$DEV_DSKDIR/} 2013 typeset state=$3 2014 2015 [[ -z $pool ]] || [[ -z $state ]] \ 2016 && log_fail "Arguments invalid or missing" 2017 2018 if [[ -z $disk ]]; then 2019 #check pool state only 2020 zpool get -H -o value health $pool \ 2021 | grep -i "$state" > /dev/null 2>&1 2022 else 2023 zpool status -v $pool | grep "$disk" \ 2024 | grep -i "$state" > /dev/null 2>&1 2025 fi 2026 2027 return $? 2028} 2029 2030# 2031# Get the mountpoint of snapshot 2032# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap> 2033# as its mountpoint 2034# 2035function snapshot_mountpoint 2036{ 2037 typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP} 2038 2039 if [[ $dataset != *@* ]]; then 2040 log_fail "Error name of snapshot '$dataset'." 2041 fi 2042 2043 typeset fs=${dataset%@*} 2044 typeset snap=${dataset#*@} 2045 2046 if [[ -z $fs || -z $snap ]]; then 2047 log_fail "Error name of snapshot '$dataset'." 2048 fi 2049 2050 echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap 2051} 2052 2053# 2054# Given a device and 'ashift' value verify it's correctly set on every label 2055# 2056function verify_ashift # device ashift 2057{ 2058 typeset device="$1" 2059 typeset ashift="$2" 2060 2061 zdb -e -lll $device | awk -v ashift=$ashift '/ashift: / { 2062 if (ashift != $2) 2063 exit 1; 2064 else 2065 count++; 2066 } END { 2067 if (count != 4) 2068 exit 1; 2069 else 2070 exit 0; 2071 }' 2072 2073 return $? 2074} 2075 2076# 2077# Given a pool and file system, this function will verify the file system 2078# using the zdb internal tool. Note that the pool is exported and imported 2079# to ensure it has consistent state. 2080# 2081function verify_filesys # pool filesystem dir 2082{ 2083 typeset pool="$1" 2084 typeset filesys="$2" 2085 typeset zdbout="/tmp/zdbout.$$" 2086 2087 shift 2088 shift 2089 typeset dirs=$@ 2090 typeset search_path="" 2091 2092 log_note "Calling zdb to verify filesystem '$filesys'" 2093 zfs unmount -a > /dev/null 2>&1 2094 log_must zpool export $pool 2095 2096 if [[ -n $dirs ]] ; then 2097 for dir in $dirs ; do 2098 search_path="$search_path -d $dir" 2099 done 2100 fi 2101 2102 log_must zpool import $search_path $pool 2103 2104 zdb -cudi $filesys > $zdbout 2>&1 2105 if [[ $? != 0 ]]; then 2106 log_note "Output: zdb -cudi $filesys" 2107 cat $zdbout 2108 log_fail "zdb detected errors with: '$filesys'" 2109 fi 2110 2111 log_must zfs mount -a 2112 log_must rm -rf $zdbout 2113} 2114 2115# 2116# Given a pool issue a scrub and verify that no checksum errors are reported. 2117# 2118function verify_pool 2119{ 2120 typeset pool=${1:-$TESTPOOL} 2121 2122 log_must zpool scrub $pool 2123 log_must wait_scrubbed $pool 2124 2125 typeset -i cksum=$(zpool status $pool | awk ' 2126 !NF { isvdev = 0 } 2127 isvdev { errors += $NF } 2128 /CKSUM$/ { isvdev = 1 } 2129 END { print errors } 2130 ') 2131 if [[ $cksum != 0 ]]; then 2132 log_must zpool status -v 2133 log_fail "Unexpected CKSUM errors found on $pool ($cksum)" 2134 fi 2135} 2136 2137# 2138# Given a pool, and this function list all disks in the pool 2139# 2140function get_disklist # pool 2141{ 2142 typeset disklist="" 2143 2144 disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \ 2145 grep -v "\-\-\-\-\-" | \ 2146 egrep -v -e "^(mirror|raidz[1-3]|draid[1-3]|spare|log|cache|special|dedup)|\-[0-9]$") 2147 2148 echo $disklist 2149} 2150 2151# 2152# Given a pool, and this function list all disks in the pool with their full 2153# path (like "/dev/sda" instead of "sda"). 2154# 2155function get_disklist_fullpath # pool 2156{ 2157 args="-P $1" 2158 get_disklist $args 2159} 2160 2161 2162 2163# /** 2164# This function kills a given list of processes after a time period. We use 2165# this in the stress tests instead of STF_TIMEOUT so that we can have processes 2166# run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT 2167# would be listed as FAIL, which we don't want : we're happy with stress tests 2168# running for a certain amount of time, then finishing. 2169# 2170# @param $1 the time in seconds after which we should terminate these processes 2171# @param $2..$n the processes we wish to terminate. 2172# */ 2173function stress_timeout 2174{ 2175 typeset -i TIMEOUT=$1 2176 shift 2177 typeset cpids="$@" 2178 2179 log_note "Waiting for child processes($cpids). " \ 2180 "It could last dozens of minutes, please be patient ..." 2181 log_must sleep $TIMEOUT 2182 2183 log_note "Killing child processes after ${TIMEOUT} stress timeout." 2184 typeset pid 2185 for pid in $cpids; do 2186 ps -p $pid > /dev/null 2>&1 2187 if (($? == 0)); then 2188 log_must kill -USR1 $pid 2189 fi 2190 done 2191} 2192 2193# 2194# Verify a given hotspare disk is inuse or avail 2195# 2196# Return 0 is pool/disk matches expected state, 1 otherwise 2197# 2198function check_hotspare_state # pool disk state{inuse,avail} 2199{ 2200 typeset pool=$1 2201 typeset disk=${2#$DEV_DSKDIR/} 2202 typeset state=$3 2203 2204 cur_state=$(get_device_state $pool $disk "spares") 2205 2206 if [[ $state != ${cur_state} ]]; then 2207 return 1 2208 fi 2209 return 0 2210} 2211 2212# 2213# Wait until a hotspare transitions to a given state or times out. 2214# 2215# Return 0 when pool/disk matches expected state, 1 on timeout. 2216# 2217function wait_hotspare_state # pool disk state timeout 2218{ 2219 typeset pool=$1 2220 typeset disk=${2#*$DEV_DSKDIR/} 2221 typeset state=$3 2222 typeset timeout=${4:-60} 2223 typeset -i i=0 2224 2225 while [[ $i -lt $timeout ]]; do 2226 if check_hotspare_state $pool $disk $state; then 2227 return 0 2228 fi 2229 2230 i=$((i+1)) 2231 sleep 1 2232 done 2233 2234 return 1 2235} 2236 2237# 2238# Verify a given slog disk is inuse or avail 2239# 2240# Return 0 is pool/disk matches expected state, 1 otherwise 2241# 2242function check_slog_state # pool disk state{online,offline,unavail} 2243{ 2244 typeset pool=$1 2245 typeset disk=${2#$DEV_DSKDIR/} 2246 typeset state=$3 2247 2248 cur_state=$(get_device_state $pool $disk "logs") 2249 2250 if [[ $state != ${cur_state} ]]; then 2251 return 1 2252 fi 2253 return 0 2254} 2255 2256# 2257# Verify a given vdev disk is inuse or avail 2258# 2259# Return 0 is pool/disk matches expected state, 1 otherwise 2260# 2261function check_vdev_state # pool disk state{online,offline,unavail} 2262{ 2263 typeset pool=$1 2264 typeset disk=${2#*$DEV_DSKDIR/} 2265 typeset state=$3 2266 2267 cur_state=$(get_device_state $pool $disk) 2268 2269 if [[ $state != ${cur_state} ]]; then 2270 return 1 2271 fi 2272 return 0 2273} 2274 2275# 2276# Wait until a vdev transitions to a given state or times out. 2277# 2278# Return 0 when pool/disk matches expected state, 1 on timeout. 2279# 2280function wait_vdev_state # pool disk state timeout 2281{ 2282 typeset pool=$1 2283 typeset disk=${2#*$DEV_DSKDIR/} 2284 typeset state=$3 2285 typeset timeout=${4:-60} 2286 typeset -i i=0 2287 2288 while [[ $i -lt $timeout ]]; do 2289 if check_vdev_state $pool $disk $state; then 2290 return 0 2291 fi 2292 2293 i=$((i+1)) 2294 sleep 1 2295 done 2296 2297 return 1 2298} 2299 2300# 2301# Check the output of 'zpool status -v <pool>', 2302# and to see if the content of <token> contain the <keyword> specified. 2303# 2304# Return 0 is contain, 1 otherwise 2305# 2306function check_pool_status # pool token keyword <verbose> 2307{ 2308 typeset pool=$1 2309 typeset token=$2 2310 typeset keyword=$3 2311 typeset verbose=${4:-false} 2312 2313 scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" ' 2314 ($1==token) {print $0}') 2315 if [[ $verbose == true ]]; then 2316 log_note $scan 2317 fi 2318 echo $scan | egrep -i "$keyword" > /dev/null 2>&1 2319 2320 return $? 2321} 2322 2323# 2324# The following functions are instance of check_pool_status() 2325# is_pool_resilvering - to check if the pool resilver is in progress 2326# is_pool_resilvered - to check if the pool resilver is completed 2327# is_pool_scrubbing - to check if the pool scrub is in progress 2328# is_pool_scrubbed - to check if the pool scrub is completed 2329# is_pool_scrub_stopped - to check if the pool scrub is stopped 2330# is_pool_scrub_paused - to check if the pool scrub has paused 2331# is_pool_removing - to check if the pool removing is a vdev 2332# is_pool_removed - to check if the pool remove is completed 2333# is_pool_discarding - to check if the pool checkpoint is being discarded 2334# 2335function is_pool_resilvering #pool <verbose> 2336{ 2337 check_pool_status "$1" "scan" \ 2338 "resilver[ ()0-9A-Za-z:_-]* in progress since" $2 2339 return $? 2340} 2341 2342function is_pool_resilvered #pool <verbose> 2343{ 2344 check_pool_status "$1" "scan" "resilvered " $2 2345 return $? 2346} 2347 2348function is_pool_scrubbing #pool <verbose> 2349{ 2350 check_pool_status "$1" "scan" "scrub in progress since " $2 2351 return $? 2352} 2353 2354function is_pool_scrubbed #pool <verbose> 2355{ 2356 check_pool_status "$1" "scan" "scrub repaired" $2 2357 return $? 2358} 2359 2360function is_pool_scrub_stopped #pool <verbose> 2361{ 2362 check_pool_status "$1" "scan" "scrub canceled" $2 2363 return $? 2364} 2365 2366function is_pool_scrub_paused #pool <verbose> 2367{ 2368 check_pool_status "$1" "scan" "scrub paused since " $2 2369 return $? 2370} 2371 2372function is_pool_removing #pool 2373{ 2374 check_pool_status "$1" "remove" "in progress since " 2375 return $? 2376} 2377 2378function is_pool_removed #pool 2379{ 2380 check_pool_status "$1" "remove" "completed on" 2381 return $? 2382} 2383 2384function is_pool_discarding #pool 2385{ 2386 check_pool_status "$1" "checkpoint" "discarding" 2387 return $? 2388} 2389 2390function wait_for_degraded 2391{ 2392 typeset pool=$1 2393 typeset timeout=${2:-30} 2394 typeset t0=$SECONDS 2395 2396 while :; do 2397 [[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break 2398 log_note "$pool is not yet degraded." 2399 sleep 1 2400 if ((SECONDS - t0 > $timeout)); then 2401 log_note "$pool not degraded after $timeout seconds." 2402 return 1 2403 fi 2404 done 2405 2406 return 0 2407} 2408 2409# 2410# Use create_pool()/destroy_pool() to clean up the information in 2411# in the given disk to avoid slice overlapping. 2412# 2413function cleanup_devices #vdevs 2414{ 2415 typeset pool="foopool$$" 2416 2417 for vdev in $@; do 2418 zero_partitions $vdev 2419 done 2420 2421 poolexists $pool && destroy_pool $pool 2422 create_pool $pool $@ 2423 destroy_pool $pool 2424 2425 return 0 2426} 2427 2428#/** 2429# A function to find and locate free disks on a system or from given 2430# disks as the parameter. It works by locating disks that are in use 2431# as swap devices and dump devices, and also disks listed in /etc/vfstab 2432# 2433# $@ given disks to find which are free, default is all disks in 2434# the test system 2435# 2436# @return a string containing the list of available disks 2437#*/ 2438function find_disks 2439{ 2440 # Trust provided list, no attempt is made to locate unused devices. 2441 if is_linux || is_freebsd; then 2442 echo "$@" 2443 return 2444 fi 2445 2446 2447 sfi=/tmp/swaplist.$$ 2448 dmpi=/tmp/dumpdev.$$ 2449 max_finddisksnum=${MAX_FINDDISKSNUM:-6} 2450 2451 swap -l > $sfi 2452 dumpadm > $dmpi 2>/dev/null 2453 2454# write an awk script that can process the output of format 2455# to produce a list of disks we know about. Note that we have 2456# to escape "$2" so that the shell doesn't interpret it while 2457# we're creating the awk script. 2458# ------------------- 2459 cat > /tmp/find_disks.awk <<EOF 2460#!/bin/nawk -f 2461 BEGIN { FS="."; } 2462 2463 /^Specify disk/{ 2464 searchdisks=0; 2465 } 2466 2467 { 2468 if (searchdisks && \$2 !~ "^$"){ 2469 split(\$2,arr," "); 2470 print arr[1]; 2471 } 2472 } 2473 2474 /^AVAILABLE DISK SELECTIONS:/{ 2475 searchdisks=1; 2476 } 2477EOF 2478#--------------------- 2479 2480 chmod 755 /tmp/find_disks.awk 2481 disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)} 2482 rm /tmp/find_disks.awk 2483 2484 unused="" 2485 for disk in $disks; do 2486 # Check for mounted 2487 grep "${disk}[sp]" /etc/mnttab >/dev/null 2488 (($? == 0)) && continue 2489 # Check for swap 2490 grep "${disk}[sp]" $sfi >/dev/null 2491 (($? == 0)) && continue 2492 # check for dump device 2493 grep "${disk}[sp]" $dmpi >/dev/null 2494 (($? == 0)) && continue 2495 # check to see if this disk hasn't been explicitly excluded 2496 # by a user-set environment variable 2497 echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null 2498 (($? == 0)) && continue 2499 unused_candidates="$unused_candidates $disk" 2500 done 2501 rm $sfi 2502 rm $dmpi 2503 2504# now just check to see if those disks do actually exist 2505# by looking for a device pointing to the first slice in 2506# each case. limit the number to max_finddisksnum 2507 count=0 2508 for disk in $unused_candidates; do 2509 if is_disk_device $DEV_DSKDIR/${disk}s0 && \ 2510 [ $count -lt $max_finddisksnum ]; then 2511 unused="$unused $disk" 2512 # do not impose limit if $@ is provided 2513 [[ -z $@ ]] && ((count = count + 1)) 2514 fi 2515 done 2516 2517# finally, return our disk list 2518 echo $unused 2519} 2520 2521function add_user_freebsd #<group_name> <user_name> <basedir> 2522{ 2523 typeset group=$1 2524 typeset user=$2 2525 typeset basedir=$3 2526 2527 # Check to see if the user exists. 2528 if id $user > /dev/null 2>&1; then 2529 return 0 2530 fi 2531 2532 # Assign 1000 as the base uid 2533 typeset -i uid=1000 2534 while true; do 2535 typeset -i ret 2536 pw useradd -u $uid -g $group -d $basedir/$user -m -n $user 2537 ret=$? 2538 case $ret in 2539 0) break ;; 2540 # The uid is not unique 2541 65) ((uid += 1)) ;; 2542 *) return 1 ;; 2543 esac 2544 if [[ $uid == 65000 ]]; then 2545 log_fail "No user id available under 65000 for $user" 2546 fi 2547 done 2548 2549 # Silence MOTD 2550 touch $basedir/$user/.hushlogin 2551 2552 return 0 2553} 2554 2555# 2556# Delete the specified user. 2557# 2558# $1 login name 2559# 2560function del_user_freebsd #<logname> 2561{ 2562 typeset user=$1 2563 2564 if id $user > /dev/null 2>&1; then 2565 log_must pw userdel $user 2566 fi 2567 2568 return 0 2569} 2570 2571# 2572# Select valid gid and create specified group. 2573# 2574# $1 group name 2575# 2576function add_group_freebsd #<group_name> 2577{ 2578 typeset group=$1 2579 2580 # See if the group already exists. 2581 if pw groupshow $group >/dev/null 2>&1; then 2582 return 0 2583 fi 2584 2585 # Assign 1000 as the base gid 2586 typeset -i gid=1000 2587 while true; do 2588 pw groupadd -g $gid -n $group > /dev/null 2>&1 2589 typeset -i ret=$? 2590 case $ret in 2591 0) return 0 ;; 2592 # The gid is not unique 2593 65) ((gid += 1)) ;; 2594 *) return 1 ;; 2595 esac 2596 if [[ $gid == 65000 ]]; then 2597 log_fail "No user id available under 65000 for $group" 2598 fi 2599 done 2600} 2601 2602# 2603# Delete the specified group. 2604# 2605# $1 group name 2606# 2607function del_group_freebsd #<group_name> 2608{ 2609 typeset group=$1 2610 2611 pw groupdel -n $group > /dev/null 2>&1 2612 typeset -i ret=$? 2613 case $ret in 2614 # Group does not exist, or was deleted successfully. 2615 0|6|65) return 0 ;; 2616 # Name already exists as a group name 2617 9) log_must pw groupdel $group ;; 2618 *) return 1 ;; 2619 esac 2620 2621 return 0 2622} 2623 2624function add_user_illumos #<group_name> <user_name> <basedir> 2625{ 2626 typeset group=$1 2627 typeset user=$2 2628 typeset basedir=$3 2629 2630 log_must useradd -g $group -d $basedir/$user -m $user 2631 2632 return 0 2633} 2634 2635function del_user_illumos #<user_name> 2636{ 2637 typeset user=$1 2638 2639 if id $user > /dev/null 2>&1; then 2640 log_must_retry "currently used" 6 userdel $user 2641 fi 2642 2643 return 0 2644} 2645 2646function add_group_illumos #<group_name> 2647{ 2648 typeset group=$1 2649 2650 typeset -i gid=100 2651 while true; do 2652 groupadd -g $gid $group > /dev/null 2>&1 2653 typeset -i ret=$? 2654 case $ret in 2655 0) return 0 ;; 2656 # The gid is not unique 2657 4) ((gid += 1)) ;; 2658 *) return 1 ;; 2659 esac 2660 done 2661} 2662 2663function del_group_illumos #<group_name> 2664{ 2665 typeset group=$1 2666 2667 groupmod -n $grp $grp > /dev/null 2>&1 2668 typeset -i ret=$? 2669 case $ret in 2670 # Group does not exist. 2671 6) return 0 ;; 2672 # Name already exists as a group name 2673 9) log_must groupdel $grp ;; 2674 *) return 1 ;; 2675 esac 2676} 2677 2678function add_user_linux #<group_name> <user_name> <basedir> 2679{ 2680 typeset group=$1 2681 typeset user=$2 2682 typeset basedir=$3 2683 2684 log_must useradd -g $group -d $basedir/$user -m $user 2685 2686 # Add new users to the same group and the command line utils. 2687 # This allows them to be run out of the original users home 2688 # directory as long as it permissioned to be group readable. 2689 cmd_group=$(stat --format="%G" $(which zfs)) 2690 log_must usermod -a -G $cmd_group $user 2691 2692 return 0 2693} 2694 2695function del_user_linux #<user_name> 2696{ 2697 typeset user=$1 2698 2699 if id $user > /dev/null 2>&1; then 2700 log_must_retry "currently used" 6 userdel $user 2701 fi 2702 2703 return 0 2704} 2705 2706function add_group_linux #<group_name> 2707{ 2708 typeset group=$1 2709 2710 # Assign 100 as the base gid, a larger value is selected for 2711 # Linux because for many distributions 1000 and under are reserved. 2712 while true; do 2713 groupadd $group > /dev/null 2>&1 2714 typeset -i ret=$? 2715 case $ret in 2716 0) return 0 ;; 2717 *) return 1 ;; 2718 esac 2719 done 2720} 2721 2722function del_group_linux #<group_name> 2723{ 2724 typeset group=$1 2725 2726 getent group $group > /dev/null 2>&1 2727 typeset -i ret=$? 2728 case $ret in 2729 # Group does not exist. 2730 2) return 0 ;; 2731 # Name already exists as a group name 2732 0) log_must groupdel $group ;; 2733 *) return 1 ;; 2734 esac 2735 2736 return 0 2737} 2738 2739# 2740# Add specified user to specified group 2741# 2742# $1 group name 2743# $2 user name 2744# $3 base of the homedir (optional) 2745# 2746function add_user #<group_name> <user_name> <basedir> 2747{ 2748 typeset group=$1 2749 typeset user=$2 2750 typeset basedir=${3:-"/var/tmp"} 2751 2752 if ((${#group} == 0 || ${#user} == 0)); then 2753 log_fail "group name or user name are not defined." 2754 fi 2755 2756 case $(uname) in 2757 FreeBSD) 2758 add_user_freebsd "$group" "$user" "$basedir" 2759 ;; 2760 Linux) 2761 add_user_linux "$group" "$user" "$basedir" 2762 ;; 2763 *) 2764 add_user_illumos "$group" "$user" "$basedir" 2765 ;; 2766 esac 2767 2768 return 0 2769} 2770 2771# 2772# Delete the specified user. 2773# 2774# $1 login name 2775# $2 base of the homedir (optional) 2776# 2777function del_user #<logname> <basedir> 2778{ 2779 typeset user=$1 2780 typeset basedir=${2:-"/var/tmp"} 2781 2782 if ((${#user} == 0)); then 2783 log_fail "login name is necessary." 2784 fi 2785 2786 case $(uname) in 2787 FreeBSD) 2788 del_user_freebsd "$user" 2789 ;; 2790 Linux) 2791 del_user_linux "$user" 2792 ;; 2793 *) 2794 del_user_illumos "$user" 2795 ;; 2796 esac 2797 2798 [[ -d $basedir/$user ]] && rm -fr $basedir/$user 2799 2800 return 0 2801} 2802 2803# 2804# Select valid gid and create specified group. 2805# 2806# $1 group name 2807# 2808function add_group #<group_name> 2809{ 2810 typeset group=$1 2811 2812 if ((${#group} == 0)); then 2813 log_fail "group name is necessary." 2814 fi 2815 2816 case $(uname) in 2817 FreeBSD) 2818 add_group_freebsd "$group" 2819 ;; 2820 Linux) 2821 add_group_linux "$group" 2822 ;; 2823 *) 2824 add_group_illumos "$group" 2825 ;; 2826 esac 2827 2828 return 0 2829} 2830 2831# 2832# Delete the specified group. 2833# 2834# $1 group name 2835# 2836function del_group #<group_name> 2837{ 2838 typeset group=$1 2839 2840 if ((${#group} == 0)); then 2841 log_fail "group name is necessary." 2842 fi 2843 2844 case $(uname) in 2845 FreeBSD) 2846 del_group_freebsd "$group" 2847 ;; 2848 Linux) 2849 del_group_linux "$group" 2850 ;; 2851 *) 2852 del_group_illumos "$group" 2853 ;; 2854 esac 2855 2856 return 0 2857} 2858 2859# 2860# This function will return true if it's safe to destroy the pool passed 2861# as argument 1. It checks for pools based on zvols and files, and also 2862# files contained in a pool that may have a different mountpoint. 2863# 2864function safe_to_destroy_pool { # $1 the pool name 2865 2866 typeset pool="" 2867 typeset DONT_DESTROY="" 2868 2869 # We check that by deleting the $1 pool, we're not 2870 # going to pull the rug out from other pools. Do this 2871 # by looking at all other pools, ensuring that they 2872 # aren't built from files or zvols contained in this pool. 2873 2874 for pool in $(zpool list -H -o name) 2875 do 2876 ALTMOUNTPOOL="" 2877 2878 # this is a list of the top-level directories in each of the 2879 # files that make up the path to the files the pool is based on 2880 FILEPOOL=$(zpool status -v $pool | grep /$1/ | \ 2881 awk '{print $1}') 2882 2883 # this is a list of the zvols that make up the pool 2884 ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \ 2885 | awk '{print $1}') 2886 2887 # also want to determine if it's a file-based pool using an 2888 # alternate mountpoint... 2889 POOL_FILE_DIRS=$(zpool status -v $pool | \ 2890 grep / | awk '{print $1}' | \ 2891 awk -F/ '{print $2}' | grep -v "dev") 2892 2893 for pooldir in $POOL_FILE_DIRS 2894 do 2895 OUTPUT=$(zfs list -H -r -o mountpoint $1 | \ 2896 grep "${pooldir}$" | awk '{print $1}') 2897 2898 ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}" 2899 done 2900 2901 2902 if [ ! -z "$ZVOLPOOL" ] 2903 then 2904 DONT_DESTROY="true" 2905 log_note "Pool $pool is built from $ZVOLPOOL on $1" 2906 fi 2907 2908 if [ ! -z "$FILEPOOL" ] 2909 then 2910 DONT_DESTROY="true" 2911 log_note "Pool $pool is built from $FILEPOOL on $1" 2912 fi 2913 2914 if [ ! -z "$ALTMOUNTPOOL" ] 2915 then 2916 DONT_DESTROY="true" 2917 log_note "Pool $pool is built from $ALTMOUNTPOOL on $1" 2918 fi 2919 done 2920 2921 if [ -z "${DONT_DESTROY}" ] 2922 then 2923 return 0 2924 else 2925 log_note "Warning: it is not safe to destroy $1!" 2926 return 1 2927 fi 2928} 2929 2930# 2931# Verify zfs operation with -p option work as expected 2932# $1 operation, value could be create, clone or rename 2933# $2 dataset type, value could be fs or vol 2934# $3 dataset name 2935# $4 new dataset name 2936# 2937function verify_opt_p_ops 2938{ 2939 typeset ops=$1 2940 typeset datatype=$2 2941 typeset dataset=$3 2942 typeset newdataset=$4 2943 2944 if [[ $datatype != "fs" && $datatype != "vol" ]]; then 2945 log_fail "$datatype is not supported." 2946 fi 2947 2948 # check parameters accordingly 2949 case $ops in 2950 create) 2951 newdataset=$dataset 2952 dataset="" 2953 if [[ $datatype == "vol" ]]; then 2954 ops="create -V $VOLSIZE" 2955 fi 2956 ;; 2957 clone) 2958 if [[ -z $newdataset ]]; then 2959 log_fail "newdataset should not be empty" \ 2960 "when ops is $ops." 2961 fi 2962 log_must datasetexists $dataset 2963 log_must snapexists $dataset 2964 ;; 2965 rename) 2966 if [[ -z $newdataset ]]; then 2967 log_fail "newdataset should not be empty" \ 2968 "when ops is $ops." 2969 fi 2970 log_must datasetexists $dataset 2971 ;; 2972 *) 2973 log_fail "$ops is not supported." 2974 ;; 2975 esac 2976 2977 # make sure the upper level filesystem does not exist 2978 destroy_dataset "${newdataset%/*}" "-rRf" 2979 2980 # without -p option, operation will fail 2981 log_mustnot zfs $ops $dataset $newdataset 2982 log_mustnot datasetexists $newdataset ${newdataset%/*} 2983 2984 # with -p option, operation should succeed 2985 log_must zfs $ops -p $dataset $newdataset 2986 block_device_wait 2987 2988 if ! datasetexists $newdataset ; then 2989 log_fail "-p option does not work for $ops" 2990 fi 2991 2992 # when $ops is create or clone, redo the operation still return zero 2993 if [[ $ops != "rename" ]]; then 2994 log_must zfs $ops -p $dataset $newdataset 2995 fi 2996 2997 return 0 2998} 2999 3000# 3001# Get configuration of pool 3002# $1 pool name 3003# $2 config name 3004# 3005function get_config 3006{ 3007 typeset pool=$1 3008 typeset config=$2 3009 typeset alt_root 3010 3011 if ! poolexists "$pool" ; then 3012 return 1 3013 fi 3014 alt_root=$(zpool list -H $pool | awk '{print $NF}') 3015 if [[ $alt_root == "-" ]]; then 3016 value=$(zdb -C $pool | grep "$config:" | awk -F: \ 3017 '{print $2}') 3018 else 3019 value=$(zdb -e $pool | grep "$config:" | awk -F: \ 3020 '{print $2}') 3021 fi 3022 if [[ -n $value ]] ; then 3023 value=${value#'} 3024 value=${value%'} 3025 fi 3026 echo $value 3027 3028 return 0 3029} 3030 3031# 3032# Privated function. Random select one of items from arguments. 3033# 3034# $1 count 3035# $2-n string 3036# 3037function _random_get 3038{ 3039 typeset cnt=$1 3040 shift 3041 3042 typeset str="$@" 3043 typeset -i ind 3044 ((ind = RANDOM % cnt + 1)) 3045 3046 typeset ret=$(echo "$str" | cut -f $ind -d ' ') 3047 echo $ret 3048} 3049 3050# 3051# Random select one of item from arguments which include NONE string 3052# 3053function random_get_with_non 3054{ 3055 typeset -i cnt=$# 3056 ((cnt =+ 1)) 3057 3058 _random_get "$cnt" "$@" 3059} 3060 3061# 3062# Random select one of item from arguments which doesn't include NONE string 3063# 3064function random_get 3065{ 3066 _random_get "$#" "$@" 3067} 3068 3069# 3070# Detect if the current system support slog 3071# 3072function verify_slog_support 3073{ 3074 typeset dir=$TEST_BASE_DIR/disk.$$ 3075 typeset pool=foo.$$ 3076 typeset vdev=$dir/a 3077 typeset sdev=$dir/b 3078 3079 mkdir -p $dir 3080 mkfile $MINVDEVSIZE $vdev $sdev 3081 3082 typeset -i ret=0 3083 if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then 3084 ret=1 3085 fi 3086 rm -r $dir 3087 3088 return $ret 3089} 3090 3091# 3092# The function will generate a dataset name with specific length 3093# $1, the length of the name 3094# $2, the base string to construct the name 3095# 3096function gen_dataset_name 3097{ 3098 typeset -i len=$1 3099 typeset basestr="$2" 3100 typeset -i baselen=${#basestr} 3101 typeset -i iter=0 3102 typeset l_name="" 3103 3104 if ((len % baselen == 0)); then 3105 ((iter = len / baselen)) 3106 else 3107 ((iter = len / baselen + 1)) 3108 fi 3109 while ((iter > 0)); do 3110 l_name="${l_name}$basestr" 3111 3112 ((iter -= 1)) 3113 done 3114 3115 echo $l_name 3116} 3117 3118# 3119# Get cksum tuple of dataset 3120# $1 dataset name 3121# 3122# sample zdb output: 3123# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp 3124# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4 3125# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P 3126# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744 3127function datasetcksum 3128{ 3129 typeset cksum 3130 sync 3131 cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \ 3132 | awk -F= '{print $7}') 3133 echo $cksum 3134} 3135 3136# 3137# Get cksum of file 3138# #1 file path 3139# 3140function checksum 3141{ 3142 typeset cksum 3143 cksum=$(cksum $1 | awk '{print $1}') 3144 echo $cksum 3145} 3146 3147# 3148# Get the given disk/slice state from the specific field of the pool 3149# 3150function get_device_state #pool disk field("", "spares","logs") 3151{ 3152 typeset pool=$1 3153 typeset disk=${2#$DEV_DSKDIR/} 3154 typeset field=${3:-$pool} 3155 3156 state=$(zpool status -v "$pool" 2>/dev/null | \ 3157 nawk -v device=$disk -v pool=$pool -v field=$field \ 3158 'BEGIN {startconfig=0; startfield=0; } 3159 /config:/ {startconfig=1} 3160 (startconfig==1) && ($1==field) {startfield=1; next;} 3161 (startfield==1) && ($1==device) {print $2; exit;} 3162 (startfield==1) && 3163 ($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}') 3164 echo $state 3165} 3166 3167 3168# 3169# print the given directory filesystem type 3170# 3171# $1 directory name 3172# 3173function get_fstype 3174{ 3175 typeset dir=$1 3176 3177 if [[ -z $dir ]]; then 3178 log_fail "Usage: get_fstype <directory>" 3179 fi 3180 3181 # 3182 # $ df -n / 3183 # / : ufs 3184 # 3185 df -n $dir | awk '{print $3}' 3186} 3187 3188# 3189# Given a disk, label it to VTOC regardless what label was on the disk 3190# $1 disk 3191# 3192function labelvtoc 3193{ 3194 typeset disk=$1 3195 if [[ -z $disk ]]; then 3196 log_fail "The disk name is unspecified." 3197 fi 3198 typeset label_file=/var/tmp/labelvtoc.$$ 3199 typeset arch=$(uname -p) 3200 3201 if is_linux || is_freebsd; then 3202 log_note "Currently unsupported by the test framework" 3203 return 1 3204 fi 3205 3206 if [[ $arch == "i386" ]]; then 3207 echo "label" > $label_file 3208 echo "0" >> $label_file 3209 echo "" >> $label_file 3210 echo "q" >> $label_file 3211 echo "q" >> $label_file 3212 3213 fdisk -B $disk >/dev/null 2>&1 3214 # wait a while for fdisk finishes 3215 sleep 60 3216 elif [[ $arch == "sparc" ]]; then 3217 echo "label" > $label_file 3218 echo "0" >> $label_file 3219 echo "" >> $label_file 3220 echo "" >> $label_file 3221 echo "" >> $label_file 3222 echo "q" >> $label_file 3223 else 3224 log_fail "unknown arch type" 3225 fi 3226 3227 format -e -s -d $disk -f $label_file 3228 typeset -i ret_val=$? 3229 rm -f $label_file 3230 # 3231 # wait the format to finish 3232 # 3233 sleep 60 3234 if ((ret_val != 0)); then 3235 log_fail "unable to label $disk as VTOC." 3236 fi 3237 3238 return 0 3239} 3240 3241# 3242# check if the system was installed as zfsroot or not 3243# return: 0 if zfsroot, non-zero if not 3244# 3245function is_zfsroot 3246{ 3247 df -n / | grep zfs > /dev/null 2>&1 3248 return $? 3249} 3250 3251# 3252# get the root filesystem name if it's zfsroot system. 3253# 3254# return: root filesystem name 3255function get_rootfs 3256{ 3257 typeset rootfs="" 3258 3259 if is_freebsd; then 3260 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3261 elif ! is_linux; then 3262 rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \ 3263 /etc/mnttab) 3264 fi 3265 if [[ -z "$rootfs" ]]; then 3266 log_fail "Can not get rootfs" 3267 fi 3268 zfs list $rootfs > /dev/null 2>&1 3269 if (($? == 0)); then 3270 echo $rootfs 3271 else 3272 log_fail "This is not a zfsroot system." 3273 fi 3274} 3275 3276# 3277# get the rootfs's pool name 3278# return: 3279# rootpool name 3280# 3281function get_rootpool 3282{ 3283 typeset rootfs="" 3284 typeset rootpool="" 3285 3286 if is_freebsd; then 3287 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}') 3288 elif ! is_linux; then 3289 rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \ 3290 /etc/mnttab) 3291 fi 3292 if [[ -z "$rootfs" ]]; then 3293 log_fail "Can not get rootpool" 3294 fi 3295 zfs list $rootfs > /dev/null 2>&1 3296 if (($? == 0)); then 3297 echo ${rootfs%%/*} 3298 else 3299 log_fail "This is not a zfsroot system." 3300 fi 3301} 3302 3303# 3304# Get the word numbers from a string separated by white space 3305# 3306function get_word_count 3307{ 3308 echo $1 | wc -w 3309} 3310 3311# 3312# To verify if the require numbers of disks is given 3313# 3314function verify_disk_count 3315{ 3316 typeset -i min=${2:-1} 3317 3318 typeset -i count=$(get_word_count "$1") 3319 3320 if ((count < min)); then 3321 log_untested "A minimum of $min disks is required to run." \ 3322 " You specified $count disk(s)" 3323 fi 3324} 3325 3326function ds_is_volume 3327{ 3328 typeset type=$(get_prop type $1) 3329 [[ $type = "volume" ]] && return 0 3330 return 1 3331} 3332 3333function ds_is_filesystem 3334{ 3335 typeset type=$(get_prop type $1) 3336 [[ $type = "filesystem" ]] && return 0 3337 return 1 3338} 3339 3340function ds_is_snapshot 3341{ 3342 typeset type=$(get_prop type $1) 3343 [[ $type = "snapshot" ]] && return 0 3344 return 1 3345} 3346 3347# 3348# Check if Trusted Extensions are installed and enabled 3349# 3350function is_te_enabled 3351{ 3352 svcs -H -o state labeld 2>/dev/null | grep "enabled" 3353 if (($? != 0)); then 3354 return 1 3355 else 3356 return 0 3357 fi 3358} 3359 3360# Utility function to determine if a system has multiple cpus. 3361function is_mp 3362{ 3363 if is_linux; then 3364 (($(nproc) > 1)) 3365 elif is_freebsd; then 3366 sysctl -n kern.smp.cpus 3367 else 3368 (($(psrinfo | wc -l) > 1)) 3369 fi 3370 3371 return $? 3372} 3373 3374function get_cpu_freq 3375{ 3376 if is_linux; then 3377 lscpu | awk '/CPU MHz/ { print $3 }' 3378 elif is_freebsd; then 3379 sysctl -n hw.clockrate 3380 else 3381 psrinfo -v 0 | awk '/processor operates at/ {print $6}' 3382 fi 3383} 3384 3385# Run the given command as the user provided. 3386function user_run 3387{ 3388 typeset user=$1 3389 shift 3390 3391 log_note "user: $user" 3392 log_note "cmd: $*" 3393 3394 typeset out=$TEST_BASE_DIR/out 3395 typeset err=$TEST_BASE_DIR/err 3396 3397 sudo -Eu $user env PATH="$PATH" ksh <<<"$*" >$out 2>$err 3398 typeset res=$? 3399 log_note "out: $(<$out)" 3400 log_note "err: $(<$err)" 3401 return $res 3402} 3403 3404# 3405# Check if the pool contains the specified vdevs 3406# 3407# $1 pool 3408# $2..n <vdev> ... 3409# 3410# Return 0 if the vdevs are contained in the pool, 1 if any of the specified 3411# vdevs is not in the pool, and 2 if pool name is missing. 3412# 3413function vdevs_in_pool 3414{ 3415 typeset pool=$1 3416 typeset vdev 3417 3418 if [[ -z $pool ]]; then 3419 log_note "Missing pool name." 3420 return 2 3421 fi 3422 3423 shift 3424 3425 # We could use 'zpool list' to only get the vdevs of the pool but we 3426 # can't reference a mirror/raidz vdev using its ID (i.e mirror-0), 3427 # therefore we use the 'zpool status' output. 3428 typeset tmpfile=$(mktemp) 3429 zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile 3430 for vdev in $@; do 3431 grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1 3432 [[ $? -ne 0 ]] && return 1 3433 done 3434 3435 rm -f $tmpfile 3436 3437 return 0; 3438} 3439 3440function get_max 3441{ 3442 typeset -l i max=$1 3443 shift 3444 3445 for i in "$@"; do 3446 max=$((max > i ? max : i)) 3447 done 3448 3449 echo $max 3450} 3451 3452function get_min 3453{ 3454 typeset -l i min=$1 3455 shift 3456 3457 for i in "$@"; do 3458 min=$((min < i ? min : i)) 3459 done 3460 3461 echo $min 3462} 3463 3464# Write data that can be compressed into a directory 3465function write_compressible 3466{ 3467 typeset dir=$1 3468 typeset megs=$2 3469 typeset nfiles=${3:-1} 3470 typeset bs=${4:-1024k} 3471 typeset fname=${5:-file} 3472 3473 [[ -d $dir ]] || log_fail "No directory: $dir" 3474 3475 # Under Linux fio is not currently used since its behavior can 3476 # differ significantly across versions. This includes missing 3477 # command line options and cases where the --buffer_compress_* 3478 # options fail to behave as expected. 3479 if is_linux; then 3480 typeset file_bytes=$(to_bytes $megs) 3481 typeset bs_bytes=4096 3482 typeset blocks=$(($file_bytes / $bs_bytes)) 3483 3484 for (( i = 0; i < $nfiles; i++ )); do 3485 truncate -s $file_bytes $dir/$fname.$i 3486 3487 # Write every third block to get 66% compression. 3488 for (( j = 0; j < $blocks; j += 3 )); do 3489 dd if=/dev/urandom of=$dir/$fname.$i \ 3490 seek=$j bs=$bs_bytes count=1 \ 3491 conv=notrunc >/dev/null 2>&1 3492 done 3493 done 3494 else 3495 log_must eval "fio \ 3496 --name=job \ 3497 --fallocate=0 \ 3498 --minimal \ 3499 --randrepeat=0 \ 3500 --buffer_compress_percentage=66 \ 3501 --buffer_compress_chunk=4096 \ 3502 --directory=$dir \ 3503 --numjobs=$nfiles \ 3504 --nrfiles=$nfiles \ 3505 --rw=write \ 3506 --bs=$bs \ 3507 --filesize=$megs \ 3508 --filename_format='$fname.\$jobnum' >/dev/null" 3509 fi 3510} 3511 3512function get_objnum 3513{ 3514 typeset pathname=$1 3515 typeset objnum 3516 3517 [[ -e $pathname ]] || log_fail "No such file or directory: $pathname" 3518 if is_freebsd; then 3519 objnum=$(stat -f "%i" $pathname) 3520 else 3521 objnum=$(stat -c %i $pathname) 3522 fi 3523 echo $objnum 3524} 3525 3526# 3527# Sync data to the pool 3528# 3529# $1 pool name 3530# $2 boolean to force uberblock (and config including zpool cache file) update 3531# 3532function sync_pool #pool <force> 3533{ 3534 typeset pool=${1:-$TESTPOOL} 3535 typeset force=${2:-false} 3536 3537 if [[ $force == true ]]; then 3538 log_must zpool sync -f $pool 3539 else 3540 log_must zpool sync $pool 3541 fi 3542 3543 return 0 3544} 3545 3546# 3547# Wait for zpool 'freeing' property drops to zero. 3548# 3549# $1 pool name 3550# 3551function wait_freeing #pool 3552{ 3553 typeset pool=${1:-$TESTPOOL} 3554 while true; do 3555 [[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break 3556 log_must sleep 1 3557 done 3558} 3559 3560# 3561# Wait for every device replace operation to complete 3562# 3563# $1 pool name 3564# 3565function wait_replacing #pool 3566{ 3567 typeset pool=${1:-$TESTPOOL} 3568 while true; do 3569 [[ "" == "$(zpool status $pool | 3570 awk '/replacing-[0-9]+/ {print $1}')" ]] && break 3571 log_must sleep 1 3572 done 3573} 3574 3575# 3576# Wait for a pool to be scrubbed 3577# 3578# $1 pool name 3579# 3580function wait_scrubbed 3581{ 3582 typeset pool=${1:-$TESTPOOL} 3583 while ! is_pool_scrubbed $pool ; do 3584 sleep 1 3585 done 3586} 3587 3588# Backup the zed.rc in our test directory so that we can edit it for our test. 3589# 3590# Returns: Backup file name. You will need to pass this to zed_rc_restore(). 3591function zed_rc_backup 3592{ 3593 zedrc_backup="$(mktemp)" 3594 cp $ZEDLET_DIR/zed.rc $zedrc_backup 3595 echo $zedrc_backup 3596} 3597 3598function zed_rc_restore 3599{ 3600 mv $1 $ZEDLET_DIR/zed.rc 3601} 3602 3603# 3604# Setup custom environment for the ZED. 3605# 3606# $@ Optional list of zedlets to run under zed. 3607function zed_setup 3608{ 3609 if ! is_linux; then 3610 log_unsupported "No zed on $(uname)" 3611 fi 3612 3613 if [[ ! -d $ZEDLET_DIR ]]; then 3614 log_must mkdir $ZEDLET_DIR 3615 fi 3616 3617 if [[ ! -e $VDEVID_CONF ]]; then 3618 log_must touch $VDEVID_CONF 3619 fi 3620 3621 if [[ -e $VDEVID_CONF_ETC ]]; then 3622 log_fail "Must not have $VDEVID_CONF_ETC file present on system" 3623 fi 3624 EXTRA_ZEDLETS=$@ 3625 3626 # Create a symlink for /etc/zfs/vdev_id.conf file. 3627 log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC 3628 3629 # Setup minimal ZED configuration. Individual test cases should 3630 # add additional ZEDLETs as needed for their specific test. 3631 log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR 3632 log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR 3633 3634 # Scripts must only be user writable. 3635 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3636 saved_umask=$(umask) 3637 log_must umask 0022 3638 for i in $EXTRA_ZEDLETS ; do 3639 log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR 3640 done 3641 log_must umask $saved_umask 3642 fi 3643 3644 # Customize the zed.rc file to enable the full debug log. 3645 log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc 3646 echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc 3647 3648} 3649 3650# 3651# Cleanup custom ZED environment. 3652# 3653# $@ Optional list of zedlets to remove from our test zed.d directory. 3654function zed_cleanup 3655{ 3656 if ! is_linux; then 3657 return 3658 fi 3659 EXTRA_ZEDLETS=$@ 3660 3661 log_must rm -f ${ZEDLET_DIR}/zed.rc 3662 log_must rm -f ${ZEDLET_DIR}/zed-functions.sh 3663 log_must rm -f ${ZEDLET_DIR}/all-syslog.sh 3664 log_must rm -f ${ZEDLET_DIR}/all-debug.sh 3665 log_must rm -f ${ZEDLET_DIR}/state 3666 3667 if [[ -n "$EXTRA_ZEDLETS" ]] ; then 3668 for i in $EXTRA_ZEDLETS ; do 3669 log_must rm -f ${ZEDLET_DIR}/$i 3670 done 3671 fi 3672 log_must rm -f $ZED_LOG 3673 log_must rm -f $ZED_DEBUG_LOG 3674 log_must rm -f $VDEVID_CONF_ETC 3675 log_must rm -f $VDEVID_CONF 3676 rmdir $ZEDLET_DIR 3677} 3678 3679# 3680# Check if ZED is currently running, if not start ZED. 3681# 3682function zed_start 3683{ 3684 if ! is_linux; then 3685 return 3686 fi 3687 3688 # ZEDLET_DIR=/var/tmp/zed 3689 if [[ ! -d $ZEDLET_DIR ]]; then 3690 log_must mkdir $ZEDLET_DIR 3691 fi 3692 3693 # Verify the ZED is not already running. 3694 pgrep -x zed > /dev/null 3695 if (($? == 0)); then 3696 log_note "ZED already running" 3697 else 3698 log_note "Starting ZED" 3699 # run ZED in the background and redirect foreground logging 3700 # output to $ZED_LOG. 3701 log_must truncate -s 0 $ZED_DEBUG_LOG 3702 log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \ 3703 "-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &" 3704 fi 3705 3706 return 0 3707} 3708 3709# 3710# Kill ZED process 3711# 3712function zed_stop 3713{ 3714 if ! is_linux; then 3715 return 3716 fi 3717 3718 log_note "Stopping ZED" 3719 while true; do 3720 zedpids="$(pgrep -x zed)" 3721 [ "$?" -ne 0 ] && break 3722 3723 log_must kill $zedpids 3724 sleep 1 3725 done 3726 return 0 3727} 3728 3729# 3730# Drain all zevents 3731# 3732function zed_events_drain 3733{ 3734 while [ $(zpool events -H | wc -l) -ne 0 ]; do 3735 sleep 1 3736 zpool events -c >/dev/null 3737 done 3738} 3739 3740# Set a variable in zed.rc to something, un-commenting it in the process. 3741# 3742# $1 variable 3743# $2 value 3744function zed_rc_set 3745{ 3746 var="$1" 3747 val="$2" 3748 # Remove the line 3749 cmd="'/$var/d'" 3750 eval sed -i $cmd $ZEDLET_DIR/zed.rc 3751 3752 # Add it at the end 3753 echo "$var=$val" >> $ZEDLET_DIR/zed.rc 3754} 3755 3756 3757# 3758# Check is provided device is being active used as a swap device. 3759# 3760function is_swap_inuse 3761{ 3762 typeset device=$1 3763 3764 if [[ -z $device ]] ; then 3765 log_note "No device specified." 3766 return 1 3767 fi 3768 3769 if is_linux; then 3770 swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1 3771 elif is_freebsd; then 3772 swapctl -l | grep -w $device 3773 else 3774 swap -l | grep -w $device > /dev/null 2>&1 3775 fi 3776 3777 return $? 3778} 3779 3780# 3781# Setup a swap device using the provided device. 3782# 3783function swap_setup 3784{ 3785 typeset swapdev=$1 3786 3787 if is_linux; then 3788 log_must eval "mkswap $swapdev > /dev/null 2>&1" 3789 log_must swapon $swapdev 3790 elif is_freebsd; then 3791 log_must swapctl -a $swapdev 3792 else 3793 log_must swap -a $swapdev 3794 fi 3795 3796 return 0 3797} 3798 3799# 3800# Cleanup a swap device on the provided device. 3801# 3802function swap_cleanup 3803{ 3804 typeset swapdev=$1 3805 3806 if is_swap_inuse $swapdev; then 3807 if is_linux; then 3808 log_must swapoff $swapdev 3809 elif is_freebsd; then 3810 log_must swapoff $swapdev 3811 else 3812 log_must swap -d $swapdev 3813 fi 3814 fi 3815 3816 return 0 3817} 3818 3819# 3820# Set a global system tunable (64-bit value) 3821# 3822# $1 tunable name (use a NAME defined in tunables.cfg) 3823# $2 tunable values 3824# 3825function set_tunable64 3826{ 3827 set_tunable_impl "$1" "$2" Z 3828} 3829 3830# 3831# Set a global system tunable (32-bit value) 3832# 3833# $1 tunable name (use a NAME defined in tunables.cfg) 3834# $2 tunable values 3835# 3836function set_tunable32 3837{ 3838 set_tunable_impl "$1" "$2" W 3839} 3840 3841function set_tunable_impl 3842{ 3843 typeset name="$1" 3844 typeset value="$2" 3845 typeset mdb_cmd="$3" 3846 typeset module="${4:-zfs}" 3847 3848 eval "typeset tunable=\$$name" 3849 case "$tunable" in 3850 UNSUPPORTED) 3851 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3852 ;; 3853 "") 3854 log_fail "Tunable '$name' must be added to tunables.cfg" 3855 ;; 3856 *) 3857 ;; 3858 esac 3859 3860 [[ -z "$value" ]] && return 1 3861 [[ -z "$mdb_cmd" ]] && return 1 3862 3863 case "$(uname)" in 3864 Linux) 3865 typeset zfs_tunables="/sys/module/$module/parameters" 3866 [[ -w "$zfs_tunables/$tunable" ]] || return 1 3867 cat >"$zfs_tunables/$tunable" <<<"$value" 3868 return $? 3869 ;; 3870 FreeBSD) 3871 sysctl vfs.zfs.$tunable=$value 3872 return "$?" 3873 ;; 3874 SunOS) 3875 [[ "$module" -eq "zfs" ]] || return 1 3876 echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw 3877 return $? 3878 ;; 3879 esac 3880} 3881 3882# 3883# Get a global system tunable 3884# 3885# $1 tunable name (use a NAME defined in tunables.cfg) 3886# 3887function get_tunable 3888{ 3889 get_tunable_impl "$1" 3890} 3891 3892function get_tunable_impl 3893{ 3894 typeset name="$1" 3895 typeset module="${2:-zfs}" 3896 3897 eval "typeset tunable=\$$name" 3898 case "$tunable" in 3899 UNSUPPORTED) 3900 log_unsupported "Tunable '$name' is unsupported on $(uname)" 3901 ;; 3902 "") 3903 log_fail "Tunable '$name' must be added to tunables.cfg" 3904 ;; 3905 *) 3906 ;; 3907 esac 3908 3909 case "$(uname)" in 3910 Linux) 3911 typeset zfs_tunables="/sys/module/$module/parameters" 3912 [[ -f "$zfs_tunables/$tunable" ]] || return 1 3913 cat $zfs_tunables/$tunable 3914 return $? 3915 ;; 3916 FreeBSD) 3917 sysctl -n vfs.zfs.$tunable 3918 ;; 3919 SunOS) 3920 [[ "$module" -eq "zfs" ]] || return 1 3921 ;; 3922 esac 3923 3924 return 1 3925} 3926 3927# 3928# Prints the current time in seconds since UNIX Epoch. 3929# 3930function current_epoch 3931{ 3932 printf '%(%s)T' 3933} 3934 3935# 3936# Get decimal value of global uint32_t variable using mdb. 3937# 3938function mdb_get_uint32 3939{ 3940 typeset variable=$1 3941 typeset value 3942 3943 value=$(mdb -k -e "$variable/X | ::eval .=U") 3944 if [[ $? -ne 0 ]]; then 3945 log_fail "Failed to get value of '$variable' from mdb." 3946 return 1 3947 fi 3948 3949 echo $value 3950 return 0 3951} 3952 3953# 3954# Set global uint32_t variable to a decimal value using mdb. 3955# 3956function mdb_set_uint32 3957{ 3958 typeset variable=$1 3959 typeset value=$2 3960 3961 mdb -kw -e "$variable/W 0t$value" > /dev/null 3962 if [[ $? -ne 0 ]]; then 3963 echo "Failed to set '$variable' to '$value' in mdb." 3964 return 1 3965 fi 3966 3967 return 0 3968} 3969 3970# 3971# Set global scalar integer variable to a hex value using mdb. 3972# Note: Target should have CTF data loaded. 3973# 3974function mdb_ctf_set_int 3975{ 3976 typeset variable=$1 3977 typeset value=$2 3978 3979 mdb -kw -e "$variable/z $value" > /dev/null 3980 if [[ $? -ne 0 ]]; then 3981 echo "Failed to set '$variable' to '$value' in mdb." 3982 return 1 3983 fi 3984 3985 return 0 3986} 3987 3988# 3989# Compute MD5 digest for given file or stdin if no file given. 3990# Note: file path must not contain spaces 3991# 3992function md5digest 3993{ 3994 typeset file=$1 3995 3996 case $(uname) in 3997 FreeBSD) 3998 md5 -q $file 3999 ;; 4000 *) 4001 md5sum -b $file | awk '{ print $1 }' 4002 ;; 4003 esac 4004} 4005 4006# 4007# Compute SHA256 digest for given file or stdin if no file given. 4008# Note: file path must not contain spaces 4009# 4010function sha256digest 4011{ 4012 typeset file=$1 4013 4014 case $(uname) in 4015 FreeBSD) 4016 sha256 -q $file 4017 ;; 4018 *) 4019 sha256sum -b $file | awk '{ print $1 }' 4020 ;; 4021 esac 4022} 4023 4024function new_fs #<args> 4025{ 4026 case $(uname) in 4027 FreeBSD) 4028 newfs "$@" 4029 ;; 4030 *) 4031 echo y | newfs -v "$@" 4032 ;; 4033 esac 4034} 4035 4036function stat_size #<path> 4037{ 4038 typeset path=$1 4039 4040 case $(uname) in 4041 FreeBSD) 4042 stat -f %z "$path" 4043 ;; 4044 *) 4045 stat -c %s "$path" 4046 ;; 4047 esac 4048} 4049 4050function stat_ctime #<path> 4051{ 4052 typeset path=$1 4053 4054 case $(uname) in 4055 FreeBSD) 4056 stat -f %c "$path" 4057 ;; 4058 *) 4059 stat -c %Z "$path" 4060 ;; 4061 esac 4062} 4063 4064function stat_crtime #<path> 4065{ 4066 typeset path=$1 4067 4068 case $(uname) in 4069 FreeBSD) 4070 stat -f %B "$path" 4071 ;; 4072 *) 4073 stat -c %W "$path" 4074 ;; 4075 esac 4076} 4077 4078# Run a command as if it was being run in a TTY. 4079# 4080# Usage: 4081# 4082# faketty command 4083# 4084function faketty 4085{ 4086 if is_freebsd; then 4087 script -q /dev/null env "$@" 4088 else 4089 script --return --quiet -c "$*" /dev/null 4090 fi 4091} 4092 4093# 4094# Produce a random permutation of the integers in a given range (inclusive). 4095# 4096function range_shuffle # begin end 4097{ 4098 typeset -i begin=$1 4099 typeset -i end=$2 4100 4101 seq ${begin} ${end} | sort -R 4102} 4103 4104# 4105# Cross-platform xattr helpers 4106# 4107 4108function get_xattr # name path 4109{ 4110 typeset name=$1 4111 typeset path=$2 4112 4113 case $(uname) in 4114 FreeBSD) 4115 getextattr -qq user "${name}" "${path}" 4116 ;; 4117 *) 4118 attr -qg "${name}" "${path}" 4119 ;; 4120 esac 4121} 4122 4123function set_xattr # name value path 4124{ 4125 typeset name=$1 4126 typeset value=$2 4127 typeset path=$3 4128 4129 case $(uname) in 4130 FreeBSD) 4131 setextattr user "${name}" "${value}" "${path}" 4132 ;; 4133 *) 4134 attr -qs "${name}" -V "${value}" "${path}" 4135 ;; 4136 esac 4137} 4138 4139function set_xattr_stdin # name value 4140{ 4141 typeset name=$1 4142 typeset path=$2 4143 4144 case $(uname) in 4145 FreeBSD) 4146 setextattr -i user "${name}" "${path}" 4147 ;; 4148 *) 4149 attr -qs "${name}" "${path}" 4150 ;; 4151 esac 4152} 4153 4154function rm_xattr # name path 4155{ 4156 typeset name=$1 4157 typeset path=$2 4158 4159 case $(uname) in 4160 FreeBSD) 4161 rmextattr -q user "${name}" "${path}" 4162 ;; 4163 *) 4164 attr -qr "${name}" "${path}" 4165 ;; 4166 esac 4167} 4168 4169function ls_xattr # path 4170{ 4171 typeset path=$1 4172 4173 case $(uname) in 4174 FreeBSD) 4175 lsextattr -qq user "${path}" 4176 ;; 4177 *) 4178 attr -ql "${path}" 4179 ;; 4180 esac 4181} 4182 4183function kstat # stat flags? 4184{ 4185 typeset stat=$1 4186 typeset flags=${2-"-n"} 4187 4188 case $(uname) in 4189 FreeBSD) 4190 sysctl $flags kstat.zfs.misc.$stat 4191 ;; 4192 Linux) 4193 typeset zfs_kstat="/proc/spl/kstat/zfs/$stat" 4194 [[ -f "$zfs_kstat" ]] || return 1 4195 cat $zfs_kstat 4196 ;; 4197 *) 4198 false 4199 ;; 4200 esac 4201} 4202 4203function get_arcstat # stat 4204{ 4205 typeset stat=$1 4206 4207 case $(uname) in 4208 FreeBSD) 4209 kstat arcstats.$stat 4210 ;; 4211 Linux) 4212 kstat arcstats | awk "/$stat/ { print \$3 }" 4213 ;; 4214 *) 4215 false 4216 ;; 4217 esac 4218} 4219 4220function punch_hole # offset length file 4221{ 4222 typeset offset=$1 4223 typeset length=$2 4224 typeset file=$3 4225 4226 case $(uname) in 4227 FreeBSD) 4228 truncate -d -o $offset -l $length "$file" 4229 ;; 4230 Linux) 4231 fallocate --punch-hole --offset $offset --length $length "$file" 4232 ;; 4233 *) 4234 false 4235 ;; 4236 esac 4237} 4238 4239# 4240# Wait for the specified arcstat to reach non-zero quiescence. 4241# If echo is 1 echo the value after reaching quiescence, otherwise 4242# if echo is 0 print the arcstat we are waiting on. 4243# 4244function arcstat_quiescence # stat echo 4245{ 4246 typeset stat=$1 4247 typeset echo=$2 4248 typeset do_once=true 4249 4250 if [[ $echo -eq 0 ]]; then 4251 echo "Waiting for arcstat $1 quiescence." 4252 fi 4253 4254 while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do 4255 typeset stat1=$(get_arcstat $stat) 4256 sleep 2 4257 typeset stat2=$(get_arcstat $stat) 4258 do_once=false 4259 done 4260 4261 if [[ $echo -eq 1 ]]; then 4262 echo $stat2 4263 fi 4264} 4265 4266function arcstat_quiescence_noecho # stat 4267{ 4268 typeset stat=$1 4269 arcstat_quiescence $stat 0 4270} 4271 4272function arcstat_quiescence_echo # stat 4273{ 4274 typeset stat=$1 4275 arcstat_quiescence $stat 1 4276} 4277 4278# 4279# Given an array of pids, wait until all processes 4280# have completed and check their return status. 4281# 4282function wait_for_children #children 4283{ 4284 rv=0 4285 children=("$@") 4286 for child in "${children[@]}" 4287 do 4288 child_exit=0 4289 wait ${child} || child_exit=$? 4290 if [ $child_exit -ne 0 ]; then 4291 echo "child ${child} failed with ${child_exit}" 4292 rv=1 4293 fi 4294 done 4295 return $rv 4296} 4297