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