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