]> git.proxmox.com Git - mirror_zfs-debian.git/blob - tests/zfs-tests/tests/functional/rsend/rsend.kshlib
fde4123be872e60bde573f58942db36b2c4ce9c7
[mirror_zfs-debian.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, 2016 by Delphix. All rights reserved.
29 #
30
31 . $STF_SUITE/include/libtest.shlib
32 . $STF_SUITE/include/math.shlib
33 . $STF_SUITE/tests/functional/rsend/rsend.cfg
34
35 #
36 # Set up test model which includes various datasets
37 #
38 # @final
39 # @snapB
40 # @init
41 # |
42 # ______ pclone
43 # | /
44 # |@psnap
45 # || @final
46 # ||@final @final @snapC
47 # ||@snapC @snapC @snapB
48 # ||@snapA @snapB @snapA
49 # ||@init @init @init
50 # ||| | |
51 # $pool -------- $FS ------- fs1 ------- fs2
52 # \ \\_____ \ |
53 # vol vol \____ \ @fsnap
54 # | | \ \ \
55 # @init @vsnap | ------------ fclone
56 # @snapA @init \ | |
57 # @final @snapB \ | @init
58 # @snapC vclone @snapA
59 # @final | @final
60 # @init
61 # @snapC
62 # @final
63 #
64 # $1 pool name
65 #
66 function setup_test_model
67 {
68 typeset pool=$1
69
70 log_must zfs create -p $pool/$FS/fs1/fs2
71
72 log_must zfs snapshot $pool@psnap
73 log_must zfs clone $pool@psnap $pool/pclone
74
75 if is_global_zone ; then
76 log_must zfs create -V 16M $pool/vol
77 log_must zfs create -V 16M $pool/$FS/vol
78 block_device_wait
79
80 log_must zfs snapshot $pool/$FS/vol@vsnap
81 log_must zfs clone $pool/$FS/vol@vsnap $pool/$FS/vclone
82 block_device_wait
83 fi
84
85 log_must snapshot_tree $pool/$FS/fs1/fs2@fsnap
86 log_must zfs clone $pool/$FS/fs1/fs2@fsnap $pool/$FS/fs1/fclone
87 log_must zfs snapshot -r $pool@init
88
89 log_must snapshot_tree $pool@snapA
90 log_must snapshot_tree $pool@snapC
91 log_must snapshot_tree $pool/pclone@snapB
92 log_must snapshot_tree $pool/$FS@snapB
93 log_must snapshot_tree $pool/$FS@snapC
94 log_must snapshot_tree $pool/$FS/fs1@snapA
95 log_must snapshot_tree $pool/$FS/fs1@snapB
96 log_must snapshot_tree $pool/$FS/fs1@snapC
97 log_must snapshot_tree $pool/$FS/fs1/fclone@snapA
98
99 if is_global_zone ; then
100 log_must zfs snapshot $pool/vol@snapA
101 log_must zfs snapshot $pool/$FS/vol@snapB
102 log_must zfs snapshot $pool/$FS/vol@snapC
103 log_must zfs snapshot $pool/$FS/vclone@snapC
104 fi
105
106 log_must zfs snapshot -r $pool@final
107
108 return 0
109 }
110
111 #
112 # Cleanup the BACKDIR and given pool content and all the sub datasets
113 #
114 # $1 pool name
115 #
116 function cleanup_pool
117 {
118 typeset pool=$1
119 log_must rm -rf $BACKDIR/*
120
121 if is_global_zone ; then
122 log_must zfs destroy -Rf $pool
123 else
124 typeset list=$(zfs list -H -r -t all -o name $pool)
125 for ds in $list ; do
126 if [[ $ds != $pool ]] ; then
127 if datasetexists $ds ; then
128 log_must zfs destroy -Rf $ds
129 fi
130 fi
131 done
132 fi
133
134 typeset mntpnt=$(get_prop mountpoint $pool)
135 if ! ismounted $pool ; then
136 # Make sure mountpoint directory is empty
137 if [[ -d $mntpnt ]]; then
138 log_must rm -rf $mntpnt/*
139 fi
140
141 log_must zfs mount $pool
142 fi
143 if [[ -d $mntpnt ]]; then
144 rm -rf $mntpnt/*
145 fi
146
147 return 0
148 }
149
150 function cleanup_pools
151 {
152 cleanup_pool $POOL2
153 destroy_pool $POOL3
154 }
155
156 #
157 # Detect if the given two filesystems have same sub-datasets
158 #
159 # $1 source filesystem
160 # $2 destination filesystem
161 #
162 function cmp_ds_subs
163 {
164 typeset src_fs=$1
165 typeset dst_fs=$2
166
167 zfs list -r -H -t all -o name $src_fs > $BACKDIR/src1
168 zfs list -r -H -t all -o name $dst_fs > $BACKDIR/dst1
169
170 eval sed -e 's:^$src_fs:PREFIX:g' < $BACKDIR/src1 > $BACKDIR/src
171 eval sed -e 's:^$dst_fs:PREFIX:g' < $BACKDIR/dst1 > $BACKDIR/dst
172
173 diff $BACKDIR/src $BACKDIR/dst
174 typeset -i ret=$?
175
176 rm -f $BACKDIR/src $BACKDIR/dst $BACKDIR/src1 $BACKDIR/dst1
177
178 return $ret
179 }
180
181 #
182 # Compare all the directories and files in two filesystems
183 #
184 # $1 source filesystem
185 # $2 destination filesystem
186 #
187 function cmp_ds_cont
188 {
189 typeset src_fs=$1
190 typeset dst_fs=$2
191
192 typeset srcdir dstdir
193 srcdir=$(get_prop mountpoint $src_fs)
194 dstdir=$(get_prop mountpoint $dst_fs)
195
196 diff -r $srcdir $dstdir > /dev/null 2>&1
197 return $?
198 }
199
200 #
201 # Compare the given two dataset properties
202 #
203 # $1 dataset 1
204 # $2 dataset 2
205 #
206 function cmp_ds_prop
207 {
208 typeset dtst1=$1
209 typeset dtst2=$2
210
211 for item in "type" "origin" "volblocksize" "acltype" "dnodesize" \
212 "atime" "canmount" "checksum" "compression" "copies" "devices" \
213 "exec" "quota" "readonly" "recordsize" "reservation" "setuid" \
214 "snapdir" "version" "volsize" "xattr" "zoned" "mountpoint";
215 do
216 zfs get -H -o property,value,source $item $dtst1 >> \
217 $BACKDIR/dtst1
218 zfs get -H -o property,value,source $item $dtst2 >> \
219 $BACKDIR/dtst2
220 done
221
222 eval sed -e 's:$dtst1:PREFIX:g' < $BACKDIR/dtst1 > $BACKDIR/dtst1
223 eval sed -e 's:$dtst2:PREFIX:g' < $BACKDIR/dtst2 > $BACKDIR/dtst2
224
225 diff $BACKDIR/dtst1 $BACKDIR/dtst2
226 typeset -i ret=$?
227
228 rm -f $BACKDIR/dtst1 $BACKDIR/dtst2
229
230 return $ret
231
232 }
233
234 #
235 # Random create directories and files
236 #
237 # $1 directory
238 #
239 function random_tree
240 {
241 typeset dir=$1
242
243 if [[ -d $dir ]]; then
244 rm -rf $dir
245 fi
246 mkdir -p $dir
247 typeset -i ret=$?
248
249 typeset -i nl nd nf
250 ((nl = RANDOM % 6 + 1))
251 ((nd = RANDOM % 3 ))
252 ((nf = RANDOM % 5 ))
253 mktree -b $dir -l $nl -d $nd -f $nf
254 ((ret |= $?))
255
256 return $ret
257 }
258
259 #
260 # Put data in filesystem and take snapshot
261 #
262 # $1 snapshot name
263 #
264 function snapshot_tree
265 {
266 typeset snap=$1
267 typeset ds=${snap%%@*}
268 typeset type=$(get_prop "type" $ds)
269
270 typeset -i ret=0
271 if [[ $type == "filesystem" ]]; then
272 typeset mntpnt=$(get_prop mountpoint $ds)
273 ((ret |= $?))
274
275 if ((ret == 0)) ; then
276 eval random_tree $mntpnt/${snap##$ds}
277 ((ret |= $?))
278 fi
279 fi
280
281 if ((ret == 0)) ; then
282 zfs snapshot $snap
283 ((ret |= $?))
284 fi
285
286 return $ret
287 }
288
289 #
290 # Destroy the given snapshot and stuff
291 #
292 # $1 snapshot
293 #
294 function destroy_tree
295 {
296 typeset -i ret=0
297 typeset snap
298 for snap in "$@" ; do
299 zfs destroy $snap
300 ret=$?
301
302 typeset ds=${snap%%@*}
303 typeset type=$(get_prop "type" $ds)
304 if [[ $type == "filesystem" ]]; then
305 typeset mntpnt=$(get_prop mountpoint $ds)
306 ((ret |= $?))
307
308 if ((ret != 0)); then
309 rm -r $mntpnt/$snap
310 ((ret |= $?))
311 fi
312 fi
313
314 if ((ret != 0)); then
315 return $ret
316 fi
317 done
318
319 return 0
320 }
321
322 #
323 # Get all the sub-datasets of give dataset with specific suffix
324 #
325 # $1 Given dataset
326 # $2 Suffix
327 #
328 function getds_with_suffix
329 {
330 typeset ds=$1
331 typeset suffix=$2
332
333 typeset list=$(zfs list -r -H -t all -o name $ds | grep "$suffix$")
334
335 echo $list
336 }
337
338 #
339 # Output inherited properties whitch is edited for file system
340 #
341 function fs_inherit_prop
342 {
343 typeset fs_prop
344 if is_global_zone ; then
345 fs_prop=$(zfs inherit 2>&1 | \
346 awk '$2=="YES" && $3=="YES" {print $1}')
347 if ! is_te_enabled ; then
348 fs_prop=$(echo $fs_prop | grep -v "mlslabel")
349 fi
350 else
351 fs_prop=$(zfs inherit 2>&1 | \
352 awk '$2=="YES" && $3=="YES" {print $1}'|
353 egrep -v "devices|mlslabel|sharenfs|sharesmb|zoned")
354 fi
355
356 echo $fs_prop
357 }
358
359 #
360 # Output inherited properties for volume
361 #
362 function vol_inherit_prop
363 {
364 echo "checksum readonly"
365 }
366
367 #
368 # Get the destination dataset to compare
369 #
370 function get_dst_ds
371 {
372 typeset srcfs=$1
373 typeset dstfs=$2
374
375 #
376 # If the srcfs is not pool
377 #
378 if ! zpool list $srcfs > /dev/null 2>&1 ; then
379 eval dstfs="$dstfs/${srcfs#*/}"
380 fi
381
382 echo $dstfs
383 }
384
385 #
386 # Make test files
387 #
388 # $1 Number of files to create
389 # $2 Maximum file size
390 # $3 File ID offset
391 # $4 File system to create the files on
392 #
393 function mk_files
394 {
395 nfiles=$1
396 maxsize=$2
397 file_id_offset=$3
398 fs=$4
399
400 for ((i=0; i<$nfiles; i=i+1)); do
401 dd if=/dev/urandom \
402 of=/$fs/file-$maxsize-$((i+$file_id_offset)) \
403 bs=$((($RANDOM * $RANDOM % ($maxsize - 1)) + 1)) \
404 count=1 >/dev/null 2>&1 || log_fail \
405 "Failed to create /$fs/file-$maxsize-$((i+$file_id_offset))"
406 done
407 echo Created $nfiles files of random sizes up to $maxsize bytes
408 }
409
410 #
411 # Remove test files
412 #
413 # $1 Number of files to remove
414 # $2 Maximum file size
415 # $3 File ID offset
416 # $4 File system to remove the files from
417 #
418 function rm_files
419 {
420 nfiles=$1
421 maxsize=$2
422 file_id_offset=$3
423 fs=$4
424
425 for ((i=0; i<$nfiles; i=i+1)); do
426 rm -f /$fs/file-$maxsize-$((i+$file_id_offset))
427 done
428 echo Removed $nfiles files of random sizes up to $maxsize bytes
429 }
430
431 #
432 # Mess up file contents
433 #
434 # $1 The file path
435 #
436 function mess_file
437 {
438 file=$1
439
440 filesize=$(stat -c '%s' $file)
441 offset=$(($RANDOM * $RANDOM % $filesize))
442 if (($RANDOM % 7 <= 1)); then
443 #
444 # We corrupt 2 bytes to minimize the chance that we
445 # write the same value that's already there.
446 #
447 log_must eval "dd if=/dev/urandom of=$file conv=notrunc " \
448 "bs=1 count=2 seek=$offset >/dev/null 2>&1"
449 else
450 log_must truncate -s $offset $file
451 fi
452 }
453
454 #
455 # Diff the send/receive filesystems
456 #
457 # $1 The sent filesystem
458 # $2 The received filesystem
459 #
460 function file_check
461 {
462 sendfs=$1
463 recvfs=$2
464
465 if [[ -d /$recvfs/.zfs/snapshot/a && -d \
466 /$sendfs/.zfs/snapshot/a ]]; then
467 diff -r /$recvfs/.zfs/snapshot/a /$sendfs/.zfs/snapshot/a
468 [[ $? -eq 0 ]] || log_fail "Differences found in snap a"
469 fi
470 if [[ -d /$recvfs/.zfs/snapshot/b && -d \
471 /$sendfs/.zfs/snapshot/b ]]; then
472 diff -r /$recvfs/.zfs/snapshot/b /$sendfs/.zfs/snapshot/b
473 [[ $? -eq 0 ]] || log_fail "Differences found in snap b"
474 fi
475 }
476
477 #
478 # Resume test helper
479 #
480 # $1 The ZFS send command
481 # $2 The filesystem where the streams are sent
482 # $3 The receive filesystem
483 #
484 function resume_test
485 {
486 sendcmd=$1
487 streamfs=$2
488 recvfs=$3
489
490 stream_num=1
491 log_must eval "$sendcmd >/$streamfs/$stream_num"
492
493 for ((i=0; i<2; i=i+1)); do
494 mess_file /$streamfs/$stream_num
495 log_mustnot zfs recv -suv $recvfs </$streamfs/$stream_num
496 stream_num=$((stream_num+1))
497
498 token=$(zfs get -Hp -o value receive_resume_token $recvfs)
499 log_must eval "zfs send -v -t $token >/$streamfs/$stream_num"
500 [[ -f /$streamfs/$stream_num ]] || \
501 log_fail "NO FILE /$streamfs/$stream_num"
502 done
503 log_must zfs recv -suv $recvfs </$streamfs/$stream_num
504 }
505
506 #
507 # Setup filesystems for the resumable send/receive tests
508 #
509 # $1 The "send" filesystem
510 # $2 The "recv" filesystem
511 #
512 function test_fs_setup
513 {
514 typeset sendfs=$1
515 typeset recvfs=$2
516 typeset sendpool=${sendfs%%/*}
517 typeset recvpool=${recvfs%%/*}
518
519 datasetexists $sendfs && log_must zfs destroy -r $sendpool
520 datasetexists $recvfs && log_must zfs destroy -r $recvpool
521
522 if $(datasetexists $sendfs || zfs create -o compress=lz4 $sendfs); then
523 mk_files 1000 256 0 $sendfs &
524 mk_files 1000 131072 0 $sendfs &
525 mk_files 100 1048576 0 $sendfs &
526 mk_files 10 10485760 0 $sendfs &
527 mk_files 1 104857600 0 $sendfs &
528 log_must wait
529 log_must zfs snapshot $sendfs@a
530
531 rm_files 200 256 0 $sendfs &
532 rm_files 200 131072 0 $sendfs &
533 rm_files 20 1048576 0 $sendfs &
534 rm_files 2 10485760 0 $sendfs &
535 log_must wait
536
537 mk_files 400 256 0 $sendfs &
538 mk_files 400 131072 0 $sendfs &
539 mk_files 40 1048576 0 $sendfs &
540 mk_files 4 10485760 0 $sendfs &
541 log_must wait
542
543 log_must zfs snapshot $sendfs@b
544 log_must eval "zfs send -v $sendfs@a >/$sendpool/initial.zsend"
545 log_must eval "zfs send -v -i @a $sendfs@b " \
546 ">/$sendpool/incremental.zsend"
547 fi
548
549 if datasetexists $streamfs; then
550 log_must zfs destroy -r $streamfs
551 fi
552 log_must zfs create -o compress=lz4 $sendpool/stream
553 }
554
555 #
556 # Check to see if the specified features are set in a send stream.
557 # The values for these features are found in include/sys/zfs_ioctl.h
558 #
559 # $1 The stream file
560 # $2-$n The flags expected in the stream
561 #
562 function stream_has_features
563 {
564 typeset file=$1
565 shift
566
567 [[ -f $file ]] || log_fail "Couldn't find file: $file"
568 typeset flags=$(cat $file | zstreamdump | \
569 awk '/features =/ {features = $3} END {print features}')
570 typeset -A feature
571 feature[dedup]="1"
572 feature[dedupprops]="2"
573 feature[sa_spill]="4"
574 feature[embed_data]="10000"
575 feature[lz4]="20000"
576 feature[mooch_byteswap]="40000"
577 feature[large_blocks]="80000"
578 feature[resuming]="100000"
579 feature[redacted]="200000"
580 feature[compressed]="400000"
581
582 typeset flag known derived=0
583 for flag in "$@"; do
584 known=${feature[$flag]}
585 [[ -z $known ]] && log_fail "Unknown feature: $flag"
586
587 derived=$(printf "%x" $((0x${flags} & 0x${feature[$flag]})))
588 [[ $derived = $known ]] || return 1
589 done
590
591 return 0
592 }
593
594 #
595 # Parse zstreamdump -v output. The output varies for each kind of record:
596 # BEGIN records are simply output as "BEGIN"
597 # END records are output as "END"
598 # OBJECT records become "OBJECT <object num>"
599 # FREEOBJECTS records become "FREEOBJECTS <startobj> <numobjs>"
600 # FREE records become "<record type> <start> <length>"
601 # WRITE records become:
602 # "<record type> <compression type> <start> <logical size> <compressed size>
603 # <data size>"
604 #
605 function parse_dump
606 {
607 sed '/^WRITE/{N;s/\n/ /;}' | grep "^[A-Z]" | awk '{
608 if ($1 == "BEGIN" || $1 == "END") print $1
609 if ($1 == "OBJECT") print $1" "$4
610 if ($1 == "FREEOBJECTS") print $1" "$4" "$7
611 if ($1 == "FREE") print $1" "$7" "$10
612 if ($1 == "WRITE") print $1" "$15" "$18" "$21" "$24" "$27}'
613 }
614
615 #
616 # Given a send stream, verify that the size of the stream matches what's
617 # expected based on the source or target dataset. If the stream is an
618 # incremental stream, subtract the size of the source snapshot before
619 # comparing. This function does not currently handle incremental streams
620 # that remove data.
621 #
622 # $1 The zstreamdump output file
623 # $2 The dataset to compare against
624 # This can be a source of a send or recv target (fs, not snapshot)
625 # $3 The percentage below which verification is deemed a failure
626 # $4 The source snapshot of an incremental send
627 #
628
629 function verify_stream_size
630 {
631 typeset stream=$1
632 typeset ds=$2
633 typeset percent=${3:-90}
634 typeset inc_src=$4
635
636 [[ -f $stream ]] || log_fail "No such file: $stream"
637 datasetexists $ds || log_fail "No such dataset: $ds"
638
639 typeset stream_size=$(cat $stream | zstreamdump | sed -n \
640 's/ Total write size = \(.*\) (0x.*)/\1/p')
641
642 typeset inc_size=0
643 if [[ -n $inc_src ]]; then
644 inc_size=$(get_prop lrefer $inc_src)
645 if stream_has_features $stream compressed; then
646 inc_size=$(get_prop refer $inc_src)
647 fi
648 fi
649
650 if stream_has_features $stream compressed; then
651 ds_size=$(get_prop refer $ds)
652 else
653 ds_size=$(get_prop lrefer $ds)
654 fi
655 ds_size=$((ds_size - inc_size))
656
657 within_percent $stream_size $ds_size $percent || log_fail \
658 "$stream_size $ds_size differed by too much"
659 }
660
661 # Cleanup function for tests involving resumable send
662 function resume_cleanup
663 {
664 typeset sendfs=$1
665 typeset streamfs=$2
666
667 datasetexists $sendfs && log_must zfs destroy -r $sendfs
668 datasetexists $streamfs && log_must zfs destroy -r $streamfs
669 cleanup_pool $POOL2
670 rm -f /$POOL/initial.zsend /$POOL/incremental.zsend
671 }