xref: /illumos-gate/usr/src/test/zfs-tests/include/libtest.shlib (revision 30b280d1254c7864ed6ca5515d969e55e82bf2b9)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25# Copyright (c) 2012, 2017 by Delphix. All rights reserved.
26# Copyright (c) 2017 by Tim Chase. All rights reserved.
27# Copyright (c) 2017 by Nexenta Systems, Inc. All rights reserved.
28# Copyright (c) 2017 Datto Inc.
29# Copyright 2020 Joyent, Inc.
30# Copyright (c) 2025, Klara, Inc.
31# Copyright 2024 MNX Cloud, Inc.
32# Copyright 2026 Gordon W. Ross
33#
34
35UNAME=$(uname)
36
37. ${STF_TOOLS}/contrib/include/logapi.shlib
38. ${STF_SUITE}/include/math.shlib
39. ${STF_SUITE}/include/blkdev.shlib
40
41# Determine if this is a Linux test system
42#
43# Return 0 if platform Linux, 1 if otherwise
44
45function is_linux
46{
47	[ "$UNAME" = "Linux" ]
48}
49
50# Determine if this is an illumos test system
51#
52# Return 0 if platform illumos, 1 if otherwise
53function is_illumos
54{
55	[ "$UNAME" = "SunOS" ]
56}
57
58# Determine if this is a FreeBSD test system
59#
60# Return 0 if platform FreeBSD, 1 if otherwise
61
62function is_freebsd
63{
64	[ "$UNAME" = "FreeBSD" ]
65}
66
67# Determine whether a dataset is mounted
68#
69# $1 dataset name
70# $2 filesystem type; optional - defaulted to zfs
71#
72# Return 0 if dataset is mounted; 1 if unmounted; 2 on error
73
74function ismounted
75{
76	typeset fstype=$2
77	[[ -z $fstype ]] && fstype=zfs
78	typeset out dir name ret
79
80	case $fstype in
81		zfs)
82			if [[ "$1" == "/"* ]] ; then
83				for out in $(zfs mount | awk '{print $2}'); do
84					[[ $1 == $out ]] && return 0
85				done
86			else
87				for out in $(zfs mount | awk '{print $1}'); do
88					[[ $1 == $out ]] && return 0
89				done
90			fi
91		;;
92		ufs|nfs)
93			out=$(df -F $fstype $1 2>/dev/null)
94			ret=$?
95			(($ret != 0)) && return $ret
96
97			dir=${out%%\(*}
98			dir=${dir%% *}
99			name=${out##*\(}
100			name=${name%%\)*}
101			name=${name%% *}
102
103			[[ "$1" == "$dir" || "$1" == "$name" ]] && return 0
104		;;
105	esac
106
107	return 1
108}
109
110# Return 0 if a dataset is mounted; 1 otherwise
111#
112# $1 dataset name
113# $2 filesystem type; optional - defaulted to zfs
114
115function mounted
116{
117	ismounted $1 $2
118	(($? == 0)) && return 0
119	return 1
120}
121
122# Return 0 if a dataset is unmounted; 1 otherwise
123#
124# $1 dataset name
125# $2 filesystem type; optional - defaulted to zfs
126
127function unmounted
128{
129	ismounted $1 $2
130	(($? == 1)) && return 0
131	return 1
132}
133
134# split line on ","
135#
136# $1 - line to split
137
138function splitline
139{
140	echo $1 | sed "s/,/ /g"
141}
142
143function default_setup
144{
145	default_setup_noexit "$@"
146
147	log_pass
148}
149
150#
151# Given a list of disks, setup storage pools and datasets.
152#
153function default_setup_noexit
154{
155	typeset disklist=$1
156	typeset container=$2
157	typeset volume=$3
158
159	if is_global_zone; then
160		if poolexists $TESTPOOL ; then
161			destroy_pool $TESTPOOL
162		fi
163		[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
164		log_must zpool create -f $TESTPOOL $disklist
165	else
166		reexport_pool
167	fi
168
169	rm -rf $TESTDIR  || log_unresolved Could not remove $TESTDIR
170	mkdir -p $TESTDIR || log_unresolved Could not create $TESTDIR
171
172	log_must zfs create $TESTPOOL/$TESTFS
173	log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
174
175	if [[ -n $container ]]; then
176		rm -rf $TESTDIR1  || \
177			log_unresolved Could not remove $TESTDIR1
178		mkdir -p $TESTDIR1 || \
179			log_unresolved Could not create $TESTDIR1
180
181		log_must zfs create $TESTPOOL/$TESTCTR
182		log_must zfs set canmount=off $TESTPOOL/$TESTCTR
183		log_must zfs create $TESTPOOL/$TESTCTR/$TESTFS1
184		log_must zfs set mountpoint=$TESTDIR1 \
185		    $TESTPOOL/$TESTCTR/$TESTFS1
186	fi
187
188	if [[ -n $volume ]]; then
189		if is_global_zone ; then
190			log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL
191		else
192			log_must zfs create $TESTPOOL/$TESTVOL
193		fi
194	fi
195}
196
197#
198# Given a list of disks, setup a storage pool, file system and
199# a container.
200#
201function default_container_setup
202{
203	typeset disklist=$1
204
205	default_setup "$disklist" "true"
206}
207
208#
209# Given a list of disks, setup a storage pool,file system
210# and a volume.
211#
212function default_volume_setup
213{
214	typeset disklist=$1
215
216	default_setup "$disklist" "" "true"
217}
218
219#
220# Given a list of disks, setup a storage pool,file system,
221# a container and a volume.
222#
223function default_container_volume_setup
224{
225	typeset disklist=$1
226
227	default_setup "$disklist" "true" "true"
228}
229
230#
231# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on
232# filesystem
233#
234# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS
235# $2 snapshot name. Default, $TESTSNAP
236#
237function create_snapshot
238{
239	typeset fs_vol=${1:-$TESTPOOL/$TESTFS}
240	typeset snap=${2:-$TESTSNAP}
241
242	[[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
243	[[ -z $snap ]] && log_fail "Snapshot's name is undefined."
244
245	if snapexists $fs_vol@$snap; then
246		log_fail "$fs_vol@$snap already exists."
247	fi
248	datasetexists $fs_vol || \
249		log_fail "$fs_vol must exist."
250
251	log_must zfs snapshot $fs_vol@$snap
252}
253
254#
255# Create a clone from a snapshot, default clone name is $TESTCLONE.
256#
257# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default.
258# $2 Clone name, $TESTPOOL/$TESTCLONE is default.
259#
260function create_clone   # snapshot clone
261{
262	typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
263	typeset clone=${2:-$TESTPOOL/$TESTCLONE}
264
265	[[ -z $snap ]] && \
266		log_fail "Snapshot name is undefined."
267	[[ -z $clone ]] && \
268		log_fail "Clone name is undefined."
269
270	log_must zfs clone $snap $clone
271}
272
273#
274# Create a bookmark of the given snapshot.  Defaultly create a bookmark on
275# filesystem.
276#
277# $1 Existing filesystem or volume name. Default, $TESTFS
278# $2 Existing snapshot name. Default, $TESTSNAP
279# $3 bookmark name. Default, $TESTBKMARK
280#
281function create_bookmark
282{
283	typeset fs_vol=${1:-$TESTFS}
284	typeset snap=${2:-$TESTSNAP}
285	typeset bkmark=${3:-$TESTBKMARK}
286
287	[[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
288	[[ -z $snap ]] && log_fail "Snapshot's name is undefined."
289	[[ -z $bkmark ]] && log_fail "Bookmark's name is undefined."
290
291	if bkmarkexists $fs_vol#$bkmark; then
292		log_fail "$fs_vol#$bkmark already exists."
293	fi
294	datasetexists $fs_vol || \
295		log_fail "$fs_vol must exist."
296	snapexists $fs_vol@$snap || \
297		log_fail "$fs_vol@$snap must exist."
298
299	log_must zfs bookmark $fs_vol@$snap $fs_vol#$bkmark
300}
301
302#
303# Create a temporary clone result of an interrupted resumable 'zfs receive'
304# $1 Destination filesystem name. Must not exist, will be created as the result
305#    of this function along with its %recv temporary clone
306# $2 Source filesystem name. Must not exist, will be created and destroyed
307#
308function create_recv_clone
309{
310	typeset recvfs="$1"
311	typeset sendfs="${2:-$TESTPOOL/create_recv_clone}"
312	typeset snap="$sendfs@snap1"
313	typeset incr="$sendfs@snap2"
314	typeset mountpoint="$TESTDIR/create_recv_clone"
315	typeset sendfile="$TESTDIR/create_recv_clone.zsnap"
316
317	[[ -z $recvfs ]] && log_fail "Recv filesystem's name is undefined."
318
319	datasetexists $recvfs && log_fail "Recv filesystem must not exist."
320	datasetexists $sendfs && log_fail "Send filesystem must not exist."
321
322	log_must zfs create -o mountpoint="$mountpoint" $sendfs
323	log_must zfs snapshot $snap
324	log_must eval "zfs send $snap | zfs recv -u $recvfs"
325	log_must mkfile 1m "$mountpoint/data"
326	log_must zfs snapshot $incr
327	log_must eval "zfs send -i $snap $incr | dd bs=10k count=1 > $sendfile"
328	log_mustnot eval "zfs recv -su $recvfs < $sendfile"
329	destroy_dataset "$sendfs" "-r"
330	log_must rm -f "$sendfile"
331
332	if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then
333		log_fail "Error creating temporary $recvfs/%recv clone"
334	fi
335}
336
337function default_mirror_setup
338{
339	default_mirror_setup_noexit $1 $2 $3
340
341	log_pass
342}
343
344function default_mirror_2way_setup
345{
346	default_mirror_setup_noexit $1 $2
347
348	log_pass
349}
350
351#
352# Given a pair of disks, set up a storage pool and dataset for the mirror
353# @parameters: $1 the primary side of the mirror
354#   $2 the secondary side of the mirror
355# @uses: ZPOOL ZFS TESTPOOL TESTFS
356function default_mirror_setup_noexit
357{
358	readonly func="default_mirror_setup_noexit"
359	typeset primary=$1
360	typeset secondary=$2
361
362	[[ -z $primary ]] && \
363		log_fail "$func: No parameters passed"
364	[[ -z $secondary ]] && \
365		log_fail "$func: No secondary partition passed"
366	[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
367	log_must zpool create -f $TESTPOOL mirror $@
368	log_must zfs create $TESTPOOL/$TESTFS
369	log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
370}
371
372#
373# create a number of mirrors.
374# We create a number($1) of 2 way mirrors using the pairs of disks named
375# on the command line. These mirrors are *not* mounted
376# @parameters: $1 the number of mirrors to create
377#  $... the devices to use to create the mirrors on
378# @uses: ZPOOL ZFS TESTPOOL
379function setup_mirrors
380{
381	typeset -i nmirrors=$1
382
383	shift
384	while ((nmirrors > 0)); do
385		log_must test -n "$1" -a -n "$2"
386		[[ -d /$TESTPOOL$nmirrors ]] && rm -rf /$TESTPOOL$nmirrors
387		log_must zpool create -f $TESTPOOL$nmirrors mirror $1 $2
388		shift 2
389		((nmirrors = nmirrors - 1))
390	done
391}
392
393#
394# create a number of raidz pools.
395# We create a number($1) of 2 raidz pools  using the pairs of disks named
396# on the command line. These pools are *not* mounted
397# @parameters: $1 the number of pools to create
398#  $... the devices to use to create the pools on
399# @uses: ZPOOL ZFS TESTPOOL
400function setup_raidzs
401{
402	typeset -i nraidzs=$1
403
404	shift
405	while ((nraidzs > 0)); do
406		log_must test -n "$1" -a -n "$2"
407		[[ -d /$TESTPOOL$nraidzs ]] && rm -rf /$TESTPOOL$nraidzs
408		log_must zpool create -f $TESTPOOL$nraidzs raidz $1 $2
409		shift 2
410		((nraidzs = nraidzs - 1))
411	done
412}
413
414#
415# Destroy the configured testpool mirrors.
416# the mirrors are of the form ${TESTPOOL}{number}
417# @uses: ZPOOL ZFS TESTPOOL
418function destroy_mirrors
419{
420	default_cleanup_noexit
421
422	log_pass
423}
424
425#
426# Given a minimum of two disks, set up a storage pool and dataset for the raid-z
427# $1 the list of disks
428#
429function default_raidz_setup
430{
431	typeset disklist="$*"
432	disks=(${disklist[*]})
433
434	if [[ ${#disks[*]} -lt 2 ]]; then
435		log_fail "A raid-z requires a minimum of two disks."
436	fi
437
438	[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
439	log_must zpool create -f $TESTPOOL raidz $disklist
440	log_must zfs create $TESTPOOL/$TESTFS
441	log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
442
443	log_pass
444}
445
446#
447# Common function used to cleanup storage pools and datasets.
448#
449# Invoked at the start of the test suite to ensure the system
450# is in a known state, and also at the end of each set of
451# sub-tests to ensure errors from one set of tests doesn't
452# impact the execution of the next set.
453
454function default_cleanup
455{
456	default_cleanup_noexit
457
458	log_pass
459}
460
461function default_cleanup_noexit
462{
463	typeset exclude=""
464	typeset pool=""
465	#
466	# Destroying the pool will also destroy any
467	# filesystems it contains.
468	#
469	if is_global_zone; then
470		zfs unmount -a > /dev/null 2>&1
471		exclude=`eval echo \"'(${KEEP})'\"`
472		ALL_POOLS=$(zpool list -H -o name \
473		    | grep -v "$NO_POOLS" | egrep -v "$exclude")
474		# Here, we loop through the pools we're allowed to
475		# destroy, only destroying them if it's safe to do
476		# so.
477		while [ ! -z ${ALL_POOLS} ]
478		do
479			for pool in ${ALL_POOLS}
480			do
481				if safe_to_destroy_pool $pool ;
482				then
483					destroy_pool $pool
484				fi
485				ALL_POOLS=$(zpool list -H -o name \
486				    | grep -v "$NO_POOLS" \
487				    | egrep -v "$exclude")
488			done
489		done
490
491		zfs mount -a
492	else
493		typeset fs=""
494		for fs in $(zfs list -H -o name \
495		    | grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do
496			destroy_dataset "$fs" "-Rf"
497		done
498
499		# Need cleanup here to avoid garbage dir left.
500		for fs in $(zfs list -H -o name); do
501			[[ $fs == /$ZONE_POOL ]] && continue
502			[[ -d $fs ]] && log_must rm -rf $fs/*
503		done
504
505		#
506		# Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to
507		# the default value
508		#
509		for fs in $(zfs list -H -o name); do
510			if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then
511				log_must zfs set reservation=none $fs
512				log_must zfs set recordsize=128K $fs
513				log_must zfs set mountpoint=/$fs $fs
514				typeset enc=""
515				enc=$(get_prop encryption $fs)
516				if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \
517					[[ "$enc" == "off" ]]; then
518					log_must zfs set checksum=on $fs
519				fi
520				log_must zfs set compression=off $fs
521				log_must zfs set atime=on $fs
522				log_must zfs set devices=off $fs
523				log_must zfs set exec=on $fs
524				log_must zfs set setuid=on $fs
525				log_must zfs set readonly=off $fs
526				log_must zfs set snapdir=hidden $fs
527				log_must zfs set aclmode=groupmask $fs
528				log_must zfs set aclinherit=secure $fs
529			fi
530		done
531	fi
532
533	[[ -d $TESTDIR ]] && \
534		log_must rm -rf $TESTDIR
535}
536
537
538#
539# Common function used to cleanup storage pools, file systems
540# and containers.
541#
542function default_container_cleanup
543{
544	if ! is_global_zone; then
545		reexport_pool
546	fi
547
548	ismounted $TESTPOOL/$TESTCTR/$TESTFS1
549	[[ $? -eq 0 ]] && \
550	    log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1
551
552	destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R"
553	destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf"
554
555	[[ -e $TESTDIR1 ]] && \
556	    log_must rm -rf $TESTDIR1 > /dev/null 2>&1
557
558	default_cleanup
559}
560
561#
562# Common function used to cleanup snapshot of file system or volume. Default to
563# delete the file system's snapshot
564#
565# $1 snapshot name
566#
567function destroy_snapshot
568{
569	typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
570
571	if ! snapexists $snap; then
572		log_fail "'$snap' does not existed."
573	fi
574
575	#
576	# For the sake of the value which come from 'get_prop' is not equal
577	# to the really mountpoint when the snapshot is unmounted. So, firstly
578	# check and make sure this snapshot's been mounted in current system.
579	#
580	typeset mtpt=""
581	if ismounted $snap; then
582		mtpt=$(get_prop mountpoint $snap)
583		(($? != 0)) && \
584			log_fail "get_prop mountpoint $snap failed."
585	fi
586
587	destroy_dataset $snap
588	[[ $mtpt != "" && -d $mtpt ]] && \
589		log_must rm -rf $mtpt
590}
591
592#
593# Common function used to cleanup clone.
594#
595# $1 clone name
596#
597function destroy_clone
598{
599	typeset clone=${1:-$TESTPOOL/$TESTCLONE}
600
601	if ! datasetexists $clone; then
602		log_fail "'$clone' does not existed."
603	fi
604
605	# With the same reason in destroy_snapshot
606	typeset mtpt=""
607	if ismounted $clone; then
608		mtpt=$(get_prop mountpoint $clone)
609		(($? != 0)) && \
610			log_fail "get_prop mountpoint $clone failed."
611	fi
612
613	destroy_dataset $clone
614	[[ $mtpt != "" && -d $mtpt ]] && \
615		log_must rm -rf $mtpt
616}
617
618#
619# Common function used to cleanup bookmark of file system or volume.  Default
620# to delete the file system's bookmark.
621#
622# $1 bookmark name
623#
624function destroy_bookmark
625{
626	typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK}
627
628	if ! bkmarkexists $bkmark; then
629		log_fail "'$bkmarkp' does not existed."
630	fi
631
632	destroy_dataset $bkmark
633}
634
635# Return 0 if a snapshot exists; $? otherwise
636#
637# $1 - snapshot name
638
639function snapexists
640{
641	zfs list -H -t snapshot "$1" > /dev/null 2>&1
642	return $?
643}
644
645#
646# Return 0 if a bookmark exists; $? otherwise
647#
648# $1 - bookmark name
649#
650function bkmarkexists
651{
652	zfs list -H -t bookmark "$1" > /dev/null 2>&1
653	return $?
654}
655
656#
657# Set a property to a certain value on a dataset.
658# Sets a property of the dataset to the value as passed in.
659# @param:
660#	$1 dataset who's property is being set
661#	$2 property to set
662#	$3 value to set property to
663# @return:
664#	0 if the property could be set.
665#	non-zero otherwise.
666# @use: ZFS
667#
668function dataset_setprop
669{
670	typeset fn=dataset_setprop
671
672	if (($# < 3)); then
673		log_note "$fn: Insufficient parameters (need 3, had $#)"
674		return 1
675	fi
676	typeset output=
677	output=$(zfs set $2=$3 $1 2>&1)
678	typeset rv=$?
679	if ((rv != 0)); then
680		log_note "Setting property on $1 failed."
681		log_note "property $2=$3"
682		log_note "Return Code: $rv"
683		log_note "Output: $output"
684		return $rv
685	fi
686	return 0
687}
688
689#
690# Assign suite defined dataset properties.
691# This function is used to apply the suite's defined default set of
692# properties to a dataset.
693# @parameters: $1 dataset to use
694# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP
695# @returns:
696#   0 if the dataset has been altered.
697#   1 if no pool name was passed in.
698#   2 if the dataset could not be found.
699#   3 if the dataset could not have it's properties set.
700#
701function dataset_set_defaultproperties
702{
703	typeset dataset="$1"
704
705	[[ -z $dataset ]] && return 1
706
707	typeset confset=
708	typeset -i found=0
709	for confset in $(zfs list); do
710		if [[ $dataset = $confset ]]; then
711			found=1
712			break
713		fi
714	done
715	[[ $found -eq 0 ]] && return 2
716	if [[ -n $COMPRESSION_PROP ]]; then
717		dataset_setprop $dataset compression $COMPRESSION_PROP || \
718			return 3
719		log_note "Compression set to '$COMPRESSION_PROP' on $dataset"
720	fi
721	if [[ -n $CHECKSUM_PROP ]]; then
722		dataset_setprop $dataset checksum $CHECKSUM_PROP || \
723			return 3
724		log_note "Checksum set to '$CHECKSUM_PROP' on $dataset"
725	fi
726	return 0
727}
728
729#
730# Check a numeric assertion
731# @parameter: $@ the assertion to check
732# @output: big loud notice if assertion failed
733# @use: log_fail
734#
735function assert
736{
737	(($@)) || log_fail "$@"
738}
739
740#
741# Function to format partition size of a disk
742# Given a disk cxtxdx reduces all partitions
743# to 0 size
744#
745function zero_partitions #<whole_disk_name>
746{
747	typeset diskname=$1
748	typeset i
749
750	for i in 0 1 3 4 5 6 7
751	do
752		set_partition $i "" 0mb $diskname
753	done
754}
755
756#
757# Given a slice, size and disk, this function
758# formats the slice to the specified size.
759# Size should be specified with units as per
760# the `format` command requirements eg. 100mb 3gb
761#
762function set_partition #<slice_num> <slice_start> <size_plus_units>  <whole_disk_name>
763{
764	typeset -i slicenum=$1
765	typeset start=$2
766	typeset size=$3
767	typeset disk=$4
768	[[ -z $slicenum || -z $size || -z $disk ]] && \
769	    log_fail "The slice, size or disk name is unspecified."
770	typeset format_file=/var/tmp/format_in.$$
771
772	echo "partition" >$format_file
773	echo "$slicenum" >> $format_file
774	echo "" >> $format_file
775	echo "" >> $format_file
776	echo "$start" >> $format_file
777	echo "$size" >> $format_file
778	echo "label" >> $format_file
779	echo "" >> $format_file
780	echo "q" >> $format_file
781	echo "q" >> $format_file
782
783	format -e -s -d $disk -f $format_file
784	typeset ret_val=$?
785	rm -f $format_file
786	[[ $ret_val -ne 0 ]] && \
787	    log_fail "Unable to format $disk slice $slicenum to $size"
788	return 0
789}
790
791#
792# Get the end cyl of the given slice
793#
794function get_endslice #<disk> <slice>
795{
796	typeset disk=$1
797	typeset slice=$2
798	if [[ -z $disk || -z $slice ]] ; then
799		log_fail "The disk name or slice number is unspecified."
800	fi
801
802	disk=${disk#/dev/dsk/}
803	disk=${disk#/dev/rdsk/}
804	disk=${disk%s*}
805
806	typeset -i ratio=0
807	ratio=$(prtvtoc /dev/rdsk/${disk}s2 | \
808		grep "sectors\/cylinder" | \
809		awk '{print $2}')
810
811	if ((ratio == 0)); then
812		return
813	fi
814
815	typeset -i endcyl=$(prtvtoc -h /dev/rdsk/${disk}s2 |
816		nawk -v token="$slice" '{if ($1==token) print $6}')
817
818	((endcyl = (endcyl + 1) / ratio))
819	echo $endcyl
820}
821
822
823#
824# Given a size,disk and total slice number,  this function formats the
825# disk slices from 0 to the total slice number with the same specified
826# size.
827#
828function partition_disk	#<slice_size> <whole_disk_name>	<total_slices>
829{
830	typeset -i i=0
831	typeset slice_size=$1
832	typeset disk_name=$2
833	typeset total_slices=$3
834	typeset cyl
835
836	zero_partitions $disk_name
837	while ((i < $total_slices)); do
838		if ((i == 2)); then
839			((i = i + 1))
840			continue
841		fi
842		set_partition $i "$cyl" $slice_size $disk_name
843		cyl=$(get_endslice $disk_name $i)
844		((i = i+1))
845	done
846}
847
848#
849# This function continues to write to a filenum number of files into dirnum
850# number of directories until either file_write returns an error or the
851# maximum number of files per directory have been written.
852#
853# Usage:
854# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data]
855#
856# Return value: 0 on success
857#		non 0 on error
858#
859# Where :
860#	destdir:    is the directory where everything is to be created under
861#	dirnum:	    the maximum number of subdirectories to use, -1 no limit
862#	filenum:    the maximum number of files per subdirectory
863#	bytes:	    number of bytes to write
864#	num_writes: numer of types to write out bytes
865#	data:	    the data that will be writen
866#
867#	E.g.
868#	file_fs /testdir 20 25 1024 256 0
869#
870# Note: bytes * num_writes equals the size of the testfile
871#
872function fill_fs # destdir dirnum filenum bytes num_writes data
873{
874	typeset destdir=${1:-$TESTDIR}
875	typeset -i dirnum=${2:-50}
876	typeset -i filenum=${3:-50}
877	typeset -i bytes=${4:-8192}
878	typeset -i num_writes=${5:-10240}
879	typeset data=${6:-0}
880
881	typeset -i odirnum=1
882	typeset -i idirnum=0
883	typeset -i fn=0
884	typeset -i retval=0
885
886	mkdir -p $destdir/$idirnum
887	while (($odirnum > 0)); do
888		if ((dirnum >= 0 && idirnum >= dirnum)); then
889			odirnum=0
890			break
891		fi
892		file_write -o create -f $destdir/$idirnum/$TESTFILE.$fn \
893		    -b $bytes -c $num_writes -d $data
894		retval=$?
895		if (($retval != 0)); then
896			odirnum=0
897			break
898		fi
899		if (($fn >= $filenum)); then
900			fn=0
901			((idirnum = idirnum + 1))
902			mkdir -p $destdir/$idirnum
903		else
904			((fn = fn + 1))
905		fi
906	done
907	return $retval
908}
909
910#
911# Simple function to get the specified property. If unable to
912# get the property then exits.
913#
914# Note property is in 'parsable' format (-p)
915#
916function get_prop # property dataset
917{
918	typeset prop_val
919	typeset prop=$1
920	typeset dataset=$2
921
922	prop_val=$(zfs get -pH -o value $prop $dataset 2>/dev/null)
923	if [[ $? -ne 0 ]]; then
924		log_note "Unable to get $prop property for dataset " \
925		"$dataset"
926		return 1
927	fi
928
929	echo "$prop_val"
930	return 0
931}
932
933#
934# Simple function to get the specified property of pool. If unable to
935# get the property then exits.
936#
937function get_pool_prop # property pool
938{
939	typeset prop_val
940	typeset prop=$1
941	typeset pool=$2
942
943	if poolexists $pool ; then
944		prop_val=$(zpool get $prop $pool 2>/dev/null | tail -1 | \
945			awk '{print $3}')
946		if [[ $? -ne 0 ]]; then
947			log_note "Unable to get $prop property for pool " \
948			"$pool"
949			return 1
950		fi
951	else
952		log_note "Pool $pool not exists."
953		return 1
954	fi
955
956	echo $prop_val
957	return 0
958}
959
960# Return 0 if a pool exists; $? otherwise
961#
962# $1 - pool name
963
964function poolexists
965{
966	typeset pool=$1
967
968	if [[ -z $pool ]]; then
969		log_note "No pool name given."
970		return 1
971	fi
972
973	zpool get name "$pool" > /dev/null 2>&1
974	return $?
975}
976
977# Return 0 if all the specified datasets exist; $? otherwise
978#
979# $1-n  dataset name
980function datasetexists
981{
982	if (($# == 0)); then
983		log_note "No dataset name given."
984		return 1
985	fi
986
987	while (($# > 0)); do
988		zfs get name $1 > /dev/null 2>&1 || \
989			return $?
990		shift
991	done
992
993	return 0
994}
995
996# return 0 if none of the specified datasets exists, otherwise return 1.
997#
998# $1-n  dataset name
999function datasetnonexists
1000{
1001	if (($# == 0)); then
1002		log_note "No dataset name given."
1003		return 1
1004	fi
1005
1006	while (($# > 0)); do
1007		zfs list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 \
1008		    && return 1
1009		shift
1010	done
1011
1012	return 0
1013}
1014
1015#
1016# Given a mountpoint, or a dataset name, determine if it is shared.
1017#
1018# Returns 0 if shared, 1 otherwise.
1019#
1020function is_shared
1021{
1022	typeset fs=$1
1023	typeset mtpt
1024
1025	if [[ $fs != "/"* ]] ; then
1026		if datasetnonexists "$fs" ; then
1027			return 1
1028		else
1029			mtpt=$(get_prop mountpoint "$fs")
1030			case $mtpt in
1031				none|legacy|-) return 1
1032					;;
1033				*)	fs=$mtpt
1034					;;
1035			esac
1036		fi
1037	fi
1038
1039	for mtpt in `share | awk '{print $2}'` ; do
1040		if [[ $mtpt == $fs ]] ; then
1041			return 0
1042		fi
1043	done
1044
1045	typeset stat=$(svcs -H -o STA nfs/server:default)
1046	if [[ $stat != "ON" ]]; then
1047		log_note "Current nfs/server status: $stat"
1048	fi
1049
1050	return 1
1051}
1052
1053#
1054# Given a mountpoint, determine if it is not shared.
1055#
1056# Returns 0 if not shared, 1 otherwise.
1057#
1058function not_shared
1059{
1060	typeset fs=$1
1061
1062	is_shared $fs
1063	if (($? == 0)); then
1064		return 1
1065	fi
1066
1067	return 0
1068}
1069
1070#
1071# Helper function to unshare a mountpoint.
1072#
1073function unshare_fs #fs
1074{
1075	typeset fs=$1
1076
1077	is_shared $fs
1078	if (($? == 0)); then
1079		log_must zfs unshare $fs
1080	fi
1081
1082	return 0
1083}
1084
1085#
1086# Check NFS server status and trigger it online.
1087#
1088function setup_nfs_server
1089{
1090	# Cannot share directory in non-global zone.
1091	#
1092	if ! is_global_zone; then
1093		log_note "Cannot trigger NFS server by sharing in LZ."
1094		return
1095	fi
1096
1097	typeset nfs_fmri="svc:/network/nfs/server:default"
1098	if [[ $(svcs -Ho STA $nfs_fmri) != "ON" ]]; then
1099		#
1100		# Only really sharing operation can enable NFS server
1101		# to online permanently.
1102		#
1103		typeset dummy=/tmp/dummy
1104
1105		if [[ -d $dummy ]]; then
1106			log_must rm -rf $dummy
1107		fi
1108
1109		log_must mkdir $dummy
1110		log_must share $dummy
1111
1112		#
1113		# Waiting for fmri's status to be the final status.
1114		# Otherwise, in transition, an asterisk (*) is appended for
1115		# instances, unshare will reverse status to 'DIS' again.
1116		#
1117		# Waiting for 1's at least.
1118		#
1119		log_must sleep 1
1120		timeout=10
1121		while [[ timeout -ne 0 && $(svcs -Ho STA $nfs_fmri) == *'*' ]]
1122		do
1123			log_must sleep 1
1124
1125			((timeout -= 1))
1126		done
1127
1128		log_must unshare $dummy
1129		log_must rm -rf $dummy
1130	fi
1131
1132	log_note "Current NFS status: '$(svcs -Ho STA,FMRI $nfs_fmri)'"
1133}
1134
1135#
1136# To verify whether calling process is in global zone
1137#
1138# Return 0 if in global zone, 1 in non-global zone
1139#
1140function is_global_zone
1141{
1142	typeset cur_zone=$(zonename 2>/dev/null)
1143	if [[ $cur_zone != "global" ]]; then
1144		return 1
1145	fi
1146	return 0
1147}
1148
1149#
1150# Verify whether test is permitted to run from
1151# global zone, local zone, or both
1152#
1153# $1 zone limit, could be "global", "local", or "both"(no limit)
1154#
1155# Return 0 if permitted, otherwise exit with log_unsupported
1156#
1157function verify_runnable # zone limit
1158{
1159	typeset limit=$1
1160
1161	[[ -z $limit ]] && return 0
1162
1163	if is_global_zone ; then
1164		case $limit in
1165			global|both)
1166				;;
1167			local)	log_unsupported "Test is unable to run from "\
1168					"global zone."
1169				;;
1170			*)	log_note "Warning: unknown limit $limit - " \
1171					"use both."
1172				;;
1173		esac
1174	else
1175		case $limit in
1176			local|both)
1177				;;
1178			global)	log_unsupported "Test is unable to run from "\
1179					"local zone."
1180				;;
1181			*)	log_note "Warning: unknown limit $limit - " \
1182					"use both."
1183				;;
1184		esac
1185
1186		reexport_pool
1187	fi
1188
1189	return 0
1190}
1191
1192# Return 0 if create successfully or the pool exists; $? otherwise
1193# Note: In local zones, this function should return 0 silently.
1194#
1195# $1 - pool name
1196# $2-n - [keyword] devs_list
1197
1198function create_pool #pool devs_list
1199{
1200	typeset pool=${1%%/*}
1201
1202	shift
1203
1204	if [[ -z $pool ]]; then
1205		log_note "Missing pool name."
1206		return 1
1207	fi
1208
1209	if poolexists $pool ; then
1210		destroy_pool $pool
1211	fi
1212
1213	if is_global_zone ; then
1214		[[ -d /$pool ]] && rm -rf /$pool
1215		log_must zpool create -f $pool $@
1216	fi
1217
1218	return 0
1219}
1220
1221# Return 0 if destroy successfully or the pool exists; $? otherwise
1222# Note: In local zones, this function should return 0 silently.
1223#
1224# $1 - pool name
1225# Destroy pool with the given parameters.
1226
1227function destroy_pool #pool
1228{
1229	typeset pool=${1%%/*}
1230	typeset mtpt
1231
1232	if [[ -z $pool ]]; then
1233		log_note "No pool name given."
1234		return 1
1235	fi
1236
1237	if is_global_zone ; then
1238		if poolexists "$pool" ; then
1239			mtpt=$(get_prop mountpoint "$pool")
1240
1241			# Clear any zinject fault handlers before attempting
1242			# destroy. Active handlers can cause I/O to hang
1243			# indefinitely, blocking zpool destroy.
1244			zinject -c all
1245
1246			# If pool I/O is suspended (e.g. due to injected
1247			# errors), clear the error state so destroy can proceed.
1248			typeset health=$(zpool list -H -o health $pool \
1249			    2>/dev/null)
1250			if [[ "$health" == "SUSPENDED" ]]; then
1251				log_note "Pool $pool is suspended; clearing" \
1252				    "before destroy."
1253				zpool clear $pool
1254			fi
1255
1256			# At times, syseventd activity can cause attempts to
1257			# destroy a pool to fail with EBUSY. We retry a few
1258			# times allowing failures before requiring the destroy
1259			# to succeed.
1260			typeset -i wait_time=10 ret=1 count=0
1261			must=""
1262			while [[ $ret -ne 0 ]]; do
1263				$must zpool destroy -f $pool
1264				ret=$?
1265				[[ $ret -eq 0 ]] && break
1266				log_note "zpool destroy failed with $ret"
1267				[[ count++ -ge 7 ]] && must=log_must
1268				sleep $wait_time
1269			done
1270
1271			[[ -d $mtpt ]] && \
1272				log_must rm -rf $mtpt
1273
1274			if poolexists "$pool"; then
1275				log_note "destroy_pool: pool '$pool' still" \
1276				    "exists after destroy"
1277				exit $STF_ABORTED
1278			fi
1279		else
1280			log_note "Pool does not exist. ($pool)"
1281			return 1
1282		fi
1283	fi
1284
1285	return 0
1286}
1287
1288# Return 0 if created successfully; $? otherwise
1289#
1290# $1 - dataset name
1291# $2-n - dataset options
1292
1293function create_dataset #dataset dataset_options
1294{
1295	typeset dataset=$1
1296
1297	shift
1298
1299	if [[ -z $dataset ]]; then
1300		log_note "Missing dataset name."
1301		return 1
1302	fi
1303
1304	if datasetexists $dataset ; then
1305		destroy_dataset $dataset
1306	fi
1307
1308	log_must zfs create $@ $dataset
1309
1310	return 0
1311}
1312
1313# Return 0 if destroy successfully or the dataset exists; $? otherwise
1314# Note: In local zones, this function should return 0 silently.
1315#
1316# $1 - dataset name
1317# $2 - custom arguments for zfs destroy
1318# Destroy dataset with the given parameters.
1319
1320function destroy_dataset #dataset #args
1321{
1322	typeset dataset=$1
1323	typeset mtpt
1324	typeset args=${2:-""}
1325
1326	if [[ -z $dataset ]]; then
1327		log_note "No dataset name given."
1328		return 1
1329	fi
1330
1331	if is_global_zone ; then
1332		if datasetexists "$dataset" ; then
1333			mtpt=$(get_prop mountpoint "$dataset")
1334			log_must zfs destroy $args $dataset
1335
1336			[[ -d $mtpt ]] && \
1337				log_must rm -rf $mtpt
1338		else
1339			log_note "Dataset does not exist. ($dataset)"
1340			return 1
1341		fi
1342	fi
1343
1344	return 0
1345}
1346
1347#
1348# Firstly, create a pool with 5 datasets. Then, create a single zone and
1349# export the 5 datasets to it. In addition, we also add a ZFS filesystem
1350# and a zvol device to the zone.
1351#
1352# $1 zone name
1353# $2 zone root directory prefix
1354# $3 zone ip
1355#
1356function zfs_zones_setup #zone_name zone_root zone_ip
1357{
1358	typeset zone_name=${1:-$(hostname)-z}
1359	typeset zone_root=${2:-"/zone_root"}
1360	typeset zone_ip=${3:-"10.1.1.10"}
1361	typeset prefix_ctr=$ZONE_CTR
1362	typeset pool_name=$ZONE_POOL
1363	typeset -i cntctr=5
1364	typeset -i i=0
1365
1366	# Create pool and 5 container within it
1367	#
1368	[[ -d /$pool_name ]] && rm -rf /$pool_name
1369	log_must zpool create -f $pool_name $DISKS
1370	while ((i < cntctr)); do
1371		log_must zfs create $pool_name/$prefix_ctr$i
1372		((i += 1))
1373	done
1374
1375	# create a zvol
1376	log_must zfs create -V 1g $pool_name/zone_zvol
1377
1378	#
1379	# If current system support slog, add slog device for pool
1380	#
1381	if verify_slog_support ; then
1382		typeset sdevs="/var/tmp/sdev1 /var/tmp/sdev2"
1383		log_must mkfile $MINVDEVSIZE $sdevs
1384		log_must zpool add $pool_name log mirror $sdevs
1385	fi
1386
1387	# this isn't supported just yet.
1388	# Create a filesystem. In order to add this to
1389	# the zone, it must have it's mountpoint set to 'legacy'
1390	# log_must zfs create $pool_name/zfs_filesystem
1391	# log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem
1392
1393	[[ -d $zone_root ]] && \
1394		log_must rm -rf $zone_root/$zone_name
1395	[[ ! -d $zone_root ]] && \
1396		log_must mkdir -p -m 0700 $zone_root/$zone_name
1397
1398	# Create zone configure file and configure the zone
1399	#
1400	typeset zone_conf=/tmp/zone_conf.$$
1401	echo "create" > $zone_conf
1402	echo "set zonepath=$zone_root/$zone_name" >> $zone_conf
1403	echo "set autoboot=true" >> $zone_conf
1404	i=0
1405	while ((i < cntctr)); do
1406		echo "add dataset" >> $zone_conf
1407		echo "set name=$pool_name/$prefix_ctr$i" >> \
1408			$zone_conf
1409		echo "end" >> $zone_conf
1410		((i += 1))
1411	done
1412
1413	# add our zvol to the zone
1414	echo "add device" >> $zone_conf
1415	echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf
1416	echo "end" >> $zone_conf
1417
1418	# add a corresponding zvol rdsk to the zone
1419	echo "add device" >> $zone_conf
1420	echo "set match=/dev/zvol/rdsk/$pool_name/zone_zvol" >> $zone_conf
1421	echo "end" >> $zone_conf
1422
1423	# once it's supported, we'll add our filesystem to the zone
1424	# echo "add fs" >> $zone_conf
1425	# echo "set type=zfs" >> $zone_conf
1426	# echo "set special=$pool_name/zfs_filesystem" >> $zone_conf
1427	# echo "set dir=/export/zfs_filesystem" >> $zone_conf
1428	# echo "end" >> $zone_conf
1429
1430	echo "verify" >> $zone_conf
1431	echo "commit" >> $zone_conf
1432	log_must zonecfg -z $zone_name -f $zone_conf
1433	log_must rm -f $zone_conf
1434
1435	# Install the zone
1436	zoneadm -z $zone_name install
1437	if (($? == 0)); then
1438		log_note "SUCCESS: zoneadm -z $zone_name install"
1439	else
1440		log_fail "FAIL: zoneadm -z $zone_name install"
1441	fi
1442
1443	# Install sysidcfg file
1444	#
1445	typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg
1446	echo "system_locale=C" > $sysidcfg
1447	echo  "terminal=dtterm" >> $sysidcfg
1448	echo  "network_interface=primary {" >> $sysidcfg
1449	echo  "hostname=$zone_name" >> $sysidcfg
1450	echo  "}" >> $sysidcfg
1451	echo  "name_service=NONE" >> $sysidcfg
1452	echo  "root_password=mo791xfZ/SFiw" >> $sysidcfg
1453	echo  "security_policy=NONE" >> $sysidcfg
1454	echo  "timezone=US/Eastern" >> $sysidcfg
1455
1456	# Boot this zone
1457	log_must zoneadm -z $zone_name boot
1458}
1459
1460#
1461# Reexport TESTPOOL & TESTPOOL(1-4)
1462#
1463function reexport_pool
1464{
1465	typeset -i cntctr=5
1466	typeset -i i=0
1467
1468	while ((i < cntctr)); do
1469		if ((i == 0)); then
1470			TESTPOOL=$ZONE_POOL/$ZONE_CTR$i
1471			if ! ismounted $TESTPOOL; then
1472				log_must zfs mount $TESTPOOL
1473			fi
1474		else
1475			eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i
1476			if eval ! ismounted \$TESTPOOL$i; then
1477				log_must eval zfs mount \$TESTPOOL$i
1478			fi
1479		fi
1480		((i += 1))
1481	done
1482}
1483
1484#
1485# Verify a given disk is online or offline
1486#
1487# Return 0 is pool/disk matches expected state, 1 otherwise
1488#
1489function check_state # pool disk state{online,offline}
1490{
1491	typeset pool=$1
1492	typeset disk=${2#/dev/dsk/}
1493	typeset state=$3
1494
1495	zpool status -v $pool | grep "$disk"  \
1496	    | grep -i "$state" > /dev/null 2>&1
1497
1498	return $?
1499}
1500
1501#
1502# Get the mountpoint of snapshot
1503# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap>
1504# as its mountpoint
1505#
1506function snapshot_mountpoint
1507{
1508	typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
1509
1510	if [[ $dataset != *@* ]]; then
1511		log_fail "Error name of snapshot '$dataset'."
1512	fi
1513
1514	typeset fs=${dataset%@*}
1515	typeset snap=${dataset#*@}
1516
1517	if [[ -z $fs || -z $snap ]]; then
1518		log_fail "Error name of snapshot '$dataset'."
1519	fi
1520
1521	echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap
1522}
1523
1524#
1525# Given a device and 'ashift' value verify it's correctly set on every label
1526#
1527function verify_ashift # device ashift
1528{
1529	typeset device="$1"
1530	typeset ashift="$2"
1531
1532	zdb -e -lll $device | nawk -v ashift=$ashift '/ashift: / {
1533	    if (ashift != $2)
1534	        exit 1;
1535	    else
1536	        count++;
1537	    } END {
1538	    if (count != 4)
1539	        exit 1;
1540	    else
1541	        exit 0;
1542	    }'
1543
1544	return $?
1545}
1546
1547#
1548# Given a pool and file system, this function will verify the file system
1549# using the zdb internal tool. Note that the pool is exported and imported
1550# to ensure it has consistent state.
1551#
1552function verify_filesys # pool filesystem dir
1553{
1554	typeset pool="$1"
1555	typeset filesys="$2"
1556	typeset zdbout="/tmp/zdbout.$$"
1557
1558	shift
1559	shift
1560	typeset dirs=$@
1561	typeset search_path=""
1562
1563	log_note "Calling zdb to verify filesystem '$filesys'"
1564	zfs unmount -a > /dev/null 2>&1
1565	log_must zpool export $pool
1566
1567	if [[ -n $dirs ]] ; then
1568		for dir in $dirs ; do
1569			search_path="$search_path -d $dir"
1570		done
1571	fi
1572
1573	log_must zpool import $search_path $pool
1574
1575	zdb -cudi $filesys > $zdbout 2>&1
1576	if [[ $? != 0 ]]; then
1577		log_note "Output: zdb -cudi $filesys"
1578		cat $zdbout
1579		log_fail "zdb detected errors with: '$filesys'"
1580	fi
1581
1582	log_must zfs mount -a
1583	log_must rm -rf $zdbout
1584}
1585
1586#
1587# Given a pool issue a scrub and verify that no checksum errors are reported.
1588#
1589function verify_pool
1590{
1591	typeset pool=${1:-$TESTPOOL}
1592
1593	log_must zpool scrub $pool
1594	log_must wait_scrubbed $pool
1595
1596	cksum=$(zpool status $pool | \
1597	    awk '{if ($5 == "CKSUM"){L=1; next} if (L) {print $NF;L=0}}')
1598	if [[ $cksum != 0 ]]; then
1599		log_must zpool status -v
1600	        log_fail "Unexpected CKSUM errors found on $pool ($cksum)"
1601	fi
1602}
1603
1604#
1605# Given a pool, and this function list all disks in the pool
1606#
1607function get_disklist # pool
1608{
1609	typeset disklist=""
1610
1611	disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \
1612	    grep -v "\-\-\-\-\-" | \
1613	    egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$")
1614
1615	echo $disklist
1616}
1617
1618# /**
1619#  This function kills a given list of processes after a time period. We use
1620#  this in the stress tests instead of STF_TIMEOUT so that we can have processes
1621#  run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT
1622#  would be listed as FAIL, which we don't want : we're happy with stress tests
1623#  running for a certain amount of time, then finishing.
1624#
1625# @param $1 the time in seconds after which we should terminate these processes
1626# @param $2..$n the processes we wish to terminate.
1627# */
1628function stress_timeout
1629{
1630	typeset -i TIMEOUT=$1
1631	shift
1632	typeset cpids="$@"
1633
1634	log_note "Waiting for child processes($cpids). " \
1635		"It could last dozens of minutes, please be patient ..."
1636	log_must sleep $TIMEOUT
1637
1638	log_note "Killing child processes after ${TIMEOUT} stress timeout."
1639	typeset pid
1640	for pid in $cpids; do
1641		ps -p $pid > /dev/null 2>&1
1642		if (($? == 0)); then
1643			log_must kill -USR1 $pid
1644		fi
1645	done
1646}
1647
1648#
1649# Verify a given hotspare disk is inuse or avail
1650#
1651# Return 0 is pool/disk matches expected state, 1 otherwise
1652#
1653function check_hotspare_state # pool disk state{inuse,avail}
1654{
1655	typeset pool=$1
1656	typeset disk=${2#/dev/dsk/}
1657	typeset state=$3
1658
1659	cur_state=$(get_device_state $pool $disk "spares")
1660
1661	if [[ $state != ${cur_state} ]]; then
1662		return 1
1663	fi
1664	return 0
1665}
1666
1667#
1668# Wait until a hotspare transitions to a given state or times out.
1669#
1670# Return 0 when  pool/disk matches expected state, 1 on timeout.
1671#
1672function wait_hotspare_state # pool disk state timeout
1673{
1674	typeset pool=$1
1675	typeset disk=${2#$/DEV_DSKDIR/}
1676	typeset state=$3
1677	typeset timeout=${4:-60}
1678	typeset -i i=0
1679
1680	while [[ $i -lt $timeout ]]; do
1681		if check_hotspare_state $pool $disk $state; then
1682			return 0
1683		fi
1684
1685		i=$((i+1))
1686		sleep 1
1687	done
1688
1689	return 1
1690}
1691
1692#
1693# Verify a given slog disk is inuse or avail
1694#
1695# Return 0 is pool/disk matches expected state, 1 otherwise
1696#
1697function check_slog_state # pool disk state{online,offline,unavail}
1698{
1699	typeset pool=$1
1700	typeset disk=${2#/dev/dsk/}
1701	typeset state=$3
1702
1703	cur_state=$(get_device_state $pool $disk "logs")
1704
1705	if [[ $state != ${cur_state} ]]; then
1706		return 1
1707	fi
1708	return 0
1709}
1710
1711#
1712# Verify a given vdev disk is inuse or avail
1713#
1714# Return 0 is pool/disk matches expected state, 1 otherwise
1715#
1716function check_vdev_state # pool disk state{online,offline,unavail}
1717{
1718	typeset pool=$1
1719	typeset disk=${2#/dev/dsk/}
1720	typeset state=$3
1721
1722	cur_state=$(get_device_state $pool $disk)
1723
1724	if [[ $state != ${cur_state} ]]; then
1725		return 1
1726	fi
1727	return 0
1728}
1729
1730#
1731# Wait until a vdev transitions to a given state or times out.
1732#
1733# Return 0 when  pool/disk matches expected state, 1 on timeout.
1734#
1735function wait_vdev_state # pool disk state timeout
1736{
1737	typeset pool=$1
1738	typeset disk=${2#$/DEV_DSKDIR/}
1739	typeset state=$3
1740	typeset timeout=${4:-60}
1741	typeset -i i=0
1742
1743	while [[ $i -lt $timeout ]]; do
1744		if check_vdev_state $pool $disk $state; then
1745			return 0
1746		fi
1747
1748		i=$((i+1))
1749		sleep 1
1750	done
1751
1752	return 1
1753}
1754
1755#
1756# Check the output of 'zpool status -v <pool>',
1757# and to see if the content of <token> contain the <keyword> specified.
1758#
1759# Return 0 is contain, 1 otherwise
1760#
1761function check_pool_status # pool token keyword <verbose>
1762{
1763	typeset pool=$1
1764	typeset token=$2
1765	typeset keyword=$3
1766	typeset verbose=${4:-false}
1767
1768	scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" '
1769		($1==token) {print $0}')
1770	if [[ $verbose == true ]]; then
1771		log_note $scan
1772	fi
1773	echo $scan | grep -i "$keyword" > /dev/null 2>&1
1774
1775	return $?
1776}
1777
1778#
1779# The following functions are instance of check_pool_status()
1780#	is_pool_resilvering - to check if the pool is resilver in progress
1781#	is_pool_resilvered - to check if the pool is resilver completed
1782#	is_pool_scrubbing - to check if the pool is scrub in progress
1783#	is_pool_scrubbed - to check if the pool is scrub completed
1784#	is_pool_scrub_stopped - to check if the pool is scrub stopped
1785#	is_pool_scrub_paused - to check if the pool has scrub paused
1786#	is_pool_removing - to check if the pool is removing a vdev
1787#	is_pool_removed - to check if the pool is remove completed
1788#	is_pool_discarding - to check if the pool has checkpoint being discarded
1789#
1790function is_pool_resilvering #pool <verbose>
1791{
1792	check_pool_status "$1" "scan" "resilver in progress since " $2
1793	return $?
1794}
1795
1796function is_pool_resilvered #pool <verbose>
1797{
1798	check_pool_status "$1" "scan" "resilvered " $2
1799	return $?
1800}
1801
1802function is_pool_scrubbing #pool <verbose>
1803{
1804	check_pool_status "$1" "scan" "scrub in progress since " $2
1805	return $?
1806}
1807
1808function is_pool_scrubbed #pool <verbose>
1809{
1810	check_pool_status "$1" "scan" "scrub repaired" $2
1811	return $?
1812}
1813
1814function is_pool_scrub_stopped #pool <verbose>
1815{
1816	check_pool_status "$1" "scan" "scrub canceled" $2
1817	return $?
1818}
1819
1820function is_pool_scrub_paused #pool <verbose>
1821{
1822	check_pool_status "$1" "scan" "scrub paused since " $2
1823	return $?
1824}
1825
1826function is_pool_removing #pool
1827{
1828	check_pool_status "$1" "remove" "in progress since "
1829	return $?
1830}
1831
1832function is_pool_removed #pool
1833{
1834	check_pool_status "$1" "remove" "completed on"
1835	return $?
1836}
1837
1838function is_pool_discarding #pool
1839{
1840	check_pool_status "$1" "checkpoint" "discarding"
1841	return $?
1842}
1843
1844function wait_for_degraded
1845{
1846	typeset pool=$1
1847	typeset timeout=${2:-30}
1848	typeset t0=$SECONDS
1849
1850	while :; do
1851		[[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break
1852		log_note "$pool is not yet degraded."
1853		sleep 1
1854		if ((SECONDS - t0 > $timeout)); then
1855			log_note "$pool not degraded after $timeout seconds."
1856			return 1
1857		fi
1858	done
1859
1860	return 0
1861}
1862
1863#
1864# Wait for a pool to be scrubbed
1865#
1866# $1 pool name
1867# $2 number of seconds to wait (optional)
1868#
1869# Returns true when pool has been scrubbed, or false if there's a timeout or if
1870# no scrub was done.
1871#
1872function wait_scrubbed
1873{
1874	typeset pool=${1:-$TESTPOOL}
1875	while true ; do
1876		is_pool_scrubbed $pool && break
1877		log_must sleep 1
1878	done
1879}
1880
1881#
1882# Use create_pool()/destroy_pool() to clean up the infomation in
1883# in the given disk to avoid slice overlapping.
1884#
1885function cleanup_devices #vdevs
1886{
1887	typeset pool="foopool$$"
1888
1889	if poolexists $pool ; then
1890		destroy_pool $pool
1891	fi
1892
1893	create_pool $pool $@
1894	destroy_pool $pool
1895
1896	return 0
1897}
1898
1899#/**
1900# A function to find and locate free disks on a system or from given
1901# disks as the parameter. It works by locating disks that are in use
1902# as swap devices and dump devices, and also disks listed in /etc/vfstab
1903#
1904# $@ given disks to find which are free, default is all disks in
1905# the test system
1906#
1907# @return a string containing the list of available disks
1908#*/
1909function find_disks
1910{
1911	sfi=/tmp/swaplist.$$
1912	dmpi=/tmp/dumpdev.$$
1913	max_finddisksnum=${MAX_FINDDISKSNUM:-6}
1914
1915	swap -l > $sfi
1916	dumpadm > $dmpi 2>/dev/null
1917
1918# write an awk script that can process the output of format
1919# to produce a list of disks we know about. Note that we have
1920# to escape "$2" so that the shell doesn't interpret it while
1921# we're creating the awk script.
1922# -------------------
1923	cat > /tmp/find_disks.awk <<EOF
1924#!/bin/nawk -f
1925	BEGIN { FS="."; }
1926
1927	/^Specify disk/{
1928		searchdisks=0;
1929	}
1930
1931	{
1932		if (searchdisks && \$2 !~ "^$"){
1933			split(\$2,arr," ");
1934			print arr[1];
1935		}
1936	}
1937
1938	/^AVAILABLE DISK SELECTIONS:/{
1939		searchdisks=1;
1940	}
1941EOF
1942#---------------------
1943
1944	chmod 755 /tmp/find_disks.awk
1945	disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)}
1946	rm /tmp/find_disks.awk
1947
1948	unused=""
1949	for disk in $disks; do
1950	# Check for mounted
1951		grep "${disk}[sp]" /etc/mnttab >/dev/null
1952		(($? == 0)) && continue
1953	# Check for swap
1954		grep "${disk}[sp]" $sfi >/dev/null
1955		(($? == 0)) && continue
1956	# check for dump device
1957		grep "${disk}[sp]" $dmpi >/dev/null
1958		(($? == 0)) && continue
1959	# check to see if this disk hasn't been explicitly excluded
1960	# by a user-set environment variable
1961		echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null
1962		(($? == 0)) && continue
1963		unused_candidates="$unused_candidates $disk"
1964	done
1965	rm $sfi
1966	rm $dmpi
1967
1968# now just check to see if those disks do actually exist
1969# by looking for a device pointing to the first slice in
1970# each case. limit the number to max_finddisksnum
1971	count=0
1972	for disk in $unused_candidates; do
1973		if [ -b /dev/dsk/${disk}s0 ]; then
1974		if [ $count -lt $max_finddisksnum ]; then
1975			unused="$unused $disk"
1976			# do not impose limit if $@ is provided
1977			[[ -z $@ ]] && ((count = count + 1))
1978		fi
1979		fi
1980	done
1981
1982# finally, return our disk list
1983	echo $unused
1984}
1985
1986#
1987# Add specified user to specified group
1988#
1989# $1 group name
1990# $2 user name
1991# $3 base of the homedir (optional)
1992#
1993function add_user #<group_name> <user_name> <basedir>
1994{
1995	typeset gname=$1
1996	typeset uname=$2
1997	typeset basedir=${3:-"/var/tmp"}
1998
1999	if ((${#gname} == 0 || ${#uname} == 0)); then
2000		log_fail "group name or user name are not defined."
2001	fi
2002
2003	log_must useradd -g $gname -d $basedir/$uname -m $uname
2004	log_must passwd -N $uname
2005
2006	return 0
2007}
2008
2009#
2010# Delete the specified user.
2011#
2012# $1 login name
2013# $2 base of the homedir (optional)
2014#
2015function del_user #<logname> <basedir>
2016{
2017	typeset user=$1
2018	typeset basedir=${2:-"/var/tmp"}
2019
2020	if ((${#user} == 0)); then
2021		log_fail "login name is necessary."
2022	fi
2023
2024	if id $user > /dev/null 2>&1; then
2025		log_must userdel $user
2026	fi
2027
2028	[[ -d $basedir/$user ]] && rm -fr $basedir/$user
2029
2030	return 0
2031}
2032
2033#
2034# Select valid gid and create specified group.
2035#
2036# $1 group name
2037#
2038function add_group #<group_name>
2039{
2040	typeset group=$1
2041
2042	if ((${#group} == 0)); then
2043		log_fail "group name is necessary."
2044	fi
2045
2046	# Assign 100 as the base gid
2047	typeset -i gid=100
2048	while true; do
2049		groupadd -g $gid $group > /dev/null 2>&1
2050		typeset -i ret=$?
2051		case $ret in
2052			0) return 0 ;;
2053			# The gid is not  unique
2054			4) ((gid += 1)) ;;
2055			*) return 1 ;;
2056		esac
2057	done
2058}
2059
2060#
2061# Delete the specified group.
2062#
2063# $1 group name
2064#
2065function del_group #<group_name>
2066{
2067	typeset grp=$1
2068	if ((${#grp} == 0)); then
2069		log_fail "group name is necessary."
2070	fi
2071
2072	groupmod -n $grp $grp > /dev/null 2>&1
2073	typeset -i ret=$?
2074	case $ret in
2075		# Group does not exist.
2076		6) return 0 ;;
2077		# Name already exists as a group name
2078		9) log_must groupdel $grp ;;
2079		*) return 1 ;;
2080	esac
2081
2082	return 0
2083}
2084
2085#
2086# This function will return true if it's safe to destroy the pool passed
2087# as argument 1. It checks for pools based on zvols and files, and also
2088# files contained in a pool that may have a different mountpoint.
2089#
2090function safe_to_destroy_pool { # $1 the pool name
2091
2092	typeset pool=""
2093	typeset DONT_DESTROY=""
2094
2095	# We check that by deleting the $1 pool, we're not
2096	# going to pull the rug out from other pools. Do this
2097	# by looking at all other pools, ensuring that they
2098	# aren't built from files or zvols contained in this pool.
2099
2100	for pool in $(zpool list -H -o name)
2101	do
2102		ALTMOUNTPOOL=""
2103
2104		# this is a list of the top-level directories in each of the
2105		# files that make up the path to the files the pool is based on
2106		FILEPOOL=$(zpool status -v $pool | grep /$1/ | \
2107			awk '{print $1}')
2108
2109		# this is a list of the zvols that make up the pool
2110		ZVOLPOOL=$(zpool status -v $pool | grep "/dev/zvol/dsk/$1$" \
2111		    | awk '{print $1}')
2112
2113		# also want to determine if it's a file-based pool using an
2114		# alternate mountpoint...
2115		POOL_FILE_DIRS=$(zpool status -v $pool | \
2116					grep / | awk '{print $1}' | \
2117					awk -F/ '{print $2}' | grep -v "dev")
2118
2119		for pooldir in $POOL_FILE_DIRS
2120		do
2121			OUTPUT=$(zfs list -H -r -o mountpoint $1 | \
2122					grep "${pooldir}$" | awk '{print $1}')
2123
2124			ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}"
2125		done
2126
2127
2128		if [ ! -z "$ZVOLPOOL" ]
2129		then
2130			DONT_DESTROY="true"
2131			log_note "Pool $pool is built from $ZVOLPOOL on $1"
2132		fi
2133
2134		if [ ! -z "$FILEPOOL" ]
2135		then
2136			DONT_DESTROY="true"
2137			log_note "Pool $pool is built from $FILEPOOL on $1"
2138		fi
2139
2140		if [ ! -z "$ALTMOUNTPOOL" ]
2141		then
2142			DONT_DESTROY="true"
2143			log_note "Pool $pool is built from $ALTMOUNTPOOL on $1"
2144		fi
2145	done
2146
2147	if [ -z "${DONT_DESTROY}" ]
2148	then
2149		return 0
2150	else
2151		log_note "Warning: it is not safe to destroy $1!"
2152		return 1
2153	fi
2154}
2155
2156#
2157# Get the available ZFS compression options
2158# $1 option type zfs_set|zfs_compress
2159#
2160function get_compress_opts
2161{
2162	typeset COMPRESS_OPTS
2163	typeset GZIP_OPTS="gzip gzip-1 gzip-2 gzip-3 gzip-4 gzip-5 \
2164			gzip-6 gzip-7 gzip-8 gzip-9"
2165
2166	if [[ $1 == "zfs_compress" ]] ; then
2167		COMPRESS_OPTS="on lzjb"
2168	elif [[ $1 == "zfs_set" ]] ; then
2169		COMPRESS_OPTS="on off lzjb"
2170	fi
2171	typeset valid_opts="$COMPRESS_OPTS"
2172	zfs get 2>&1 | grep gzip >/dev/null 2>&1
2173	if [[ $? -eq 0 ]]; then
2174		valid_opts="$valid_opts $GZIP_OPTS"
2175	fi
2176	echo "$valid_opts"
2177}
2178
2179#
2180# Verify zfs operation with -p option work as expected
2181# $1 operation, value could be create, clone or rename
2182# $2 dataset type, value could be fs or vol
2183# $3 dataset name
2184# $4 new dataset name
2185#
2186function verify_opt_p_ops
2187{
2188	typeset ops=$1
2189	typeset datatype=$2
2190	typeset dataset=$3
2191	typeset newdataset=$4
2192
2193	if [[ $datatype != "fs" && $datatype != "vol" ]]; then
2194		log_fail "$datatype is not supported."
2195	fi
2196
2197	# check parameters accordingly
2198	case $ops in
2199		create)
2200			newdataset=$dataset
2201			dataset=""
2202			if [[ $datatype == "vol" ]]; then
2203				ops="create -V $VOLSIZE"
2204			fi
2205			;;
2206		clone)
2207			if [[ -z $newdataset ]]; then
2208				log_fail "newdataset should not be empty" \
2209					"when ops is $ops."
2210			fi
2211			log_must datasetexists $dataset
2212			log_must snapexists $dataset
2213			;;
2214		rename)
2215			if [[ -z $newdataset ]]; then
2216				log_fail "newdataset should not be empty" \
2217					"when ops is $ops."
2218			fi
2219			log_must datasetexists $dataset
2220			log_mustnot snapexists $dataset
2221			;;
2222		*)
2223			log_fail "$ops is not supported."
2224			;;
2225	esac
2226
2227	# make sure the upper level filesystem does not exist
2228	destroy_dataset ${newdataset%/*} "-rRf"
2229
2230	# without -p option, operation will fail
2231	log_mustnot zfs $ops $dataset $newdataset
2232	log_mustnot datasetexists $newdataset ${newdataset%/*}
2233
2234	# with -p option, operation should succeed
2235	log_must zfs $ops -p $dataset $newdataset
2236	if ! datasetexists $newdataset ; then
2237		log_fail "-p option does not work for $ops"
2238	fi
2239
2240	# when $ops is create or clone, redo the operation still return zero
2241	if [[ $ops != "rename" ]]; then
2242		log_must zfs $ops -p $dataset $newdataset
2243	fi
2244
2245	return 0
2246}
2247
2248#
2249# Get configuration of pool
2250# $1 pool name
2251# $2 config name
2252#
2253function get_config
2254{
2255	typeset pool=$1
2256	typeset config=$2
2257	typeset alt_root
2258
2259	if ! poolexists "$pool" ; then
2260		return 1
2261	fi
2262	alt_root=$(zpool list -H $pool | awk '{print $NF}')
2263	if [[ $alt_root == "-" ]]; then
2264		value=$(zdb -C $pool | grep "$config:" | awk -F: \
2265		    '{print $2}')
2266	else
2267		value=$(zdb -e $pool | grep "$config:" | awk -F: \
2268		    '{print $2}')
2269	fi
2270	if [[ -n $value ]] ; then
2271		value=${value#'}
2272		value=${value%'}
2273	fi
2274	echo $value
2275
2276	return 0
2277}
2278
2279#
2280# Privated function. Random select one of items from arguments.
2281#
2282# $1 count
2283# $2-n string
2284#
2285function _random_get
2286{
2287	typeset cnt=$1
2288	shift
2289
2290	typeset str="$@"
2291	typeset -i ind
2292	((ind = RANDOM % cnt + 1))
2293
2294	typeset ret=$(echo "$str" | cut -f $ind -d ' ')
2295	echo $ret
2296}
2297
2298#
2299# Random select one of item from arguments which include NONE string
2300#
2301function random_get_with_non
2302{
2303	typeset -i cnt=$#
2304	((cnt =+ 1))
2305
2306	_random_get "$cnt" "$@"
2307}
2308
2309#
2310# Random select one of item from arguments which doesn't include NONE string
2311#
2312function random_get
2313{
2314	_random_get "$#" "$@"
2315}
2316
2317#
2318# Detect if the current system support slog
2319#
2320function verify_slog_support
2321{
2322	typeset dir=/tmp/disk.$$
2323	typeset pool=foo.$$
2324	typeset vdev=$dir/a
2325	typeset sdev=$dir/b
2326
2327	mkdir -p $dir
2328	mkfile $MINVDEVSIZE $vdev $sdev
2329
2330	typeset -i ret=0
2331	if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then
2332		ret=1
2333	fi
2334	rm -r $dir
2335
2336	return $ret
2337}
2338
2339#
2340# The function will generate a dataset name with specific length
2341# $1, the length of the name
2342# $2, the base string to construct the name
2343#
2344function gen_dataset_name
2345{
2346	typeset -i len=$1
2347	typeset basestr="$2"
2348	typeset -i baselen=${#basestr}
2349	typeset -i iter=0
2350	typeset l_name=""
2351
2352	if ((len % baselen == 0)); then
2353		((iter = len / baselen))
2354	else
2355		((iter = len / baselen + 1))
2356	fi
2357	while ((iter > 0)); do
2358		l_name="${l_name}$basestr"
2359
2360		((iter -= 1))
2361	done
2362
2363	echo $l_name
2364}
2365
2366#
2367# Get cksum tuple of dataset
2368# $1 dataset name
2369#
2370# sample zdb output:
2371# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp
2372# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4
2373# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P
2374# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744
2375function datasetcksum
2376{
2377	typeset cksum
2378	sync
2379	sync_all_pools
2380	cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \
2381		| awk -F= '{print $7}')
2382	echo $cksum
2383}
2384
2385#
2386# Get cksum of file
2387# #1 file path
2388#
2389function checksum
2390{
2391	typeset cksum
2392	cksum=$(cksum $1 | awk '{print $1}')
2393	echo $cksum
2394}
2395
2396#
2397# Get the given disk/slice state from the specific field of the pool
2398#
2399function get_device_state #pool disk field("", "spares","logs")
2400{
2401	typeset pool=$1
2402	typeset disk=${2#/dev/dsk/}
2403	typeset field=${3:-$pool}
2404
2405	state=$(zpool status -v "$pool" 2>/dev/null | \
2406		nawk -v device=$disk -v pool=$pool -v field=$field \
2407		'BEGIN {startconfig=0; startfield=0; }
2408		/config:/ {startconfig=1}
2409		(startconfig==1) && ($1==field) {startfield=1; next;}
2410		(startfield==1) && ($1==device) {print $2; exit;}
2411		(startfield==1) &&
2412		($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}')
2413	echo $state
2414}
2415
2416
2417#
2418# print the given directory filesystem type
2419#
2420# $1 directory name
2421#
2422function get_fstype
2423{
2424	typeset dir=$1
2425
2426	if [[ -z $dir ]]; then
2427		log_fail "Usage: get_fstype <directory>"
2428	fi
2429
2430	#
2431	#  $ df -n /
2432	#  /		  : ufs
2433	#
2434	df -n $dir | awk '{print $3}'
2435}
2436
2437#
2438# Given a disk, label it to VTOC regardless what label was on the disk
2439# $1 disk
2440#
2441function labelvtoc
2442{
2443	typeset disk=$1
2444	typeset -i iter=120
2445	typeset -i ret_val=1
2446
2447	if [[ -z $disk ]]; then
2448		log_fail "The disk name is unspecified."
2449	fi
2450	typeset label_file=/var/tmp/labelvtoc.$$
2451	typeset arch=$(uname -p)
2452
2453	if [[ $arch == "i386" ]]; then
2454		log_must fdisk -B ${disk}p0
2455
2456		echo "label" > $label_file
2457		echo "0" >> $label_file
2458		echo "" >> $label_file
2459		echo "q" >> $label_file
2460		echo "q" >> $label_file
2461	elif [[ $arch == "sparc" ]]; then
2462		echo "label" > $label_file
2463		echo "0" >> $label_file
2464		echo "" >> $label_file
2465		echo "" >> $label_file
2466		echo "" >> $label_file
2467		echo "q" >> $label_file
2468	else
2469		log_fail "unknown arch type"
2470	fi
2471
2472	# Disk update from fdisk -B may be delayed
2473	while ((iter > 0)); do
2474		if format -e -s -d $disk -f $label_file ; then
2475			iter=0
2476			ret_val=0
2477		else
2478			sleep 1
2479			((iter -= 1))
2480		fi
2481	done
2482	rm -f $label_file
2483	if ((ret_val != 0)); then
2484		log_fail "unable to label $disk as VTOC."
2485	fi
2486
2487	return 0
2488}
2489
2490#
2491# check if the system was installed as zfsroot or not
2492# return: 0 ture, otherwise false
2493#
2494function is_zfsroot
2495{
2496	df -n / | grep zfs > /dev/null 2>&1
2497	return $?
2498}
2499
2500#
2501# get the root filesystem name if it's zfsroot system.
2502#
2503# return: root filesystem name
2504function get_rootfs
2505{
2506	typeset rootfs=""
2507	rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \
2508		/etc/mnttab)
2509	if [[ -z "$rootfs" ]]; then
2510		log_fail "Can not get rootfs"
2511	fi
2512	zfs list $rootfs > /dev/null 2>&1
2513	if (($? == 0)); then
2514		echo $rootfs
2515	else
2516		log_fail "This is not a zfsroot system."
2517	fi
2518}
2519
2520#
2521# get the rootfs's pool name
2522# return:
2523#       rootpool name
2524#
2525function get_rootpool
2526{
2527	typeset rootfs=""
2528	typeset rootpool=""
2529	rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \
2530		 /etc/mnttab)
2531	if [[ -z "$rootfs" ]]; then
2532		log_fail "Can not get rootpool"
2533	fi
2534	zfs list $rootfs > /dev/null 2>&1
2535	if (($? == 0)); then
2536		rootpool=`echo $rootfs | awk -F\/ '{print $1}'`
2537		echo $rootpool
2538	else
2539		log_fail "This is not a zfsroot system."
2540	fi
2541}
2542
2543#
2544# Check if the given device is physical device
2545#
2546function is_physical_device #device
2547{
2548	typeset device=${1#/dev/dsk/}
2549	device=${device#/dev/rdsk/}
2550
2551	echo $device | egrep "^c[0-F]+([td][0-F]+)+$" > /dev/null 2>&1
2552	return $?
2553}
2554
2555#
2556# Get the directory path of given device
2557#
2558function get_device_dir #device
2559{
2560	typeset device=$1
2561
2562	if ! $(is_physical_device $device) ; then
2563		if [[ $device != "/" ]]; then
2564			device=${device%/*}
2565		fi
2566		echo $device
2567	else
2568		echo "/dev/dsk"
2569	fi
2570}
2571
2572#
2573# Get the package name
2574#
2575function get_package_name
2576{
2577	typeset dirpath=${1:-$STC_NAME}
2578
2579	echo "SUNWstc-${dirpath}" | /usr/bin/sed -e "s/\//-/g"
2580}
2581
2582#
2583# Get the word numbers from a string separated by white space
2584#
2585function get_word_count
2586{
2587	echo $1 | wc -w
2588}
2589
2590#
2591# To verify if the require numbers of disks is given
2592#
2593function verify_disk_count
2594{
2595	typeset -i min=${2:-1}
2596
2597	typeset -i count=$(get_word_count "$1")
2598
2599	if ((count < min)); then
2600		log_untested "A minimum of $min disks is required to run." \
2601			" You specified $count disk(s)"
2602	fi
2603}
2604
2605function ds_is_volume
2606{
2607	typeset type=$(get_prop type $1)
2608	[[ $type = "volume" ]] && return 0
2609	return 1
2610}
2611
2612function ds_is_filesystem
2613{
2614	typeset type=$(get_prop type $1)
2615	[[ $type = "filesystem" ]] && return 0
2616	return 1
2617}
2618
2619function ds_is_snapshot
2620{
2621	typeset type=$(get_prop type $1)
2622	[[ $type = "snapshot" ]] && return 0
2623	return 1
2624}
2625
2626#
2627# Check if Trusted Extensions are installed and enabled
2628#
2629function is_te_enabled
2630{
2631	svcs -H -o state labeld 2>/dev/null | grep "enabled"
2632	if (($? != 0)); then
2633		return 1
2634	else
2635		return 0
2636	fi
2637}
2638
2639# Utility function to determine if a system has multiple cpus.
2640function is_mp
2641{
2642	(($(psrinfo | wc -l) > 1))
2643}
2644
2645function get_cpu_freq
2646{
2647	psrinfo -v 0 | awk '/processor operates at/ {print $6}'
2648}
2649
2650# Run the given command as the user provided.
2651function user_run
2652{
2653	typeset user=$1
2654	shift
2655
2656	eval su \$user -c \"$@\" > /tmp/out 2>/tmp/err
2657	return $?
2658}
2659
2660#
2661# Check if the pool contains the specified vdevs
2662#
2663# $1 pool
2664# $2..n <vdev> ...
2665#
2666# Return 0 if the vdevs are contained in the pool, 1 if any of the specified
2667# vdevs is not in the pool, and 2 if pool name is missing.
2668#
2669function vdevs_in_pool
2670{
2671	typeset pool=$1
2672	typeset vdev
2673
2674        if [[ -z $pool ]]; then
2675                log_note "Missing pool name."
2676                return 2
2677        fi
2678
2679	shift
2680
2681	typeset tmpfile=$(mktemp)
2682	zpool list -Hv "$pool" >$tmpfile
2683	for vdev in $@; do
2684		grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1
2685		[[ $? -ne 0 ]] && return 1
2686	done
2687
2688	rm -f $tmpfile
2689
2690	return 0;
2691}
2692
2693function get_max
2694{
2695	typeset -l i max=$1
2696	shift
2697
2698	for i in "$@"; do
2699		max=$(echo $((max > i ? max : i)))
2700	done
2701
2702	echo $max
2703}
2704
2705function get_min
2706{
2707	typeset -l i min=$1
2708	shift
2709
2710	for i in "$@"; do
2711		min=$(echo $((min < i ? min : i)))
2712	done
2713
2714	echo $min
2715}
2716
2717#
2718# Generate a random number between 1 and the argument.
2719#
2720function random
2721{
2722        typeset max=$1
2723        echo $(( ($RANDOM % $max) + 1 ))
2724}
2725
2726# Write data that can be compressed into a directory
2727function write_compressible
2728{
2729	typeset dir=$1
2730	typeset megs=$2
2731	typeset nfiles=${3:-1}
2732	typeset bs=${4:-1024k}
2733	typeset fname=${5:-file}
2734
2735	[[ -d $dir ]] || log_fail "No directory: $dir"
2736
2737	log_must eval "fio \
2738	    --name=job \
2739	    --fallocate=0 \
2740	    --minimal \
2741	    --randrepeat=0 \
2742	    --buffer_compress_percentage=66 \
2743	    --buffer_compress_chunk=4096 \
2744	    --directory=$dir \
2745	    --numjobs=$nfiles \
2746	    --rw=write \
2747	    --bs=$bs \
2748	    --filesize=$megs \
2749	    --filename_format='$fname.\$jobnum' >/dev/null"
2750}
2751
2752function get_objnum
2753{
2754	typeset pathname=$1
2755	typeset objnum
2756
2757	[[ -e $pathname ]] || log_fail "No such file or directory: $pathname"
2758	objnum=$(stat -c %i $pathname)
2759	echo $objnum
2760}
2761
2762#
2763# Sync data to the pool
2764#
2765# $1 pool name
2766# $2 boolean to force uberblock (and config including zpool cache file) update
2767#
2768function sync_pool #pool <force>
2769{
2770	typeset pool=${1:-$TESTPOOL}
2771	typeset force=${2:-false}
2772
2773	if [[ $force == true ]]; then
2774		log_must zpool sync -f $pool
2775	else
2776		log_must zpool sync $pool
2777	fi
2778
2779	return 0
2780}
2781
2782#
2783# Sync all pools
2784#
2785# $1 boolean to force uberblock (and config including zpool cache file) update
2786#
2787function sync_all_pools #<force>
2788{
2789	typeset force=${1:-false}
2790
2791	if [[ $force == true ]]; then
2792		log_must zpool sync -f
2793	else
2794		log_must zpool sync
2795	fi
2796
2797	return 0
2798}
2799
2800#
2801# Wait for zpool 'freeing' property drops to zero.
2802#
2803# $1 pool name
2804#
2805function wait_freeing #pool
2806{
2807	typeset pool=${1:-$TESTPOOL}
2808	while true; do
2809		[[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break
2810		log_must sleep 1
2811	done
2812}
2813
2814#
2815# Prints the current time in seconds since UNIX Epoch.
2816#
2817function current_epoch
2818{
2819	printf '%(%s)T'
2820}
2821
2822#
2823# Wait for every device replace operation to complete
2824#
2825# $1 pool name
2826#
2827function wait_replacing #pool
2828{
2829	typeset pool=${1:-$TESTPOOL}
2830	while true; do
2831		[[ "" == "$(zpool status $pool |
2832		    awk '/replacing-[0-9]+/ {print $1}')" ]] && break
2833		log_must sleep 1
2834	done
2835}
2836
2837#
2838# Set a global system tunable (64-bit value)
2839#
2840# $1 tunable name
2841# $2 tunable values
2842#
2843function set_tunable64
2844{
2845	set_tunable_impl "$1" "$2" Z
2846}
2847
2848#
2849# Set a global system tunable (32-bit value)
2850#
2851# $1 tunable name
2852# $2 tunable values
2853#
2854function set_tunable32
2855{
2856	set_tunable_impl "$1" "$2" W
2857}
2858
2859function set_tunable_impl
2860{
2861	typeset tunable="$1"
2862	typeset value="$2"
2863	typeset mdb_cmd="$3"
2864	typeset module="${4:-zfs}"
2865
2866	[[ -z "$tunable" ]] && return 1
2867	[[ -z "$value" ]] && return 1
2868	[[ -z "$mdb_cmd" ]] && return 1
2869
2870	case "$(uname)" in
2871	Linux)
2872		typeset zfs_tunables="/sys/module/$module/parameters"
2873		[[ -w "$zfs_tunables/$tunable" ]] || return 1
2874		cat >"$zfs_tunables/$tunable" <<<"$value"
2875		return $?
2876		;;
2877	SunOS)
2878		[[ "$module" -eq "zfs" ]] || return 1
2879		echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw
2880		return $?
2881		;;
2882	esac
2883}
2884
2885#
2886# Get a global system tunable
2887#
2888# $1 tunable name
2889#
2890function get_tunable
2891{
2892	get_tunable_impl "$1"
2893}
2894
2895function get_tunable_impl
2896{
2897	typeset tunable="$1"
2898	typeset module="${2:-zfs}"
2899
2900	[[ -z "$tunable" ]] && return 1
2901
2902	case "$(uname)" in
2903	Linux)
2904		typeset zfs_tunables="/sys/module/$module/parameters"
2905		[[ -f "$zfs_tunables/$tunable" ]] || return 1
2906		cat $zfs_tunables/$tunable
2907		return $?
2908		;;
2909	SunOS)
2910		typeset value=$(mdb -k -e "$tunable::print | ::eval .=E")
2911		if [[ $? -ne 0 ]]; then
2912			log_fail "Failed to get value of '$tunable' from mdb."
2913			return 1
2914		fi
2915		echo $value
2916		return 0
2917		;;
2918	esac
2919
2920	return 1
2921}
2922
2923function new_fs #<args>
2924{
2925	case "$(uname)" in
2926	FreeBSD)
2927		newfs "$@"
2928		;;
2929	SunOS)
2930		echo y | newfs "$@"
2931		;;
2932	*)
2933		echo y | newfs -v "$@"
2934		;;
2935	esac
2936}
2937
2938#
2939# Wait for the specified arcstat to reach non-zero quiescence.
2940# If echo is 1 echo the value after reaching quiescence, otherwise
2941# if echo is 0 print the arcstat we are waiting on.
2942#
2943function arcstat_quiescence # stat echo
2944{
2945	typeset stat=$1
2946	typeset echo=$2
2947	typeset do_once=true
2948
2949	if [[ $echo -eq 0 ]]; then
2950		echo "Waiting for arcstat $1 quiescence."
2951	fi
2952
2953	while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do
2954		typeset stat1=$(kstat arcstats.$stat)
2955		sleep 2
2956		typeset stat2=$(kstat arcstats.$stat)
2957		do_once=false
2958	done
2959
2960	if [[ $echo -eq 1 ]]; then
2961		echo $stat2
2962	fi
2963}
2964
2965function arcstat_quiescence_noecho # stat
2966{
2967	typeset stat=$1
2968	arcstat_quiescence $stat 0
2969}
2970
2971function arcstat_quiescence_echo # stat
2972{
2973	typeset stat=$1
2974	arcstat_quiescence $stat 1
2975}
2976
2977#
2978# Compute SHA256 digest for given file or stdin if no file given.
2979# Note: file path must not contain spaces
2980#
2981function sha256digest
2982{
2983        typeset file=$1
2984
2985	if [ -x /usr/bin/digest ]; then
2986		/usr/bin/digest -a sha256 $file
2987	elif [ -x /usr/bin/sha256sum ]; then
2988		/usr/bin/sha256sum -b $file | awk '{ print $1 }'
2989	else
2990		echo "Cannot calculate SHA256 digest"
2991		return 1
2992	fi
2993	return 0
2994}
2995
2996. ${STF_SUITE}/include/kstat.shlib
2997