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