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	# If current system support slog, add slog device for pool
1900	#
1901	if verify_slog_support ; then
1902		typeset sdevs="$TEST_BASE_DIR/sdev1 $TEST_BASE_DIR/sdev2"
1903		log_must mkfile $MINVDEVSIZE $sdevs
1904		log_must zpool add $pool_name log mirror $sdevs
1905	fi
1906
1907	# this isn't supported just yet.
1908	# Create a filesystem. In order to add this to
1909	# the zone, it must have it's mountpoint set to 'legacy'
1910	# log_must zfs create $pool_name/zfs_filesystem
1911	# log_must zfs set mountpoint=legacy $pool_name/zfs_filesystem
1912
1913	[[ -d $zone_root ]] && \
1914		log_must rm -rf $zone_root/$zone_name
1915	[[ ! -d $zone_root ]] && \
1916		log_must mkdir -p -m 0700 $zone_root/$zone_name
1917
1918	# Create zone configure file and configure the zone
1919	#
1920	typeset zone_conf=/tmp/zone_conf.$$
1921	echo "create" > $zone_conf
1922	echo "set zonepath=$zone_root/$zone_name" >> $zone_conf
1923	echo "set autoboot=true" >> $zone_conf
1924	i=0
1925	while ((i < cntctr)); do
1926		echo "add dataset" >> $zone_conf
1927		echo "set name=$pool_name/$prefix_ctr$i" >> \
1928			$zone_conf
1929		echo "end" >> $zone_conf
1930		((i += 1))
1931	done
1932
1933	# add our zvol to the zone
1934	echo "add device" >> $zone_conf
1935	echo "set match=/dev/zvol/dsk/$pool_name/zone_zvol" >> $zone_conf
1936	echo "end" >> $zone_conf
1937
1938	# add a corresponding zvol rdsk to the zone
1939	echo "add device" >> $zone_conf
1940	echo "set match=$ZVOL_RDEVDIR/$pool_name/zone_zvol" >> $zone_conf
1941	echo "end" >> $zone_conf
1942
1943	# once it's supported, we'll add our filesystem to the zone
1944	# echo "add fs" >> $zone_conf
1945	# echo "set type=zfs" >> $zone_conf
1946	# echo "set special=$pool_name/zfs_filesystem" >> $zone_conf
1947	# echo "set dir=/export/zfs_filesystem" >> $zone_conf
1948	# echo "end" >> $zone_conf
1949
1950	echo "verify" >> $zone_conf
1951	echo "commit" >> $zone_conf
1952	log_must zonecfg -z $zone_name -f $zone_conf
1953	log_must rm -f $zone_conf
1954
1955	# Install the zone
1956	zoneadm -z $zone_name install
1957	if (($? == 0)); then
1958		log_note "SUCCESS: zoneadm -z $zone_name install"
1959	else
1960		log_fail "FAIL: zoneadm -z $zone_name install"
1961	fi
1962
1963	# Install sysidcfg file
1964	#
1965	typeset sysidcfg=$zone_root/$zone_name/root/etc/sysidcfg
1966	echo "system_locale=C" > $sysidcfg
1967	echo  "terminal=dtterm" >> $sysidcfg
1968	echo  "network_interface=primary {" >> $sysidcfg
1969	echo  "hostname=$zone_name" >> $sysidcfg
1970	echo  "}" >> $sysidcfg
1971	echo  "name_service=NONE" >> $sysidcfg
1972	echo  "root_password=mo791xfZ/SFiw" >> $sysidcfg
1973	echo  "security_policy=NONE" >> $sysidcfg
1974	echo  "timezone=US/Eastern" >> $sysidcfg
1975
1976	# Boot this zone
1977	log_must zoneadm -z $zone_name boot
1978}
1979
1980#
1981# Reexport TESTPOOL & TESTPOOL(1-4)
1982#
1983function reexport_pool
1984{
1985	typeset -i cntctr=5
1986	typeset -i i=0
1987
1988	while ((i < cntctr)); do
1989		if ((i == 0)); then
1990			TESTPOOL=$ZONE_POOL/$ZONE_CTR$i
1991			if ! ismounted $TESTPOOL; then
1992				log_must zfs mount $TESTPOOL
1993			fi
1994		else
1995			eval TESTPOOL$i=$ZONE_POOL/$ZONE_CTR$i
1996			if eval ! ismounted \$TESTPOOL$i; then
1997				log_must eval zfs mount \$TESTPOOL$i
1998			fi
1999		fi
2000		((i += 1))
2001	done
2002}
2003
2004#
2005# Verify a given disk or pool state
2006#
2007# Return 0 is pool/disk matches expected state, 1 otherwise
2008#
2009function check_state # pool disk state{online,offline,degraded}
2010{
2011	typeset pool=$1
2012	typeset disk=${2#$DEV_DSKDIR/}
2013	typeset state=$3
2014
2015	[[ -z $pool ]] || [[ -z $state ]] \
2016	    && log_fail "Arguments invalid or missing"
2017
2018	if [[ -z $disk ]]; then
2019		#check pool state only
2020		zpool get -H -o value health $pool \
2021		    | grep -i "$state" > /dev/null 2>&1
2022	else
2023		zpool status -v $pool | grep "$disk"  \
2024		    | grep -i "$state" > /dev/null 2>&1
2025	fi
2026
2027	return $?
2028}
2029
2030#
2031# Get the mountpoint of snapshot
2032# For the snapshot use <mp_filesystem>/.zfs/snapshot/<snap>
2033# as its mountpoint
2034#
2035function snapshot_mountpoint
2036{
2037	typeset dataset=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
2038
2039	if [[ $dataset != *@* ]]; then
2040		log_fail "Error name of snapshot '$dataset'."
2041	fi
2042
2043	typeset fs=${dataset%@*}
2044	typeset snap=${dataset#*@}
2045
2046	if [[ -z $fs || -z $snap ]]; then
2047		log_fail "Error name of snapshot '$dataset'."
2048	fi
2049
2050	echo $(get_prop mountpoint $fs)/.zfs/snapshot/$snap
2051}
2052
2053#
2054# Given a device and 'ashift' value verify it's correctly set on every label
2055#
2056function verify_ashift # device ashift
2057{
2058	typeset device="$1"
2059	typeset ashift="$2"
2060
2061	zdb -e -lll $device | awk -v ashift=$ashift '/ashift: / {
2062	    if (ashift != $2)
2063	        exit 1;
2064	    else
2065	        count++;
2066	    } END {
2067	    if (count != 4)
2068	        exit 1;
2069	    else
2070	        exit 0;
2071	    }'
2072
2073	return $?
2074}
2075
2076#
2077# Given a pool and file system, this function will verify the file system
2078# using the zdb internal tool. Note that the pool is exported and imported
2079# to ensure it has consistent state.
2080#
2081function verify_filesys # pool filesystem dir
2082{
2083	typeset pool="$1"
2084	typeset filesys="$2"
2085	typeset zdbout="/tmp/zdbout.$$"
2086
2087	shift
2088	shift
2089	typeset dirs=$@
2090	typeset search_path=""
2091
2092	log_note "Calling zdb to verify filesystem '$filesys'"
2093	zfs unmount -a > /dev/null 2>&1
2094	log_must zpool export $pool
2095
2096	if [[ -n $dirs ]] ; then
2097		for dir in $dirs ; do
2098			search_path="$search_path -d $dir"
2099		done
2100	fi
2101
2102	log_must zpool import $search_path $pool
2103
2104	zdb -cudi $filesys > $zdbout 2>&1
2105	if [[ $? != 0 ]]; then
2106		log_note "Output: zdb -cudi $filesys"
2107		cat $zdbout
2108		log_fail "zdb detected errors with: '$filesys'"
2109	fi
2110
2111	log_must zfs mount -a
2112	log_must rm -rf $zdbout
2113}
2114
2115#
2116# Given a pool issue a scrub and verify that no checksum errors are reported.
2117#
2118function verify_pool
2119{
2120	typeset pool=${1:-$TESTPOOL}
2121
2122	log_must zpool scrub $pool
2123	log_must wait_scrubbed $pool
2124
2125	typeset -i cksum=$(zpool status $pool | awk '
2126	    !NF { isvdev = 0 }
2127	    isvdev { errors += $NF }
2128	    /CKSUM$/ { isvdev = 1 }
2129	    END { print errors }
2130	')
2131	if [[ $cksum != 0 ]]; then
2132		log_must zpool status -v
2133	        log_fail "Unexpected CKSUM errors found on $pool ($cksum)"
2134	fi
2135}
2136
2137#
2138# Given a pool, and this function list all disks in the pool
2139#
2140function get_disklist # pool
2141{
2142	typeset disklist=""
2143
2144	disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \
2145	    grep -v "\-\-\-\-\-" | \
2146	    egrep -v -e "^(mirror|raidz[1-3]|draid[1-3]|spare|log|cache|special|dedup)|\-[0-9]$")
2147
2148	echo $disklist
2149}
2150
2151#
2152# Given a pool, and this function list all disks in the pool with their full
2153# path (like "/dev/sda" instead of "sda").
2154#
2155function get_disklist_fullpath # pool
2156{
2157	args="-P $1"
2158	get_disklist $args
2159}
2160
2161
2162
2163# /**
2164#  This function kills a given list of processes after a time period. We use
2165#  this in the stress tests instead of STF_TIMEOUT so that we can have processes
2166#  run for a fixed amount of time, yet still pass. Tests that hit STF_TIMEOUT
2167#  would be listed as FAIL, which we don't want : we're happy with stress tests
2168#  running for a certain amount of time, then finishing.
2169#
2170# @param $1 the time in seconds after which we should terminate these processes
2171# @param $2..$n the processes we wish to terminate.
2172# */
2173function stress_timeout
2174{
2175	typeset -i TIMEOUT=$1
2176	shift
2177	typeset cpids="$@"
2178
2179	log_note "Waiting for child processes($cpids). " \
2180		"It could last dozens of minutes, please be patient ..."
2181	log_must sleep $TIMEOUT
2182
2183	log_note "Killing child processes after ${TIMEOUT} stress timeout."
2184	typeset pid
2185	for pid in $cpids; do
2186		ps -p $pid > /dev/null 2>&1
2187		if (($? == 0)); then
2188			log_must kill -USR1 $pid
2189		fi
2190	done
2191}
2192
2193#
2194# Verify a given hotspare disk is inuse or avail
2195#
2196# Return 0 is pool/disk matches expected state, 1 otherwise
2197#
2198function check_hotspare_state # pool disk state{inuse,avail}
2199{
2200	typeset pool=$1
2201	typeset disk=${2#$DEV_DSKDIR/}
2202	typeset state=$3
2203
2204	cur_state=$(get_device_state $pool $disk "spares")
2205
2206	if [[ $state != ${cur_state} ]]; then
2207		return 1
2208	fi
2209	return 0
2210}
2211
2212#
2213# Wait until a hotspare transitions to a given state or times out.
2214#
2215# Return 0 when  pool/disk matches expected state, 1 on timeout.
2216#
2217function wait_hotspare_state # pool disk state timeout
2218{
2219	typeset pool=$1
2220	typeset disk=${2#*$DEV_DSKDIR/}
2221	typeset state=$3
2222	typeset timeout=${4:-60}
2223	typeset -i i=0
2224
2225	while [[ $i -lt $timeout ]]; do
2226		if check_hotspare_state $pool $disk $state; then
2227			return 0
2228		fi
2229
2230		i=$((i+1))
2231		sleep 1
2232	done
2233
2234	return 1
2235}
2236
2237#
2238# Verify a given slog disk is inuse or avail
2239#
2240# Return 0 is pool/disk matches expected state, 1 otherwise
2241#
2242function check_slog_state # pool disk state{online,offline,unavail}
2243{
2244	typeset pool=$1
2245	typeset disk=${2#$DEV_DSKDIR/}
2246	typeset state=$3
2247
2248	cur_state=$(get_device_state $pool $disk "logs")
2249
2250	if [[ $state != ${cur_state} ]]; then
2251		return 1
2252	fi
2253	return 0
2254}
2255
2256#
2257# Verify a given vdev disk is inuse or avail
2258#
2259# Return 0 is pool/disk matches expected state, 1 otherwise
2260#
2261function check_vdev_state # pool disk state{online,offline,unavail}
2262{
2263	typeset pool=$1
2264	typeset disk=${2#*$DEV_DSKDIR/}
2265	typeset state=$3
2266
2267	cur_state=$(get_device_state $pool $disk)
2268
2269	if [[ $state != ${cur_state} ]]; then
2270		return 1
2271	fi
2272	return 0
2273}
2274
2275#
2276# Wait until a vdev transitions to a given state or times out.
2277#
2278# Return 0 when  pool/disk matches expected state, 1 on timeout.
2279#
2280function wait_vdev_state # pool disk state timeout
2281{
2282	typeset pool=$1
2283	typeset disk=${2#*$DEV_DSKDIR/}
2284	typeset state=$3
2285	typeset timeout=${4:-60}
2286	typeset -i i=0
2287
2288	while [[ $i -lt $timeout ]]; do
2289		if check_vdev_state $pool $disk $state; then
2290			return 0
2291		fi
2292
2293		i=$((i+1))
2294		sleep 1
2295	done
2296
2297	return 1
2298}
2299
2300#
2301# Check the output of 'zpool status -v <pool>',
2302# and to see if the content of <token> contain the <keyword> specified.
2303#
2304# Return 0 is contain, 1 otherwise
2305#
2306function check_pool_status # pool token keyword <verbose>
2307{
2308	typeset pool=$1
2309	typeset token=$2
2310	typeset keyword=$3
2311	typeset verbose=${4:-false}
2312
2313	scan=$(zpool status -v "$pool" 2>/dev/null | nawk -v token="$token:" '
2314		($1==token) {print $0}')
2315	if [[ $verbose == true ]]; then
2316		log_note $scan
2317	fi
2318	echo $scan | egrep -i "$keyword" > /dev/null 2>&1
2319
2320	return $?
2321}
2322
2323#
2324# The following functions are instance of check_pool_status()
2325#	is_pool_resilvering - to check if the pool resilver is in progress
2326#	is_pool_resilvered - to check if the pool resilver is completed
2327#	is_pool_scrubbing - to check if the pool scrub is in progress
2328#	is_pool_scrubbed - to check if the pool scrub is completed
2329#	is_pool_scrub_stopped - to check if the pool scrub is stopped
2330#	is_pool_scrub_paused - to check if the pool scrub has paused
2331#	is_pool_removing - to check if the pool removing is a vdev
2332#	is_pool_removed - to check if the pool remove is completed
2333#	is_pool_discarding - to check if the pool checkpoint is being discarded
2334#
2335function is_pool_resilvering #pool <verbose>
2336{
2337	check_pool_status "$1" "scan" \
2338	    "resilver[ ()0-9A-Za-z:_-]* in progress since" $2
2339	return $?
2340}
2341
2342function is_pool_resilvered #pool <verbose>
2343{
2344	check_pool_status "$1" "scan" "resilvered " $2
2345	return $?
2346}
2347
2348function is_pool_scrubbing #pool <verbose>
2349{
2350	check_pool_status "$1" "scan" "scrub in progress since " $2
2351	return $?
2352}
2353
2354function is_pool_scrubbed #pool <verbose>
2355{
2356	check_pool_status "$1" "scan" "scrub repaired" $2
2357	return $?
2358}
2359
2360function is_pool_scrub_stopped #pool <verbose>
2361{
2362	check_pool_status "$1" "scan" "scrub canceled" $2
2363	return $?
2364}
2365
2366function is_pool_scrub_paused #pool <verbose>
2367{
2368	check_pool_status "$1" "scan" "scrub paused since " $2
2369	return $?
2370}
2371
2372function is_pool_removing #pool
2373{
2374	check_pool_status "$1" "remove" "in progress since "
2375	return $?
2376}
2377
2378function is_pool_removed #pool
2379{
2380	check_pool_status "$1" "remove" "completed on"
2381	return $?
2382}
2383
2384function is_pool_discarding #pool
2385{
2386	check_pool_status "$1" "checkpoint" "discarding"
2387	return $?
2388}
2389
2390function wait_for_degraded
2391{
2392	typeset pool=$1
2393	typeset timeout=${2:-30}
2394	typeset t0=$SECONDS
2395
2396	while :; do
2397		[[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break
2398		log_note "$pool is not yet degraded."
2399		sleep 1
2400		if ((SECONDS - t0 > $timeout)); then
2401			log_note "$pool not degraded after $timeout seconds."
2402			return 1
2403		fi
2404	done
2405
2406	return 0
2407}
2408
2409#
2410# Use create_pool()/destroy_pool() to clean up the information in
2411# in the given disk to avoid slice overlapping.
2412#
2413function cleanup_devices #vdevs
2414{
2415	typeset pool="foopool$$"
2416
2417	for vdev in $@; do
2418		zero_partitions $vdev
2419	done
2420
2421	poolexists $pool && destroy_pool $pool
2422	create_pool $pool $@
2423	destroy_pool $pool
2424
2425	return 0
2426}
2427
2428#/**
2429# A function to find and locate free disks on a system or from given
2430# disks as the parameter. It works by locating disks that are in use
2431# as swap devices and dump devices, and also disks listed in /etc/vfstab
2432#
2433# $@ given disks to find which are free, default is all disks in
2434# the test system
2435#
2436# @return a string containing the list of available disks
2437#*/
2438function find_disks
2439{
2440	# Trust provided list, no attempt is made to locate unused devices.
2441	if is_linux || is_freebsd; then
2442		echo "$@"
2443		return
2444	fi
2445
2446
2447	sfi=/tmp/swaplist.$$
2448	dmpi=/tmp/dumpdev.$$
2449	max_finddisksnum=${MAX_FINDDISKSNUM:-6}
2450
2451	swap -l > $sfi
2452	dumpadm > $dmpi 2>/dev/null
2453
2454# write an awk script that can process the output of format
2455# to produce a list of disks we know about. Note that we have
2456# to escape "$2" so that the shell doesn't interpret it while
2457# we're creating the awk script.
2458# -------------------
2459	cat > /tmp/find_disks.awk <<EOF
2460#!/bin/nawk -f
2461	BEGIN { FS="."; }
2462
2463	/^Specify disk/{
2464		searchdisks=0;
2465	}
2466
2467	{
2468		if (searchdisks && \$2 !~ "^$"){
2469			split(\$2,arr," ");
2470			print arr[1];
2471		}
2472	}
2473
2474	/^AVAILABLE DISK SELECTIONS:/{
2475		searchdisks=1;
2476	}
2477EOF
2478#---------------------
2479
2480	chmod 755 /tmp/find_disks.awk
2481	disks=${@:-$(echo "" | format -e 2>/dev/null | /tmp/find_disks.awk)}
2482	rm /tmp/find_disks.awk
2483
2484	unused=""
2485	for disk in $disks; do
2486	# Check for mounted
2487		grep "${disk}[sp]" /etc/mnttab >/dev/null
2488		(($? == 0)) && continue
2489	# Check for swap
2490		grep "${disk}[sp]" $sfi >/dev/null
2491		(($? == 0)) && continue
2492	# check for dump device
2493		grep "${disk}[sp]" $dmpi >/dev/null
2494		(($? == 0)) && continue
2495	# check to see if this disk hasn't been explicitly excluded
2496	# by a user-set environment variable
2497		echo "${ZFS_HOST_DEVICES_IGNORE}" | grep "${disk}" > /dev/null
2498		(($? == 0)) && continue
2499		unused_candidates="$unused_candidates $disk"
2500	done
2501	rm $sfi
2502	rm $dmpi
2503
2504# now just check to see if those disks do actually exist
2505# by looking for a device pointing to the first slice in
2506# each case. limit the number to max_finddisksnum
2507	count=0
2508	for disk in $unused_candidates; do
2509		if is_disk_device $DEV_DSKDIR/${disk}s0 && \
2510		    [ $count -lt $max_finddisksnum ]; then
2511			unused="$unused $disk"
2512			# do not impose limit if $@ is provided
2513			[[ -z $@ ]] && ((count = count + 1))
2514		fi
2515	done
2516
2517# finally, return our disk list
2518	echo $unused
2519}
2520
2521function add_user_freebsd #<group_name> <user_name> <basedir>
2522{
2523	typeset group=$1
2524	typeset user=$2
2525	typeset basedir=$3
2526
2527	# Check to see if the user exists.
2528	if id $user > /dev/null 2>&1; then
2529		return 0
2530	fi
2531
2532	# Assign 1000 as the base uid
2533	typeset -i uid=1000
2534	while true; do
2535		typeset -i ret
2536		pw useradd -u $uid -g $group -d $basedir/$user -m -n $user
2537		ret=$?
2538		case $ret in
2539			0) break ;;
2540			# The uid is not unique
2541			65) ((uid += 1)) ;;
2542			*) return 1 ;;
2543		esac
2544		if [[ $uid == 65000 ]]; then
2545			log_fail "No user id available under 65000 for $user"
2546		fi
2547	done
2548
2549	# Silence MOTD
2550	touch $basedir/$user/.hushlogin
2551
2552	return 0
2553}
2554
2555#
2556# Delete the specified user.
2557#
2558# $1 login name
2559#
2560function del_user_freebsd #<logname>
2561{
2562	typeset user=$1
2563
2564	if id $user > /dev/null 2>&1; then
2565		log_must pw userdel $user
2566	fi
2567
2568	return 0
2569}
2570
2571#
2572# Select valid gid and create specified group.
2573#
2574# $1 group name
2575#
2576function add_group_freebsd #<group_name>
2577{
2578	typeset group=$1
2579
2580	# See if the group already exists.
2581	if pw groupshow $group >/dev/null 2>&1; then
2582		return 0
2583	fi
2584
2585	# Assign 1000 as the base gid
2586	typeset -i gid=1000
2587	while true; do
2588		pw groupadd -g $gid -n $group > /dev/null 2>&1
2589		typeset -i ret=$?
2590		case $ret in
2591			0) return 0 ;;
2592			# The gid is not  unique
2593			65) ((gid += 1)) ;;
2594			*) return 1 ;;
2595		esac
2596		if [[ $gid == 65000 ]]; then
2597			log_fail "No user id available under 65000 for $group"
2598		fi
2599	done
2600}
2601
2602#
2603# Delete the specified group.
2604#
2605# $1 group name
2606#
2607function del_group_freebsd #<group_name>
2608{
2609	typeset group=$1
2610
2611	pw groupdel -n $group > /dev/null 2>&1
2612	typeset -i ret=$?
2613	case $ret in
2614		# Group does not exist, or was deleted successfully.
2615		0|6|65) return 0 ;;
2616		# Name already exists as a group name
2617		9) log_must pw groupdel $group ;;
2618		*) return 1 ;;
2619	esac
2620
2621	return 0
2622}
2623
2624function add_user_illumos #<group_name> <user_name> <basedir>
2625{
2626	typeset group=$1
2627	typeset user=$2
2628	typeset basedir=$3
2629
2630	log_must useradd -g $group -d $basedir/$user -m $user
2631
2632	return 0
2633}
2634
2635function del_user_illumos #<user_name>
2636{
2637	typeset user=$1
2638
2639	if id $user > /dev/null 2>&1; then
2640		log_must_retry "currently used" 6 userdel $user
2641	fi
2642
2643	return 0
2644}
2645
2646function add_group_illumos #<group_name>
2647{
2648	typeset group=$1
2649
2650	typeset -i gid=100
2651	while true; do
2652		groupadd -g $gid $group > /dev/null 2>&1
2653		typeset -i ret=$?
2654		case $ret in
2655			0) return 0 ;;
2656			# The gid is not  unique
2657			4) ((gid += 1)) ;;
2658			*) return 1 ;;
2659		esac
2660	done
2661}
2662
2663function del_group_illumos #<group_name>
2664{
2665	typeset group=$1
2666
2667	groupmod -n $grp $grp > /dev/null 2>&1
2668	typeset -i ret=$?
2669	case $ret in
2670		# Group does not exist.
2671		6) return 0 ;;
2672		# Name already exists as a group name
2673		9) log_must groupdel $grp ;;
2674		*) return 1 ;;
2675	esac
2676}
2677
2678function add_user_linux #<group_name> <user_name> <basedir>
2679{
2680	typeset group=$1
2681	typeset user=$2
2682	typeset basedir=$3
2683
2684	log_must useradd -g $group -d $basedir/$user -m $user
2685
2686	# Add new users to the same group and the command line utils.
2687	# This allows them to be run out of the original users home
2688	# directory as long as it permissioned to be group readable.
2689	cmd_group=$(stat --format="%G" $(which zfs))
2690	log_must usermod -a -G $cmd_group $user
2691
2692	return 0
2693}
2694
2695function del_user_linux #<user_name>
2696{
2697	typeset user=$1
2698
2699	if id $user > /dev/null 2>&1; then
2700		log_must_retry "currently used" 6 userdel $user
2701	fi
2702
2703	return 0
2704}
2705
2706function add_group_linux #<group_name>
2707{
2708	typeset group=$1
2709
2710	# Assign 100 as the base gid, a larger value is selected for
2711	# Linux because for many distributions 1000 and under are reserved.
2712	while true; do
2713		groupadd $group > /dev/null 2>&1
2714		typeset -i ret=$?
2715		case $ret in
2716			0) return 0 ;;
2717			*) return 1 ;;
2718		esac
2719	done
2720}
2721
2722function del_group_linux #<group_name>
2723{
2724	typeset group=$1
2725
2726	getent group $group > /dev/null 2>&1
2727	typeset -i ret=$?
2728	case $ret in
2729		# Group does not exist.
2730		2) return 0 ;;
2731		# Name already exists as a group name
2732		0) log_must groupdel $group ;;
2733		*) return 1 ;;
2734	esac
2735
2736	return 0
2737}
2738
2739#
2740# Add specified user to specified group
2741#
2742# $1 group name
2743# $2 user name
2744# $3 base of the homedir (optional)
2745#
2746function add_user #<group_name> <user_name> <basedir>
2747{
2748	typeset group=$1
2749	typeset user=$2
2750	typeset basedir=${3:-"/var/tmp"}
2751
2752	if ((${#group} == 0 || ${#user} == 0)); then
2753		log_fail "group name or user name are not defined."
2754	fi
2755
2756	case $(uname) in
2757	FreeBSD)
2758		add_user_freebsd "$group" "$user" "$basedir"
2759		;;
2760	Linux)
2761		add_user_linux "$group" "$user" "$basedir"
2762		;;
2763	*)
2764		add_user_illumos "$group" "$user" "$basedir"
2765		;;
2766	esac
2767
2768	return 0
2769}
2770
2771#
2772# Delete the specified user.
2773#
2774# $1 login name
2775# $2 base of the homedir (optional)
2776#
2777function del_user #<logname> <basedir>
2778{
2779	typeset user=$1
2780	typeset basedir=${2:-"/var/tmp"}
2781
2782	if ((${#user} == 0)); then
2783		log_fail "login name is necessary."
2784	fi
2785
2786	case $(uname) in
2787	FreeBSD)
2788		del_user_freebsd "$user"
2789		;;
2790	Linux)
2791		del_user_linux "$user"
2792		;;
2793	*)
2794		del_user_illumos "$user"
2795		;;
2796	esac
2797
2798	[[ -d $basedir/$user ]] && rm -fr $basedir/$user
2799
2800	return 0
2801}
2802
2803#
2804# Select valid gid and create specified group.
2805#
2806# $1 group name
2807#
2808function add_group #<group_name>
2809{
2810	typeset group=$1
2811
2812	if ((${#group} == 0)); then
2813		log_fail "group name is necessary."
2814	fi
2815
2816	case $(uname) in
2817	FreeBSD)
2818		add_group_freebsd "$group"
2819		;;
2820	Linux)
2821		add_group_linux "$group"
2822		;;
2823	*)
2824		add_group_illumos "$group"
2825		;;
2826	esac
2827
2828	return 0
2829}
2830
2831#
2832# Delete the specified group.
2833#
2834# $1 group name
2835#
2836function del_group #<group_name>
2837{
2838	typeset group=$1
2839
2840	if ((${#group} == 0)); then
2841		log_fail "group name is necessary."
2842	fi
2843
2844	case $(uname) in
2845	FreeBSD)
2846		del_group_freebsd "$group"
2847		;;
2848	Linux)
2849		del_group_linux "$group"
2850		;;
2851	*)
2852		del_group_illumos "$group"
2853		;;
2854	esac
2855
2856	return 0
2857}
2858
2859#
2860# This function will return true if it's safe to destroy the pool passed
2861# as argument 1. It checks for pools based on zvols and files, and also
2862# files contained in a pool that may have a different mountpoint.
2863#
2864function safe_to_destroy_pool { # $1 the pool name
2865
2866	typeset pool=""
2867	typeset DONT_DESTROY=""
2868
2869	# We check that by deleting the $1 pool, we're not
2870	# going to pull the rug out from other pools. Do this
2871	# by looking at all other pools, ensuring that they
2872	# aren't built from files or zvols contained in this pool.
2873
2874	for pool in $(zpool list -H -o name)
2875	do
2876		ALTMOUNTPOOL=""
2877
2878		# this is a list of the top-level directories in each of the
2879		# files that make up the path to the files the pool is based on
2880		FILEPOOL=$(zpool status -v $pool | grep /$1/ | \
2881			awk '{print $1}')
2882
2883		# this is a list of the zvols that make up the pool
2884		ZVOLPOOL=$(zpool status -v $pool | grep "$ZVOL_DEVDIR/$1$" \
2885		    | awk '{print $1}')
2886
2887		# also want to determine if it's a file-based pool using an
2888		# alternate mountpoint...
2889		POOL_FILE_DIRS=$(zpool status -v $pool | \
2890					grep / | awk '{print $1}' | \
2891					awk -F/ '{print $2}' | grep -v "dev")
2892
2893		for pooldir in $POOL_FILE_DIRS
2894		do
2895			OUTPUT=$(zfs list -H -r -o mountpoint $1 | \
2896					grep "${pooldir}$" | awk '{print $1}')
2897
2898			ALTMOUNTPOOL="${ALTMOUNTPOOL}${OUTPUT}"
2899		done
2900
2901
2902		if [ ! -z "$ZVOLPOOL" ]
2903		then
2904			DONT_DESTROY="true"
2905			log_note "Pool $pool is built from $ZVOLPOOL on $1"
2906		fi
2907
2908		if [ ! -z "$FILEPOOL" ]
2909		then
2910			DONT_DESTROY="true"
2911			log_note "Pool $pool is built from $FILEPOOL on $1"
2912		fi
2913
2914		if [ ! -z "$ALTMOUNTPOOL" ]
2915		then
2916			DONT_DESTROY="true"
2917			log_note "Pool $pool is built from $ALTMOUNTPOOL on $1"
2918		fi
2919	done
2920
2921	if [ -z "${DONT_DESTROY}" ]
2922	then
2923		return 0
2924	else
2925		log_note "Warning: it is not safe to destroy $1!"
2926		return 1
2927	fi
2928}
2929
2930#
2931# Verify zfs operation with -p option work as expected
2932# $1 operation, value could be create, clone or rename
2933# $2 dataset type, value could be fs or vol
2934# $3 dataset name
2935# $4 new dataset name
2936#
2937function verify_opt_p_ops
2938{
2939	typeset ops=$1
2940	typeset datatype=$2
2941	typeset dataset=$3
2942	typeset newdataset=$4
2943
2944	if [[ $datatype != "fs" && $datatype != "vol" ]]; then
2945		log_fail "$datatype is not supported."
2946	fi
2947
2948	# check parameters accordingly
2949	case $ops in
2950		create)
2951			newdataset=$dataset
2952			dataset=""
2953			if [[ $datatype == "vol" ]]; then
2954				ops="create -V $VOLSIZE"
2955			fi
2956			;;
2957		clone)
2958			if [[ -z $newdataset ]]; then
2959				log_fail "newdataset should not be empty" \
2960					"when ops is $ops."
2961			fi
2962			log_must datasetexists $dataset
2963			log_must snapexists $dataset
2964			;;
2965		rename)
2966			if [[ -z $newdataset ]]; then
2967				log_fail "newdataset should not be empty" \
2968					"when ops is $ops."
2969			fi
2970			log_must datasetexists $dataset
2971			;;
2972		*)
2973			log_fail "$ops is not supported."
2974			;;
2975	esac
2976
2977	# make sure the upper level filesystem does not exist
2978	destroy_dataset "${newdataset%/*}" "-rRf"
2979
2980	# without -p option, operation will fail
2981	log_mustnot zfs $ops $dataset $newdataset
2982	log_mustnot datasetexists $newdataset ${newdataset%/*}
2983
2984	# with -p option, operation should succeed
2985	log_must zfs $ops -p $dataset $newdataset
2986	block_device_wait
2987
2988	if ! datasetexists $newdataset ; then
2989		log_fail "-p option does not work for $ops"
2990	fi
2991
2992	# when $ops is create or clone, redo the operation still return zero
2993	if [[ $ops != "rename" ]]; then
2994		log_must zfs $ops -p $dataset $newdataset
2995	fi
2996
2997	return 0
2998}
2999
3000#
3001# Get configuration of pool
3002# $1 pool name
3003# $2 config name
3004#
3005function get_config
3006{
3007	typeset pool=$1
3008	typeset config=$2
3009	typeset alt_root
3010
3011	if ! poolexists "$pool" ; then
3012		return 1
3013	fi
3014	alt_root=$(zpool list -H $pool | awk '{print $NF}')
3015	if [[ $alt_root == "-" ]]; then
3016		value=$(zdb -C $pool | grep "$config:" | awk -F: \
3017		    '{print $2}')
3018	else
3019		value=$(zdb -e $pool | grep "$config:" | awk -F: \
3020		    '{print $2}')
3021	fi
3022	if [[ -n $value ]] ; then
3023		value=${value#'}
3024		value=${value%'}
3025	fi
3026	echo $value
3027
3028	return 0
3029}
3030
3031#
3032# Privated function. Random select one of items from arguments.
3033#
3034# $1 count
3035# $2-n string
3036#
3037function _random_get
3038{
3039	typeset cnt=$1
3040	shift
3041
3042	typeset str="$@"
3043	typeset -i ind
3044	((ind = RANDOM % cnt + 1))
3045
3046	typeset ret=$(echo "$str" | cut -f $ind -d ' ')
3047	echo $ret
3048}
3049
3050#
3051# Random select one of item from arguments which include NONE string
3052#
3053function random_get_with_non
3054{
3055	typeset -i cnt=$#
3056	((cnt =+ 1))
3057
3058	_random_get "$cnt" "$@"
3059}
3060
3061#
3062# Random select one of item from arguments which doesn't include NONE string
3063#
3064function random_get
3065{
3066	_random_get "$#" "$@"
3067}
3068
3069#
3070# Detect if the current system support slog
3071#
3072function verify_slog_support
3073{
3074	typeset dir=$TEST_BASE_DIR/disk.$$
3075	typeset pool=foo.$$
3076	typeset vdev=$dir/a
3077	typeset sdev=$dir/b
3078
3079	mkdir -p $dir
3080	mkfile $MINVDEVSIZE $vdev $sdev
3081
3082	typeset -i ret=0
3083	if ! zpool create -n $pool $vdev log $sdev > /dev/null 2>&1; then
3084		ret=1
3085	fi
3086	rm -r $dir
3087
3088	return $ret
3089}
3090
3091#
3092# The function will generate a dataset name with specific length
3093# $1, the length of the name
3094# $2, the base string to construct the name
3095#
3096function gen_dataset_name
3097{
3098	typeset -i len=$1
3099	typeset basestr="$2"
3100	typeset -i baselen=${#basestr}
3101	typeset -i iter=0
3102	typeset l_name=""
3103
3104	if ((len % baselen == 0)); then
3105		((iter = len / baselen))
3106	else
3107		((iter = len / baselen + 1))
3108	fi
3109	while ((iter > 0)); do
3110		l_name="${l_name}$basestr"
3111
3112		((iter -= 1))
3113	done
3114
3115	echo $l_name
3116}
3117
3118#
3119# Get cksum tuple of dataset
3120# $1 dataset name
3121#
3122# sample zdb output:
3123# Dataset data/test [ZPL], ID 355, cr_txg 2413856, 31.0K, 7 objects, rootbp
3124# DVA[0]=<0:803046400:200> DVA[1]=<0:81199000:200> [L0 DMU objset] fletcher4
3125# lzjb LE contiguous unique double size=800L/200P birth=2413856L/2413856P
3126# fill=7 cksum=11ce125712:643a9c18ee2:125e25238fca0:254a3f74b59744
3127function datasetcksum
3128{
3129	typeset cksum
3130	sync
3131	cksum=$(zdb -vvv $1 | grep "^Dataset $1 \[" | grep "cksum" \
3132		| awk -F= '{print $7}')
3133	echo $cksum
3134}
3135
3136#
3137# Get cksum of file
3138# #1 file path
3139#
3140function checksum
3141{
3142	typeset cksum
3143	cksum=$(cksum $1 | awk '{print $1}')
3144	echo $cksum
3145}
3146
3147#
3148# Get the given disk/slice state from the specific field of the pool
3149#
3150function get_device_state #pool disk field("", "spares","logs")
3151{
3152	typeset pool=$1
3153	typeset disk=${2#$DEV_DSKDIR/}
3154	typeset field=${3:-$pool}
3155
3156	state=$(zpool status -v "$pool" 2>/dev/null | \
3157		nawk -v device=$disk -v pool=$pool -v field=$field \
3158		'BEGIN {startconfig=0; startfield=0; }
3159		/config:/ {startconfig=1}
3160		(startconfig==1) && ($1==field) {startfield=1; next;}
3161		(startfield==1) && ($1==device) {print $2; exit;}
3162		(startfield==1) &&
3163		($1==field || $1 ~ "^spares$" || $1 ~ "^logs$") {startfield=0}')
3164	echo $state
3165}
3166
3167
3168#
3169# print the given directory filesystem type
3170#
3171# $1 directory name
3172#
3173function get_fstype
3174{
3175	typeset dir=$1
3176
3177	if [[ -z $dir ]]; then
3178		log_fail "Usage: get_fstype <directory>"
3179	fi
3180
3181	#
3182	#  $ df -n /
3183	#  /		  : ufs
3184	#
3185	df -n $dir | awk '{print $3}'
3186}
3187
3188#
3189# Given a disk, label it to VTOC regardless what label was on the disk
3190# $1 disk
3191#
3192function labelvtoc
3193{
3194	typeset disk=$1
3195	if [[ -z $disk ]]; then
3196		log_fail "The disk name is unspecified."
3197	fi
3198	typeset label_file=/var/tmp/labelvtoc.$$
3199	typeset arch=$(uname -p)
3200
3201	if is_linux || is_freebsd; then
3202		log_note "Currently unsupported by the test framework"
3203		return 1
3204	fi
3205
3206	if [[ $arch == "i386" ]]; then
3207		echo "label" > $label_file
3208		echo "0" >> $label_file
3209		echo "" >> $label_file
3210		echo "q" >> $label_file
3211		echo "q" >> $label_file
3212
3213		fdisk -B $disk >/dev/null 2>&1
3214		# wait a while for fdisk finishes
3215		sleep 60
3216	elif [[ $arch == "sparc" ]]; then
3217		echo "label" > $label_file
3218		echo "0" >> $label_file
3219		echo "" >> $label_file
3220		echo "" >> $label_file
3221		echo "" >> $label_file
3222		echo "q" >> $label_file
3223	else
3224		log_fail "unknown arch type"
3225	fi
3226
3227	format -e -s -d $disk -f $label_file
3228	typeset -i ret_val=$?
3229	rm -f $label_file
3230	#
3231	# wait the format to finish
3232	#
3233	sleep 60
3234	if ((ret_val != 0)); then
3235		log_fail "unable to label $disk as VTOC."
3236	fi
3237
3238	return 0
3239}
3240
3241#
3242# check if the system was installed as zfsroot or not
3243# return: 0 if zfsroot, non-zero if not
3244#
3245function is_zfsroot
3246{
3247	df -n / | grep zfs > /dev/null 2>&1
3248	return $?
3249}
3250
3251#
3252# get the root filesystem name if it's zfsroot system.
3253#
3254# return: root filesystem name
3255function get_rootfs
3256{
3257	typeset rootfs=""
3258
3259	if is_freebsd; then
3260		rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3261	elif ! is_linux; then
3262		rootfs=$(awk '{if ($2 == "/" && $3 == "zfs") print $1}' \
3263			/etc/mnttab)
3264	fi
3265	if [[ -z "$rootfs" ]]; then
3266		log_fail "Can not get rootfs"
3267	fi
3268	zfs list $rootfs > /dev/null 2>&1
3269	if (($? == 0)); then
3270		echo $rootfs
3271	else
3272		log_fail "This is not a zfsroot system."
3273	fi
3274}
3275
3276#
3277# get the rootfs's pool name
3278# return:
3279#       rootpool name
3280#
3281function get_rootpool
3282{
3283	typeset rootfs=""
3284	typeset rootpool=""
3285
3286	if is_freebsd; then
3287		rootfs=$(mount -p | awk '$2 == "/" && $3 == "zfs" {print $1}')
3288	elif ! is_linux; then
3289		rootfs=$(awk '{if ($2 == "/" && $3 =="zfs") print $1}' \
3290			 /etc/mnttab)
3291	fi
3292	if [[ -z "$rootfs" ]]; then
3293		log_fail "Can not get rootpool"
3294	fi
3295	zfs list $rootfs > /dev/null 2>&1
3296	if (($? == 0)); then
3297		echo ${rootfs%%/*}
3298	else
3299		log_fail "This is not a zfsroot system."
3300	fi
3301}
3302
3303#
3304# Get the word numbers from a string separated by white space
3305#
3306function get_word_count
3307{
3308	echo $1 | wc -w
3309}
3310
3311#
3312# To verify if the require numbers of disks is given
3313#
3314function verify_disk_count
3315{
3316	typeset -i min=${2:-1}
3317
3318	typeset -i count=$(get_word_count "$1")
3319
3320	if ((count < min)); then
3321		log_untested "A minimum of $min disks is required to run." \
3322			" You specified $count disk(s)"
3323	fi
3324}
3325
3326function ds_is_volume
3327{
3328	typeset type=$(get_prop type $1)
3329	[[ $type = "volume" ]] && return 0
3330	return 1
3331}
3332
3333function ds_is_filesystem
3334{
3335	typeset type=$(get_prop type $1)
3336	[[ $type = "filesystem" ]] && return 0
3337	return 1
3338}
3339
3340function ds_is_snapshot
3341{
3342	typeset type=$(get_prop type $1)
3343	[[ $type = "snapshot" ]] && return 0
3344	return 1
3345}
3346
3347#
3348# Check if Trusted Extensions are installed and enabled
3349#
3350function is_te_enabled
3351{
3352	svcs -H -o state labeld 2>/dev/null | grep "enabled"
3353	if (($? != 0)); then
3354		return 1
3355	else
3356		return 0
3357	fi
3358}
3359
3360# Utility function to determine if a system has multiple cpus.
3361function is_mp
3362{
3363	if is_linux; then
3364		(($(nproc) > 1))
3365	elif is_freebsd; then
3366		sysctl -n kern.smp.cpus
3367	else
3368		(($(psrinfo | wc -l) > 1))
3369	fi
3370
3371	return $?
3372}
3373
3374function get_cpu_freq
3375{
3376	if is_linux; then
3377		lscpu | awk '/CPU MHz/ { print $3 }'
3378	elif is_freebsd; then
3379		sysctl -n hw.clockrate
3380	else
3381		psrinfo -v 0 | awk '/processor operates at/ {print $6}'
3382	fi
3383}
3384
3385# Run the given command as the user provided.
3386function user_run
3387{
3388	typeset user=$1
3389	shift
3390
3391	log_note "user: $user"
3392	log_note "cmd: $*"
3393
3394	typeset out=$TEST_BASE_DIR/out
3395	typeset err=$TEST_BASE_DIR/err
3396
3397	sudo -Eu $user env PATH="$PATH" ksh <<<"$*" >$out 2>$err
3398	typeset res=$?
3399	log_note "out: $(<$out)"
3400	log_note "err: $(<$err)"
3401	return $res
3402}
3403
3404#
3405# Check if the pool contains the specified vdevs
3406#
3407# $1 pool
3408# $2..n <vdev> ...
3409#
3410# Return 0 if the vdevs are contained in the pool, 1 if any of the specified
3411# vdevs is not in the pool, and 2 if pool name is missing.
3412#
3413function vdevs_in_pool
3414{
3415	typeset pool=$1
3416	typeset vdev
3417
3418	if [[ -z $pool ]]; then
3419		log_note "Missing pool name."
3420		return 2
3421	fi
3422
3423	shift
3424
3425	# We could use 'zpool list' to only get the vdevs of the pool but we
3426	# can't reference a mirror/raidz vdev using its ID (i.e mirror-0),
3427	# therefore we use the 'zpool status' output.
3428	typeset tmpfile=$(mktemp)
3429	zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile
3430	for vdev in $@; do
3431		grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1
3432		[[ $? -ne 0 ]] && return 1
3433	done
3434
3435	rm -f $tmpfile
3436
3437	return 0;
3438}
3439
3440function get_max
3441{
3442	typeset -l i max=$1
3443	shift
3444
3445	for i in "$@"; do
3446		max=$((max > i ? max : i))
3447	done
3448
3449	echo $max
3450}
3451
3452function get_min
3453{
3454	typeset -l i min=$1
3455	shift
3456
3457	for i in "$@"; do
3458		min=$((min < i ? min : i))
3459	done
3460
3461	echo $min
3462}
3463
3464# Write data that can be compressed into a directory
3465function write_compressible
3466{
3467	typeset dir=$1
3468	typeset megs=$2
3469	typeset nfiles=${3:-1}
3470	typeset bs=${4:-1024k}
3471	typeset fname=${5:-file}
3472
3473	[[ -d $dir ]] || log_fail "No directory: $dir"
3474
3475	# Under Linux fio is not currently used since its behavior can
3476	# differ significantly across versions.  This includes missing
3477	# command line options and cases where the --buffer_compress_*
3478	# options fail to behave as expected.
3479	if is_linux; then
3480		typeset file_bytes=$(to_bytes $megs)
3481		typeset bs_bytes=4096
3482		typeset blocks=$(($file_bytes / $bs_bytes))
3483
3484		for (( i = 0; i < $nfiles; i++ )); do
3485			truncate -s $file_bytes $dir/$fname.$i
3486
3487			# Write every third block to get 66% compression.
3488			for (( j = 0; j < $blocks; j += 3 )); do
3489				dd if=/dev/urandom of=$dir/$fname.$i \
3490				    seek=$j bs=$bs_bytes count=1 \
3491				    conv=notrunc >/dev/null 2>&1
3492			done
3493		done
3494	else
3495		log_must eval "fio \
3496		    --name=job \
3497		    --fallocate=0 \
3498		    --minimal \
3499		    --randrepeat=0 \
3500		    --buffer_compress_percentage=66 \
3501		    --buffer_compress_chunk=4096 \
3502		    --directory=$dir \
3503		    --numjobs=$nfiles \
3504		    --nrfiles=$nfiles \
3505		    --rw=write \
3506		    --bs=$bs \
3507		    --filesize=$megs \
3508		    --filename_format='$fname.\$jobnum' >/dev/null"
3509	fi
3510}
3511
3512function get_objnum
3513{
3514	typeset pathname=$1
3515	typeset objnum
3516
3517	[[ -e $pathname ]] || log_fail "No such file or directory: $pathname"
3518	if is_freebsd; then
3519		objnum=$(stat -f "%i" $pathname)
3520	else
3521		objnum=$(stat -c %i $pathname)
3522	fi
3523	echo $objnum
3524}
3525
3526#
3527# Sync data to the pool
3528#
3529# $1 pool name
3530# $2 boolean to force uberblock (and config including zpool cache file) update
3531#
3532function sync_pool #pool <force>
3533{
3534	typeset pool=${1:-$TESTPOOL}
3535	typeset force=${2:-false}
3536
3537	if [[ $force == true ]]; then
3538		log_must zpool sync -f $pool
3539	else
3540		log_must zpool sync $pool
3541	fi
3542
3543	return 0
3544}
3545
3546#
3547# Wait for zpool 'freeing' property drops to zero.
3548#
3549# $1 pool name
3550#
3551function wait_freeing #pool
3552{
3553	typeset pool=${1:-$TESTPOOL}
3554	while true; do
3555		[[ "0" == "$(zpool list -Ho freeing $pool)" ]] && break
3556		log_must sleep 1
3557	done
3558}
3559
3560#
3561# Wait for every device replace operation to complete
3562#
3563# $1 pool name
3564#
3565function wait_replacing #pool
3566{
3567	typeset pool=${1:-$TESTPOOL}
3568	while true; do
3569		[[ "" == "$(zpool status $pool |
3570		    awk '/replacing-[0-9]+/ {print $1}')" ]] && break
3571		log_must sleep 1
3572	done
3573}
3574
3575#
3576# Wait for a pool to be scrubbed
3577#
3578# $1 pool name
3579#
3580function wait_scrubbed
3581{
3582	typeset pool=${1:-$TESTPOOL}
3583	while ! is_pool_scrubbed $pool ; do
3584		sleep 1
3585	done
3586}
3587
3588# Backup the zed.rc in our test directory so that we can edit it for our test.
3589#
3590# Returns: Backup file name.  You will need to pass this to zed_rc_restore().
3591function zed_rc_backup
3592{
3593	zedrc_backup="$(mktemp)"
3594	cp $ZEDLET_DIR/zed.rc $zedrc_backup
3595	echo $zedrc_backup
3596}
3597
3598function zed_rc_restore
3599{
3600	mv $1 $ZEDLET_DIR/zed.rc
3601}
3602
3603#
3604# Setup custom environment for the ZED.
3605#
3606# $@ Optional list of zedlets to run under zed.
3607function zed_setup
3608{
3609	if ! is_linux; then
3610		log_unsupported "No zed on $(uname)"
3611	fi
3612
3613	if [[ ! -d $ZEDLET_DIR ]]; then
3614		log_must mkdir $ZEDLET_DIR
3615	fi
3616
3617	if [[ ! -e $VDEVID_CONF ]]; then
3618		log_must touch $VDEVID_CONF
3619	fi
3620
3621	if [[ -e $VDEVID_CONF_ETC ]]; then
3622		log_fail "Must not have $VDEVID_CONF_ETC file present on system"
3623	fi
3624	EXTRA_ZEDLETS=$@
3625
3626	# Create a symlink for /etc/zfs/vdev_id.conf file.
3627	log_must ln -s $VDEVID_CONF $VDEVID_CONF_ETC
3628
3629	# Setup minimal ZED configuration.  Individual test cases should
3630	# add additional ZEDLETs as needed for their specific test.
3631	log_must cp ${ZEDLET_ETC_DIR}/zed.rc $ZEDLET_DIR
3632	log_must cp ${ZEDLET_ETC_DIR}/zed-functions.sh $ZEDLET_DIR
3633
3634	# Scripts must only be user writable.
3635	if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3636		saved_umask=$(umask)
3637		log_must umask 0022
3638		for i in $EXTRA_ZEDLETS ; do
3639			log_must cp ${ZEDLET_LIBEXEC_DIR}/$i $ZEDLET_DIR
3640		done
3641		log_must umask $saved_umask
3642	fi
3643
3644	# Customize the zed.rc file to enable the full debug log.
3645	log_must sed -i '/\#ZED_DEBUG_LOG=.*/d' $ZEDLET_DIR/zed.rc
3646	echo "ZED_DEBUG_LOG=$ZED_DEBUG_LOG" >>$ZEDLET_DIR/zed.rc
3647
3648}
3649
3650#
3651# Cleanup custom ZED environment.
3652#
3653# $@ Optional list of zedlets to remove from our test zed.d directory.
3654function zed_cleanup
3655{
3656	if ! is_linux; then
3657		return
3658	fi
3659	EXTRA_ZEDLETS=$@
3660
3661	log_must rm -f ${ZEDLET_DIR}/zed.rc
3662	log_must rm -f ${ZEDLET_DIR}/zed-functions.sh
3663	log_must rm -f ${ZEDLET_DIR}/all-syslog.sh
3664	log_must rm -f ${ZEDLET_DIR}/all-debug.sh
3665	log_must rm -f ${ZEDLET_DIR}/state
3666
3667	if [[ -n "$EXTRA_ZEDLETS" ]] ; then
3668		for i in $EXTRA_ZEDLETS ; do
3669			log_must rm -f ${ZEDLET_DIR}/$i
3670		done
3671	fi
3672	log_must rm -f $ZED_LOG
3673	log_must rm -f $ZED_DEBUG_LOG
3674	log_must rm -f $VDEVID_CONF_ETC
3675	log_must rm -f $VDEVID_CONF
3676	rmdir $ZEDLET_DIR
3677}
3678
3679#
3680# Check if ZED is currently running, if not start ZED.
3681#
3682function zed_start
3683{
3684	if ! is_linux; then
3685		return
3686	fi
3687
3688	# ZEDLET_DIR=/var/tmp/zed
3689	if [[ ! -d $ZEDLET_DIR ]]; then
3690		log_must mkdir $ZEDLET_DIR
3691	fi
3692
3693	# Verify the ZED is not already running.
3694	pgrep -x zed > /dev/null
3695	if (($? == 0)); then
3696		log_note "ZED already running"
3697	else
3698		log_note "Starting ZED"
3699		# run ZED in the background and redirect foreground logging
3700		# output to $ZED_LOG.
3701		log_must truncate -s 0 $ZED_DEBUG_LOG
3702		log_must eval "zed -vF -d $ZEDLET_DIR -P $PATH" \
3703		    "-s $ZEDLET_DIR/state -j 1 2>$ZED_LOG &"
3704	fi
3705
3706	return 0
3707}
3708
3709#
3710# Kill ZED process
3711#
3712function zed_stop
3713{
3714	if ! is_linux; then
3715		return
3716	fi
3717
3718	log_note "Stopping ZED"
3719	while true; do
3720		zedpids="$(pgrep -x zed)"
3721		[ "$?" -ne 0 ] && break
3722
3723		log_must kill $zedpids
3724		sleep 1
3725	done
3726	return 0
3727}
3728
3729#
3730# Drain all zevents
3731#
3732function zed_events_drain
3733{
3734	while [ $(zpool events -H | wc -l) -ne 0 ]; do
3735		sleep 1
3736		zpool events -c >/dev/null
3737	done
3738}
3739
3740# Set a variable in zed.rc to something, un-commenting it in the process.
3741#
3742# $1 variable
3743# $2 value
3744function zed_rc_set
3745{
3746	var="$1"
3747	val="$2"
3748	# Remove the line
3749	cmd="'/$var/d'"
3750	eval sed -i $cmd $ZEDLET_DIR/zed.rc
3751
3752	# Add it at the end
3753	echo "$var=$val" >> $ZEDLET_DIR/zed.rc
3754}
3755
3756
3757#
3758# Check is provided device is being active used as a swap device.
3759#
3760function is_swap_inuse
3761{
3762	typeset device=$1
3763
3764	if [[ -z $device ]] ; then
3765		log_note "No device specified."
3766		return 1
3767	fi
3768
3769	if is_linux; then
3770		swapon -s | grep -w $(readlink -f $device) > /dev/null 2>&1
3771	elif is_freebsd; then
3772		swapctl -l | grep -w $device
3773	else
3774		swap -l | grep -w $device > /dev/null 2>&1
3775	fi
3776
3777	return $?
3778}
3779
3780#
3781# Setup a swap device using the provided device.
3782#
3783function swap_setup
3784{
3785	typeset swapdev=$1
3786
3787	if is_linux; then
3788		log_must eval "mkswap $swapdev > /dev/null 2>&1"
3789		log_must swapon $swapdev
3790	elif is_freebsd; then
3791		log_must swapctl -a $swapdev
3792	else
3793	        log_must swap -a $swapdev
3794	fi
3795
3796	return 0
3797}
3798
3799#
3800# Cleanup a swap device on the provided device.
3801#
3802function swap_cleanup
3803{
3804	typeset swapdev=$1
3805
3806	if is_swap_inuse $swapdev; then
3807		if is_linux; then
3808			log_must swapoff $swapdev
3809		elif is_freebsd; then
3810			log_must swapoff $swapdev
3811		else
3812			log_must swap -d $swapdev
3813		fi
3814	fi
3815
3816	return 0
3817}
3818
3819#
3820# Set a global system tunable (64-bit value)
3821#
3822# $1 tunable name (use a NAME defined in tunables.cfg)
3823# $2 tunable values
3824#
3825function set_tunable64
3826{
3827	set_tunable_impl "$1" "$2" Z
3828}
3829
3830#
3831# Set a global system tunable (32-bit value)
3832#
3833# $1 tunable name (use a NAME defined in tunables.cfg)
3834# $2 tunable values
3835#
3836function set_tunable32
3837{
3838	set_tunable_impl "$1" "$2" W
3839}
3840
3841function set_tunable_impl
3842{
3843	typeset name="$1"
3844	typeset value="$2"
3845	typeset mdb_cmd="$3"
3846	typeset module="${4:-zfs}"
3847
3848	eval "typeset tunable=\$$name"
3849	case "$tunable" in
3850	UNSUPPORTED)
3851		log_unsupported "Tunable '$name' is unsupported on $(uname)"
3852		;;
3853	"")
3854		log_fail "Tunable '$name' must be added to tunables.cfg"
3855		;;
3856	*)
3857		;;
3858	esac
3859
3860	[[ -z "$value" ]] && return 1
3861	[[ -z "$mdb_cmd" ]] && return 1
3862
3863	case "$(uname)" in
3864	Linux)
3865		typeset zfs_tunables="/sys/module/$module/parameters"
3866		[[ -w "$zfs_tunables/$tunable" ]] || return 1
3867		cat >"$zfs_tunables/$tunable" <<<"$value"
3868		return $?
3869		;;
3870	FreeBSD)
3871		sysctl vfs.zfs.$tunable=$value
3872		return "$?"
3873		;;
3874	SunOS)
3875		[[ "$module" -eq "zfs" ]] || return 1
3876		echo "${tunable}/${mdb_cmd}0t${value}" | mdb -kw
3877		return $?
3878		;;
3879	esac
3880}
3881
3882#
3883# Get a global system tunable
3884#
3885# $1 tunable name (use a NAME defined in tunables.cfg)
3886#
3887function get_tunable
3888{
3889	get_tunable_impl "$1"
3890}
3891
3892function get_tunable_impl
3893{
3894	typeset name="$1"
3895	typeset module="${2:-zfs}"
3896
3897	eval "typeset tunable=\$$name"
3898	case "$tunable" in
3899	UNSUPPORTED)
3900		log_unsupported "Tunable '$name' is unsupported on $(uname)"
3901		;;
3902	"")
3903		log_fail "Tunable '$name' must be added to tunables.cfg"
3904		;;
3905	*)
3906		;;
3907	esac
3908
3909	case "$(uname)" in
3910	Linux)
3911		typeset zfs_tunables="/sys/module/$module/parameters"
3912		[[ -f "$zfs_tunables/$tunable" ]] || return 1
3913		cat $zfs_tunables/$tunable
3914		return $?
3915		;;
3916	FreeBSD)
3917		sysctl -n vfs.zfs.$tunable
3918		;;
3919	SunOS)
3920		[[ "$module" -eq "zfs" ]] || return 1
3921		;;
3922	esac
3923
3924	return 1
3925}
3926
3927#
3928# Prints the current time in seconds since UNIX Epoch.
3929#
3930function current_epoch
3931{
3932	printf '%(%s)T'
3933}
3934
3935#
3936# Get decimal value of global uint32_t variable using mdb.
3937#
3938function mdb_get_uint32
3939{
3940	typeset variable=$1
3941	typeset value
3942
3943	value=$(mdb -k -e "$variable/X | ::eval .=U")
3944	if [[ $? -ne 0 ]]; then
3945		log_fail "Failed to get value of '$variable' from mdb."
3946		return 1
3947	fi
3948
3949	echo $value
3950	return 0
3951}
3952
3953#
3954# Set global uint32_t variable to a decimal value using mdb.
3955#
3956function mdb_set_uint32
3957{
3958	typeset variable=$1
3959	typeset value=$2
3960
3961	mdb -kw -e "$variable/W 0t$value" > /dev/null
3962	if [[ $? -ne 0 ]]; then
3963		echo "Failed to set '$variable' to '$value' in mdb."
3964		return 1
3965	fi
3966
3967	return 0
3968}
3969
3970#
3971# Set global scalar integer variable to a hex value using mdb.
3972# Note: Target should have CTF data loaded.
3973#
3974function mdb_ctf_set_int
3975{
3976	typeset variable=$1
3977	typeset value=$2
3978
3979	mdb -kw -e "$variable/z $value" > /dev/null
3980	if [[ $? -ne 0 ]]; then
3981		echo "Failed to set '$variable' to '$value' in mdb."
3982		return 1
3983	fi
3984
3985	return 0
3986}
3987
3988#
3989# Compute MD5 digest for given file or stdin if no file given.
3990# Note: file path must not contain spaces
3991#
3992function md5digest
3993{
3994	typeset file=$1
3995
3996	case $(uname) in
3997	FreeBSD)
3998		md5 -q $file
3999		;;
4000	*)
4001		md5sum -b $file | awk '{ print $1 }'
4002		;;
4003	esac
4004}
4005
4006#
4007# Compute SHA256 digest for given file or stdin if no file given.
4008# Note: file path must not contain spaces
4009#
4010function sha256digest
4011{
4012	typeset file=$1
4013
4014	case $(uname) in
4015	FreeBSD)
4016		sha256 -q $file
4017		;;
4018	*)
4019		sha256sum -b $file | awk '{ print $1 }'
4020		;;
4021	esac
4022}
4023
4024function new_fs #<args>
4025{
4026	case $(uname) in
4027	FreeBSD)
4028		newfs "$@"
4029		;;
4030	*)
4031		echo y | newfs -v "$@"
4032		;;
4033	esac
4034}
4035
4036function stat_size #<path>
4037{
4038	typeset path=$1
4039
4040	case $(uname) in
4041	FreeBSD)
4042		stat -f %z "$path"
4043		;;
4044	*)
4045		stat -c %s "$path"
4046		;;
4047	esac
4048}
4049
4050function stat_ctime #<path>
4051{
4052	typeset path=$1
4053
4054	case $(uname) in
4055	FreeBSD)
4056		stat -f %c "$path"
4057		;;
4058	*)
4059		stat -c %Z "$path"
4060		;;
4061	esac
4062}
4063
4064function stat_crtime #<path>
4065{
4066	typeset path=$1
4067
4068	case $(uname) in
4069	FreeBSD)
4070		stat -f %B "$path"
4071		;;
4072	*)
4073		stat -c %W "$path"
4074		;;
4075	esac
4076}
4077
4078# Run a command as if it was being run in a TTY.
4079#
4080# Usage:
4081#
4082#    faketty command
4083#
4084function faketty
4085{
4086    if is_freebsd; then
4087        script -q /dev/null env "$@"
4088    else
4089        script --return --quiet -c "$*" /dev/null
4090    fi
4091}
4092
4093#
4094# Produce a random permutation of the integers in a given range (inclusive).
4095#
4096function range_shuffle # begin end
4097{
4098	typeset -i begin=$1
4099	typeset -i end=$2
4100
4101	seq ${begin} ${end} | sort -R
4102}
4103
4104#
4105# Cross-platform xattr helpers
4106#
4107
4108function get_xattr # name path
4109{
4110	typeset name=$1
4111	typeset path=$2
4112
4113	case $(uname) in
4114	FreeBSD)
4115		getextattr -qq user "${name}" "${path}"
4116		;;
4117	*)
4118		attr -qg "${name}" "${path}"
4119		;;
4120	esac
4121}
4122
4123function set_xattr # name value path
4124{
4125	typeset name=$1
4126	typeset value=$2
4127	typeset path=$3
4128
4129	case $(uname) in
4130	FreeBSD)
4131		setextattr user "${name}" "${value}" "${path}"
4132		;;
4133	*)
4134		attr -qs "${name}" -V "${value}" "${path}"
4135		;;
4136	esac
4137}
4138
4139function set_xattr_stdin # name value
4140{
4141	typeset name=$1
4142	typeset path=$2
4143
4144	case $(uname) in
4145	FreeBSD)
4146		setextattr -i user "${name}" "${path}"
4147		;;
4148	*)
4149		attr -qs "${name}" "${path}"
4150		;;
4151	esac
4152}
4153
4154function rm_xattr # name path
4155{
4156	typeset name=$1
4157	typeset path=$2
4158
4159	case $(uname) in
4160	FreeBSD)
4161		rmextattr -q user "${name}" "${path}"
4162		;;
4163	*)
4164		attr -qr "${name}" "${path}"
4165		;;
4166	esac
4167}
4168
4169function ls_xattr # path
4170{
4171	typeset path=$1
4172
4173	case $(uname) in
4174	FreeBSD)
4175		lsextattr -qq user "${path}"
4176		;;
4177	*)
4178		attr -ql "${path}"
4179		;;
4180	esac
4181}
4182
4183function kstat # stat flags?
4184{
4185	typeset stat=$1
4186	typeset flags=${2-"-n"}
4187
4188	case $(uname) in
4189	FreeBSD)
4190		sysctl $flags kstat.zfs.misc.$stat
4191		;;
4192	Linux)
4193		typeset zfs_kstat="/proc/spl/kstat/zfs/$stat"
4194		[[ -f "$zfs_kstat" ]] || return 1
4195		cat $zfs_kstat
4196		;;
4197	*)
4198		false
4199		;;
4200	esac
4201}
4202
4203function get_arcstat # stat
4204{
4205	typeset stat=$1
4206
4207	case $(uname) in
4208	FreeBSD)
4209		kstat arcstats.$stat
4210		;;
4211	Linux)
4212		kstat arcstats | awk "/$stat/ { print \$3 }"
4213		;;
4214	*)
4215		false
4216		;;
4217	esac
4218}
4219
4220function punch_hole # offset length file
4221{
4222	typeset offset=$1
4223	typeset length=$2
4224	typeset file=$3
4225
4226	case $(uname) in
4227	FreeBSD)
4228		truncate -d -o $offset -l $length "$file"
4229		;;
4230	Linux)
4231		fallocate --punch-hole --offset $offset --length $length "$file"
4232		;;
4233	*)
4234		false
4235		;;
4236	esac
4237}
4238
4239#
4240# Wait for the specified arcstat to reach non-zero quiescence.
4241# If echo is 1 echo the value after reaching quiescence, otherwise
4242# if echo is 0 print the arcstat we are waiting on.
4243#
4244function arcstat_quiescence # stat echo
4245{
4246	typeset stat=$1
4247	typeset echo=$2
4248	typeset do_once=true
4249
4250	if [[ $echo -eq 0 ]]; then
4251		echo "Waiting for arcstat $1 quiescence."
4252	fi
4253
4254	while $do_once || [ $stat1 -ne $stat2 ] || [ $stat2 -eq 0 ]; do
4255		typeset stat1=$(get_arcstat $stat)
4256		sleep 2
4257		typeset stat2=$(get_arcstat $stat)
4258		do_once=false
4259	done
4260
4261	if [[ $echo -eq 1 ]]; then
4262		echo $stat2
4263	fi
4264}
4265
4266function arcstat_quiescence_noecho # stat
4267{
4268	typeset stat=$1
4269	arcstat_quiescence $stat 0
4270}
4271
4272function arcstat_quiescence_echo # stat
4273{
4274	typeset stat=$1
4275	arcstat_quiescence $stat 1
4276}
4277
4278#
4279# Given an array of pids, wait until all processes
4280# have completed and check their return status.
4281#
4282function wait_for_children #children
4283{
4284	rv=0
4285	children=("$@")
4286	for child in "${children[@]}"
4287	do
4288		child_exit=0
4289		wait ${child} || child_exit=$?
4290		if [ $child_exit -ne 0 ]; then
4291			echo "child ${child} failed with ${child_exit}"
4292			rv=1
4293		fi
4294	done
4295	return $rv
4296}
4297