1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright (c) 2009, Sun Microsystems Inc. All rights reserved.
24# Copyright (c) 2012, 2020, Delphix. All rights reserved.
25# Copyright (c) 2017, Tim Chase. All rights reserved.
26# Copyright (c) 2017, Nexenta Systems Inc. All rights reserved.
27# Copyright (c) 2017, Lawrence Livermore National Security LLC.
28# Copyright (c) 2017, Datto Inc. All rights reserved.
29# Copyright (c) 2017, Open-E Inc. All rights reserved.
30# Use is subject to license terms.
31#
32
33. ${STF_TOOLS}/include/logapi.shlib
34. ${STF_SUITE}/include/math.shlib
35. ${STF_SUITE}/include/blkdev.shlib
36
37. ${STF_SUITE}/include/tunables.cfg
38
39#
40# Apply constrained path when available.  This is required since the
41# PATH may have been modified by sudo's secure_path behavior.
42#
43if [ -n "$STF_PATH" ]; then
44	export PATH="$STF_PATH"
45fi
46
47#
48# Generic dot version comparison function
49#
50# Returns success when version $1 is greater than or equal to $2.
51#
52function compare_version_gte
53{
54	if [[ "$(printf "$1\n$2" | sort -V | tail -n1)" == "$1" ]]; then
55		return 0
56	else
57		return 1
58	fi
59}
60
61# Linux kernel version comparison function
62#
63# $1 Linux version ("4.10", "2.6.32") or blank for installed Linux version
64#
65# Used for comparison: if [ $(linux_version) -ge $(linux_version "2.6.32") ]
66#
67function linux_version
68{
69	typeset ver="$1"
70
71	[[ -z "$ver" ]] && ver=$(uname -r | grep -Eo "^[0-9]+\.[0-9]+\.[0-9]+")
72
73	typeset version=$(echo $ver | cut -d '.' -f 1)
74	typeset major=$(echo $ver | cut -d '.' -f 2)
75	typeset minor=$(echo $ver | cut -d '.' -f 3)
76
77	[[ -z "$version" ]] && version=0
78	[[ -z "$major" ]] && major=0
79	[[ -z "$minor" ]] && minor=0
80
81	echo $((version * 10000 + major * 100 + minor))
82}
83
84# Determine if this is a Linux test system
85#
86# Return 0 if platform Linux, 1 if otherwise
87
88function is_linux
89{
90	if [[ $(uname -o) == "GNU/Linux" ]]; then
91		return 0
92	else
93		return 1
94	fi
95}
96
97# Determine if this is an illumos test system
98#
99# Return 0 if platform illumos, 1 if otherwise
100function is_illumos
101{
102	if [[ $(uname -o) == "illumos" ]]; then
103		return 0
104	else
105		return 1
106	fi
107}
108
109# Determine if this is a FreeBSD test system
110#
111# Return 0 if platform FreeBSD, 1 if otherwise
112
113function is_freebsd
114{
115	if [[ $(uname -o) == "FreeBSD" ]]; then
116		return 0
117	else
118		return 1
119	fi
120}
121
122# Determine if this is a DilOS test system
123#
124# Return 0 if platform DilOS, 1 if otherwise
125
126function is_dilos
127{
128	typeset ID=""
129	[[ -f /etc/os-release ]] && . /etc/os-release
130	if [[ $ID == "dilos" ]]; then
131		return 0
132	else
133		return 1
134	fi
135}
136
137# Determine if this is a 32-bit system
138#
139# Return 0 if platform is 32-bit, 1 if otherwise
140
141function is_32bit
142{
143	if [[ $(getconf LONG_BIT) == "32" ]]; then
144		return 0
145	else
146		return 1
147	fi
148}
149
150# Determine if kmemleak is enabled
151#
152# Return 0 if kmemleak is enabled, 1 if otherwise
153
154function is_kmemleak
155{
156	if is_linux && [[ -e /sys/kernel/debug/kmemleak ]]; then
157		return 0
158	else
159		return 1
160	fi
161}
162
163# Determine whether a dataset is mounted
164#
165# $1 dataset name
166# $2 filesystem type; optional - defaulted to zfs
167#
168# Return 0 if dataset is mounted; 1 if unmounted; 2 on error
169
170function ismounted
171{
172	typeset fstype=$2
173	[[ -z $fstype ]] && fstype=zfs
174	typeset out dir name ret
175
176	case $fstype in
177		zfs)
178			if [[ "$1" == "/"* ]] ; then
179				for out in $(zfs mount | awk '{print $2}'); do
180					[[ $1 == $out ]] && return 0
181				done
182			else
183				for out in $(zfs mount | awk '{print $1}'); do
184					[[ $1 == $out ]] && return 0
185				done
186			fi
187		;;
188		ufs|nfs)
189			if is_freebsd; then
190				mount -pt $fstype | while read dev dir _t _flags; do
191					[[ "$1" == "$dev" || "$1" == "$dir" ]] && return 0
192				done
193			else
194				out=$(df -F $fstype $1 2>/dev/null)
195				ret=$?
196				(($ret != 0)) && return $ret
197
198				dir=${out%%\(*}
199				dir=${dir%% *}
200				name=${out##*\(}
201				name=${name%%\)*}
202				name=${name%% *}
203
204				[[ "$1" == "$dir" || "$1" == "$name" ]] && return 0
205			fi
206		;;
207		ext*)
208			out=$(df -t $fstype $1 2>/dev/null)
209			return $?
210		;;
211		zvol)
212			if [[ -L "$ZVOL_DEVDIR/$1" ]]; then
213				link=$(readlink -f $ZVOL_DEVDIR/$1)
214				[[ -n "$link" ]] && \
215					mount | grep -q "^$link" && \
216						return 0
217			fi
218		;;
219	esac
220
221	return 1
222}
223
224# Return 0 if a dataset is mounted; 1 otherwise
225#
226# $1 dataset name
227# $2 filesystem type; optional - defaulted to zfs
228
229function mounted
230{
231	ismounted $1 $2
232	(($? == 0)) && return 0
233	return 1
234}
235
236# Return 0 if a dataset is unmounted; 1 otherwise
237#
238# $1 dataset name
239# $2 filesystem type; optional - defaulted to zfs
240
241function unmounted
242{
243	ismounted $1 $2
244	(($? == 1)) && return 0
245	return 1
246}
247
248# split line on ","
249#
250# $1 - line to split
251
252function splitline
253{
254	echo $1 | sed "s/,/ /g"
255}
256
257function default_setup
258{
259	default_setup_noexit "$@"
260
261	log_pass
262}
263
264function default_setup_no_mountpoint
265{
266	default_setup_noexit "$1" "$2" "$3" "yes"
267
268	log_pass
269}
270
271#
272# Given a list of disks, setup storage pools and datasets.
273#
274function default_setup_noexit
275{
276	typeset disklist=$1
277	typeset container=$2
278	typeset volume=$3
279	typeset no_mountpoint=$4
280	log_note begin default_setup_noexit
281
282	if is_global_zone; then
283		if poolexists $TESTPOOL ; then
284			destroy_pool $TESTPOOL
285		fi
286		[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
287		log_must zpool create -f $TESTPOOL $disklist
288	else
289		reexport_pool
290	fi
291
292	rm -rf $TESTDIR  || log_unresolved Could not remove $TESTDIR
293	mkdir -p $TESTDIR || log_unresolved Could not create $TESTDIR
294
295	log_must zfs create $TESTPOOL/$TESTFS
296	if [[ -z $no_mountpoint ]]; then
297		log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
298	fi
299
300	if [[ -n $container ]]; then
301		rm -rf $TESTDIR1  || \
302			log_unresolved Could not remove $TESTDIR1
303		mkdir -p $TESTDIR1 || \
304			log_unresolved Could not create $TESTDIR1
305
306		log_must zfs create $TESTPOOL/$TESTCTR
307		log_must zfs set canmount=off $TESTPOOL/$TESTCTR
308		log_must zfs create $TESTPOOL/$TESTCTR/$TESTFS1
309		if [[ -z $no_mountpoint ]]; then
310			log_must zfs set mountpoint=$TESTDIR1 \
311			    $TESTPOOL/$TESTCTR/$TESTFS1
312		fi
313	fi
314
315	if [[ -n $volume ]]; then
316		if is_global_zone ; then
317			log_must zfs create -V $VOLSIZE $TESTPOOL/$TESTVOL
318			block_device_wait
319		else
320			log_must zfs create $TESTPOOL/$TESTVOL
321		fi
322	fi
323}
324
325#
326# Given a list of disks, setup a storage pool, file system and
327# a container.
328#
329function default_container_setup
330{
331	typeset disklist=$1
332
333	default_setup "$disklist" "true"
334}
335
336#
337# Given a list of disks, setup a storage pool,file system
338# and a volume.
339#
340function default_volume_setup
341{
342	typeset disklist=$1
343
344	default_setup "$disklist" "" "true"
345}
346
347#
348# Given a list of disks, setup a storage pool,file system,
349# a container and a volume.
350#
351function default_container_volume_setup
352{
353	typeset disklist=$1
354
355	default_setup "$disklist" "true" "true"
356}
357
358#
359# Create a snapshot on a filesystem or volume. Defaultly create a snapshot on
360# filesystem
361#
362# $1 Existing filesystem or volume name. Default, $TESTPOOL/$TESTFS
363# $2 snapshot name. Default, $TESTSNAP
364#
365function create_snapshot
366{
367	typeset fs_vol=${1:-$TESTPOOL/$TESTFS}
368	typeset snap=${2:-$TESTSNAP}
369
370	[[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
371	[[ -z $snap ]] && log_fail "Snapshot's name is undefined."
372
373	if snapexists $fs_vol@$snap; then
374		log_fail "$fs_vol@$snap already exists."
375	fi
376	datasetexists $fs_vol || \
377		log_fail "$fs_vol must exist."
378
379	log_must zfs snapshot $fs_vol@$snap
380}
381
382#
383# Create a clone from a snapshot, default clone name is $TESTCLONE.
384#
385# $1 Existing snapshot, $TESTPOOL/$TESTFS@$TESTSNAP is default.
386# $2 Clone name, $TESTPOOL/$TESTCLONE is default.
387#
388function create_clone   # snapshot clone
389{
390	typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
391	typeset clone=${2:-$TESTPOOL/$TESTCLONE}
392
393	[[ -z $snap ]] && \
394		log_fail "Snapshot name is undefined."
395	[[ -z $clone ]] && \
396		log_fail "Clone name is undefined."
397
398	log_must zfs clone $snap $clone
399}
400
401#
402# Create a bookmark of the given snapshot.  Defaultly create a bookmark on
403# filesystem.
404#
405# $1 Existing filesystem or volume name. Default, $TESTFS
406# $2 Existing snapshot name. Default, $TESTSNAP
407# $3 bookmark name. Default, $TESTBKMARK
408#
409function create_bookmark
410{
411	typeset fs_vol=${1:-$TESTFS}
412	typeset snap=${2:-$TESTSNAP}
413	typeset bkmark=${3:-$TESTBKMARK}
414
415	[[ -z $fs_vol ]] && log_fail "Filesystem or volume's name is undefined."
416	[[ -z $snap ]] && log_fail "Snapshot's name is undefined."
417	[[ -z $bkmark ]] && log_fail "Bookmark's name is undefined."
418
419	if bkmarkexists $fs_vol#$bkmark; then
420		log_fail "$fs_vol#$bkmark already exists."
421	fi
422	datasetexists $fs_vol || \
423		log_fail "$fs_vol must exist."
424	snapexists $fs_vol@$snap || \
425		log_fail "$fs_vol@$snap must exist."
426
427	log_must zfs bookmark $fs_vol@$snap $fs_vol#$bkmark
428}
429
430#
431# Create a temporary clone result of an interrupted resumable 'zfs receive'
432# $1 Destination filesystem name. Must not exist, will be created as the result
433#    of this function along with its %recv temporary clone
434# $2 Source filesystem name. Must not exist, will be created and destroyed
435#
436function create_recv_clone
437{
438	typeset recvfs="$1"
439	typeset sendfs="${2:-$TESTPOOL/create_recv_clone}"
440	typeset snap="$sendfs@snap1"
441	typeset incr="$sendfs@snap2"
442	typeset mountpoint="$TESTDIR/create_recv_clone"
443	typeset sendfile="$TESTDIR/create_recv_clone.zsnap"
444
445	[[ -z $recvfs ]] && log_fail "Recv filesystem's name is undefined."
446
447	datasetexists $recvfs && log_fail "Recv filesystem must not exist."
448	datasetexists $sendfs && log_fail "Send filesystem must not exist."
449
450	log_must zfs create -o mountpoint="$mountpoint" $sendfs
451	log_must zfs snapshot $snap
452	log_must eval "zfs send $snap | zfs recv -u $recvfs"
453	log_must mkfile 1m "$mountpoint/data"
454	log_must zfs snapshot $incr
455	log_must eval "zfs send -i $snap $incr | dd bs=10K count=1 \
456	    iflag=fullblock > $sendfile"
457	log_mustnot eval "zfs recv -su $recvfs < $sendfile"
458	destroy_dataset "$sendfs" "-r"
459	log_must rm -f "$sendfile"
460
461	if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then
462		log_fail "Error creating temporary $recvfs/%recv clone"
463	fi
464}
465
466function default_mirror_setup
467{
468	default_mirror_setup_noexit $1 $2 $3
469
470	log_pass
471}
472
473#
474# Given a pair of disks, set up a storage pool and dataset for the mirror
475# @parameters: $1 the primary side of the mirror
476#   $2 the secondary side of the mirror
477# @uses: ZPOOL ZFS TESTPOOL TESTFS
478function default_mirror_setup_noexit
479{
480	readonly func="default_mirror_setup_noexit"
481	typeset primary=$1
482	typeset secondary=$2
483
484	[[ -z $primary ]] && \
485		log_fail "$func: No parameters passed"
486	[[ -z $secondary ]] && \
487		log_fail "$func: No secondary partition passed"
488	[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
489	log_must zpool create -f $TESTPOOL mirror $@
490	log_must zfs create $TESTPOOL/$TESTFS
491	log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
492}
493
494#
495# create a number of mirrors.
496# We create a number($1) of 2 way mirrors using the pairs of disks named
497# on the command line. These mirrors are *not* mounted
498# @parameters: $1 the number of mirrors to create
499#  $... the devices to use to create the mirrors on
500# @uses: ZPOOL ZFS TESTPOOL
501function setup_mirrors
502{
503	typeset -i nmirrors=$1
504
505	shift
506	while ((nmirrors > 0)); do
507		log_must test -n "$1" -a -n "$2"
508		[[ -d /$TESTPOOL$nmirrors ]] && rm -rf /$TESTPOOL$nmirrors
509		log_must zpool create -f $TESTPOOL$nmirrors mirror $1 $2
510		shift 2
511		((nmirrors = nmirrors - 1))
512	done
513}
514
515#
516# create a number of raidz pools.
517# We create a number($1) of 2 raidz pools  using the pairs of disks named
518# on the command line. These pools are *not* mounted
519# @parameters: $1 the number of pools to create
520#  $... the devices to use to create the pools on
521# @uses: ZPOOL ZFS TESTPOOL
522function setup_raidzs
523{
524	typeset -i nraidzs=$1
525
526	shift
527	while ((nraidzs > 0)); do
528		log_must test -n "$1" -a -n "$2"
529		[[ -d /$TESTPOOL$nraidzs ]] && rm -rf /$TESTPOOL$nraidzs
530		log_must zpool create -f $TESTPOOL$nraidzs raidz $1 $2
531		shift 2
532		((nraidzs = nraidzs - 1))
533	done
534}
535
536#
537# Destroy the configured testpool mirrors.
538# the mirrors are of the form ${TESTPOOL}{number}
539# @uses: ZPOOL ZFS TESTPOOL
540function destroy_mirrors
541{
542	default_cleanup_noexit
543
544	log_pass
545}
546
547#
548# Given a minimum of two disks, set up a storage pool and dataset for the raid-z
549# $1 the list of disks
550#
551function default_raidz_setup
552{
553	typeset disklist="$*"
554	disks=(${disklist[*]})
555
556	if [[ ${#disks[*]} -lt 2 ]]; then
557		log_fail "A raid-z requires a minimum of two disks."
558	fi
559
560	[[ -d /$TESTPOOL ]] && rm -rf /$TESTPOOL
561	log_must zpool create -f $TESTPOOL raidz $disklist
562	log_must zfs create $TESTPOOL/$TESTFS
563	log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS
564
565	log_pass
566}
567
568#
569# Common function used to cleanup storage pools and datasets.
570#
571# Invoked at the start of the test suite to ensure the system
572# is in a known state, and also at the end of each set of
573# sub-tests to ensure errors from one set of tests doesn't
574# impact the execution of the next set.
575
576function default_cleanup
577{
578	default_cleanup_noexit
579
580	log_pass
581}
582
583#
584# Utility function used to list all available pool names.
585#
586# NOTE: $KEEP is a variable containing pool names, separated by a newline
587# character, that must be excluded from the returned list.
588#
589function get_all_pools
590{
591	zpool list -H -o name | grep -Fvx "$KEEP" | grep -v "$NO_POOLS"
592}
593
594function default_cleanup_noexit
595{
596	typeset pool=""
597	#
598	# Destroying the pool will also destroy any
599	# filesystems it contains.
600	#
601	if is_global_zone; then
602		zfs unmount -a > /dev/null 2>&1
603		ALL_POOLS=$(get_all_pools)
604		# Here, we loop through the pools we're allowed to
605		# destroy, only destroying them if it's safe to do
606		# so.
607		while [ ! -z ${ALL_POOLS} ]
608		do
609			for pool in ${ALL_POOLS}
610			do
611				if safe_to_destroy_pool $pool ;
612				then
613					destroy_pool $pool
614				fi
615			done
616			ALL_POOLS=$(get_all_pools)
617		done
618
619		zfs mount -a
620	else
621		typeset fs=""
622		for fs in $(zfs list -H -o name \
623		    | grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do
624			destroy_dataset "$fs" "-Rf"
625		done
626
627		# Need cleanup here to avoid garbage dir left.
628		for fs in $(zfs list -H -o name); do
629			[[ $fs == /$ZONE_POOL ]] && continue
630			[[ -d $fs ]] && log_must rm -rf $fs/*
631		done
632
633		#
634		# Reset the $ZONE_POOL/$ZONE_CTR[01234] file systems property to
635		# the default value
636		#
637		for fs in $(zfs list -H -o name); do
638			if [[ $fs == $ZONE_POOL/$ZONE_CTR[01234] ]]; then
639				log_must zfs set reservation=none $fs
640				log_must zfs set recordsize=128K $fs
641				log_must zfs set mountpoint=/$fs $fs
642				typeset enc=""
643				enc=$(get_prop encryption $fs)
644				if [[ $? -ne 0 ]] || [[ -z "$enc" ]] || \
645					[[ "$enc" == "off" ]]; then
646					log_must zfs set checksum=on $fs
647				fi
648				log_must zfs set compression=off $fs
649				log_must zfs set atime=on $fs
650				log_must zfs set devices=off $fs
651				log_must zfs set exec=on $fs
652				log_must zfs set setuid=on $fs
653				log_must zfs set readonly=off $fs
654				log_must zfs set snapdir=hidden $fs
655				log_must zfs set aclmode=groupmask $fs
656				log_must zfs set aclinherit=secure $fs
657			fi
658		done
659	fi
660
661	[[ -d $TESTDIR ]] && \
662		log_must rm -rf $TESTDIR
663
664	disk1=${DISKS%% *}
665	if is_mpath_device $disk1; then
666		delete_partitions
667	fi
668
669	rm -f $TEST_BASE_DIR/{err,out}
670}
671
672
673#
674# Common function used to cleanup storage pools, file systems
675# and containers.
676#
677function default_container_cleanup
678{
679	if ! is_global_zone; then
680		reexport_pool
681	fi
682
683	ismounted $TESTPOOL/$TESTCTR/$TESTFS1
684	[[ $? -eq 0 ]] && \
685	    log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1
686
687	destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R"
688	destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf"
689
690	[[ -e $TESTDIR1 ]] && \
691	    log_must rm -rf $TESTDIR1 > /dev/null 2>&1
692
693	default_cleanup
694}
695
696#
697# Common function used to cleanup snapshot of file system or volume. Default to
698# delete the file system's snapshot
699#
700# $1 snapshot name
701#
702function destroy_snapshot
703{
704	typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
705
706	if ! snapexists $snap; then
707		log_fail "'$snap' does not exist."
708	fi
709
710	#
711	# For the sake of the value which come from 'get_prop' is not equal
712	# to the really mountpoint when the snapshot is unmounted. So, firstly
713	# check and make sure this snapshot's been mounted in current system.
714	#
715	typeset mtpt=""
716	if ismounted $snap; then
717		mtpt=$(get_prop mountpoint $snap)
718		(($? != 0)) && \
719			log_fail "get_prop mountpoint $snap failed."
720	fi
721
722	destroy_dataset "$snap"
723	[[ $mtpt != "" && -d $mtpt ]] && \
724		log_must rm -rf $mtpt
725}
726
727#
728# Common function used to cleanup clone.
729#
730# $1 clone name
731#
732function destroy_clone
733{
734	typeset clone=${1:-$TESTPOOL/$TESTCLONE}
735
736	if ! datasetexists $clone; then
737		log_fail "'$clone' does not existed."
738	fi
739
740	# With the same reason in destroy_snapshot
741	typeset mtpt=""
742	if ismounted $clone; then
743		mtpt=$(get_prop mountpoint $clone)
744		(($? != 0)) && \
745			log_fail "get_prop mountpoint $clone failed."
746	fi
747
748	destroy_dataset "$clone"
749	[[ $mtpt != "" && -d $mtpt ]] && \
750		log_must rm -rf $mtpt
751}
752
753#
754# Common function used to cleanup bookmark of file system or volume.  Default
755# to delete the file system's bookmark.
756#
757# $1 bookmark name
758#
759function destroy_bookmark
760{
761	typeset bkmark=${1:-$TESTPOOL/$TESTFS#$TESTBKMARK}
762
763	if ! bkmarkexists $bkmark; then
764		log_fail "'$bkmarkp' does not existed."
765	fi
766
767	destroy_dataset "$bkmark"
768}
769
770# Return 0 if a snapshot exists; $? otherwise
771#
772# $1 - snapshot name
773
774function snapexists
775{
776	zfs list -H -t snapshot "$1" > /dev/null 2>&1
777	return $?
778}
779
780#
781# Return 0 if a bookmark exists; $? otherwise
782#
783# $1 - bookmark name
784#
785function bkmarkexists
786{
787	zfs list -H -t bookmark "$1" > /dev/null 2>&1
788	return $?
789}
790
791#
792# Return 0 if a hold exists; $? otherwise
793#
794# $1 - hold tag
795# $2 - snapshot name
796#
797function holdexists
798{
799	zfs holds "$2" | awk '{ print $2 }' | grep "$1" > /dev/null 2>&1
800	return $?
801}
802
803#
804# Set a property to a certain value on a dataset.
805# Sets a property of the dataset to the value as passed in.
806# @param:
807#	$1 dataset who's property is being set
808#	$2 property to set
809#	$3 value to set property to
810# @return:
811#	0 if the property could be set.
812#	non-zero otherwise.
813# @use: ZFS
814#
815function dataset_setprop
816{
817	typeset fn=dataset_setprop
818
819	if (($# < 3)); then
820		log_note "$fn: Insufficient parameters (need 3, had $#)"
821		return 1
822	fi
823	typeset output=
824	output=$(zfs set $2=$3 $1 2>&1)
825	typeset rv=$?
826	if ((rv != 0)); then
827		log_note "Setting property on $1 failed."
828		log_note "property $2=$3"
829		log_note "Return Code: $rv"
830		log_note "Output: $output"
831		return $rv
832	fi
833	return 0
834}
835
836#
837# Assign suite defined dataset properties.
838# This function is used to apply the suite's defined default set of
839# properties to a dataset.
840# @parameters: $1 dataset to use
841# @uses: ZFS COMPRESSION_PROP CHECKSUM_PROP
842# @returns:
843#   0 if the dataset has been altered.
844#   1 if no pool name was passed in.
845#   2 if the dataset could not be found.
846#   3 if the dataset could not have it's properties set.
847#
848function dataset_set_defaultproperties
849{
850	typeset dataset="$1"
851
852	[[ -z $dataset ]] && return 1
853
854	typeset confset=
855	typeset -i found=0
856	for confset in $(zfs list); do
857		if [[ $dataset = $confset ]]; then
858			found=1
859			break
860		fi
861	done
862	[[ $found -eq 0 ]] && return 2
863	if [[ -n $COMPRESSION_PROP ]]; then
864		dataset_setprop $dataset compression $COMPRESSION_PROP || \
865			return 3
866		log_note "Compression set to '$COMPRESSION_PROP' on $dataset"
867	fi
868	if [[ -n $CHECKSUM_PROP ]]; then
869		dataset_setprop $dataset checksum $CHECKSUM_PROP || \
870			return 3
871		log_note "Checksum set to '$CHECKSUM_PROP' on $dataset"
872	fi
873	return 0
874}
875
876#
877# Check a numeric assertion
878# @parameter: $@ the assertion to check
879# @output: big loud notice if assertion failed
880# @use: log_fail
881#
882function assert
883{
884	(($@)) || log_fail "$@"
885}
886
887#
888# Function to format partition size of a disk
889# Given a disk cxtxdx reduces all partitions
890# to 0 size
891#
892function zero_partitions #<whole_disk_name>
893{
894	typeset diskname=$1
895	typeset i
896
897	if is_freebsd; then
898		gpart destroy -F $diskname
899	elif is_linux; then
900		DSK=$DEV_DSKDIR/$diskname
901		DSK=$(echo $DSK | sed -e "s|//|/|g")
902		log_must parted $DSK -s -- mklabel gpt
903		blockdev --rereadpt $DSK 2>/dev/null
904		block_device_wait
905	else
906		for i in 0 1 3 4 5 6 7
907		do
908			log_must set_partition $i "" 0mb $diskname
909		done
910	fi
911
912	return 0
913}
914
915#
916# Given a slice, size and disk, this function
917# formats the slice to the specified size.
918# Size should be specified with units as per
919# the `format` command requirements eg. 100mb 3gb
920#
921# NOTE: This entire interface is problematic for the Linux parted utility
922# which requires the end of the partition to be specified.  It would be
923# best to retire this interface and replace it with something more flexible.
924# At the moment a best effort is made.
925#
926# arguments: <slice_num> <slice_start> <size_plus_units>  <whole_disk_name>
927function set_partition
928{
929	typeset -i slicenum=$1
930	typeset start=$2
931	typeset size=$3
932	typeset disk=${4#$DEV_DSKDIR/}
933	disk=${disk#$DEV_RDSKDIR/}
934
935	case "$(uname)" in
936	Linux)
937		if [[ -z $size || -z $disk ]]; then
938			log_fail "The size or disk name is unspecified."
939		fi
940		disk=$DEV_DSKDIR/$disk
941		typeset size_mb=${size%%[mMgG]}
942
943		size_mb=${size_mb%%[mMgG][bB]}
944		if [[ ${size:1:1} == 'g' ]]; then
945			((size_mb = size_mb * 1024))
946		fi
947
948		# Create GPT partition table when setting slice 0 or
949		# when the device doesn't already contain a GPT label.
950		parted $disk -s -- print 1 >/dev/null
951		typeset ret_val=$?
952		if [[ $slicenum -eq 0 || $ret_val -ne 0 ]]; then
953			parted $disk -s -- mklabel gpt
954			if [[ $? -ne 0 ]]; then
955				log_note "Failed to create GPT partition table on $disk"
956				return 1
957			fi
958		fi
959
960		# When no start is given align on the first cylinder.
961		if [[ -z "$start" ]]; then
962			start=1
963		fi
964
965		# Determine the cylinder size for the device and using
966		# that calculate the end offset in cylinders.
967		typeset -i cly_size_kb=0
968		cly_size_kb=$(parted -m $disk -s -- \
969			unit cyl print | head -3 | tail -1 | \
970			awk -F '[:k.]' '{print $4}')
971		((end = (size_mb * 1024 / cly_size_kb) + start))
972
973		parted $disk -s -- \
974		    mkpart part$slicenum ${start}cyl ${end}cyl
975		typeset ret_val=$?
976		if [[ $ret_val -ne 0 ]]; then
977			log_note "Failed to create partition $slicenum on $disk"
978			return 1
979		fi
980
981		blockdev --rereadpt $disk 2>/dev/null
982		block_device_wait $disk
983		;;
984	FreeBSD)
985		if [[ -z $size || -z $disk ]]; then
986			log_fail "The size or disk name is unspecified."
987		fi
988		disk=$DEV_DSKDIR/$disk
989
990		if [[ $slicenum -eq 0 ]] || ! gpart show $disk >/dev/null 2>&1; then
991			gpart destroy -F $disk >/dev/null 2>&1
992			gpart create -s GPT $disk
993			if [[ $? -ne 0 ]]; then
994				log_note "Failed to create GPT partition table on $disk"
995				return 1
996			fi
997		fi
998
999		typeset index=$((slicenum + 1))
1000
1001		if [[ -n $start ]]; then
1002			start="-b $start"
1003		fi
1004		gpart add -t freebsd-zfs $start -s $size -i $index $disk
1005		if [[ $ret_val -ne 0 ]]; then
1006			log_note "Failed to create partition $slicenum on $disk"
1007			return 1
1008		fi
1009
1010		block_device_wait $disk
1011		;;
1012	*)
1013		if [[ -z $slicenum || -z $size || -z $disk ]]; then
1014			log_fail "The slice, size or disk name is unspecified."
1015		fi
1016
1017		typeset format_file=/var/tmp/format_in.$$
1018
1019		echo "partition" >$format_file
1020		echo "$slicenum" >> $format_file
1021		echo "" >> $format_file
1022		echo "" >> $format_file
1023		echo "$start" >> $format_file
1024		echo "$size" >> $format_file
1025		echo "label" >> $format_file
1026		echo "" >> $format_file
1027		echo "q" >> $format_file
1028		echo "q" >> $format_file
1029
1030		format -e -s -d $disk -f $format_file
1031		typeset ret_val=$?
1032		rm -f $format_file
1033		;;
1034	esac
1035
1036	if [[ $ret_val -ne 0 ]]; then
1037		log_note "Unable to format $disk slice $slicenum to $size"
1038		return 1
1039	fi
1040	return 0
1041}
1042
1043#
1044# Delete all partitions on all disks - this is specifically for the use of multipath
1045# devices which currently can only be used in the test suite as raw/un-partitioned
1046# devices (ie a zpool cannot be created on a whole mpath device that has partitions)
1047#
1048function delete_partitions
1049{
1050	typeset disk
1051
1052	if [[ -z $DISKSARRAY ]]; then
1053		DISKSARRAY=$DISKS
1054	fi
1055
1056	if is_linux; then
1057		typeset -i part
1058		for disk in $DISKSARRAY; do
1059			for (( part = 1; part < MAX_PARTITIONS; part++ )); do
1060				typeset partition=${disk}${SLICE_PREFIX}${part}
1061				parted $DEV_DSKDIR/$disk -s rm $part > /dev/null 2>&1
1062				if lsblk | grep -qF ${partition}; then
1063					log_fail "Partition ${partition} not deleted"
1064				else
1065					log_note "Partition ${partition} deleted"
1066				fi
1067			done
1068		done
1069	elif is_freebsd; then
1070		for disk in $DISKSARRAY; do
1071			if gpart destroy -F $disk; then
1072				log_note "Partitions for ${disk} deleted"
1073			else
1074				log_fail "Partitions for ${disk} not deleted"
1075			fi
1076		done
1077	fi
1078}
1079
1080#
1081# Get the end cyl of the given slice
1082#
1083function get_endslice #<disk> <slice>
1084{
1085	typeset disk=$1
1086	typeset slice=$2
1087	if [[ -z $disk || -z $slice ]] ; then
1088		log_fail "The disk name or slice number is unspecified."
1089	fi
1090
1091	case "$(uname)" in
1092	Linux)
1093		endcyl=$(parted -s $DEV_DSKDIR/$disk -- unit cyl print | \
1094			grep "part${slice}" | \
1095			awk '{print $3}' | \
1096			sed 's,cyl,,')
1097		((endcyl = (endcyl + 1)))
1098		;;
1099	FreeBSD)
1100		disk=${disk#/dev/zvol/}
1101		disk=${disk%p*}
1102		slice=$((slice + 1))
1103		endcyl=$(gpart show $disk | \
1104			awk -v slice=$slice '$3 == slice { print $1 + $2 }')
1105		;;
1106	*)
1107		disk=${disk#/dev/dsk/}
1108		disk=${disk#/dev/rdsk/}
1109		disk=${disk%s*}
1110
1111		typeset -i ratio=0
1112		ratio=$(prtvtoc /dev/rdsk/${disk}s2 | \
1113		    grep "sectors\/cylinder" | \
1114		    awk '{print $2}')
1115
1116		if ((ratio == 0)); then
1117			return
1118		fi
1119
1120		typeset -i endcyl=$(prtvtoc -h /dev/rdsk/${disk}s2 |
1121		    nawk -v token="$slice" '{if ($1==token) print $6}')
1122
1123		((endcyl = (endcyl + 1) / ratio))
1124		;;
1125	esac
1126
1127	echo $endcyl
1128}
1129
1130
1131#
1132# Given a size,disk and total slice number,  this function formats the
1133# disk slices from 0 to the total slice number with the same specified
1134# size.
1135#
1136function partition_disk	#<slice_size> <whole_disk_name>	<total_slices>
1137{
1138	typeset -i i=0
1139	typeset slice_size=$1
1140	typeset disk_name=$2
1141	typeset total_slices=$3
1142	typeset cyl
1143
1144	zero_partitions $disk_name
1145	while ((i < $total_slices)); do
1146		if ! is_linux; then
1147			if ((i == 2)); then
1148				((i = i + 1))
1149				continue
1150			fi
1151		fi
1152		log_must set_partition $i "$cyl" $slice_size $disk_name
1153		cyl=$(get_endslice $disk_name $i)
1154		((i = i+1))
1155	done
1156}
1157
1158#
1159# This function continues to write to a filenum number of files into dirnum
1160# number of directories until either file_write returns an error or the
1161# maximum number of files per directory have been written.
1162#
1163# Usage:
1164# fill_fs [destdir] [dirnum] [filenum] [bytes] [num_writes] [data]
1165#
1166# Return value: 0 on success
1167#		non 0 on error
1168#
1169# Where :
1170#	destdir:    is the directory where everything is to be created under
1171#	dirnum:	    the maximum number of subdirectories to use, -1 no limit
1172#	filenum:    the maximum number of files per subdirectory
1173#	bytes:	    number of bytes to write
1174#	num_writes: number of types to write out bytes
1175#	data:	    the data that will be written
1176#
1177#	E.g.
1178#	fill_fs /testdir 20 25 1024 256 0
1179#
1180# Note: bytes * num_writes equals the size of the testfile
1181#
1182function fill_fs # destdir dirnum filenum bytes num_writes data
1183{
1184	typeset destdir=${1:-$TESTDIR}
1185	typeset -i dirnum=${2:-50}
1186	typeset -i filenum=${3:-50}
1187	typeset -i bytes=${4:-8192}
1188	typeset -i num_writes=${5:-10240}
1189	typeset data=${6:-0}
1190
1191	mkdir -p $destdir/{1..$dirnum}
1192	for f in $destdir/{1..$dirnum}/$TESTFILE{1..$filenum}; do
1193		file_write -o create -f $f -b $bytes -c $num_writes -d $data \
1194		|| return $?
1195	done
1196	return 0
1197}
1198
1199#
1200# Simple function to get the specified property. If unable to
1201# get the property then exits.
1202#
1203# Note property is in 'parsable' format (-p)
1204#
1205function get_prop # property dataset
1206{
1207	typeset prop_val
1208	typeset prop=$1
1209	typeset dataset=$2
1210
1211	prop_val=$(zfs get -pH -o value $prop $dataset 2>/dev/null)
1212	if [[ $? -ne 0 ]]; then
1213		log_note "Unable to get $prop property for dataset " \
1214		"$dataset"
1215		return 1
1216	fi
1217
1218	echo "$prop_val"
1219	return 0
1220}
1221
1222#
1223# Simple function to get the specified property of pool. If unable to
1224# get the property then exits.
1225#
1226# Note property is in 'parsable' format (-p)
1227#
1228function get_pool_prop # property pool
1229{
1230	typeset prop_val
1231	typeset prop=$1
1232	typeset pool=$2
1233
1234	if poolexists $pool ; then
1235		prop_val=$(zpool get -pH $prop $pool 2>/dev/null | tail -1 | \
1236			awk '{print $3}')
1237		if [[ $? -ne 0 ]]; then
1238			log_note "Unable to get $prop property for pool " \
1239			"$pool"
1240			return 1
1241		fi
1242	else
1243		log_note "Pool $pool not exists."
1244		return 1
1245	fi
1246
1247	echo "$prop_val"
1248	return 0
1249}
1250
1251# Return 0 if a pool exists; $? otherwise
1252#
1253# $1 - pool name
1254
1255function poolexists
1256{
1257	typeset pool=$1
1258
1259	if [[ -z $pool ]]; then
1260		log_note "No pool name given."
1261		return 1
1262	fi
1263
1264	zpool get name "$pool" > /dev/null 2>&1
1265	return $?
1266}
1267
1268# Return 0 if all the specified datasets exist; $? otherwise
1269#
1270# $1-n  dataset name
1271function datasetexists
1272{
1273	if (($# == 0)); then
1274		log_note "No dataset name given."
1275		return 1
1276	fi
1277
1278	while (($# > 0)); do
1279		zfs get name $1 > /dev/null 2>&1 || \
1280			return $?
1281		shift
1282	done
1283
1284	return 0
1285}
1286
1287# return 0 if none of the specified datasets exists, otherwise return 1.
1288#
1289# $1-n  dataset name
1290function datasetnonexists
1291{
1292	if (($# == 0)); then
1293		log_note "No dataset name given."
1294		return 1
1295	fi
1296
1297	while (($# > 0)); do
1298		zfs list -H -t filesystem,snapshot,volume $1 > /dev/null 2>&1 \
1299		    && return 1
1300		shift
1301	done
1302
1303	return 0
1304}
1305
1306function is_shared_freebsd
1307{
1308	typeset fs=$1
1309
1310	pgrep -q mountd && showmount -E | grep -qx $fs
1311}
1312
1313function is_shared_illumos
1314{
1315	typeset fs=$1
1316	typeset mtpt
1317
1318	for mtpt in `share | awk '{print $2}'` ; do
1319		if [[ $mtpt == $fs ]] ; then
1320			return 0
1321		fi
1322	done
1323
1324	typeset stat=$(svcs -H -o STA nfs/server:default)
1325	if [[ $stat != "ON" ]]; then
1326		log_note "Current nfs/server status: $stat"
1327	fi
1328
1329	return 1
1330}
1331
1332function is_shared_linux
1333{
1334	typeset fs=$1
1335	typeset mtpt
1336
1337	for mtpt in `share | awk '{print $1}'` ; do
1338		if [[ $mtpt == $fs ]] ; then
1339			return 0
1340		fi
1341	done
1342	return 1
1343}
1344
1345#
1346# Given a mountpoint, or a dataset name, determine if it is shared via NFS.
1347#
1348# Returns 0 if shared, 1 otherwise.
1349#
1350function is_shared
1351{
1352	typeset fs=$1
1353	typeset mtpt
1354
1355	if [[ $fs != "/"* ]] ; then
1356		if datasetnonexists "$fs" ; then
1357			return 1
1358		else
1359			mtpt=$(get_prop mountpoint "$fs")
1360			case $mtpt in
1361				none|legacy|-) return 1
1362					;;
1363				*)	fs=$mtpt
1364					;;
1365			esac
1366		fi
1367	fi
1368
1369	case $(uname) in
1370	FreeBSD)	is_shared_freebsd "$fs"	;;
1371	Linux)		is_shared_linux "$fs"	;;
1372	*)		is_shared_illumos "$fs"	;;
1373	esac
1374}
1375
1376function is_exported_illumos
1377{
1378	typeset fs=$1
1379	typeset mtpt
1380
1381	for mtpt in `awk '{print $1}' /etc/dfs/sharetab` ; do
1382		if [[ $mtpt == $fs ]] ; then
1383			return 0
1384		fi
1385	done
1386
1387	return 1
1388}
1389
1390function is_exported_freebsd
1391{
1392	typeset fs=$1
1393	typeset mtpt
1394
1395	for mtpt in `awk '{print $1}' /etc/zfs/exports` ; do
1396		if [[ $mtpt == $fs ]] ; then
1397			return 0
1398		fi
1399	done
1400
1401	return 1
1402}
1403
1404function is_exported_linux
1405{
1406	typeset fs=$1
1407	typeset mtpt
1408
1409	for mtpt in `awk '{print $1}' /etc/exports.d/zfs.exports` ; do
1410		if [[ $mtpt == $fs ]] ; then
1411			return 0
1412		fi
1413	done
1414
1415	return 1
1416}
1417
1418#
1419# Given a mountpoint, or a dataset name, determine if it is exported via
1420# the os-specific NFS exports file.
1421#
1422# Returns 0 if exported, 1 otherwise.
1423#
1424function is_exported
1425{
1426	typeset fs=$1
1427	typeset mtpt
1428
1429	if [[ $fs != "/"* ]] ; then
1430		if datasetnonexists "$fs" ; then
1431			return 1
1432		else
1433			mtpt=$(get_prop mountpoint "$fs")
1434			case $mtpt in
1435				none|legacy|-) return 1
1436					;;
1437				*)	fs=$mtpt
1438					;;
1439			esac
1440		fi
1441	fi
1442
1443	case $(uname) in
1444	FreeBSD)	is_exported_freebsd "$fs"	;;
1445	Linux)		is_exported_linux "$fs"	;;
1446	*)		is_exported_illumos "$fs"	;;
1447	esac
1448}
1449
1450#
1451# Given a dataset name determine if it is shared via SMB.
1452#
1453# Returns 0 if shared, 1 otherwise.
1454#
1455function is_shared_smb
1456{
1457	typeset fs=$1
1458	typeset mtpt
1459
1460	if datasetnonexists "$fs" ; then
1461		return 1
1462	else
1463		fs=$(echo $fs | sed 's@/@_@g')
1464	fi
1465
1466	if is_linux; then
1467		for mtpt in `net usershare list | awk '{print $1}'` ; do
1468			if [[ $mtpt == $fs ]] ; then
1469				return 0
1470			fi
1471		done
1472		return 1
1473	else
1474		log_note "Currently unsupported by the test framework"
1475		return 1
1476	fi
1477}
1478
1479#
1480# Given a mountpoint, determine if it is not shared via NFS.
1481#
1482# Returns 0 if not shared, 1 otherwise.
1483#
1484function not_shared
1485{
1486	typeset fs=$1
1487
1488	is_shared $fs
1489	if (($? == 0)); then
1490		return 1
1491	fi
1492
1493	return 0
1494}
1495
1496#
1497# Given a dataset determine if it is not shared via SMB.
1498#
1499# Returns 0 if not shared, 1 otherwise.
1500#
1501function not_shared_smb
1502{
1503	typeset fs=$1
1504
1505	is_shared_smb $fs
1506	if (($? == 0)); then
1507		return 1
1508	fi
1509
1510	return 0
1511}
1512
1513#
1514# Helper function to unshare a mountpoint.
1515#
1516function unshare_fs #fs
1517{
1518	typeset fs=$1
1519
1520	is_shared $fs || is_shared_smb $fs
1521	if (($? == 0)); then
1522		zfs unshare $fs || log_fail "zfs unshare $fs failed"
1523	fi
1524
1525	return 0
1526}
1527
1528#
1529# Helper function to share a NFS mountpoint.
1530#
1531function share_nfs #fs
1532{
1533	typeset fs=$1
1534
1535	if is_linux; then
1536		is_shared $fs
1537		if (($? != 0)); then
1538			log_must share "*:$fs"
1539		fi
1540	else
1541		is_shared $fs
1542		if (($? != 0)); then
1543			log_must share -F nfs $fs
1544		fi
1545	fi
1546
1547	return 0
1548}
1549
1550#
1551# Helper function to unshare a NFS mountpoint.
1552#
1553function unshare_nfs #fs
1554{
1555	typeset fs=$1
1556
1557	if is_linux; then
1558		is_shared $fs
1559		if (($? == 0)); then
1560			log_must unshare -u "*:$fs"
1561		fi
1562	else
1563		is_shared $fs
1564		if (($? == 0)); then
1565			log_must unshare -F nfs $fs
1566		fi
1567	fi
1568
1569	return 0
1570}
1571
1572#
1573# Helper function to show NFS shares.
1574#
1575function showshares_nfs
1576{
1577	if is_linux; then
1578		share -v
1579	else
1580		share -F nfs
1581	fi
1582
1583	return 0
1584}
1585
1586#
1587# Helper function to show SMB shares.
1588#
1589function showshares_smb
1590{
1591	if is_linux; then
1592		net usershare list
1593	else
1594		share -F smb
1595	fi
1596
1597	return 0
1598}
1599
1600function check_nfs
1601{
1602	if is_linux; then
1603		share -s
1604	elif is_freebsd; then
1605		showmount -e
1606	else
1607		log_unsupported "Unknown platform"
1608	fi
1609
1610	if [[ $? -ne 0 ]]; then
1611		log_unsupported "The NFS utilities are not installed"
1612	fi
1613}
1614
1615#
1616# Check NFS server status and trigger it online.
1617#
1618function setup_nfs_server
1619{
1620	# Cannot share directory in non-global zone.
1621	#
1622	if ! is_global_zone; then
1623		log_note "Cannot trigger NFS server by sharing in LZ."
1624		return
1625	fi
1626
1627	if is_linux; then
1628		#
1629		# Re-synchronize /var/lib/nfs/etab with /etc/exports and
1630		# /etc/exports.d./* to provide a clean test environment.
1631		#
1632		log_must share -r
1633
1634		log_note "NFS server must be started prior to running ZTS."
1635		return
1636	elif is_freebsd; then
1637		kill -s HUP $(cat /var/run/mountd.pid)
1638
1639		log_note "NFS server must be started prior to running ZTS."
1640		return
1641	fi
1642
1643	typeset nfs_fmri="svc:/network/nfs/server:default"
1644	if [[ $(svcs -Ho STA $nfs_fmri) != "ON" ]]; then
1645		#
1646		# Only really sharing operation can enable NFS server
1647		# to online permanently.
1648		#
1649		typeset dummy=/tmp/dummy
1650
1651		if [[ -d $dummy ]]; then
1652			log_must rm -rf $dummy
1653		fi
1654
1655		log_must mkdir $dummy
1656		log_must share $dummy
1657
1658		#
1659		# Waiting for fmri's status to be the final status.
1660		# Otherwise, in transition, an asterisk (*) is appended for
1661		# instances, unshare will reverse status to 'DIS' again.
1662		#
1663		# Waiting for 1's at least.
1664		#
1665		log_must sleep 1
1666		timeout=10
1667		while [[ timeout -ne 0 && $(svcs -Ho STA $nfs_fmri) == *'*' ]]
1668		do
1669			log_must sleep 1
1670
1671			((timeout -= 1))
1672		done
1673
1674		log_must unshare $dummy
1675		log_must rm -rf $dummy
1676	fi
1677
1678	log_note "Current NFS status: '$(svcs -Ho STA,FMRI $nfs_fmri)'"
1679}
1680
1681#
1682# To verify whether calling process is in global zone
1683#
1684# Return 0 if in global zone, 1 in non-global zone
1685#
1686function is_global_zone
1687{
1688	if is_linux || is_freebsd; then
1689		return 0
1690	else
1691		typeset cur_zone=$(zonename 2>/dev/null)
1692		if [[ $cur_zone != "global" ]]; then
1693			return 1
1694		fi
1695		return 0
1696	fi
1697}
1698
1699#
1700# Verify whether test is permitted to run from
1701# global zone, local zone, or both
1702#
1703# $1 zone limit, could be "global", "local", or "both"(no limit)
1704#
1705# Return 0 if permitted, otherwise exit with log_unsupported
1706#
1707function verify_runnable # zone limit
1708{
1709	typeset limit=$1
1710
1711	[[ -z $limit ]] && return 0
1712
1713	if is_global_zone ; then
1714		case $limit in
1715			global|both)
1716				;;
1717			local)	log_unsupported "Test is unable to run from "\
1718					"global zone."
1719				;;
1720			*)	log_note "Warning: unknown limit $limit - " \
1721					"use both."
1722				;;
1723		esac
1724	else
1725		case $limit in
1726			local|both)
1727				;;
1728			global)	log_unsupported "Test is unable to run from "\
1729					"local zone."
1730				;;
1731			*)	log_note "Warning: unknown limit $limit - " \
1732					"use both."
1733				;;
1734		esac
1735
1736		reexport_pool
1737	fi
1738
1739	return 0
1740}
1741
1742# Return 0 if create successfully or the pool exists; $? otherwise
1743# Note: In local zones, this function should return 0 silently.
1744#
1745# $1 - pool name
1746# $2-n - [keyword] devs_list
1747
1748function create_pool #pool devs_list
1749{
1750	typeset pool=${1%%/*}
1751
1752	shift
1753
1754	if [[ -z $pool ]]; then
1755		log_note "Missing pool name."
1756		return 1
1757	fi
1758
1759	if poolexists $pool ; then
1760		destroy_pool $pool
1761	fi
1762
1763	if is_global_zone ; then
1764		[[ -d /$pool ]] && rm -rf /$pool
1765		log_must zpool create -f $pool $@
1766	fi
1767
1768	return 0
1769}
1770
1771# Return 0 if destroy successfully or the pool exists; $? otherwise
1772# Note: In local zones, this function should return 0 silently.
1773#
1774# $1 - pool name
1775# Destroy pool with the given parameters.
1776
1777function destroy_pool #pool
1778{
1779	typeset pool=${1%%/*}
1780	typeset mtpt
1781
1782	if [[ -z $pool ]]; then
1783		log_note "No pool name given."
1784		return 1
1785	fi
1786
1787	if is_global_zone ; then
1788		if poolexists "$pool" ; then
1789			mtpt=$(get_prop mountpoint "$pool")
1790
1791			# At times, syseventd/udev activity can cause attempts
1792			# to destroy a pool to fail with EBUSY. We retry a few
1793			# times allowing failures before requiring the destroy
1794			# to succeed.
1795			log_must_busy zpool destroy -f $pool
1796
1797			[[ -d $mtpt ]] && \
1798				log_must rm -rf $mtpt
1799		else
1800			log_note "Pool does not exist. ($pool)"
1801			return 1
1802		fi
1803	fi
1804
1805	return 0
1806}
1807
1808# Return 0 if created successfully; $? otherwise
1809#
1810# $1 - dataset name
1811# $2-n - dataset options
1812
1813function create_dataset #dataset dataset_options
1814{
1815	typeset dataset=$1
1816
1817	shift
1818
1819	if [[ -z $dataset ]]; then
1820		log_note "Missing dataset name."
1821		return 1
1822	fi
1823
1824	if datasetexists $dataset ; then
1825		destroy_dataset $dataset
1826	fi
1827
1828	log_must zfs create $@ $dataset
1829
1830	return 0
1831}
1832
1833# Return 0 if destroy successfully or the dataset exists; $? otherwise
1834# Note: In local zones, this function should return 0 silently.
1835#
1836# $1 - dataset name
1837# $2 - custom arguments for zfs destroy
1838# Destroy dataset with the given parameters.
1839
1840function destroy_dataset #dataset #args
1841{
1842	typeset dataset=$1
1843	typeset mtpt
1844	typeset args=${2:-""}
1845
1846	if [[ -z $dataset ]]; then
1847		log_note "No dataset name given."
1848		return 1
1849	fi
1850
1851	if is_global_zone ; then
1852		if datasetexists "$dataset" ; then
1853			mtpt=$(get_prop mountpoint "$dataset")
1854			log_must_busy zfs destroy $args $dataset
1855
1856			[[ -d $mtpt ]] && \
1857				log_must rm -rf $mtpt
1858		else
1859			log_note "Dataset does not exist. ($dataset)"
1860			return 1
1861		fi
1862	fi
1863
1864	return 0
1865}
1866
1867#
1868# Firstly, create a pool with 5 datasets. Then, create a single zone and
1869# export the 5 datasets to it. In addition, we also add a ZFS filesystem
1870# and a zvol device to the zone.
1871#
1872# $1 zone name
1873# $2 zone root directory prefix
1874# $3 zone ip
1875#
1876function zfs_zones_setup #zone_name zone_root zone_ip
1877{
1878	typeset zone_name=${1:-$(hostname)-z}
1879	typeset zone_root=${2:-"/zone_root"}
1880	typeset zone_ip=${3:-"10.1.1.10"}
1881	typeset prefix_ctr=$ZONE_CTR
1882	typeset pool_name=$ZONE_POOL
1883	typeset -i cntctr=5
1884	typeset -i i=0
1885
1886	# Create pool and 5 container within it
1887	#
1888	[[ -d /$pool_name ]] && rm -rf /$pool_name
1889	log_must zpool create -f $pool_name $DISKS
1890	while ((i < cntctr)); do
1891		log_must zfs create $pool_name/$prefix_ctr$i
1892		((i += 1))
1893	done
1894
1895	# create a zvol
1896	log_must zfs create -V 1g $pool_name/zone_zvol
1897	block_device_wait
1898
1899	#
1900	# If current system support slog, add slog device for pool
1901	#
1902	if verify_slog_support ; then
1903		typeset sdevs="$TEST_BASE_DIR/sdev1 $TEST_BASE_DIR/sdev2"
1904		log_must mkfile $MINVDEVSIZE $sdevs
1905		log_must zpool add $pool_name log mirror $sdevs
1906	fi
1907
1908	# this isn't supported just yet.
1909	# Create a filesystem. In order to add this to
1910	# the zone, it must have it's mountpoint set to 'legacy'
1911	# log_must zfs create $pool_name/zfs_filesystem
1912	# log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem
1913
1914	[[ -d $zone_root ]] && \
1915		log_must rm -rf $zone_root/$zone_name
1916	[[ ! -d $zone_root ]] && \
1917		log_must mkdir -p -m 0700 $zone_root/$zone_name
1918
1919	# Create zone configure file and configure the zone
1920	#
1921	typeset zone_conf=/tmp/zone_conf.$$
1922	echo "create" > $zone_conf
1923	echo "set zonepath=$zone_root/$zone_name" >> $zone_conf
1924	echo "set autoboot=true" >> $zone_conf
1925	i=0
1926	while ((i < cntctr)); do
1927		echo "add dataset" >> $zone_conf
1928		echo "set name=$pool_name/$prefix_ctr$i" >> \
1929			$zone_conf
1930		echo "end" >> $zone_conf
1931		((i += 1))
1932	done
1933
1934	# add our zvol to the zone
1935	echo "add device" >> $zone_conf
1936	echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf
1937	echo "end" >> $zone_conf
1938
1939	# add a corresponding zvol rdsk to the zone
1940	echo "add device" >> $zone_conf
1941	echo "set match=$ZVOL_RDEVDIR/$pool_name/zone_zvol" >> $zone_conf
1942	echo "end" >> $zone_conf
1943
1944	# once it's supported, we'll add our filesystem to the zone
1945	# echo "add fs" >> $zone_conf
1946	# echo "set type=zfs" >> $zone_conf
1947	# echo "set special=$pool_name/zfs_filesystem" >> $zone_conf
1948	# echo "set dir=/export/zfs_filesystem" >> $zone_conf
1949	# echo "end" >> $zone_conf
1950
1951	echo "verify" >> $zone_conf
1952	echo "commit" >> $zone_conf
1953	log_must zonecfg -z $zone_name -f $zone_conf
1954	log_must rm -f $zone_conf
1955
1956	# Install the zone
1957	zoneadm -z $zone_name install
1958	if (($? == 0)); then
1959		log_note "SUCCESS: zoneadm -z $zone_name install"
1960	else
1961		log_fail "FAIL: zoneadm -z $zone_name install"
1962	fi
1963
1964	# Install sysidcfg file
1965	#
1966	typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg
1967	echo "system_locale=C" > $sysidcfg
1968	echo  "terminal=dtterm" >> $sysidcfg
1969	echo  "network_interface=primary {" >> $sysidcfg
1970	echo  "hostname=$zone_name" >> $sysidcfg
1971	echo  "}" >> $sysidcfg
1972	echo  "name_service=NONE" >> $sysidcfg
1973	echo  "root_password=mo791xfZ/SFiw" >> $sysidcfg
1974	echo  "security_policy=NONE" >> $sysidcfg
1975	echo  "timezone=US/Eastern" >> $sysidcfg
1976
1977	# Boot this zone
1978	log_must zoneadm -z $zone_name boot
1979}
1980
1981#
1982# Reexport TESTPOOL & TESTPOOL(1-4)
1983#
1984function reexport_pool
1985{
1986	typeset -i cntctr=5
1987	typeset -i i=0
1988
1989	while ((i < cntctr)); do
1990		if ((i == 0)); then
1991			TESTPOOL=$ZONE_POOL/$ZONE_CTR$i
1992			if ! ismounted $TESTPOOL; then
1993				log_must zfs mount $TESTPOOL
1994			fi
1995		else
1996			eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i
1997			if eval ! ismounted \$TESTPOOL$i; then
1998				log_must eval zfs mount \$TESTPOOL$i
1999			fi
2000		fi
2001		((i += 1))
2002	done
2003}
2004
2005#
2006# Verify a given disk or pool state
2007#
2008# Return 0 is pool/disk matches expected state, 1 otherwise
2009#
2010function check_state # pool disk state{online,offline,degraded}
2011{
2012	typeset pool=$1
2013	typeset disk=${2#$DEV_DSKDIR/}
2014	typeset state=$3
2015
2016	[[ -z $pool ]] || [[ -z $state ]] \
2017	    && log_fail "Arguments invalid or missing"
2018
2019	if [[ -z $disk ]]; then
2020		#check pool state only
2021		zpool get -H -o value health $pool \
2022		    | grep -i "$state" > /dev/null 2>&1
2023	else
2024		zpool status -v $pool | grep "$disk"  \
2025		    | grep -i "$state" > /dev/null 2>&1
2026	fi
2027
2028	return $?
2029}
2030
2031#
2032# Get the mountpoint of snapshot
2033# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap>
2034# as its mountpoint
2035#
2036function snapshot_mountpoint
2037{
2038	typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
2039
2040	if [[ $dataset != *@* ]]; then
2041		log_fail "Error name of snapshot '$dataset'."
2042	fi
2043
2044	typeset fs=${dataset%@*}
2045	typeset snap=${dataset#*@}
2046
2047	if [[ -z $fs || -z $snap ]]; then
2048		log_fail "Error name of snapshot '$dataset'."
2049	fi
2050
2051	echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap
2052}
2053
2054#
2055# Given a device and 'ashift' value verify it's correctly set on every label
2056#
2057function verify_ashift # device ashift
2058{
2059	typeset device="$1"
2060	typeset ashift="$2"
2061
2062	zdb -e -lll $device | awk -v ashift=$ashift '/ashift: / {
2063	    if (ashift != $2)
2064	        exit 1;
2065	    else
2066	        count++;
2067	    } END {
2068	    if (count != 4)
2069	        exit 1;
2070	    else
2071	        exit 0;
2072	    }'
2073
2074	return $?
2075}
2076
2077#
2078# Given a pool and file system, this function will verify the file system
2079# using the zdb internal tool. Note that the pool is exported and imported
2080# to ensure it has consistent state.
2081#
2082function verify_filesys # pool filesystem dir
2083{
2084	typeset pool="$1"
2085	typeset filesys="$2"
2086	typeset zdbout="/tmp/zdbout.$$"
2087
2088	shift
2089	shift
2090	typeset dirs=$@
2091	typeset search_path=""
2092
2093	log_note "Calling zdb to verify filesystem '$filesys'"
2094	zfs unmount -a > /dev/null 2>&1
2095	log_must zpool export $pool
2096
2097	if [[ -n $dirs ]] ; then
2098		for dir in $dirs ; do
2099			search_path="$search_path -d $dir"
2100		done
2101	fi
2102
2103	log_must zpool import $search_path $pool
2104
2105	zdb -cudi $filesys > $zdbout 2>&1
2106	if [[ $? != 0 ]]; then
2107		log_note "Output: zdb -cudi $filesys"
2108		cat $zdbout
2109		log_fail "zdb detected errors with: '$filesys'"
2110	fi
2111
2112	log_must zfs mount -a
2113	log_must rm -rf $zdbout
2114}
2115
2116#
2117# Given a pool issue a scrub and verify that no checksum errors are reported.
2118#
2119function verify_pool
2120{
2121	typeset pool=${1:-$TESTPOOL}
2122
2123	log_must zpool scrub $pool
2124	log_must wait_scrubbed $pool
2125
2126	typeset -i cksum=$(zpool status $pool | awk '
2127	    !NF { isvdev = 0 }
2128	    isvdev { errors += $NF }
2129	    /CKSUM$/ { isvdev = 1 }
2130	    END { print errors }
2131	')
2132	if [[ $cksum != 0 ]]; then
2133		log_must zpool status -v
2134	        log_fail "Unexpected CKSUM errors found on $pool ($cksum)"
2135	fi
2136}
2137
2138#
2139# Given a pool, and this function list all disks in the pool
2140#
2141function get_disklist # pool
2142{
2143	typeset disklist=""
2144
2145	disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \
2146	    grep -v "\-\-\-\-\-" | \
2147	    egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$")
2148
2149	echo $disklist
2150}
2151
2152#
2153# Given a pool, and this function list all disks in the pool with their full
2154# path (like "/dev/sda" instead of "sda").
2155#
2156function get_disklist_fullpath # pool
2157{
2158	args="-P $1"
2159	get_disklist $args
2160}
2161
2162
2163
2164# /**
2165#  This function kills a given list of processes after a time period. We use
2166#  this in the stress tests instead of STF_TIMEOUT so that we can have processes
2167#  run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT
2168#  would be listed as FAIL, which we don't want : we're happy with stress tests
2169#  running for a certain amount of time, then finishing.
2170#
2171# @param $1 the time in seconds after which we should terminate these processes
2172# @param $2..$n the processes we wish to terminate.
2173# */
2174function stress_timeout
2175{
2176	typeset -i TIMEOUT=$1
2177	shift
2178	typeset cpids="$@"
2179
2180	log_note "Waiting for child processes($cpids). " \
2181		"It could last dozens of minutes, please be patient ..."
2182	log_must sleep $TIMEOUT
2183
2184	log_note "Killing child processes after ${TIMEOUT} stress timeout."
2185	typeset pid
2186	for pid in $cpids; do
2187		ps -p $pid > /dev/null 2>&1
2188		if (($? == 0)); then
2189			log_must kill -USR1 $pid
2190		fi
2191	done
2192}
2193
2194#
2195# Verify a given hotspare disk is inuse or avail
2196#
2197# Return 0 is pool/disk matches expected state, 1 otherwise
2198#
2199function check_hotspare_state # pool disk state{inuse,avail}
2200{
2201	typeset pool=$1
2202	typeset disk=${2#$DEV_DSKDIR/}
2203	typeset state=$3
2204
2205	cur_state=$(get_device_state $pool $disk "spares")
2206
2207	if [[ $state != ${cur_state} ]]; then
2208		return 1
2209	fi
2210	return 0
2211}
2212
2213#
2214# Wait until a hotspare transitions to a given state or times out.
2215#
2216# Return 0 when  pool/disk matches expected state, 1 on timeout.
2217#
2218function wait_hotspare_state # pool disk state timeout
2219{
2220	typeset pool=$1
2221	typeset disk=${2#*$DEV_DSKDIR/}
2222	typeset state=$3
2223	typeset timeout=${4:-60}
2224	typeset -i i=0
2225
2226	while [[ $i -lt $timeout ]]; do
2227		if check_hotspare_state $pool $disk $state; then
2228			return 0
2229		fi
2230
2231		i=$((i+1))
2232		sleep 1
2233	done
2234
2235	return 1
2236}
2237
2238#
2239# Verify a given slog disk is inuse or avail
2240#
2241# Return 0 is pool/disk matches expected state, 1 otherwise
2242#
2243function check_slog_state # pool disk state{online,offline,unavail}
2244{
2245	typeset pool=$1
2246	typeset disk=${2#$DEV_DSKDIR/}
2247	typeset state=$3
2248
2249	cur_state=$(get_device_state $pool $disk "logs")
2250
2251	if [[ $state != ${cur_state} ]]; then
2252		return 1
2253	fi
2254	return 0
2255}
2256
2257#
2258# Verify a given vdev disk is inuse or avail
2259#
2260# Return 0 is pool/disk matches expected state, 1 otherwise
2261#
2262function check_vdev_state # pool disk state{online,offline,unavail}
2263{
2264	typeset pool=$1
2265	typeset disk=${2#*$DEV_DSKDIR/}
2266	typeset state=$3
2267
2268	cur_state=$(get_device_state $pool $disk)
2269
2270	if [[ $state != ${cur_state} ]]; then
2271		return 1
2272	fi
2273	return 0
2274}
2275
2276#
2277# Wait until a vdev transitions to a given state or times out.
2278#
2279# Return 0 when  pool/disk matches expected state, 1 on timeout.
2280#
2281function wait_vdev_state # pool disk state timeout
2282{
2283	typeset pool=$1
2284	typeset disk=${2#*$DEV_DSKDIR/}
2285	typeset state=$3
2286	typeset timeout=${4:-60}
2287	typeset -i i=0
2288
2289	while [[ $i -lt $timeout ]]; do
2290		if check_vdev_state $pool $disk $state; then
2291			return 0
2292		fi
2293
2294		i=$((i+1))
2295		sleep 1
2296	done
2297
2298	return 1
2299}
2300
2301#
2302# Check the output of 'zpool status -v <pool>',
2303# and to see if the content of <token> contain the <keyword> specified.
2304#
2305# Return 0 is contain, 1 otherwise
2306#
2307function check_pool_status # pool token keyword <verbose>
2308{
2309	typeset pool=$1
2310	typeset token=$2
2311	typeset keyword=$3
2312	typeset verbose=${4:-false}
2313
2314	scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" '
2315		($1==token) {print $0}')
2316	if [[ $verbose == true ]]; then
2317		log_note $scan
2318	fi
2319	echo $scan | egrep -i "$keyword" > /dev/null 2>&1
2320
2321	return $?
2322}
2323
2324#
2325# The following functions are instance of check_pool_status()
2326#	is_pool_resilvering - to check if the pool resilver is in progress
2327#	is_pool_resilvered - to check if the pool resilver is completed
2328#	is_pool_scrubbing - to check if the pool scrub is in progress
2329#	is_pool_scrubbed - to check if the pool scrub is completed
2330#	is_pool_scrub_stopped - to check if the pool scrub is stopped
2331#	is_pool_scrub_paused - to check if the pool scrub has paused
2332#	is_pool_removing - to check if the pool removing is a vdev
2333#	is_pool_removed - to check if the pool remove is completed
2334#	is_pool_discarding - to check if the pool checkpoint is being discarded
2335#
2336function is_pool_resilvering #pool <verbose>
2337{
2338	check_pool_status "$1" "scan" \
2339	    "resilver[ ()0-9A-Za-z:_-]* in progress since" $2
2340	return $?
2341}
2342
2343function is_pool_resilvered #pool <verbose>
2344{
2345	check_pool_status "$1" "scan" "resilvered " $2
2346	return $?
2347}
2348
2349function is_pool_scrubbing #pool <verbose>
2350{
2351	check_pool_status "$1" "scan" "scrub in progress since " $2
2352	return $?
2353}
2354
2355function is_pool_scrubbed #pool <verbose>
2356{
2357	check_pool_status "$1" "scan" "scrub repaired" $2
2358	return $?
2359}
2360
2361function is_pool_scrub_stopped #pool <verbose>
2362{
2363	check_pool_status "$1" "scan" "scrub canceled" $2
2364	return $?
2365}
2366
2367function is_pool_scrub_paused #pool <verbose>
2368{
2369	check_pool_status "$1" "scan" "scrub paused since " $2
2370	return $?
2371}
2372
2373function is_pool_removing #pool
2374{
2375	check_pool_status "$1" "remove" "in progress since "
2376	return $?
2377}
2378
2379function is_pool_removed #pool
2380{
2381	check_pool_status "$1" "remove" "completed on"
2382	return $?
2383}
2384
2385function is_pool_discarding #pool
2386{
2387	check_pool_status "$1" "checkpoint" "discarding"
2388	return $?
2389}
2390
2391function wait_for_degraded
2392{
2393	typeset pool=$1
2394	typeset timeout=${2:-30}
2395	typeset t0=$SECONDS
2396
2397	while :; do
2398		[[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break
2399		log_note "$pool is not yet degraded."
2400		sleep 1
2401		if ((SECONDS - t0 > $timeout)); then
2402			log_note "$pool not degraded after $timeout seconds."
2403			return 1
2404		fi
2405	done
2406
2407	return 0
2408}
2409
2410#
2411# Use create_pool()/destroy_pool() to clean up the information in
2412# in the given disk to avoid slice overlapping.
2413#
2414function cleanup_devices #vdevs
2415{
2416	typeset pool="foopool$$"
2417
2418	for vdev in $@; do
2419		zero_partitions $vdev
2420	done
2421
2422	poolexists $pool && destroy_pool $pool
2423	create_pool $pool $@
2424	destroy_pool $pool
2425
2426	return 0
2427}
2428
2429#/**
2430# A function to find and locate free disks on a system or from given
2431# disks as the parameter. It works by locating disks that are in use
2432# as swap devices and dump devices, and also disks listed in /etc/vfstab
2433#
2434# $@ given disks to find which are free, default is all disks in
2435# the test system
2436#
2437# @return a string containing the list of available disks
2438#*/
2439function find_disks
2440{
2441	# Trust provided list, no attempt is made to locate unused devices.
2442	if is_linux || is_freebsd; then
2443		echo "$@"
2444		return
2445	fi
2446
2447
2448	sfi=/tmp/swaplist.$$
2449	dmpi=/tmp/dumpdev.$$
2450	max_finddisksnum=${MAX_FINDDISKSNUM:-6}
2451
2452	swap -l > $sfi
2453	dumpadm > $dmpi 2>/dev/null
2454
2455# write an awk script that can process the output of format
2456# to produce a list of disks we know about. Note that we have
2457# to escape "$2" so that the shell doesn't interpret it while
2458# we're creating the awk script.
2459# -------------------
2460	cat > /tmp/find_disks.awk <<EOF
2461#!/bin/nawk -f
2462	BEGIN { FS="."; }
2463
2464	/^Specify disk/{
2465		searchdisks=0;
2466	}
2467
2468	{
2469		if (searchdisks && \$2 !~ "^$"){
2470			split(\$2,arr," ");
2471			print arr[1];
2472		}
2473	}
2474
2475	/^AVAILABLE DISK SELECTIONS:/{
2476		searchdisks=1;
2477	}
2478EOF
2479#---------------------
2480
2481	chmod 755 /tmp/find_disks.awk
2482	disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)}
2483	rm /tmp/find_disks.awk
2484
2485	unused=""
2486	for disk in $disks; do
2487	# Check for mounted
2488		grep "${disk}[sp]" /etc/mnttab >/dev/null
2489		(($? == 0)) && continue
2490	# Check for swap
2491		grep "${disk}[sp]" $sfi >/dev/null
2492		(($? == 0)) && continue
2493	# check for dump device
2494		grep "${disk}[sp]" $dmpi >/dev/null
2495		(($? == 0)) && continue
2496	# check to see if this disk hasn't been explicitly excluded
2497	# by a user-set environment variable
2498		echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null
2499		(($? == 0)) && continue
2500		unused_candidates="$unused_candidates $disk"
2501	done
2502	rm $sfi
2503	rm $dmpi
2504
2505# now just check to see if those disks do actually exist
2506# by looking for a device pointing to the first slice in
2507# each case. limit the number to max_finddisksnum
2508	count=0
2509	for disk in $unused_candidates; do
2510		if is_disk_device $DEV_DSKDIR/${disk}s0 && \
2511		    [ $count -lt $max_finddisksnum ]; then
2512			unused="$unused $disk"
2513			# do not impose limit if $@ is provided
2514			[[ -z $@ ]] && ((count = count + 1))
2515		fi
2516	done
2517
2518# finally, return our disk list
2519	echo $unused
2520}
2521
2522function add_user_freebsd #<group_name> <user_name> <basedir>
2523{
2524	typeset group=$1
2525	typeset user=$2
2526	typeset basedir=$3
2527
2528	# Check to see if the user exists.
2529	if id $user > /dev/null 2>&1; then
2530		return 0
2531	fi
2532
2533	# Assign 1000 as the base uid
2534	typeset -i uid=1000
2535	while true; do
2536		typeset -i ret
2537		pw useradd -u $uid -g $group -d $basedir/$user -m -n $user
2538		ret=$?
2539		case $ret in
2540			0) break ;;
2541			# The uid is not unique
2542			65) ((uid += 1)) ;;
2543			*) return 1 ;;
2544		esac
2545		if [[ $uid == 65000 ]]; then
2546			log_fail "No user id available under 65000 for $user"
2547		fi
2548	done
2549
2550	# Silence MOTD
2551	touch $basedir/$user/.hushlogin
2552
2553	return 0
2554}
2555
2556#
2557# Delete the specified user.
2558#
2559# $1 login name
2560#
2561function del_user_freebsd #<logname>
2562{
2563	typeset user=$1
2564
2565	if id $user > /dev/null 2>&1; then
2566		log_must pw userdel $user
2567	fi
2568
2569	return 0
2570}
2571
2572#
2573# Select valid gid and create specified group.
2574#
2575# $1 group name
2576#
2577function add_group_freebsd #<group_name>
2578{
2579	typeset group=$1
2580
2581	# See if the group already exists.
2582	if pw groupshow $group >/dev/null 2>&1; then
2583		return 0
2584	fi
2585
2586	# Assign 1000 as the base gid
2587	typeset -i gid=1000
2588	while true; do
2589		pw groupadd -g $gid -n $group > /dev/null 2>&1
2590		typeset -i ret=$?
2591		case $ret in
2592			0) return 0 ;;
2593			# The gid is not  unique
2594			65) ((gid += 1)) ;;
2595			*) return 1 ;;
2596		esac
2597		if [[ $gid == 65000 ]]; then
2598			log_fail "No user id available under 65000 for $group"
2599		fi
2600	done
2601}
2602
2603#
2604# Delete the specified group.
2605#
2606# $1 group name
2607#
2608function del_group_freebsd #<group_name>
2609{
2610	typeset group=$1
2611
2612	pw groupdel -n $group > /dev/null 2>&1
2613	typeset -i ret=$?
2614	case $ret in
2615		# Group does not exist, or was deleted successfully.
2616		0|6|65) return 0 ;;
2617		# Name already exists as a group name
2618		9) log_must pw groupdel $group ;;
2619		*) return 1 ;;
2620	esac
2621
2622	return 0
2623}
2624
2625function add_user_illumos #<group_name> <user_name> <basedir>
2626{
2627	typeset group=$1
2628	typeset user=$2
2629	typeset basedir=$3
2630
2631	log_must useradd -g $group -d $basedir/$user -m $user
2632
2633	return 0
2634}
2635
2636function del_user_illumos #<user_name>
2637{
2638	typeset user=$1
2639
2640	if id $user > /dev/null 2>&1; then
2641		log_must_retry "currently used" 6 userdel $user
2642	fi
2643
2644	return 0
2645}
2646
2647function add_group_illumos #<group_name>
2648{
2649	typeset group=$1
2650
2651	typeset -i gid=100
2652	while true; do
2653		groupadd -g $gid $group > /dev/null 2>&1
2654		typeset -i ret=$?
2655		case $ret in
2656			0) return 0 ;;
2657			# The gid is not  unique
2658			4) ((gid += 1)) ;;
2659			*) return 1 ;;
2660		esac
2661	done
2662}
2663
2664function del_group_illumos #<group_name>
2665{
2666	typeset group=$1
2667
2668	groupmod -n $grp $grp > /dev/null 2>&1
2669	typeset -i ret=$?
2670	case $ret in
2671		# Group does not exist.
2672		6) return 0 ;;
2673		# Name already exists as a group name
2674		9) log_must groupdel $grp ;;
2675		*) return 1 ;;
2676	esac
2677}
2678
2679function add_user_linux #<group_name> <user_name> <basedir>
2680{
2681	typeset group=$1
2682	typeset user=$2
2683	typeset basedir=$3
2684
2685	log_must useradd -g $group -d $basedir/$user -m $user
2686
2687	# Add new users to the same group and the command line utils.
2688	# This allows them to be run out of the original users home
2689	# directory as long as it permissioned to be group readable.
2690	cmd_group=$(stat --format="%G" $(which zfs))
2691	log_must usermod -a -G $cmd_group $user
2692
2693	return 0
2694}
2695
2696function del_user_linux #<user_name>
2697{
2698	typeset user=$1
2699
2700	if id $user > /dev/null 2>&1; then
2701		log_must_retry "currently used" 6 userdel $user
2702	fi
2703
2704	return 0
2705}
2706
2707function add_group_linux #<group_name>
2708{
2709	typeset group=$1
2710
2711	# Assign 100 as the base gid, a larger value is selected for
2712	# Linux because for many distributions 1000 and under are reserved.
2713	while true; do
2714		groupadd $group > /dev/null 2>&1
2715		typeset -i ret=$?
2716		case $ret in
2717			0) return 0 ;;
2718			*) return 1 ;;
2719		esac
2720	done
2721}
2722
2723function del_group_linux #<group_name>
2724{
2725	typeset group=$1
2726
2727	getent group $group > /dev/null 2>&1
2728	typeset -i ret=$?
2729	case $ret in
2730		# Group does not exist.
2731		2) return 0 ;;
2732		# Name already exists as a group name
2733		0) log_must groupdel $group ;;
2734		*) return 1 ;;
2735	esac
2736
2737	return 0
2738}
2739
2740#
2741# Add specified user to specified group
2742#
2743# $1 group name
2744# $2 user name
2745# $3 base of the homedir (optional)
2746#
2747function add_user #<group_name> <user_name> <basedir>
2748{
2749	typeset group=$1
2750	typeset user=$2
2751	typeset basedir=${3:-"/var/tmp"}
2752
2753	if ((${#group} == 0 || ${#user} == 0)); then
2754		log_fail "group name or user name are not defined."
2755	fi
2756
2757	case $(uname) in
2758	FreeBSD)
2759		add_user_freebsd "$group" "$user" "$basedir"
2760		;;
2761	Linux)
2762		add_user_linux "$group" "$user" "$basedir"
2763		;;
2764	*)
2765		add_user_illumos "$group" "$user" "$basedir"
2766		;;
2767	esac
2768
2769	return 0
2770}
2771
2772#
2773# Delete the specified user.
2774#
2775# $1 login name
2776# $2 base of the homedir (optional)
2777#
2778function del_user #<logname> <basedir>
2779{
2780	typeset user=$1
2781	typeset basedir=${2:-"/var/tmp"}
2782
2783	if ((${#user} == 0)); then
2784		log_fail "login name is necessary."
2785	fi
2786
2787	case $(uname) in
2788	FreeBSD)
2789		del_user_freebsd "$user"
2790		;;
2791	Linux)
2792		del_user_linux "$user"
2793		;;
2794	*)
2795		del_user_illumos "$user"
2796		;;
2797	esac
2798
2799	[[ -d $basedir/$user ]] && rm -fr $basedir/$user
2800
2801	return 0
2802}
2803
2804#
2805# Select valid gid and create specified group.
2806#
2807# $1 group name
2808#
2809function add_group #<group_name>
2810{
2811	typeset group=$1
2812
2813	if ((${#group} == 0)); then
2814		log_fail "group name is necessary."
2815	fi
2816
2817	case $(uname) in
2818	FreeBSD)
2819		add_group_freebsd "$group"
2820		;;
2821	Linux)
2822		add_group_linux "$group"
2823		;;
2824	*)
2825		add_group_illumos "$group"
2826		;;
2827	esac
2828
2829	return 0
2830}
2831
2832#
2833# Delete the specified group.
2834#
2835# $1 group name
2836#
2837function del_group #<group_name>
2838{
2839	typeset group=$1
2840
2841	if ((${#group} == 0)); then
2842		log_fail "group name is necessary."
2843	fi
2844
2845	case $(uname) in
2846	FreeBSD)
2847		del_group_freebsd "$group"
2848		;;
2849	Linux)
2850		del_group_linux "$group"
2851		;;
2852	*)
2853		del_group_illumos "$group"
2854		;;
2855	esac
2856
2857	return 0
2858}
2859
2860#
2861# This function will return true if it's safe to destroy the pool passed
2862# as argument 1. It checks for pools based on zvols and files, and also
2863# files contained in a pool that may have a different mountpoint.
2864#
2865function safe_to_destroy_pool { # $1 the pool name
2866
2867	typeset pool=""
2868	typeset DONT_DESTROY=""
2869
2870	# We check that by deleting the $1 pool, we're not
2871	# going to pull the rug out from other pools. Do this
2872	# by looking at all other pools, ensuring that they
2873	# aren't built from files or zvols contained in this pool.
2874
2875	for pool in $(zpool list -H -o name)
2876	do
2877		ALTMOUNTPOOL=""
2878
2879		# this is a list of the top-level directories in each of the
2880		# files that make up the path to the files the pool is based on
2881		FILEPOOL=$(zpool status -v $pool | grep /$1/ | \
2882			awk '{print $1}')
2883
2884		# this is a list of the zvols that make up the pool
2885		ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \
2886		    | awk '{print $1}')
2887
2888		# also want to determine if it's a file-based pool using an
2889		# alternate mountpoint...
2890		POOL_FILE_DIRS=$(zpool status -v $pool | \
2891					grep / | awk '{print $1}' | \
2892					awk -F/ '{print $2}' | grep -v "dev")
2893
2894		for pooldir in $POOL_FILE_DIRS
2895		do
2896			OUTPUT=$(zfs list -H -r -o mountpoint $1 | \
2897					grep "${pooldir}$" | awk '{print $1}')
2898
2899			ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}"
2900		done
2901
2902
2903		if [ ! -z "$ZVOLPOOL" ]
2904		then
2905			DONT_DESTROY="true"
2906			log_note "Pool $pool is built from $ZVOLPOOL on $1"
2907		fi
2908
2909		if [ ! -z "$FILEPOOL" ]
2910		then
2911			DONT_DESTROY="true"
2912			log_note "Pool $pool is built from $FILEPOOL on $1"
2913		fi
2914
2915		if [ ! -z "$ALTMOUNTPOOL" ]
2916		then
2917			DONT_DESTROY="true"
2918			log_note "Pool $pool is built from $ALTMOUNTPOOL on $1"
2919		fi
2920	done
2921
2922	if [ -z "${DONT_DESTROY}" ]
2923	then
2924		return 0
2925	else
2926		log_note "Warning: it is not safe to destroy $1!"
2927		return 1
2928	fi
2929}
2930
2931#
2932# Verify zfs operation with -p option work as expected
2933# $1 operation, value could be create, clone or rename
2934# $2 dataset type, value could be fs or vol
2935# $3 dataset name
2936# $4 new dataset name
2937#
2938function verify_opt_p_ops
2939{
2940	typeset ops=$1
2941	typeset datatype=$2
2942	typeset dataset=$3
2943	typeset newdataset=$4
2944
2945	if [[ $datatype != "fs" && $datatype != "vol" ]]; then
2946		log_fail "$datatype is not supported."
2947	fi
2948
2949	# check parameters accordingly
2950	case $ops in
2951		create)
2952			newdataset=$dataset
2953			dataset=""
2954			if [[ $datatype == "vol" ]]; then
2955				ops="create -V $VOLSIZE"
2956			fi
2957			;;
2958		clone)
2959			if [[ -z $newdataset ]]; then
2960				log_fail "newdataset should not be empty" \
2961					"when ops is $ops."
2962			fi
2963			log_must datasetexists $dataset
2964			log_must snapexists $dataset
2965			;;
2966		rename)
2967			if [[ -z $newdataset ]]; then
2968				log_fail "newdataset should not be empty" \
2969					"when ops is $ops."
2970			fi
2971			log_must datasetexists $dataset
2972			;;
2973		*)
2974			log_fail "$ops is not supported."
2975			;;
2976	esac
2977
2978	# make sure the upper level filesystem does not exist
2979	destroy_dataset "${newdataset%/*}" "-rRf"
2980
2981	# without -p option, operation will fail
2982	log_mustnot zfs $ops $dataset $newdataset
2983	log_mustnot datasetexists $newdataset ${newdataset%/*}
2984
2985	# with -p option, operation should succeed
2986	log_must zfs $ops -p $dataset $newdataset
2987	block_device_wait
2988
2989	if ! datasetexists $newdataset ; then
2990		log_fail "-p option does not work for $ops"
2991	fi
2992
2993	# when $ops is create or clone, redo the operation still return zero
2994	if [[ $ops != "rename" ]]; then
2995		log_must zfs $ops -p $dataset $newdataset
2996	fi
2997
2998	return 0
2999}
3000
3001#
3002# Get configuration of pool
3003# $1 pool name
3004# $2 config name
3005#
3006function get_config
3007{
3008	typeset pool=$1
3009	typeset config=$2
3010	typeset alt_root
3011
3012	if ! poolexists "$pool" ; then
3013		return 1
3014	fi
3015	alt_root=$(zpool list -H $pool | awk '{print $NF}')
3016	if [[ $alt_root == "-" ]]; then
3017		value=$(zdb -C $pool | grep "$config:" | awk -F: \
3018		    '{print $2}')
3019	else
3020		value=$(zdb -e $pool | grep "$config:" | awk -F: \
3021		    '{print $2}')
3022	fi
3023	if [[ -n $value ]] ; then
3024		value=${value#'}
3025		value=${value%'}
3026	fi
3027	echo $value
3028
3029	return 0
3030}
3031
3032#
3033# Privated function. Random select one of items from arguments.
3034#
3035# $1 count
3036# $2-n string
3037#
3038function _random_get
3039{
3040	typeset cnt=$1
3041	shift
3042
3043	typeset str="$@"
3044	typeset -i ind
3045	((ind = RANDOM % cnt + 1))
3046
3047	typeset ret=$(echo "$str" | cut -f $ind -d ' ')
3048	echo $ret
3049}
3050
3051#
3052# Random select one of item from arguments which include NONE string
3053#
3054function random_get_with_non
3055{
3056	typeset -i cnt=$#
3057	((cnt =+ 1))
3058
3059	_random_get "$cnt" "$@"
3060}
3061
3062#
3063# Random select one of item from arguments which doesn't include NONE string
3064#
3065function random_get
3066{
3067	_random_get "$#" "$@"
3068}
3069
3070#
3071# Detect if the current system support slog
3072#
3073function verify_slog_support
3074{
3075	typeset dir=$TEST_BASE_DIR/disk.$$
3076	typeset pool=foo.$$
3077	typeset vdev=$dir/a
3078	typeset sdev=$dir/b
3079
3080	mkdir -p $dir
3081	mkfile $MINVDEVSIZE $vdev $sdev
3082
3083	typeset -i ret=0
3084	if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then
3085		ret=1
3086	fi
3087	rm -r $dir
3088
3089	return $ret
3090}
3091
3092#
3093# The function will generate a dataset name with specific length
3094# $1, the length of the name
3095# $2, the base string to construct the name
3096#
3097function gen_dataset_name
3098{
3099	typeset -i len=$1
3100	typeset basestr="$2"
3101	typeset -i baselen=${#basestr}
3102	typeset -i iter=0
3103	typeset l_name=""
3104
3105	if ((len % baselen == 0)); then
3106		((iter = len / baselen))
3107	else
3108		((iter = len / baselen + 1))
3109	fi
3110	while ((iter > 0)); do
3111		l_name="${l_name}$basestr"
3112
3113		((iter -= 1))
3114	done
3115
3116	echo $l_name
3117}
3118
3119#
3120# Get cksum tuple of dataset
3121# $1 dataset name
3122#
3123# sample zdb output:
3124# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp
3125# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4
3126# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P
3127# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744
3128function datasetcksum
3129{
3130	typeset cksum
3131	sync
3132	cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \
3133		| awk -F= '{print $7}')
3134	echo $cksum
3135}
3136
3137#
3138# Get cksum of file
3139# #1 file path
3140#
3141function checksum
3142{
3143	typeset cksum
3144	cksum=$(cksum $1 | awk '{print $1}')
3145	echo $cksum
3146}
3147
3148#
3149# Get the given disk/slice state from the specific field of the pool
3150#
3151function get_device_state #pool disk field("", "spares","logs")
3152{
3153	typeset pool=$1
3154	typeset disk=${2#$DEV_DSKDIR/}
3155	typeset field=${3:-$pool}
3156
3157	state=$(zpool status -v "$pool" 2>/dev/null | \
3158		nawk -v device=$disk -v pool=$pool -v field=$field \
3159		'BEGIN {startconfig=0; startfield=0; }
3160		/config:/ {startconfig=1}
3161		(startconfig==1) && ($1==field) {startfield=1; next;}
3162		(startfield==1) && ($1==device) {print $2; exit;}
3163		(startfield==1) &&
3164		($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}')
3165	echo $state
3166}
3167
3168
3169#
3170# print the given directory filesystem type
3171#
3172# $1 directory name
3173#
3174function get_fstype
3175{
3176	typeset dir=$1
3177
3178	if [[ -z $dir ]]; then
3179		log_fail "Usage: get_fstype <directory>"
3180	fi
3181
3182	#
3183	#  $ df -n /
3184	#  /		  : ufs
3185	#
3186	df -n $dir | awk '{print $3}'
3187}
3188
3189#
3190# Given a disk, label it to VTOC regardless what label was on the disk
3191# $1 disk
3192#
3193function labelvtoc
3194{
3195	typeset disk=$1
3196	if [[ -z $disk ]]; then
3197		log_fail "The disk name is unspecified."
3198	fi
3199	typeset label_file=/var/tmp/labelvtoc.$$
3200	typeset arch=$(uname -p)
3201
3202	if is_linux || is_freebsd; then
3203		log_note "Currently unsupported by the test framework"
3204		return 1
3205	fi
3206
3207	if [[ $arch == "i386" ]]; then
3208		echo "label" > $label_file
3209		echo "0" >> $label_file
3210		echo "" >> $label_file
3211		echo "q" >> $label_file
3212		echo "q" >> $label_file
3213
3214		fdisk -B $disk >/dev/null 2>&1
3215		# wait a while for fdisk finishes
3216		sleep 60
3217	elif [[ $arch == "sparc" ]]; then
3218		echo "label" > $label_file
3219		echo "0" >> $label_file
3220		echo "" >> $label_file
3221		echo "" >> $label_file
3222		echo "" >> $label_file
3223		echo "q" >> $label_file
3224	else
3225		log_fail "unknown arch type"
3226	fi
3227
3228	format -e -s -d $disk -f $label_file
3229	typeset -i ret_val=$?
3230	rm -f $label_file
3231	#
3232	# wait the format to finish
3233	#
3234	sleep 60
3235	if ((ret_val != 0)); then
3236		log_fail "unable to label $disk as VTOC."
3237	fi
3238
3239	return 0
3240}
3241
3242#
3243# check if the system was installed as zfsroot or not
3244# return: 0 if zfsroot, non-zero if not
3245#
3246function is_zfsroot
3247{
3248	df -n / | grep zfs > /dev/null 2>&1
3249	return $?
3250}
3251
3252#
3253# get the root filesystem name if it's zfsroot system.
3254#
3255# return: root filesystem name
3256function get_rootfs
3257{
3258	typeset rootfs=""
3259
3260	if is_freebsd; then
3261		rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3262	elif ! is_linux; then
3263		rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \
3264			/etc/mnttab)
3265	fi
3266	if [[ -z "$rootfs" ]]; then
3267		log_fail "Can not get rootfs"
3268	fi
3269	zfs list $rootfs > /dev/null 2>&1
3270	if (($? == 0)); then
3271		echo $rootfs
3272	else
3273		log_fail "This is not a zfsroot system."
3274	fi
3275}
3276
3277#
3278# get the rootfs's pool name
3279# return:
3280#       rootpool name
3281#
3282function get_rootpool
3283{
3284	typeset rootfs=""
3285	typeset rootpool=""
3286
3287	if is_freebsd; then
3288		rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3289	elif ! is_linux; then
3290		rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \
3291			 /etc/mnttab)
3292	fi
3293	if [[ -z "$rootfs" ]]; then
3294		log_fail "Can not get rootpool"
3295	fi
3296	zfs list $rootfs > /dev/null 2>&1
3297	if (($? == 0)); then
3298		echo ${rootfs%%/*}
3299	else
3300		log_fail "This is not a zfsroot system."
3301	fi
3302}
3303
3304#
3305# Get the word numbers from a string separated by white space
3306#
3307function get_word_count
3308{
3309	echo $1 | wc -w
3310}
3311
3312#
3313# To verify if the require numbers of disks is given
3314#
3315function verify_disk_count
3316{
3317	typeset -i min=${2:-1}
3318
3319	typeset -i count=$(get_word_count "$1")
3320
3321	if ((count < min)); then
3322		log_untested "A minimum of $min disks is required to run." \
3323			" You specified $count disk(s)"
3324	fi
3325}
3326
3327function ds_is_volume
3328{
3329	typeset type=$(get_prop type $1)
3330	[[ $type = "volume" ]] && return 0
3331	return 1
3332}
3333
3334function ds_is_filesystem
3335{
3336	typeset type=$(get_prop type $1)
3337	[[ $type = "filesystem" ]] && return 0
3338	return 1
3339}
3340
3341function ds_is_snapshot
3342{
3343	typeset type=$(get_prop type $1)
3344	[[ $type = "snapshot" ]] && return 0
3345	return 1
3346}
3347
3348#
3349# Check if Trusted Extensions are installed and enabled
3350#
3351function is_te_enabled
3352{
3353	svcs -H -o state labeld 2>/dev/null | grep "enabled"
3354	if (($? != 0)); then
3355		return 1
3356	else
3357		return 0
3358	fi
3359}
3360
3361# Utility function to determine if a system has multiple cpus.
3362function is_mp
3363{
3364	if is_linux; then
3365		(($(nproc) > 1))
3366	elif is_freebsd; then
3367		sysctl -n kern.smp.cpus
3368	else
3369		(($(psrinfo | wc -l) > 1))
3370	fi
3371
3372	return $?
3373}
3374
3375function get_cpu_freq
3376{
3377	if is_linux; then
3378		lscpu | awk '/CPU MHz/ { print $3 }'
3379	elif is_freebsd; then
3380		sysctl -n hw.clockrate
3381	else
3382		psrinfo -v 0 | awk '/processor operates at/ {print $6}'
3383	fi
3384}
3385
3386# Run the given command as the user provided.
3387function user_run
3388{
3389	typeset user=$1
3390	shift
3391
3392	log_note "user: $user"
3393	log_note "cmd: $*"
3394
3395	typeset out=$TEST_BASE_DIR/out
3396	typeset err=$TEST_BASE_DIR/err
3397
3398	sudo -Eu $user env PATH="$PATH" ksh <<<"$*" >$out 2>$err
3399	typeset res=$?
3400	log_note "out: $(<$out)"
3401	log_note "err: $(<$err)"
3402	return $res
3403}
3404
3405#
3406# Check if the pool contains the specified vdevs
3407#
3408# $1 pool
3409# $2..n <vdev> ...
3410#
3411# Return 0 if the vdevs are contained in the pool, 1 if any of the specified
3412# vdevs is not in the pool, and 2 if pool name is missing.
3413#
3414function vdevs_in_pool
3415{
3416	typeset pool=$1
3417	typeset vdev
3418
3419	if [[ -z $pool ]]; then
3420		log_note "Missing pool name."
3421		return 2
3422	fi
3423
3424	shift
3425
3426	# We could use 'zpool list' to only get the vdevs of the pool but we
3427	# can't reference a mirror/raidz vdev using its ID (i.e mirror-0),
3428	# therefore we use the 'zpool status' output.
3429	typeset tmpfile=$(mktemp)
3430	zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile
3431	for vdev in $@; do
3432		grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1
3433		[[ $? -ne 0 ]] && return 1
3434	done
3435
3436	rm -f $tmpfile
3437
3438	return 0;
3439}
3440
3441function get_max
3442{
3443	typeset -l i max=$1
3444	shift
3445
3446	for i in "$@"; do
3447		max=$((max > i ? max : i))
3448	done
3449
3450	echo $max
3451}
3452
3453function get_min
3454{
3455	typeset -l i min=$1
3456	shift
3457
3458	for i in "$@"; do
3459		min=$((min < i ? min : i))
3460	done
3461
3462	echo $min
3463}
3464
3465# Write data that can be compressed into a directory
3466function write_compressible
3467{
3468	typeset dir=$1
3469	typeset megs=$2
3470	typeset nfiles=${3:-1}
3471	typeset bs=${4:-1024k}
3472	typeset fname=${5:-file}
3473
3474	[[ -d $dir ]] || log_fail "No directory: $dir"
3475
3476	# Under Linux fio is not currently used since its behavior can
3477	# differ significantly across versions.  This includes missing
3478	# command line options and cases where the --buffer_compress_*
3479	# options fail to behave as expected.
3480	if is_linux; then
3481		typeset file_bytes=$(to_bytes $megs)
3482		typeset bs_bytes=4096
3483		typeset blocks=$(($file_bytes / $bs_bytes))
3484
3485		for (( i = 0; i < $nfiles; i++ )); do
3486			truncate -s $file_bytes $dir/$fname.$i
3487
3488			# Write every third block to get 66% compression.
3489			for (( j = 0; j < $blocks; j += 3 )); do
3490				dd if=/dev/urandom of=$dir/$fname.$i \
3491				    seek=$j bs=$bs_bytes count=1 \
3492				    conv=notrunc >/dev/null 2>&1
3493			done
3494		done
3495	else
3496		log_must eval "fio \
3497		    --name=job \
3498		    --fallocate=0 \
3499		    --minimal \
3500		    --randrepeat=0 \
3501		    --buffer_compress_percentage=66 \
3502		    --buffer_compress_chunk=4096 \
3503		    --directory=$dir \
3504		    --numjobs=$nfiles \
3505		    --nrfiles=$nfiles \
3506		    --rw=write \
3507		    --bs=$bs \
3508		    --filesize=$megs \
3509		    --filename_format='$fname.\$jobnum' >/dev/null"
3510	fi
3511}
3512
3513function get_objnum
3514{
3515	typeset pathname=$1
3516	typeset objnum
3517
3518	[[ -e $pathname ]] || log_fail "No such file or directory: $pathname"
3519	if is_freebsd; then
3520		objnum=$(stat -f "%i" $pathname)
3521	else
3522		objnum=$(stat -c %i $pathname)
3523	fi
3524	echo $objnum
3525}
3526
3527#
3528# Sync data to the pool
3529#
3530# $1 pool name
3531# $2 boolean to force uberblock (and config including zpool cache file) update
3532#
3533function sync_pool #pool <force>
3534{
3535	typeset pool=${1:-$TESTPOOL}
3536	typeset force=${2:-false}
3537
3538	if [[ $force == true ]]; then
3539		log_must zpool sync -f $pool
3540	else
3541		log_must zpool sync $pool
3542	fi
3543
3544	return 0
3545}
3546
3547#
3548# Wait for zpool 'freeing' property drops to zero.
3549#
3550# $1 pool name
3551#
3552function wait_freeing #pool
3553{
3554	typeset pool=${1:-$TESTPOOL}
3555	while true; do
3556		[[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break
3557		log_must sleep 1
3558	done
3559}
3560
3561#
3562# Wait for every device replace operation to complete
3563#
3564# $1 pool name
3565#
3566function wait_replacing #pool
3567{
3568	typeset pool=${1:-$TESTPOOL}
3569	while true; do
3570		[[ "" == "$(zpool status $pool |
3571		    awk '/replacing-[0-9]+/ {print $1}')" ]] && break
3572		log_must sleep 1
3573	done
3574}
3575
3576#
3577# Wait for a pool to be scrubbed
3578#
3579# $1 pool name
3580#
3581function wait_scrubbed
3582{
3583	typeset pool=${1:-$TESTPOOL}
3584	while ! is_pool_scrubbed $pool ; do
3585		sleep 1
3586	done
3587}
3588
3589# Backup the zed.rc in our test directory so that we can edit it for our test.
3590#
3591# Returns: Backup file name.  You will need to pass this to zed_rc_restore().
3592function zed_rc_backup
3593{
3594	zedrc_backup="$(mktemp)"
3595	cp $ZEDLET_DIR/zed.rc $zedrc_backup
3596	echo $zedrc_backup
3597}
3598
3599function zed_rc_restore
3600{
3601	mv $1 $ZEDLET_DIR/zed.rc
3602}
3603
3604#
3605# Setup custom environment for the ZED.
3606#
3607# $@ Optional list of zedlets to run under zed.
3608function zed_setup
3609{
3610	if ! is_linux; then
3611		log_unsupported "No zed on $(uname)"
3612	fi
3613
3614	if [[ ! -d $ZEDLET_DIR ]]; then
3615		log_must mkdir $ZEDLET_DIR
3616	fi
3617
3618	if [[ ! -e $VDEVID_CONF ]]; then
3619		log_must touch $VDEVID_CONF
3620	fi
3621
3622	if [[ -e $VDEVID_CONF_ETC ]]; then
3623		log_fail "Must not have $VDEVID_CONF_ETC file present on system"
3624	fi
3625	EXTRA_ZEDLETS=$@
3626
3627	# Create a symlink for /etc/zfs/vdev_id.conf file.
3628	log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC
3629
3630	# Setup minimal ZED configuration.  Individual test cases should
3631	# add additional ZEDLETs as needed for their specific test.
3632	log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR
3633	log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR
3634
3635	# Scripts must only be user writable.
3636	if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3637		saved_umask=$(umask)
3638		log_must umask 0022
3639		for i in $EXTRA_ZEDLETS ; do
3640			log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR
3641		done
3642		log_must umask $saved_umask
3643	fi
3644
3645	# Customize the zed.rc file to enable the full debug log.
3646	log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc
3647	echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc
3648
3649}
3650
3651#
3652# Cleanup custom ZED environment.
3653#
3654# $@ Optional list of zedlets to remove from our test zed.d directory.
3655function zed_cleanup
3656{
3657	if ! is_linux; then
3658		return
3659	fi
3660	EXTRA_ZEDLETS=$@
3661
3662	log_must rm -f ${ZEDLET_DIR}/zed.rc
3663	log_must rm -f ${ZEDLET_DIR}/zed-functions.sh
3664	log_must rm -f ${ZEDLET_DIR}/all-syslog.sh
3665	log_must rm -f ${ZEDLET_DIR}/all-debug.sh
3666	log_must rm -f ${ZEDLET_DIR}/state
3667
3668	if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3669		for i in $EXTRA_ZEDLETS ; do
3670			log_must rm -f ${ZEDLET_DIR}/$i
3671		done
3672	fi
3673	log_must rm -f $ZED_LOG
3674	log_must rm -f $ZED_DEBUG_LOG
3675	log_must rm -f $VDEVID_CONF_ETC
3676	log_must rm -f $VDEVID_CONF
3677	rmdir $ZEDLET_DIR
3678}
3679
3680#
3681# Check if ZED is currently running, if not start ZED.
3682#
3683function zed_start
3684{
3685	if ! is_linux; then
3686		return
3687	fi
3688
3689	# ZEDLET_DIR=/var/tmp/zed
3690	if [[ ! -d $ZEDLET_DIR ]]; then
3691		log_must mkdir $ZEDLET_DIR
3692	fi
3693
3694	# Verify the ZED is not already running.
3695	pgrep -x zed > /dev/null
3696	if (($? == 0)); then
3697		log_note "ZED already running"
3698	else
3699		log_note "Starting ZED"
3700		# run ZED in the background and redirect foreground logging
3701		# output to $ZED_LOG.
3702		log_must truncate -s 0 $ZED_DEBUG_LOG
3703		log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \
3704		    "-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &"
3705	fi
3706
3707	return 0
3708}
3709
3710#
3711# Kill ZED process
3712#
3713function zed_stop
3714{
3715	if ! is_linux; then
3716		return
3717	fi
3718
3719	log_note "Stopping ZED"
3720	while true; do
3721		zedpids="$(pgrep -x zed)"
3722		[ "$?" -ne 0 ] && break
3723
3724		log_must kill $zedpids
3725		sleep 1
3726	done
3727	return 0
3728}
3729
3730#
3731# Drain all zevents
3732#
3733function zed_events_drain
3734{
3735	while [ $(zpool events -H | wc -l) -ne 0 ]; do
3736		sleep 1
3737		zpool events -c >/dev/null
3738	done
3739}
3740
3741# Set a variable in zed.rc to something, un-commenting it in the process.
3742#
3743# $1 variable
3744# $2 value
3745function zed_rc_set
3746{
3747	var="$1"
3748	val="$2"
3749	# Remove the line
3750	cmd="'/$var/d'"
3751	eval sed -i $cmd $ZEDLET_DIR/zed.rc
3752
3753	# Add it at the end
3754	echo "$var=$val" >> $ZEDLET_DIR/zed.rc
3755}
3756
3757
3758#
3759# Check is provided device is being active used as a swap device.
3760#
3761function is_swap_inuse
3762{
3763	typeset device=$1
3764
3765	if [[ -z $device ]] ; then
3766		log_note "No device specified."
3767		return 1
3768	fi
3769
3770	if is_linux; then
3771		swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1
3772	elif is_freebsd; then
3773		swapctl -l | grep -w $device
3774	else
3775		swap -l | grep -w $device > /dev/null 2>&1
3776	fi
3777
3778	return $?
3779}
3780
3781#
3782# Setup a swap device using the provided device.
3783#
3784function swap_setup
3785{
3786	typeset swapdev=$1
3787
3788	if is_linux; then
3789		log_must eval "mkswap $swapdev > /dev/null 2>&1"
3790		log_must swapon $swapdev
3791	elif is_freebsd; then
3792		log_must swapctl -a $swapdev
3793	else
3794	        log_must swap -a $swapdev
3795	fi
3796
3797	return 0
3798}
3799
3800#
3801# Cleanup a swap device on the provided device.
3802#
3803function swap_cleanup
3804{
3805	typeset swapdev=$1
3806
3807	if is_swap_inuse $swapdev; then
3808		if is_linux; then
3809			log_must swapoff $swapdev
3810		elif is_freebsd; then
3811			log_must swapoff $swapdev
3812		else
3813			log_must swap -d $swapdev
3814		fi
3815	fi
3816
3817	return 0
3818}
3819
3820#
3821# Set a global system tunable (64-bit value)
3822#
3823# $1 tunable name (use a NAME defined in tunables.cfg)
3824# $2 tunable values
3825#
3826function set_tunable64
3827{
3828	set_tunable_impl "$1" "$2" Z
3829}
3830
3831#
3832# Set a global system tunable (32-bit value)
3833#
3834# $1 tunable name (use a NAME defined in tunables.cfg)
3835# $2 tunable values
3836#
3837function set_tunable32
3838{
3839	set_tunable_impl "$1" "$2" W
3840}
3841
3842function set_tunable_impl
3843{
3844	typeset name="$1"
3845	typeset value="$2"
3846	typeset mdb_cmd="$3"
3847	typeset module="${4:-zfs}"
3848
3849	eval "typeset tunable=\$$name"
3850	case "$tunable" in
3851	UNSUPPORTED)
3852		log_unsupported "Tunable '$name' is unsupported on $(uname)"
3853		;;
3854	"")
3855		log_fail "Tunable '$name' must be added to tunables.cfg"
3856		;;
3857	*)
3858		;;
3859	esac
3860
3861	[[ -z "$value" ]] && return 1
3862	[[ -z "$mdb_cmd" ]] && return 1
3863
3864	case "$(uname)" in
3865	Linux)
3866		typeset zfs_tunables="/sys/module/$module/parameters"
3867		[[ -w "$zfs_tunables/$tunable" ]] || return 1
3868		cat >"$zfs_tunables/$tunable" <<<"$value"
3869		return $?
3870		;;
3871	FreeBSD)
3872		sysctl vfs.zfs.$tunable=$value
3873		return "$?"
3874		;;
3875	SunOS)
3876		[[ "$module" -eq "zfs" ]] || return 1
3877		echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw
3878		return $?
3879		;;
3880	esac
3881}
3882
3883#
3884# Get a global system tunable
3885#
3886# $1 tunable name (use a NAME defined in tunables.cfg)
3887#
3888function get_tunable
3889{
3890	get_tunable_impl "$1"
3891}
3892
3893function get_tunable_impl
3894{
3895	typeset name="$1"
3896	typeset module="${2:-zfs}"
3897
3898	eval "typeset tunable=\$$name"
3899	case "$tunable" in
3900	UNSUPPORTED)
3901		log_unsupported "Tunable '$name' is unsupported on $(uname)"
3902		;;
3903	"")
3904		log_fail "Tunable '$name' must be added to tunables.cfg"
3905		;;
3906	*)
3907		;;
3908	esac
3909
3910	case "$(uname)" in
3911	Linux)
3912		typeset zfs_tunables="/sys/module/$module/parameters"
3913		[[ -f "$zfs_tunables/$tunable" ]] || return 1
3914		cat $zfs_tunables/$tunable
3915		return $?
3916		;;
3917	FreeBSD)
3918		sysctl -n vfs.zfs.$tunable
3919		;;
3920	SunOS)
3921		[[ "$module" -eq "zfs" ]] || return 1
3922		;;
3923	esac
3924
3925	return 1
3926}
3927
3928#
3929# Prints the current time in seconds since UNIX Epoch.
3930#
3931function current_epoch
3932{
3933	printf '%(%s)T'
3934}
3935
3936#
3937# Get decimal value of global uint32_t variable using mdb.
3938#
3939function mdb_get_uint32
3940{
3941	typeset variable=$1
3942	typeset value
3943
3944	value=$(mdb -k -e "$variable/X | ::eval .=U")
3945	if [[ $? -ne 0 ]]; then
3946		log_fail "Failed to get value of '$variable' from mdb."
3947		return 1
3948	fi
3949
3950	echo $value
3951	return 0
3952}
3953
3954#
3955# Set global uint32_t variable to a decimal value using mdb.
3956#
3957function mdb_set_uint32
3958{
3959	typeset variable=$1
3960	typeset value=$2
3961
3962	mdb -kw -e "$variable/W 0t$value" > /dev/null
3963	if [[ $? -ne 0 ]]; then
3964		echo "Failed to set '$variable' to '$value' in mdb."
3965		return 1
3966	fi
3967
3968	return 0
3969}
3970
3971#
3972# Set global scalar integer variable to a hex value using mdb.
3973# Note: Target should have CTF data loaded.
3974#
3975function mdb_ctf_set_int
3976{
3977	typeset variable=$1
3978	typeset value=$2
3979
3980	mdb -kw -e "$variable/z $value" > /dev/null
3981	if [[ $? -ne 0 ]]; then
3982		echo "Failed to set '$variable' to '$value' in mdb."
3983		return 1
3984	fi
3985
3986	return 0
3987}
3988
3989#
3990# Compute MD5 digest for given file or stdin if no file given.
3991# Note: file path must not contain spaces
3992#
3993function md5digest
3994{
3995	typeset file=$1
3996
3997	case $(uname) in
3998	FreeBSD)
3999		md5 -q $file
4000		;;
4001	*)
4002		md5sum -b $file | awk '{ print $1 }'
4003		;;
4004	esac
4005}
4006
4007#
4008# Compute SHA256 digest for given file or stdin if no file given.
4009# Note: file path must not contain spaces
4010#
4011function sha256digest
4012{
4013	typeset file=$1
4014
4015	case $(uname) in
4016	FreeBSD)
4017		sha256 -q $file
4018		;;
4019	*)
4020		sha256sum -b $file | awk '{ print $1 }'
4021		;;
4022	esac
4023}
4024
4025function new_fs #<args>
4026{
4027	case $(uname) in
4028	FreeBSD)
4029		newfs "$@"
4030		;;
4031	*)
4032		echo y | newfs -v "$@"
4033		;;
4034	esac
4035}
4036
4037function stat_size #<path>
4038{
4039	typeset path=$1
4040
4041	case $(uname) in
4042	FreeBSD)
4043		stat -f %z "$path"
4044		;;
4045	*)
4046		stat -c %s "$path"
4047		;;
4048	esac
4049}
4050
4051# Run a command as if it was being run in a TTY.
4052#
4053# Usage:
4054#
4055#    faketty command
4056#
4057function faketty
4058{
4059    if is_freebsd; then
4060        script -q /dev/null env "$@"
4061    else
4062        script --return --quiet -c "$*" /dev/null
4063    fi
4064}
4065
4066#
4067# Produce a random permutation of the integers in a given range (inclusive).
4068#
4069function range_shuffle # begin end
4070{
4071	typeset -i begin=$1
4072	typeset -i end=$2
4073
4074	seq ${begin} ${end} | sort -R
4075}
4076
4077#
4078# Cross-platform xattr helpers
4079#
4080
4081function get_xattr # name path
4082{
4083	typeset name=$1
4084	typeset path=$2
4085
4086	case $(uname) in
4087	FreeBSD)
4088		getextattr -qq user "${name}" "${path}"
4089		;;
4090	*)
4091		attr -qg "${name}" "${path}"
4092		;;
4093	esac
4094}
4095
4096function set_xattr # name value path
4097{
4098	typeset name=$1
4099	typeset value=$2
4100	typeset path=$3
4101
4102	case $(uname) in
4103	FreeBSD)
4104		setextattr user "${name}" "${value}" "${path}"
4105		;;
4106	*)
4107		attr -qs "${name}" -V "${value}" "${path}"
4108		;;
4109	esac
4110}
4111
4112function set_xattr_stdin # name value
4113{
4114	typeset name=$1
4115	typeset path=$2
4116
4117	case $(uname) in
4118	FreeBSD)
4119		setextattr -i user "${name}" "${path}"
4120		;;
4121	*)
4122		attr -qs "${name}" "${path}"
4123		;;
4124	esac
4125}
4126
4127function rm_xattr # name path
4128{
4129	typeset name=$1
4130	typeset path=$2
4131
4132	case $(uname) in
4133	FreeBSD)
4134		rmextattr -q user "${name}" "${path}"
4135		;;
4136	*)
4137		attr -qr "${name}" "${path}"
4138		;;
4139	esac
4140}
4141
4142function ls_xattr # path
4143{
4144	typeset path=$1
4145
4146	case $(uname) in
4147	FreeBSD)
4148		lsextattr -qq user "${path}"
4149		;;
4150	*)
4151		attr -ql "${path}"
4152		;;
4153	esac
4154}
4155
4156function kstat # stat flags?
4157{
4158	typeset stat=$1
4159	typeset flags=${2-"-n"}
4160
4161	case $(uname) in
4162	FreeBSD)
4163		sysctl $flags kstat.zfs.misc.$stat
4164		;;
4165	Linux)
4166		typeset zfs_kstat="/proc/spl/kstat/zfs/$stat"
4167		[[ -f "$zfs_kstat" ]] || return 1
4168		cat $zfs_kstat
4169		;;
4170	*)
4171		false
4172		;;
4173	esac
4174}
4175
4176function get_arcstat # stat
4177{
4178	typeset stat=$1
4179
4180	case $(uname) in
4181	FreeBSD)
4182		kstat arcstats.$stat
4183		;;
4184	Linux)
4185		kstat arcstats | awk "/$stat/ { print \$3 }"
4186		;;
4187	*)
4188		false
4189		;;
4190	esac
4191}
4192
4193#
4194# Wait for the specified arcstat to reach non-zero quiescence.
4195# If echo is 1 echo the value after reaching quiescence, otherwise
4196# if echo is 0 print the arcstat we are waiting on.
4197#
4198function arcstat_quiescence # stat echo
4199{
4200	typeset stat=$1
4201	typeset echo=$2
4202	typeset do_once=true
4203
4204	if [[ $echo -eq 0 ]]; then
4205		echo "Waiting for arcstat $1 quiescence."
4206	fi
4207
4208	while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do
4209		typeset stat1=$(get_arcstat $stat)
4210		sleep 2
4211		typeset stat2=$(get_arcstat $stat)
4212		do_once=false
4213	done
4214
4215	if [[ $echo -eq 1 ]]; then
4216		echo $stat2
4217	fi
4218}
4219
4220function arcstat_quiescence_noecho # stat
4221{
4222	typeset stat=$1
4223	arcstat_quiescence $stat 0
4224}
4225
4226function arcstat_quiescence_echo # stat
4227{
4228	typeset stat=$1
4229	arcstat_quiescence $stat 1
4230}
4231
4232#
4233# Given an array of pids, wait until all processes
4234# have completed and check their return status.
4235#
4236function wait_for_children #children
4237{
4238	rv=0
4239	children=("$@")
4240	for child in "${children[@]}"
4241	do
4242		child_exit=0
4243		wait ${child} || child_exit=$?
4244		if [ $child_exit -ne 0 ]; then
4245			echo "child ${child} failed with ${child_exit}"
4246			rv=1
4247		fi
4248	done
4249	return $rv
4250}
4251