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