]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/contrib/openzfs/tests/zfs-tests/include/libtest.shlib
Update OpenZFS to 2.0.0-rc3-gfc5966
[FreeBSD/FreeBSD.git] / sys / contrib / openzfs / tests / zfs-tests / include / libtest.shlib
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 #
43 if [ -n "$STF_PATH" ]; then
44         PATH="$STF_PATH"
45 fi
46
47 #
48 # Generic dot version comparison function
49 #
50 # Returns success when version $1 is greater than or equal to $2.
51 #
52 function 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 #
67 function 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
88 function 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
100 function 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
113 function 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
126 function 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
141 function 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
154 function 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
170 function 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
229 function 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
241 function 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
252 function splitline
253 {
254         echo $1 | sed "s/,/ /g"
255 }
256
257 function default_setup
258 {
259         default_setup_noexit "$@"
260
261         log_pass
262 }
263
264 function 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 #
274 function 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 #
329 function 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 #
340 function 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 #
351 function 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 #
365 function 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 #
388 function 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 #
409 function 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 #
436 function 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
466 function 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
478 function 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
501 function 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
522 function 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
540 function 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 #
551 function 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
576 function 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 #
589 function get_all_pools
590 {
591         zpool list -H -o name | grep -Fvx "$KEEP" | grep -v "$NO_POOLS"
592 }
593
594 function 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 #
677 function 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 #
702 function 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 #
732 function 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 #
759 function 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
774 function 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 #
785 function 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 #
797 function 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 #
815 function 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 #
848 function 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 #
882 function 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 #
892 function 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>
927 function 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 #
1048 function 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 #
1083 function 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 #
1136 function 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 #
1182 function 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 #
1205 function 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 #
1228 function 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
1255 function 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
1271 function 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
1290 function 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
1306 function is_shared_freebsd
1307 {
1308         typeset fs=$1
1309
1310         pgrep -q mountd && showmount -E | grep -qx $fs
1311 }
1312
1313 function 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
1332 function 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 #
1350 function 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
1376 function 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
1390 function 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
1404 function 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 #
1424 function 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 #
1455 function 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 #
1484 function 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 #
1501 function 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 #
1516 function 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 #
1531 function 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 #
1553 function 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 #
1575 function 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 #
1589 function 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
1600 function 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 #
1618 function 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 #
1686 function 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 #
1707 function 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
1748 function 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
1777 function 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
1813 function 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
1840 function 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 #
1876 function 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 #
1984 function 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 #
2010 function 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 #
2036 function 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 #
2057 function 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 #
2082 function 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 #
2119 function 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 #
2141 function 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 #
2156 function 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 # */
2174 function 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 #
2199 function 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 #
2218 function 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 #
2243 function 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 #
2262 function 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 #
2281 function 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 #
2307 function 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 #
2336 function 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
2343 function is_pool_resilvered #pool <verbose>
2344 {
2345         check_pool_status "$1" "scan" "resilvered " $2
2346         return $?
2347 }
2348
2349 function is_pool_scrubbing #pool <verbose>
2350 {
2351         check_pool_status "$1" "scan" "scrub in progress since " $2
2352         return $?
2353 }
2354
2355 function is_pool_scrubbed #pool <verbose>
2356 {
2357         check_pool_status "$1" "scan" "scrub repaired" $2
2358         return $?
2359 }
2360
2361 function is_pool_scrub_stopped #pool <verbose>
2362 {
2363         check_pool_status "$1" "scan" "scrub canceled" $2
2364         return $?
2365 }
2366
2367 function is_pool_scrub_paused #pool <verbose>
2368 {
2369         check_pool_status "$1" "scan" "scrub paused since " $2
2370         return $?
2371 }
2372
2373 function is_pool_removing #pool
2374 {
2375         check_pool_status "$1" "remove" "in progress since "
2376         return $?
2377 }
2378
2379 function is_pool_removed #pool
2380 {
2381         check_pool_status "$1" "remove" "completed on"
2382         return $?
2383 }
2384
2385 function is_pool_discarding #pool
2386 {
2387         check_pool_status "$1" "checkpoint" "discarding"
2388         return $?
2389 }
2390
2391 function 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 #
2414 function 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 #*/
2439 function 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         }
2478 EOF
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
2522 function 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 #
2561 function 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 #
2577 function 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 #
2608 function 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
2625 function 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
2636 function 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
2647 function 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
2664 function 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
2679 function 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
2696 function 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
2707 function 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
2723 function 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 #
2747 function 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         echo "export PATH=\"$STF_PATH\"" >>$basedir/$user/.profile
2770         echo "export PATH=\"$STF_PATH\"" >>$basedir/$user/.bash_profile
2771         echo "export PATH=\"$STF_PATH\"" >>$basedir/$user/.login
2772
2773         return 0
2774 }
2775
2776 #
2777 # Delete the specified user.
2778 #
2779 # $1 login name
2780 # $2 base of the homedir (optional)
2781 #
2782 function del_user #<logname> <basedir>
2783 {
2784         typeset user=$1
2785         typeset basedir=${2:-"/var/tmp"}
2786
2787         if ((${#user} == 0)); then
2788                 log_fail "login name is necessary."
2789         fi
2790
2791         case $(uname) in
2792         FreeBSD)
2793                 del_user_freebsd "$user"
2794                 ;;
2795         Linux)
2796                 del_user_linux "$user"
2797                 ;;
2798         *)
2799                 del_user_illumos "$user"
2800                 ;;
2801         esac
2802
2803         [[ -d $basedir/$user ]] && rm -fr $basedir/$user
2804
2805         return 0
2806 }
2807
2808 #
2809 # Select valid gid and create specified group.
2810 #
2811 # $1 group name
2812 #
2813 function add_group #<group_name>
2814 {
2815         typeset group=$1
2816
2817         if ((${#group} == 0)); then
2818                 log_fail "group name is necessary."
2819         fi
2820
2821         case $(uname) in
2822         FreeBSD)
2823                 add_group_freebsd "$group"
2824                 ;;
2825         Linux)
2826                 add_group_linux "$group"
2827                 ;;
2828         *)
2829                 add_group_illumos "$group"
2830                 ;;
2831         esac
2832
2833         return 0
2834 }
2835
2836 #
2837 # Delete the specified group.
2838 #
2839 # $1 group name
2840 #
2841 function del_group #<group_name>
2842 {
2843         typeset group=$1
2844
2845         if ((${#group} == 0)); then
2846                 log_fail "group name is necessary."
2847         fi
2848
2849         case $(uname) in
2850         FreeBSD)
2851                 del_group_freebsd "$group"
2852                 ;;
2853         Linux)
2854                 del_group_linux "$group"
2855                 ;;
2856         *)
2857                 del_group_illumos "$group"
2858                 ;;
2859         esac
2860
2861         return 0
2862 }
2863
2864 #
2865 # This function will return true if it's safe to destroy the pool passed
2866 # as argument 1. It checks for pools based on zvols and files, and also
2867 # files contained in a pool that may have a different mountpoint.
2868 #
2869 function safe_to_destroy_pool { # $1 the pool name
2870
2871         typeset pool=""
2872         typeset DONT_DESTROY=""
2873
2874         # We check that by deleting the $1 pool, we're not
2875         # going to pull the rug out from other pools. Do this
2876         # by looking at all other pools, ensuring that they
2877         # aren't built from files or zvols contained in this pool.
2878
2879         for pool in $(zpool list -H -o name)
2880         do
2881                 ALTMOUNTPOOL=""
2882
2883                 # this is a list of the top-level directories in each of the
2884                 # files that make up the path to the files the pool is based on
2885                 FILEPOOL=$(zpool status -v $pool | grep /$1/ | \
2886                         awk '{print $1}')
2887
2888                 # this is a list of the zvols that make up the pool
2889                 ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \
2890                     | awk '{print $1}')
2891
2892                 # also want to determine if it's a file-based pool using an
2893                 # alternate mountpoint...
2894                 POOL_FILE_DIRS=$(zpool status -v $pool | \
2895                                         grep / | awk '{print $1}' | \
2896                                         awk -F/ '{print $2}' | grep -v "dev")
2897
2898                 for pooldir in $POOL_FILE_DIRS
2899                 do
2900                         OUTPUT=$(zfs list -H -r -o mountpoint $1 | \
2901                                         grep "${pooldir}$" | awk '{print $1}')
2902
2903                         ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}"
2904                 done
2905
2906
2907                 if [ ! -z "$ZVOLPOOL" ]
2908                 then
2909                         DONT_DESTROY="true"
2910                         log_note "Pool $pool is built from $ZVOLPOOL on $1"
2911                 fi
2912
2913                 if [ ! -z "$FILEPOOL" ]
2914                 then
2915                         DONT_DESTROY="true"
2916                         log_note "Pool $pool is built from $FILEPOOL on $1"
2917                 fi
2918
2919                 if [ ! -z "$ALTMOUNTPOOL" ]
2920                 then
2921                         DONT_DESTROY="true"
2922                         log_note "Pool $pool is built from $ALTMOUNTPOOL on $1"
2923                 fi
2924         done
2925
2926         if [ -z "${DONT_DESTROY}" ]
2927         then
2928                 return 0
2929         else
2930                 log_note "Warning: it is not safe to destroy $1!"
2931                 return 1
2932         fi
2933 }
2934
2935 #
2936 # Verify zfs operation with -p option work as expected
2937 # $1 operation, value could be create, clone or rename
2938 # $2 dataset type, value could be fs or vol
2939 # $3 dataset name
2940 # $4 new dataset name
2941 #
2942 function verify_opt_p_ops
2943 {
2944         typeset ops=$1
2945         typeset datatype=$2
2946         typeset dataset=$3
2947         typeset newdataset=$4
2948
2949         if [[ $datatype != "fs" && $datatype != "vol" ]]; then
2950                 log_fail "$datatype is not supported."
2951         fi
2952
2953         # check parameters accordingly
2954         case $ops in
2955                 create)
2956                         newdataset=$dataset
2957                         dataset=""
2958                         if [[ $datatype == "vol" ]]; then
2959                                 ops="create -V $VOLSIZE"
2960                         fi
2961                         ;;
2962                 clone)
2963                         if [[ -z $newdataset ]]; then
2964                                 log_fail "newdataset should not be empty" \
2965                                         "when ops is $ops."
2966                         fi
2967                         log_must datasetexists $dataset
2968                         log_must snapexists $dataset
2969                         ;;
2970                 rename)
2971                         if [[ -z $newdataset ]]; then
2972                                 log_fail "newdataset should not be empty" \
2973                                         "when ops is $ops."
2974                         fi
2975                         log_must datasetexists $dataset
2976                         ;;
2977                 *)
2978                         log_fail "$ops is not supported."
2979                         ;;
2980         esac
2981
2982         # make sure the upper level filesystem does not exist
2983         destroy_dataset "${newdataset%/*}" "-rRf"
2984
2985         # without -p option, operation will fail
2986         log_mustnot zfs $ops $dataset $newdataset
2987         log_mustnot datasetexists $newdataset ${newdataset%/*}
2988
2989         # with -p option, operation should succeed
2990         log_must zfs $ops -p $dataset $newdataset
2991         block_device_wait
2992
2993         if ! datasetexists $newdataset ; then
2994                 log_fail "-p option does not work for $ops"
2995         fi
2996
2997         # when $ops is create or clone, redo the operation still return zero
2998         if [[ $ops != "rename" ]]; then
2999                 log_must zfs $ops -p $dataset $newdataset
3000         fi
3001
3002         return 0
3003 }
3004
3005 #
3006 # Get configuration of pool
3007 # $1 pool name
3008 # $2 config name
3009 #
3010 function get_config
3011 {
3012         typeset pool=$1
3013         typeset config=$2
3014         typeset alt_root
3015
3016         if ! poolexists "$pool" ; then
3017                 return 1
3018         fi
3019         alt_root=$(zpool list -H $pool | awk '{print $NF}')
3020         if [[ $alt_root == "-" ]]; then
3021                 value=$(zdb -C $pool | grep "$config:" | awk -F: \
3022                     '{print $2}')
3023         else
3024                 value=$(zdb -e $pool | grep "$config:" | awk -F: \
3025                     '{print $2}')
3026         fi
3027         if [[ -n $value ]] ; then
3028                 value=${value#'}
3029                 value=${value%'}
3030         fi
3031         echo $value
3032
3033         return 0
3034 }
3035
3036 #
3037 # Privated function. Random select one of items from arguments.
3038 #
3039 # $1 count
3040 # $2-n string
3041 #
3042 function _random_get
3043 {
3044         typeset cnt=$1
3045         shift
3046
3047         typeset str="$@"
3048         typeset -i ind
3049         ((ind = RANDOM % cnt + 1))
3050
3051         typeset ret=$(echo "$str" | cut -f $ind -d ' ')
3052         echo $ret
3053 }
3054
3055 #
3056 # Random select one of item from arguments which include NONE string
3057 #
3058 function random_get_with_non
3059 {
3060         typeset -i cnt=$#
3061         ((cnt =+ 1))
3062
3063         _random_get "$cnt" "$@"
3064 }
3065
3066 #
3067 # Random select one of item from arguments which doesn't include NONE string
3068 #
3069 function random_get
3070 {
3071         _random_get "$#" "$@"
3072 }
3073
3074 #
3075 # Detect if the current system support slog
3076 #
3077 function verify_slog_support
3078 {
3079         typeset dir=$TEST_BASE_DIR/disk.$$
3080         typeset pool=foo.$$
3081         typeset vdev=$dir/a
3082         typeset sdev=$dir/b
3083
3084         mkdir -p $dir
3085         mkfile $MINVDEVSIZE $vdev $sdev
3086
3087         typeset -i ret=0
3088         if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then
3089                 ret=1
3090         fi
3091         rm -r $dir
3092
3093         return $ret
3094 }
3095
3096 #
3097 # The function will generate a dataset name with specific length
3098 # $1, the length of the name
3099 # $2, the base string to construct the name
3100 #
3101 function gen_dataset_name
3102 {
3103         typeset -i len=$1
3104         typeset basestr="$2"
3105         typeset -i baselen=${#basestr}
3106         typeset -i iter=0
3107         typeset l_name=""
3108
3109         if ((len % baselen == 0)); then
3110                 ((iter = len / baselen))
3111         else
3112                 ((iter = len / baselen + 1))
3113         fi
3114         while ((iter > 0)); do
3115                 l_name="${l_name}$basestr"
3116
3117                 ((iter -= 1))
3118         done
3119
3120         echo $l_name
3121 }
3122
3123 #
3124 # Get cksum tuple of dataset
3125 # $1 dataset name
3126 #
3127 # sample zdb output:
3128 # Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp
3129 # DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4
3130 # lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P
3131 # fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744
3132 function datasetcksum
3133 {
3134         typeset cksum
3135         sync
3136         cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \
3137                 | awk -F= '{print $7}')
3138         echo $cksum
3139 }
3140
3141 #
3142 # Get cksum of file
3143 # #1 file path
3144 #
3145 function checksum
3146 {
3147         typeset cksum
3148         cksum=$(cksum $1 | awk '{print $1}')
3149         echo $cksum
3150 }
3151
3152 #
3153 # Get the given disk/slice state from the specific field of the pool
3154 #
3155 function get_device_state #pool disk field("", "spares","logs")
3156 {
3157         typeset pool=$1
3158         typeset disk=${2#$DEV_DSKDIR/}
3159         typeset field=${3:-$pool}
3160
3161         state=$(zpool status -v "$pool" 2>/dev/null | \
3162                 nawk -v device=$disk -v pool=$pool -v field=$field \
3163                 'BEGIN {startconfig=0; startfield=0; }
3164                 /config:/ {startconfig=1}
3165                 (startconfig==1) && ($1==field) {startfield=1; next;}
3166                 (startfield==1) && ($1==device) {print $2; exit;}
3167                 (startfield==1) &&
3168                 ($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}')
3169         echo $state
3170 }
3171
3172
3173 #
3174 # print the given directory filesystem type
3175 #
3176 # $1 directory name
3177 #
3178 function get_fstype
3179 {
3180         typeset dir=$1
3181
3182         if [[ -z $dir ]]; then
3183                 log_fail "Usage: get_fstype <directory>"
3184         fi
3185
3186         #
3187         #  $ df -n /
3188         #  /              : ufs
3189         #
3190         df -n $dir | awk '{print $3}'
3191 }
3192
3193 #
3194 # Given a disk, label it to VTOC regardless what label was on the disk
3195 # $1 disk
3196 #
3197 function labelvtoc
3198 {
3199         typeset disk=$1
3200         if [[ -z $disk ]]; then
3201                 log_fail "The disk name is unspecified."
3202         fi
3203         typeset label_file=/var/tmp/labelvtoc.$$
3204         typeset arch=$(uname -p)
3205
3206         if is_linux || is_freebsd; then
3207                 log_note "Currently unsupported by the test framework"
3208                 return 1
3209         fi
3210
3211         if [[ $arch == "i386" ]]; then
3212                 echo "label" > $label_file
3213                 echo "0" >> $label_file
3214                 echo "" >> $label_file
3215                 echo "q" >> $label_file
3216                 echo "q" >> $label_file
3217
3218                 fdisk -B $disk >/dev/null 2>&1
3219                 # wait a while for fdisk finishes
3220                 sleep 60
3221         elif [[ $arch == "sparc" ]]; then
3222                 echo "label" > $label_file
3223                 echo "0" >> $label_file
3224                 echo "" >> $label_file
3225                 echo "" >> $label_file
3226                 echo "" >> $label_file
3227                 echo "q" >> $label_file
3228         else
3229                 log_fail "unknown arch type"
3230         fi
3231
3232         format -e -s -d $disk -f $label_file
3233         typeset -i ret_val=$?
3234         rm -f $label_file
3235         #
3236         # wait the format to finish
3237         #
3238         sleep 60
3239         if ((ret_val != 0)); then
3240                 log_fail "unable to label $disk as VTOC."
3241         fi
3242
3243         return 0
3244 }
3245
3246 #
3247 # check if the system was installed as zfsroot or not
3248 # return: 0 if zfsroot, non-zero if not
3249 #
3250 function is_zfsroot
3251 {
3252         df -n / | grep zfs > /dev/null 2>&1
3253         return $?
3254 }
3255
3256 #
3257 # get the root filesystem name if it's zfsroot system.
3258 #
3259 # return: root filesystem name
3260 function get_rootfs
3261 {
3262         typeset rootfs=""
3263
3264         if is_freebsd; then
3265                 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3266         elif ! is_linux; then
3267                 rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \
3268                         /etc/mnttab)
3269         fi
3270         if [[ -z "$rootfs" ]]; then
3271                 log_fail "Can not get rootfs"
3272         fi
3273         zfs list $rootfs > /dev/null 2>&1
3274         if (($? == 0)); then
3275                 echo $rootfs
3276         else
3277                 log_fail "This is not a zfsroot system."
3278         fi
3279 }
3280
3281 #
3282 # get the rootfs's pool name
3283 # return:
3284 #       rootpool name
3285 #
3286 function get_rootpool
3287 {
3288         typeset rootfs=""
3289         typeset rootpool=""
3290
3291         if is_freebsd; then
3292                 rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3293         elif ! is_linux; then
3294                 rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \
3295                          /etc/mnttab)
3296         fi
3297         if [[ -z "$rootfs" ]]; then
3298                 log_fail "Can not get rootpool"
3299         fi
3300         zfs list $rootfs > /dev/null 2>&1
3301         if (($? == 0)); then
3302                 echo ${rootfs%%/*}
3303         else
3304                 log_fail "This is not a zfsroot system."
3305         fi
3306 }
3307
3308 #
3309 # Get the word numbers from a string separated by white space
3310 #
3311 function get_word_count
3312 {
3313         echo $1 | wc -w
3314 }
3315
3316 #
3317 # To verify if the require numbers of disks is given
3318 #
3319 function verify_disk_count
3320 {
3321         typeset -i min=${2:-1}
3322
3323         typeset -i count=$(get_word_count "$1")
3324
3325         if ((count < min)); then
3326                 log_untested "A minimum of $min disks is required to run." \
3327                         " You specified $count disk(s)"
3328         fi
3329 }
3330
3331 function ds_is_volume
3332 {
3333         typeset type=$(get_prop type $1)
3334         [[ $type = "volume" ]] && return 0
3335         return 1
3336 }
3337
3338 function ds_is_filesystem
3339 {
3340         typeset type=$(get_prop type $1)
3341         [[ $type = "filesystem" ]] && return 0
3342         return 1
3343 }
3344
3345 function ds_is_snapshot
3346 {
3347         typeset type=$(get_prop type $1)
3348         [[ $type = "snapshot" ]] && return 0
3349         return 1
3350 }
3351
3352 #
3353 # Check if Trusted Extensions are installed and enabled
3354 #
3355 function is_te_enabled
3356 {
3357         svcs -H -o state labeld 2>/dev/null | grep "enabled"
3358         if (($? != 0)); then
3359                 return 1
3360         else
3361                 return 0
3362         fi
3363 }
3364
3365 # Utility function to determine if a system has multiple cpus.
3366 function is_mp
3367 {
3368         if is_linux; then
3369                 (($(nproc) > 1))
3370         elif is_freebsd; then
3371                 sysctl -n kern.smp.cpus
3372         else
3373                 (($(psrinfo | wc -l) > 1))
3374         fi
3375
3376         return $?
3377 }
3378
3379 function get_cpu_freq
3380 {
3381         if is_linux; then
3382                 lscpu | awk '/CPU MHz/ { print $3 }'
3383         elif is_freebsd; then
3384                 sysctl -n hw.clockrate
3385         else
3386                 psrinfo -v 0 | awk '/processor operates at/ {print $6}'
3387         fi
3388 }
3389
3390 # Run the given command as the user provided.
3391 function user_run
3392 {
3393         typeset user=$1
3394         shift
3395
3396         log_note "user:$user $@"
3397         eval su - \$user -c \"$@\" > $TEST_BASE_DIR/out 2>$TEST_BASE_DIR/err
3398 }
3399
3400 #
3401 # Check if the pool contains the specified vdevs
3402 #
3403 # $1 pool
3404 # $2..n <vdev> ...
3405 #
3406 # Return 0 if the vdevs are contained in the pool, 1 if any of the specified
3407 # vdevs is not in the pool, and 2 if pool name is missing.
3408 #
3409 function vdevs_in_pool
3410 {
3411         typeset pool=$1
3412         typeset vdev
3413
3414         if [[ -z $pool ]]; then
3415                 log_note "Missing pool name."
3416                 return 2
3417         fi
3418
3419         shift
3420
3421         # We could use 'zpool list' to only get the vdevs of the pool but we
3422         # can't reference a mirror/raidz vdev using its ID (i.e mirror-0),
3423         # therefore we use the 'zpool status' output.
3424         typeset tmpfile=$(mktemp)
3425         zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile
3426         for vdev in $@; do
3427                 grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1
3428                 [[ $? -ne 0 ]] && return 1
3429         done
3430
3431         rm -f $tmpfile
3432
3433         return 0;
3434 }
3435
3436 function get_max
3437 {
3438         typeset -l i max=$1
3439         shift
3440
3441         for i in "$@"; do
3442                 max=$((max > i ? max : i))
3443         done
3444
3445         echo $max
3446 }
3447
3448 function get_min
3449 {
3450         typeset -l i min=$1
3451         shift
3452
3453         for i in "$@"; do
3454                 min=$((min < i ? min : i))
3455         done
3456
3457         echo $min
3458 }
3459
3460 # Write data that can be compressed into a directory
3461 function write_compressible
3462 {
3463         typeset dir=$1
3464         typeset megs=$2
3465         typeset nfiles=${3:-1}
3466         typeset bs=${4:-1024k}
3467         typeset fname=${5:-file}
3468
3469         [[ -d $dir ]] || log_fail "No directory: $dir"
3470
3471         # Under Linux fio is not currently used since its behavior can
3472         # differ significantly across versions.  This includes missing
3473         # command line options and cases where the --buffer_compress_*
3474         # options fail to behave as expected.
3475         if is_linux; then
3476                 typeset file_bytes=$(to_bytes $megs)
3477                 typeset bs_bytes=4096
3478                 typeset blocks=$(($file_bytes / $bs_bytes))
3479
3480                 for (( i = 0; i < $nfiles; i++ )); do
3481                         truncate -s $file_bytes $dir/$fname.$i
3482
3483                         # Write every third block to get 66% compression.
3484                         for (( j = 0; j < $blocks; j += 3 )); do
3485                                 dd if=/dev/urandom of=$dir/$fname.$i \
3486                                     seek=$j bs=$bs_bytes count=1 \
3487                                     conv=notrunc >/dev/null 2>&1
3488                         done
3489                 done
3490         else
3491                 log_must eval "fio \
3492                     --name=job \
3493                     --fallocate=0 \
3494                     --minimal \
3495                     --randrepeat=0 \
3496                     --buffer_compress_percentage=66 \
3497                     --buffer_compress_chunk=4096 \
3498                     --directory=$dir \
3499                     --numjobs=$nfiles \
3500                     --nrfiles=$nfiles \
3501                     --rw=write \
3502                     --bs=$bs \
3503                     --filesize=$megs \
3504                     --filename_format='$fname.\$jobnum' >/dev/null"
3505         fi
3506 }
3507
3508 function get_objnum
3509 {
3510         typeset pathname=$1
3511         typeset objnum
3512
3513         [[ -e $pathname ]] || log_fail "No such file or directory: $pathname"
3514         if is_freebsd; then
3515                 objnum=$(stat -f "%i" $pathname)
3516         else
3517                 objnum=$(stat -c %i $pathname)
3518         fi
3519         echo $objnum
3520 }
3521
3522 #
3523 # Sync data to the pool
3524 #
3525 # $1 pool name
3526 # $2 boolean to force uberblock (and config including zpool cache file) update
3527 #
3528 function sync_pool #pool <force>
3529 {
3530         typeset pool=${1:-$TESTPOOL}
3531         typeset force=${2:-false}
3532
3533         if [[ $force == true ]]; then
3534                 log_must zpool sync -f $pool
3535         else
3536                 log_must zpool sync $pool
3537         fi
3538
3539         return 0
3540 }
3541
3542 #
3543 # Wait for zpool 'freeing' property drops to zero.
3544 #
3545 # $1 pool name
3546 #
3547 function wait_freeing #pool
3548 {
3549         typeset pool=${1:-$TESTPOOL}
3550         while true; do
3551                 [[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break
3552                 log_must sleep 1
3553         done
3554 }
3555
3556 #
3557 # Wait for every device replace operation to complete
3558 #
3559 # $1 pool name
3560 #
3561 function wait_replacing #pool
3562 {
3563         typeset pool=${1:-$TESTPOOL}
3564         while true; do
3565                 [[ "" == "$(zpool status $pool |
3566                     awk '/replacing-[0-9]+/ {print $1}')" ]] && break
3567                 log_must sleep 1
3568         done
3569 }
3570
3571 #
3572 # Wait for a pool to be scrubbed
3573 #
3574 # $1 pool name
3575 # $2 number of seconds to wait (optional)
3576 #
3577 # Returns true when pool has been scrubbed, or false if there's a timeout or if
3578 # no scrub was done.
3579 #
3580 function wait_scrubbed
3581 {
3582         typeset pool=${1:-$TESTPOOL}
3583         while true ; do
3584                 is_pool_scrubbed $pool && break
3585                 sleep 1
3586         done
3587 }
3588
3589 # Backup the zed.rc in our test directory so that we can edit it for our test.
3590 #
3591 # Returns: Backup file name.  You will need to pass this to zed_rc_restore().
3592 function zed_rc_backup
3593 {
3594         zedrc_backup="$(mktemp)"
3595         cp $ZEDLET_DIR/zed.rc $zedrc_backup
3596         echo $zedrc_backup
3597 }
3598
3599 function zed_rc_restore
3600 {
3601         mv $1 $ZEDLET_DIR/zed.rc
3602 }
3603
3604 #
3605 # Setup custom environment for the ZED.
3606 #
3607 # $@ Optional list of zedlets to run under zed.
3608 function zed_setup
3609 {
3610         if ! is_linux; then
3611                 log_unsupported "No zed on $(uname)"
3612         fi
3613
3614         if [[ ! -d $ZEDLET_DIR ]]; then
3615                 log_must mkdir $ZEDLET_DIR
3616         fi
3617
3618         if [[ ! -e $VDEVID_CONF ]]; then
3619                 log_must touch $VDEVID_CONF
3620         fi
3621
3622         if [[ -e $VDEVID_CONF_ETC ]]; then
3623                 log_fail "Must not have $VDEVID_CONF_ETC file present on system"
3624         fi
3625         EXTRA_ZEDLETS=$@
3626
3627         # Create a symlink for /etc/zfs/vdev_id.conf file.
3628         log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC
3629
3630         # Setup minimal ZED configuration.  Individual test cases should
3631         # add additional ZEDLETs as needed for their specific test.
3632         log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR
3633         log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR
3634
3635         # Scripts must only be user writable.
3636         if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3637                 saved_umask=$(umask)
3638                 log_must umask 0022
3639                 for i in $EXTRA_ZEDLETS ; do
3640                         log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR
3641                 done
3642                 log_must umask $saved_umask
3643         fi
3644
3645         # Customize the zed.rc file to enable the full debug log.
3646         log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc
3647         echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc
3648
3649 }
3650
3651 #
3652 # Cleanup custom ZED environment.
3653 #
3654 # $@ Optional list of zedlets to remove from our test zed.d directory.
3655 function zed_cleanup
3656 {
3657         if ! is_linux; then
3658                 return
3659         fi
3660         EXTRA_ZEDLETS=$@
3661
3662         log_must rm -f ${ZEDLET_DIR}/zed.rc
3663         log_must rm -f ${ZEDLET_DIR}/zed-functions.sh
3664         log_must rm -f ${ZEDLET_DIR}/all-syslog.sh
3665         log_must rm -f ${ZEDLET_DIR}/all-debug.sh
3666         log_must rm -f ${ZEDLET_DIR}/state
3667
3668         if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3669                 for i in $EXTRA_ZEDLETS ; do
3670                         log_must rm -f ${ZEDLET_DIR}/$i
3671                 done
3672         fi
3673         log_must rm -f $ZED_LOG
3674         log_must rm -f $ZED_DEBUG_LOG
3675         log_must rm -f $VDEVID_CONF_ETC
3676         log_must rm -f $VDEVID_CONF
3677         rmdir $ZEDLET_DIR
3678 }
3679
3680 #
3681 # Check if ZED is currently running, if not start ZED.
3682 #
3683 function zed_start
3684 {
3685         if ! is_linux; then
3686                 return
3687         fi
3688
3689         # ZEDLET_DIR=/var/tmp/zed
3690         if [[ ! -d $ZEDLET_DIR ]]; then
3691                 log_must mkdir $ZEDLET_DIR
3692         fi
3693
3694         # Verify the ZED is not already running.
3695         pgrep -x zed > /dev/null
3696         if (($? == 0)); then
3697                 log_note "ZED already running"
3698         else
3699                 log_note "Starting ZED"
3700                 # run ZED in the background and redirect foreground logging
3701                 # output to $ZED_LOG.
3702                 log_must truncate -s 0 $ZED_DEBUG_LOG
3703                 log_must eval "zed -vF -d $ZEDLET_DIR -p $ZEDLET_DIR/zed.pid -P $PATH" \
3704                     "-s $ZEDLET_DIR/state 2>$ZED_LOG &"
3705         fi
3706
3707         return 0
3708 }
3709
3710 #
3711 # Kill ZED process
3712 #
3713 function zed_stop
3714 {
3715         if ! is_linux; then
3716                 return
3717         fi
3718
3719         log_note "Stopping ZED"
3720         if [[ -f ${ZEDLET_DIR}/zed.pid ]]; then
3721                 zedpid=$(<${ZEDLET_DIR}/zed.pid)
3722                 kill $zedpid
3723                 while ps -p $zedpid > /dev/null; do
3724                         sleep 1
3725                 done
3726                 rm -f ${ZEDLET_DIR}/zed.pid
3727         fi
3728         return 0
3729 }
3730
3731 #
3732 # Drain all zevents
3733 #
3734 function zed_events_drain
3735 {
3736         while [ $(zpool events -H | wc -l) -ne 0 ]; do
3737                 sleep 1
3738                 zpool events -c >/dev/null
3739         done
3740 }
3741
3742 # Set a variable in zed.rc to something, un-commenting it in the process.
3743 #
3744 # $1 variable
3745 # $2 value
3746 function zed_rc_set
3747 {
3748         var="$1"
3749         val="$2"
3750         # Remove the line
3751         cmd="'/$var/d'"
3752         eval sed -i $cmd $ZEDLET_DIR/zed.rc
3753
3754         # Add it at the end
3755         echo "$var=$val" >> $ZEDLET_DIR/zed.rc
3756 }
3757
3758
3759 #
3760 # Check is provided device is being active used as a swap device.
3761 #
3762 function is_swap_inuse
3763 {
3764         typeset device=$1
3765
3766         if [[ -z $device ]] ; then
3767                 log_note "No device specified."
3768                 return 1
3769         fi
3770
3771         if is_linux; then
3772                 swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1
3773         elif is_freebsd; then
3774                 swapctl -l | grep -w $device
3775         else
3776                 swap -l | grep -w $device > /dev/null 2>&1
3777         fi
3778
3779         return $?
3780 }
3781
3782 #
3783 # Setup a swap device using the provided device.
3784 #
3785 function swap_setup
3786 {
3787         typeset swapdev=$1
3788
3789         if is_linux; then
3790                 log_must eval "mkswap $swapdev > /dev/null 2>&1"
3791                 log_must swapon $swapdev
3792         elif is_freebsd; then
3793                 log_must swapctl -a $swapdev
3794         else
3795                 log_must swap -a $swapdev
3796         fi
3797
3798         return 0
3799 }
3800
3801 #
3802 # Cleanup a swap device on the provided device.
3803 #
3804 function swap_cleanup
3805 {
3806         typeset swapdev=$1
3807
3808         if is_swap_inuse $swapdev; then
3809                 if is_linux; then
3810                         log_must swapoff $swapdev
3811                 elif is_freebsd; then
3812                         log_must swapoff $swapdev
3813                 else
3814                         log_must swap -d $swapdev
3815                 fi
3816         fi
3817
3818         return 0
3819 }
3820
3821 #
3822 # Set a global system tunable (64-bit value)
3823 #
3824 # $1 tunable name (use a NAME defined in tunables.cfg)
3825 # $2 tunable values
3826 #
3827 function set_tunable64
3828 {
3829         set_tunable_impl "$1" "$2" Z
3830 }
3831
3832 #
3833 # Set a global system tunable (32-bit value)
3834 #
3835 # $1 tunable name (use a NAME defined in tunables.cfg)
3836 # $2 tunable values
3837 #
3838 function set_tunable32
3839 {
3840         set_tunable_impl "$1" "$2" W
3841 }
3842
3843 function set_tunable_impl
3844 {
3845         typeset name="$1"
3846         typeset value="$2"
3847         typeset mdb_cmd="$3"
3848         typeset module="${4:-zfs}"
3849
3850         eval "typeset tunable=\$$name"
3851         case "$tunable" in
3852         UNSUPPORTED)
3853                 log_unsupported "Tunable '$name' is unsupported on $(uname)"
3854                 ;;
3855         "")
3856                 log_fail "Tunable '$name' must be added to tunables.cfg"
3857                 ;;
3858         *)
3859                 ;;
3860         esac
3861
3862         [[ -z "$value" ]] && return 1
3863         [[ -z "$mdb_cmd" ]] && return 1
3864
3865         case "$(uname)" in
3866         Linux)
3867                 typeset zfs_tunables="/sys/module/$module/parameters"
3868                 [[ -w "$zfs_tunables/$tunable" ]] || return 1
3869                 cat >"$zfs_tunables/$tunable" <<<"$value"
3870                 return $?
3871                 ;;
3872         FreeBSD)
3873                 sysctl vfs.zfs.$tunable=$value
3874                 return "$?"
3875                 ;;
3876         SunOS)
3877                 [[ "$module" -eq "zfs" ]] || return 1
3878                 echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw
3879                 return $?
3880                 ;;
3881         esac
3882 }
3883
3884 #
3885 # Get a global system tunable
3886 #
3887 # $1 tunable name (use a NAME defined in tunables.cfg)
3888 #
3889 function get_tunable
3890 {
3891         get_tunable_impl "$1"
3892 }
3893
3894 function get_tunable_impl
3895 {
3896         typeset name="$1"
3897         typeset module="${2:-zfs}"
3898
3899         eval "typeset tunable=\$$name"
3900         case "$tunable" in
3901         UNSUPPORTED)
3902                 log_unsupported "Tunable '$name' is unsupported on $(uname)"
3903                 ;;
3904         "")
3905                 log_fail "Tunable '$name' must be added to tunables.cfg"
3906                 ;;
3907         *)
3908                 ;;
3909         esac
3910
3911         case "$(uname)" in
3912         Linux)
3913                 typeset zfs_tunables="/sys/module/$module/parameters"
3914                 [[ -f "$zfs_tunables/$tunable" ]] || return 1
3915                 cat $zfs_tunables/$tunable
3916                 return $?
3917                 ;;
3918         FreeBSD)
3919                 sysctl -n vfs.zfs.$tunable
3920                 ;;
3921         SunOS)
3922                 [[ "$module" -eq "zfs" ]] || return 1
3923                 ;;
3924         esac
3925
3926         return 1
3927 }
3928
3929 #
3930 # Prints the current time in seconds since UNIX Epoch.
3931 #
3932 function current_epoch
3933 {
3934         printf '%(%s)T'
3935 }
3936
3937 #
3938 # Get decimal value of global uint32_t variable using mdb.
3939 #
3940 function mdb_get_uint32
3941 {
3942         typeset variable=$1
3943         typeset value
3944
3945         value=$(mdb -k -e "$variable/X | ::eval .=U")
3946         if [[ $? -ne 0 ]]; then
3947                 log_fail "Failed to get value of '$variable' from mdb."
3948                 return 1
3949         fi
3950
3951         echo $value
3952         return 0
3953 }
3954
3955 #
3956 # Set global uint32_t variable to a decimal value using mdb.
3957 #
3958 function mdb_set_uint32
3959 {
3960         typeset variable=$1
3961         typeset value=$2
3962
3963         mdb -kw -e "$variable/W 0t$value" > /dev/null
3964         if [[ $? -ne 0 ]]; then
3965                 echo "Failed to set '$variable' to '$value' in mdb."
3966                 return 1
3967         fi
3968
3969         return 0
3970 }
3971
3972 #
3973 # Set global scalar integer variable to a hex value using mdb.
3974 # Note: Target should have CTF data loaded.
3975 #
3976 function mdb_ctf_set_int
3977 {
3978         typeset variable=$1
3979         typeset value=$2
3980
3981         mdb -kw -e "$variable/z $value" > /dev/null
3982         if [[ $? -ne 0 ]]; then
3983                 echo "Failed to set '$variable' to '$value' in mdb."
3984                 return 1
3985         fi
3986
3987         return 0
3988 }
3989
3990 #
3991 # Compute MD5 digest for given file or stdin if no file given.
3992 # Note: file path must not contain spaces
3993 #
3994 function md5digest
3995 {
3996         typeset file=$1
3997
3998         case $(uname) in
3999         FreeBSD)
4000                 md5 -q $file
4001                 ;;
4002         *)
4003                 md5sum -b $file | awk '{ print $1 }'
4004                 ;;
4005         esac
4006 }
4007
4008 #
4009 # Compute SHA256 digest for given file or stdin if no file given.
4010 # Note: file path must not contain spaces
4011 #
4012 function sha256digest
4013 {
4014         typeset file=$1
4015
4016         case $(uname) in
4017         FreeBSD)
4018                 sha256 -q $file
4019                 ;;
4020         *)
4021                 sha256sum -b $file | awk '{ print $1 }'
4022                 ;;
4023         esac
4024 }
4025
4026 function new_fs #<args>
4027 {
4028         case $(uname) in
4029         FreeBSD)
4030                 newfs "$@"
4031                 ;;
4032         *)
4033                 echo y | newfs -v "$@"
4034                 ;;
4035         esac
4036 }
4037
4038 function stat_size #<path>
4039 {
4040         typeset path=$1
4041
4042         case $(uname) in
4043         FreeBSD)
4044                 stat -f %z "$path"
4045                 ;;
4046         *)
4047                 stat -c %s "$path"
4048                 ;;
4049         esac
4050 }
4051
4052 # Run a command as if it was being run in a TTY.
4053 #
4054 # Usage:
4055 #
4056 #    faketty command
4057 #
4058 function faketty
4059 {
4060     if is_freebsd; then
4061         script -q /dev/null env "$@"
4062     else
4063         script --return --quiet -c "$*" /dev/null
4064     fi
4065 }
4066
4067 #
4068 # Produce a random permutation of the integers in a given range (inclusive).
4069 #
4070 function range_shuffle # begin end
4071 {
4072         typeset -i begin=$1
4073         typeset -i end=$2
4074
4075         seq ${begin} ${end} | sort -R
4076 }
4077
4078 #
4079 # Cross-platform xattr helpers
4080 #
4081
4082 function get_xattr # name path
4083 {
4084         typeset name=$1
4085         typeset path=$2
4086
4087         case $(uname) in
4088         FreeBSD)
4089                 getextattr -qq user "${name}" "${path}"
4090                 ;;
4091         *)
4092                 attr -qg "${name}" "${path}"
4093                 ;;
4094         esac
4095 }
4096
4097 function set_xattr # name value path
4098 {
4099         typeset name=$1
4100         typeset value=$2
4101         typeset path=$3
4102
4103         case $(uname) in
4104         FreeBSD)
4105                 setextattr user "${name}" "${value}" "${path}"
4106                 ;;
4107         *)
4108                 attr -qs "${name}" -V "${value}" "${path}"
4109                 ;;
4110         esac
4111 }
4112
4113 function set_xattr_stdin # name value
4114 {
4115         typeset name=$1
4116         typeset path=$2
4117
4118         case $(uname) in
4119         FreeBSD)
4120                 setextattr -i user "${name}" "${path}"
4121                 ;;
4122         *)
4123                 attr -qs "${name}" "${path}"
4124                 ;;
4125         esac
4126 }
4127
4128 function rm_xattr # name path
4129 {
4130         typeset name=$1
4131         typeset path=$2
4132
4133         case $(uname) in
4134         FreeBSD)
4135                 rmextattr -q user "${name}" "${path}"
4136                 ;;
4137         *)
4138                 attr -qr "${name}" "${path}"
4139                 ;;
4140         esac
4141 }
4142
4143 function ls_xattr # path
4144 {
4145         typeset path=$1
4146
4147         case $(uname) in
4148         FreeBSD)
4149                 lsextattr -qq user "${path}"
4150                 ;;
4151         *)
4152                 attr -ql "${path}"
4153                 ;;
4154         esac
4155 }
4156
4157 function kstat # stat flags?
4158 {
4159         typeset stat=$1
4160         typeset flags=${2-"-n"}
4161
4162         case $(uname) in
4163         FreeBSD)
4164                 sysctl $flags kstat.zfs.misc.$stat
4165                 ;;
4166         Linux)
4167                 typeset zfs_kstat="/proc/spl/kstat/zfs/$stat"
4168                 [[ -f "$zfs_kstat" ]] || return 1
4169                 cat $zfs_kstat
4170                 ;;
4171         *)
4172                 false
4173                 ;;
4174         esac
4175 }
4176
4177 function get_arcstat # stat
4178 {
4179         typeset stat=$1
4180
4181         case $(uname) in
4182         FreeBSD)
4183                 kstat arcstats.$stat
4184                 ;;
4185         Linux)
4186                 kstat arcstats | awk "/$stat/ { print \$3 }"
4187                 ;;
4188         *)
4189                 false
4190                 ;;
4191         esac
4192 }
4193
4194 #
4195 # Given an array of pids, wait until all processes
4196 # have completed and check their return status.
4197 #
4198 function wait_for_children #children
4199 {
4200         rv=0
4201         children=("$@")
4202         for child in "${children[@]}"
4203         do
4204                 child_exit=0
4205                 wait ${child} || child_exit=$?
4206                 if [ $child_exit -ne 0 ]; then
4207                         echo "child ${child} failed with ${child_exit}"
4208                         rv=1
4209                 fi
4210         done
4211         return $rv
4212 }