]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tests/zfs-tests/tests/functional/rsend/rsend.kshlib
Update OpenZFS to 2.0.0-rc3-gbd565f
[FreeBSD/FreeBSD.git] / tests / zfs-tests / tests / functional / rsend / rsend.kshlib
1 #
2 # CDDL HEADER START
3 #
4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
7 #
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
12 #
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
18 #
19 # CDDL HEADER END
20 #
21
22 #
23 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 # Use is subject to license terms.
25 #
26
27 #
28 # Copyright (c) 2013, 2018 by Delphix. All rights reserved.
29 # Copyright (c) 2020 by Datto Inc. All rights reserved.
30 #
31
32 . $STF_SUITE/include/libtest.shlib
33 . $STF_SUITE/include/math.shlib
34 . $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib
35 . $STF_SUITE/tests/functional/rsend/rsend.cfg
36
37 #
38 # Set up test model which includes various datasets
39 #
40 #               @final
41 #               @snapB
42 #               @init
43 #               |
44 #   ______ pclone
45 #  |      /
46 #  |@psnap
47 #  ||                         @final
48 #  ||@final       @final      @snapC
49 #  ||@snapC       @snapC      @snapB
50 #  ||@snapA       @snapB      @snapA
51 #  ||@init        @init       @init
52 #  |||            |           |
53 # $pool -------- $FS ------- fs1 ------- fs2
54 #    \             \\_____     \          |
55 #     vol           vol   \____ \         @fsnap
56 #      |              |        \ \              \
57 #      @init          @vsnap   |  ------------ fclone
58 #      @snapA         @init \  |                    |
59 #      @final         @snapB \ |                    @init
60 #                     @snapC  vclone                @snapA
61 #                     @final       |                @final
62 #                                 @init
63 #                                 @snapC
64 #                                 @final
65 #
66 # $1 pool name
67 #
68 function setup_test_model
69 {
70         typeset pool=$1
71
72         log_must zfs create -p $pool/$FS/fs1/fs2
73
74         log_must zfs snapshot $pool@psnap
75         log_must zfs clone $pool@psnap $pool/pclone
76
77         if is_global_zone ; then
78                 log_must zfs create -V 16M $pool/vol
79                 log_must zfs create -V 16M $pool/$FS/vol
80                 block_device_wait
81
82                 log_must zfs snapshot $pool/$FS/vol@vsnap
83                 log_must zfs clone $pool/$FS/vol@vsnap $pool/$FS/vclone
84                 block_device_wait
85         fi
86
87         log_must snapshot_tree $pool/$FS/fs1/fs2@fsnap
88         log_must zfs clone $pool/$FS/fs1/fs2@fsnap $pool/$FS/fs1/fclone
89         log_must zfs snapshot -r $pool@init
90
91         log_must snapshot_tree $pool@snapA
92         log_must snapshot_tree $pool@snapC
93         log_must snapshot_tree $pool/pclone@snapB
94         log_must snapshot_tree $pool/$FS@snapB
95         log_must snapshot_tree $pool/$FS@snapC
96         log_must snapshot_tree $pool/$FS/fs1@snapA
97         log_must snapshot_tree $pool/$FS/fs1@snapB
98         log_must snapshot_tree $pool/$FS/fs1@snapC
99         log_must snapshot_tree $pool/$FS/fs1/fclone@snapA
100
101         if is_global_zone ; then
102                 log_must zfs snapshot $pool/vol@snapA
103                 log_must zfs snapshot $pool/$FS/vol@snapB
104                 log_must zfs snapshot $pool/$FS/vol@snapC
105                 log_must zfs snapshot $pool/$FS/vclone@snapC
106         fi
107
108         log_must zfs snapshot -r $pool@final
109
110         return 0
111 }
112
113 #
114 # Cleanup the BACKDIR and given pool content and all the sub datasets
115 #
116 # $1 pool name
117 #
118 function cleanup_pool
119 {
120         typeset pool=$1
121         log_must rm -rf $BACKDIR/*
122
123         if is_global_zone ; then
124                 log_must_busy zfs destroy -Rf $pool
125         else
126                 typeset list=$(zfs list -H -r -t all -o name $pool)
127                 for ds in $list ; do
128                         if [[ $ds != $pool ]] ; then
129                                 if datasetexists $ds ; then
130                                         log_must_busy zfs destroy -Rf $ds
131                                 fi
132                         fi
133                 done
134         fi
135
136         typeset mntpnt=$(get_prop mountpoint $pool)
137         if ! ismounted $pool ; then
138                 # Make sure mountpoint directory is empty
139                 if [[ -d $mntpnt ]]; then
140                         log_must rm -rf $mntpnt/*
141                 fi
142
143                 log_must zfs mount $pool
144         fi
145         if [[ -d $mntpnt ]]; then
146                 rm -rf $mntpnt/*
147         fi
148
149         return 0
150 }
151
152 function cleanup_pools
153 {
154         cleanup_pool $POOL2
155         destroy_pool $POOL3
156 }
157
158 function cmp_md5s {
159         typeset file1=$1
160         typeset file2=$2
161
162         typeset sum1=$(md5digest $file1)
163         typeset sum2=$(md5digest $file2)
164         test "$sum1" = "$sum2"
165 }
166
167 #
168 # Detect if the given two filesystems have same sub-datasets
169 #
170 # $1 source filesystem
171 # $2 destination filesystem
172 #
173 function cmp_ds_subs
174 {
175         typeset src_fs=$1
176         typeset dst_fs=$2
177
178         zfs list -r -H -t all -o name $src_fs > $BACKDIR/src1
179         zfs list -r -H -t all -o name $dst_fs > $BACKDIR/dst1
180
181         eval sed -e 's:^$src_fs:PREFIX:g' < $BACKDIR/src1 > $BACKDIR/src
182         eval sed -e 's:^$dst_fs:PREFIX:g' < $BACKDIR/dst1 > $BACKDIR/dst
183
184         diff $BACKDIR/src $BACKDIR/dst
185         typeset -i ret=$?
186
187         rm -f $BACKDIR/src $BACKDIR/dst $BACKDIR/src1 $BACKDIR/dst1
188
189         return $ret
190 }
191
192 #
193 # Compare all the directories and files in two filesystems
194 #
195 # $1 source filesystem
196 # $2 destination filesystem
197 #
198 function cmp_ds_cont
199 {
200         typeset src_fs=$1
201         typeset dst_fs=$2
202
203         typeset srcdir dstdir
204         srcdir=$(get_prop mountpoint $src_fs)
205         dstdir=$(get_prop mountpoint $dst_fs)
206
207         diff -r $srcdir $dstdir > /dev/null 2>&1
208         return $?
209 }
210
211 #
212 # Compare the given two dataset properties
213 #
214 # $1 dataset 1
215 # $2 dataset 2
216 #
217 function cmp_ds_prop
218 {
219         typeset dtst1=$1
220         typeset dtst2=$2
221         typeset -a props=("type" "origin" "volblocksize" "acltype" "dnodesize" \
222             "atime" "canmount" "checksum" "compression" "copies" "devices" \
223             "exec" "quota" "readonly" "recordsize" "reservation" "setuid" \
224             "snapdir" "version" "volsize" "xattr" "mountpoint");
225         if is_freebsd; then
226                 props+=("jailed")
227         else
228                 props+=("zoned")
229         fi
230
231         for prop in $props;
232         do
233                 zfs get -H -o property,value,source $prop $dtst1 >> \
234                     $BACKDIR/dtst1
235                 zfs get -H -o property,value,source $prop $dtst2 >> \
236                     $BACKDIR/dtst2
237         done
238
239         eval sed -e 's:$dtst1:PREFIX:g' < $BACKDIR/dtst1 > $BACKDIR/dtst1
240         eval sed -e 's:$dtst2:PREFIX:g' < $BACKDIR/dtst2 > $BACKDIR/dtst2
241
242         diff $BACKDIR/dtst1 $BACKDIR/dtst2
243         typeset -i ret=$?
244
245         rm -f $BACKDIR/dtst1 $BACKDIR/dtst2
246
247         return $ret
248
249 }
250
251 #
252 # Random create directories and files
253 #
254 # $1 directory
255 #
256 function random_tree
257 {
258         typeset dir=$1
259
260         if [[ -d $dir ]]; then
261                 rm -rf $dir
262         fi
263         mkdir -p $dir
264         typeset -i ret=$?
265
266         typeset -i nl nd nf
267         ((nl = RANDOM % 6 + 1))
268         ((nd = RANDOM % 3 ))
269         ((nf = RANDOM % 5 ))
270         mktree -b $dir -l $nl -d $nd -f $nf
271         ((ret |= $?))
272
273         return $ret
274 }
275
276 #
277 # Put data in filesystem and take snapshot
278 #
279 # $1 snapshot name
280 #
281 function snapshot_tree
282 {
283         typeset snap=$1
284         typeset ds=${snap%%@*}
285         typeset type=$(get_prop "type" $ds)
286
287         typeset -i ret=0
288         if [[ $type == "filesystem" ]]; then
289                 typeset mntpnt=$(get_prop mountpoint $ds)
290                 ((ret |= $?))
291
292                 if ((ret == 0)) ; then
293                         eval random_tree $mntpnt/${snap##$ds}
294                         ((ret |= $?))
295                 fi
296         fi
297
298         if ((ret == 0)) ; then
299                 zfs snapshot $snap
300                 ((ret |= $?))
301         fi
302
303         return $ret
304 }
305
306 #
307 # Destroy the given snapshot and stuff
308 #
309 # $1 snapshot
310 #
311 function destroy_tree
312 {
313         typeset -i ret=0
314         typeset snap
315         for snap in "$@" ; do
316                 log_must_busy zfs destroy $snap
317
318                 typeset ds=${snap%%@*}
319                 typeset type=$(get_prop "type" $ds)
320                 if [[ $type == "filesystem" ]]; then
321                         typeset mntpnt=$(get_prop mountpoint $ds)
322                         if [[ -n $mntpnt ]]; then
323                                 rm -rf $mntpnt/$snap
324                         fi
325                 fi
326         done
327
328         return 0
329 }
330
331 #
332 # Get all the sub-datasets of give dataset with specific suffix
333 #
334 # $1 Given dataset
335 # $2 Suffix
336 #
337 function getds_with_suffix
338 {
339         typeset ds=$1
340         typeset suffix=$2
341
342         typeset list=$(zfs list -r -H -t all -o name $ds | grep "$suffix$")
343
344         echo $list
345 }
346
347 #
348 # Output inherited properties which is edited for file system
349 #
350 function fs_inherit_prop
351 {
352         typeset fs_prop
353         if is_global_zone ; then
354                 fs_prop=$(zfs inherit 2>&1 | \
355                     awk '$2=="YES" && $3=="YES" {print $1}')
356                 if ! is_te_enabled ; then
357                         fs_prop=$(echo $fs_prop | grep -v "mlslabel")
358                 fi
359         else
360                 fs_prop=$(zfs inherit 2>&1 | \
361                     awk '$2=="YES" && $3=="YES" {print $1}'|
362                     egrep -v "devices|mlslabel|sharenfs|sharesmb|zoned")
363         fi
364
365         echo $fs_prop
366 }
367
368 #
369 # Output inherited properties for volume
370 #
371 function vol_inherit_prop
372 {
373         echo "checksum readonly"
374 }
375
376 #
377 # Get the destination dataset to compare
378 #
379 function get_dst_ds
380 {
381         typeset srcfs=$1
382         typeset dstfs=$2
383
384         #
385         # If the srcfs is not pool
386         #
387         if ! zpool list $srcfs > /dev/null 2>&1 ; then
388                 eval dstfs="$dstfs/${srcfs#*/}"
389         fi
390
391         echo $dstfs
392 }
393
394 #
395 # Make test files
396 #
397 # $1 Number of files to create
398 # $2 Maximum file size
399 # $3 File ID offset
400 # $4 File system to create the files on
401 #
402 function mk_files
403 {
404         nfiles=$1
405         maxsize=$2
406         file_id_offset=$3
407         fs=$4
408         bs=512
409
410         for ((i=0; i<$nfiles; i=i+1)); do
411                 file_name="/$fs/file-$maxsize-$((i+$file_id_offset))"
412                 file_size=$((($RANDOM * $RANDOM % ($maxsize - 1)) + 1))
413
414                 #
415                 # Create an interesting mix of files which contain both
416                 # data blocks and holes for more realistic test coverage.
417                 # Half the files are created as sparse then partially filled,
418                 # the other half is dense then a hole is punched in the file.
419                 #
420                 if [ $((RANDOM % 2)) -eq 0 ]; then
421                         truncate -s $file_size $file_name || \
422                             log_fail "Failed to create $file_name"
423                         dd if=/dev/urandom of=$file_name \
424                             bs=$bs count=$(($file_size / 2 / $bs)) \
425                             seek=$(($RANDOM % (($file_size / 2 / $bs) + 1))) \
426                             conv=notrunc >/dev/null 2>&1 || \
427                             log_fail "Failed to create $file_name"
428                 else
429                         dd if=/dev/urandom of=$file_name \
430                             bs=$file_size count=1 >/dev/null 2>&1 || \
431                             log_fail "Failed to create $file_name"
432                         dd if=/dev/zero of=$file_name \
433                             bs=$bs count=$(($file_size / 2 / $bs)) \
434                             seek=$(($RANDOM % (($file_size / 2 / $bs) + 1))) \
435                             conv=notrunc >/dev/null 2>&1 || \
436                             log_fail "Failed to create $file_name"
437                 fi
438         done
439         echo Created $nfiles files of random sizes up to $maxsize bytes
440 }
441
442 #
443 # Remove test files
444 #
445 # $1 Number of files to remove
446 # $2 Maximum file size
447 # $3 File ID offset
448 # $4 File system to remove the files from
449 #
450 function rm_files
451 {
452         nfiles=$1
453         maxsize=$2
454         file_id_offset=$3
455         fs=$4
456
457         for ((i=0; i<$nfiles; i=i+1)); do
458                 rm -f /$fs/file-$maxsize-$((i+$file_id_offset))
459         done
460         echo Removed $nfiles files of random sizes up to $maxsize bytes
461 }
462
463 #
464 # Simulate a random set of operations which could be reasonably expected
465 # to occur on an average filesystem.
466 #
467 # $1 Number of files to modify
468 # $2 Maximum file size
469 # $3 File system to modify the file on
470 # $4 Enabled xattrs (optional)
471 #
472 function churn_files
473 {
474         nfiles=$1
475         maxsize=$2
476         fs=$3
477         xattrs=${4:-1}
478
479         #
480         # Remove roughly half of the files in order to make it more
481         # likely that a dnode will be reallocated.
482         #
483         for ((i=0; i<$nfiles; i=i+1)); do
484                 file_name="/$fs/file-$i"
485
486                 if [[ -e $file_name ]]; then
487                         if [ $((RANDOM % 2)) -eq 0 ]; then
488                                 rm $file_name || \
489                                     log_fail "Failed to remove $file_name"
490                         fi
491                 fi
492         done
493
494         #
495         # Remount the filesystem to simulate normal usage.  This resets
496         # the last allocated object id allowing for new objects to be
497         # reallocated in the locations of previously freed objects.
498         #
499         log_must zfs unmount $fs
500         log_must zfs mount $fs
501
502         for i in {0..$nfiles}; do
503                 file_name="/$fs/file-$i"
504                 file_size=$((($RANDOM * $RANDOM % ($maxsize - 1)) + 1))
505
506                 #
507                 # When the file exists modify it in one of five ways to
508                 # simulate normal usage:
509                 # - (20%) Remove and set and extended attribute on the file
510                 # - (20%) Overwrite the existing file
511                 # - (20%) Truncate the existing file to a random length
512                 # - (20%) Truncate the existing file to zero length
513                 # - (20%) Remove the file
514                 #
515                 # Otherwise create the missing file.  20% of the created
516                 # files will be small and use embedded block pointers, the
517                 # remainder with have random sizes up to the maximum size.
518                 # Three extended attributes are attached to all of the files.
519                 #
520                 if [[ -e $file_name ]]; then
521                         value=$((RANDOM % 5))
522                         if [ $value -eq 0 -a $xattrs -ne 0 ]; then
523                                 attrname="testattr$((RANDOM % 3))"
524                                 attrlen="$(((RANDOM % 1000) + 1))"
525                                 attrvalue="$(random_string VALID_NAME_CHAR \
526                                     $attrlen)"
527                                 rm_xattr $attrname $file_name || \
528                                     log_fail "Failed to remove $attrname"
529                                 set_xattr $attrname "$attrvalue" $file_name || \
530                                     log_fail "Failed to set $attrname"
531                         elif [ $value -eq 1 ]; then
532                                 dd if=/dev/urandom of=$file_name \
533                                     bs=$file_size count=1 >/dev/null 2>&1 || \
534                                     log_fail "Failed to overwrite $file_name"
535                         elif [ $value -eq 2 ]; then
536                                 truncate -s $file_size $file_name || \
537                                     log_fail "Failed to truncate $file_name"
538                         elif [ $value -eq 3 ]; then
539                                 truncate -s 0 $file_name || \
540                                     log_fail "Failed to truncate $file_name"
541                         else
542                                 rm $file_name || \
543                                     log_fail "Failed to remove $file_name"
544                         fi
545                 else
546                         if [ $((RANDOM % 5)) -eq 0 ]; then
547                                 file_size=$((($RANDOM % 64) + 1))
548                         fi
549
550                         dd if=/dev/urandom of=$file_name \
551                             bs=$file_size count=1 >/dev/null 2>&1 || \
552                             log_fail "Failed to create $file_name"
553
554                         if [ $xattrs -ne 0 ]; then
555                                 for j in {0..2}; do
556                                         attrname="testattr$j"
557                                         attrlen="$(((RANDOM % 1000) + 1))"
558                                         attrvalue="$(random_string \
559                                             VALID_NAME_CHAR $attrlen)"
560                                         set_xattr $attrname \
561                                             "$attrvalue" $file_name || \
562                                             log_fail "Failed to set $attrname"
563                                 done
564                         fi
565                 fi
566         done
567
568         return 0
569 }
570
571 #
572 # Mess up a send file's contents
573 #
574 # $1 The send file path
575 #
576 function mess_send_file
577 {
578         file=$1
579
580         filesize=$(stat_size $file)
581
582         offset=$(($RANDOM * $RANDOM % $filesize))
583
584         # The random offset might truncate the send stream to be
585         # smaller than the DRR_BEGIN record. If this happens, then
586         # the receiving system won't have enough info to create the
587         # partial dataset at all. We use zstreamdump to check for
588         # this and retry in this case.
589         nr_begins=$(head -c $offset $file | zstreamdump | \
590             grep DRR_BEGIN | awk '{ print $5 }')
591         while [ "$nr_begins" -eq 0 ]; do
592                 offset=$(($RANDOM * $RANDOM % $filesize))
593                 nr_begins=$(head -c $offset $file | zstreamdump | \
594                     grep DRR_BEGIN | awk '{ print $5 }')
595         done
596
597         if (($RANDOM % 7 <= 1)); then
598                 #
599                 # We corrupt 2 bytes to minimize the chance that we
600                 # write the same value that's already there.
601                 #
602                 log_must eval "dd if=/dev/urandom of=$file conv=notrunc " \
603                     "bs=1 count=2 seek=$offset >/dev/null 2>&1"
604         else
605                 log_must truncate -s $offset $file
606         fi
607 }
608
609 #
610 # Diff the send/receive filesystems
611 #
612 # $1 The sent filesystem
613 # $2 The received filesystem
614 #
615 function file_check
616 {
617         sendfs=$1
618         recvfs=$2
619
620         if [[ -d /$recvfs/.zfs/snapshot/a && -d \
621             /$sendfs/.zfs/snapshot/a ]]; then
622                 diff -r /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a
623                 [[ $? -eq 0 ]] || log_fail "Differences found in snap a"
624         fi
625         if [[ -d /$recvfs/.zfs/snapshot/b && -d \
626             /$sendfs/.zfs/snapshot/b ]]; then
627                 diff -r /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b
628                 [[ $? -eq 0 ]] || log_fail "Differences found in snap b"
629         fi
630 }
631
632 #
633 # Resume test helper
634 #
635 # $1 The ZFS send command
636 # $2 The filesystem where the streams are sent
637 # $3 The receive filesystem
638 # $4 Test dry-run (optional)
639 #
640 function resume_test
641 {
642         typeset sendcmd=$1
643         typeset streamfs=$2
644         typeset recvfs=$3
645         typeset dryrun=${4:-1}
646
647         stream_num=1
648         log_must eval "$sendcmd >/$streamfs/$stream_num"
649
650         for ((i=0; i<2; i=i+1)); do
651                 mess_send_file /$streamfs/$stream_num
652                 log_mustnot zfs recv -suv $recvfs </$streamfs/$stream_num
653                 stream_num=$((stream_num+1))
654
655                 token=$(zfs get -Hp -o value receive_resume_token $recvfs)
656
657                 # Do a dry-run
658                 [ $dryrun -ne 0 ] && \
659                         log_must eval "zfs send -nvt $token > /dev/null"
660
661                 log_must eval "zfs send -t $token  >/$streamfs/$stream_num"
662                 [[ -f /$streamfs/$stream_num ]] || \
663                     log_fail "NO FILE /$streamfs/$stream_num"
664         done
665         log_must zfs recv -suv $recvfs </$streamfs/$stream_num
666 }
667
668 function get_resume_token
669 {
670         sendcmd=$1
671         streamfs=$2
672         recvfs=$3
673
674         log_must eval "$sendcmd > /$streamfs/1"
675         mess_send_file /$streamfs/1
676         log_mustnot zfs recv -suv $recvfs < /$streamfs/1 2>&1
677         token=$(zfs get -Hp -o value receive_resume_token $recvfs)
678         echo "$token" > /$streamfs/resume_token
679
680         return 0
681 }
682
683 #
684 # Setup filesystems for the resumable send/receive tests
685 #
686 # $1 The "send" filesystem
687 # $2 The "recv" filesystem
688 #
689 function test_fs_setup
690 {
691         typeset sendfs=$1
692         typeset recvfs=$2
693         typeset streamfs=$3
694         typeset sendpool=${sendfs%%/*}
695         typeset recvpool=${recvfs%%/*}
696
697         datasetexists $sendfs && log_must_busy zfs destroy -r $sendpool
698         datasetexists $recvfs && log_must_busy zfs destroy -r $recvpool
699         datasetexists $streamfs && log_must_busy zfs destroy -r $streamfs
700
701         if datasetexists $sendfs || zfs create -o compress=lz4 $sendfs; then
702                 mk_files 1000 256 0 $sendfs &
703                 mk_files 1000 131072 0 $sendfs &
704                 mk_files 100 1048576 0 $sendfs &
705                 mk_files 10 10485760 0 $sendfs &
706                 mk_files 1 104857600 0 $sendfs &
707                 log_must wait
708                 log_must zfs snapshot $sendfs@a
709
710                 rm_files 200 256 0 $sendfs &
711                 rm_files 200 131072 0 $sendfs &
712                 rm_files 20 1048576 0 $sendfs &
713                 rm_files 2 10485760 0 $sendfs &
714                 log_must wait
715
716                 mk_files 400 256 0 $sendfs &
717                 mk_files 400 131072 0 $sendfs &
718                 mk_files 40 1048576 0 $sendfs &
719                 mk_files 4 10485760 0 $sendfs &
720                 log_must wait
721
722                 log_must zfs snapshot $sendfs@b
723                 log_must eval "zfs send -v $sendfs@a >/$sendpool/initial.zsend"
724                 log_must eval "zfs send -v -i @a $sendfs@b " \
725                     ">/$sendpool/incremental.zsend"
726         fi
727
728         log_must zfs create -o compress=lz4 $streamfs
729 }
730
731 #
732 # Check to see if the specified features are set in a send stream.
733 # The values for these features are found in include/sys/zfs_ioctl.h
734 #
735 # $1 The stream file
736 # $2-$n The flags expected in the stream
737 #
738 function stream_has_features
739 {
740         typeset file=$1
741         shift
742
743         [[ -f $file ]] || log_fail "Couldn't find file: $file"
744         typeset flags=$(cat $file | zstreamdump | \
745             awk '/features =/ {features = $3} END {print features}')
746         typeset -A feature
747         feature[dedup]="1"
748         feature[dedupprops]="2"
749         feature[sa_spill]="4"
750         feature[embed_data]="10000"
751         feature[lz4]="20000"
752         feature[mooch_byteswap]="40000"
753         feature[large_blocks]="80000"
754         feature[resuming]="100000"
755         feature[redacted]="200000"
756         feature[compressed]="400000"
757
758         typeset flag known derived=0
759         for flag in "$@"; do
760                 known=${feature[$flag]}
761                 [[ -z $known ]] && log_fail "Unknown feature: $flag"
762
763                 derived=$(printf "%x" $((0x${flags} & 0x${feature[$flag]})))
764                 [[ $derived = $known ]] || return 1
765         done
766
767         return 0
768 }
769
770 #
771 # Given a send stream, verify that the size of the stream matches what's
772 # expected based on the source or target dataset. If the stream is an
773 # incremental stream, subtract the size of the source snapshot before
774 # comparing. This function does not currently handle incremental streams
775 # that remove data.
776 #
777 # $1 The zstreamdump output file
778 # $2 The dataset to compare against
779 #    This can be a source of a send or recv target (fs, not snapshot)
780 # $3 The percentage below which verification is deemed a failure
781 # $4 The source snapshot of an incremental send
782 #
783
784 function verify_stream_size
785 {
786         typeset stream=$1
787         typeset ds=$2
788         typeset percent=${3:-90}
789         typeset inc_src=$4
790
791         [[ -f $stream ]] || log_fail "No such file: $stream"
792         datasetexists $ds || log_fail "No such dataset: $ds"
793
794         typeset stream_size=$(cat $stream | zstreamdump | sed -n \
795             's/ Total payload size = \(.*\) (0x.*)/\1/p')
796
797         typeset inc_size=0
798         if [[ -n $inc_src ]]; then
799                 inc_size=$(get_prop lrefer $inc_src)
800                 if stream_has_features $stream compressed; then
801                         inc_size=$(get_prop refer $inc_src)
802                 fi
803         fi
804
805         if stream_has_features $stream compressed; then
806                 ds_size=$(get_prop refer $ds)
807         else
808                 ds_size=$(get_prop lrefer $ds)
809         fi
810         ds_size=$((ds_size - inc_size))
811
812         within_percent $stream_size $ds_size $percent || log_fail \
813             "$stream_size $ds_size differed by too much"
814 }
815
816 # Cleanup function for tests involving resumable send
817 function resume_cleanup
818 {
819         typeset sendfs=$1
820         typeset streamfs=$2
821         typeset sendpool=${sendfs%%/*}
822
823         datasetexists $sendfs && log_must_busy zfs destroy -r $sendfs
824         datasetexists $streamfs && log_must_busy zfs destroy -r $streamfs
825         cleanup_pool $POOL2
826         rm -f /$sendpool/initial.zsend /$sendpool/incremental.zsend
827 }
828
829 # Randomly set the property to one of the enumerated values.
830 function rand_set_prop
831 {
832         typeset dtst=$1
833         typeset prop=$2
834         shift 2
835         typeset value=$(random_get $@)
836
837         log_must eval "zfs set $prop='$value' $dtst"
838 }
839
840 # Generate a recursive checksum of a filesystem which includes the file
841 # contents and any associated extended attributes.
842 function recursive_cksum
843 {
844         case "$(uname)" in
845         FreeBSD)
846                 find $1 -type f -exec sh -c 'sha256 -q {}; lsextattr -q \
847                     system {} | sha256 -q; lsextattr -q user {} | sha256 -q' \
848                     \; | sort | sha256 -q
849                 ;;
850         *)
851                 find $1 -type f -exec sh -c 'sha256sum {}; getfattr \
852                     --absolute-names --only-values -d {} | sha256sum' \; | \
853                     sort -k 2 | awk '{ print $1 }' | sha256sum | \
854                     awk '{ print $1 }'
855                 ;;
856         esac
857 }