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