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