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