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