1#!/bin/ksh -p
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or https://opensource.org/licenses/CDDL-1.0.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22
23#
24# Copyright (c) 2015, 2016 by Delphix. All rights reserved.
25#
26
27. $STF_SUITE/include/libtest.shlib
28
29#
30# DESCRIPTION:
31# Test that receiving a full send as a clone works correctly.
32#
33# STRATEGY:
34# 1. Create pool and filesystems.
35# 2. Send filesystem, receive as clone of itself.
36# 3. Verify that nop-write saves space.
37# 4. Send filesystem, receive as clone of other filesystem.
38# 5. Verify that contents are correct.
39# 6. Repeat steps 4 and 5 with filesystems swapped.
40#
41
42verify_runnable "both"
43
44fs=$TESTPOOL/$TESTFS/base/fs
45fs2=$TESTPOOL/$TESTFS/base/fs2
46rfs=$TESTPOOL/$TESTFS/base/rfs
47
48function make_object
49{
50	typeset objnum=$1
51	typeset mntpnt=$2
52	typeset type=$3
53	if [[ $type == "file" ]]; then
54		dd if=/dev/urandom of=${mntpnt}/f$objnum bs=512 count=16
55	elif [[ $type == "hole1" ]]; then
56		dd if=/dev/zero of=${mntpnt}/fh$objnum bs=512 count=5 seek=4 conv=notrunc
57	elif [[ $type == "hole2" ]]; then
58		dd if=/dev/zero of=${mntpnt}/fh$objnum bs=512 count=4 seek=5 conv=notrunc
59	elif [[ $type == "directory" ]]; then
60		mkdir ${mntpnt}/d$objnum
61	elif [[ $type == "missing" ]]; then
62		touch ${mntpnt}/h$objnum
63	fi
64}
65
66function create_pair
67{
68	typeset objnum=$1
69	typeset mntpnt1=$2
70	typeset mntpnt2=$3
71	typeset type1=$4
72	typeset type2=$5
73	make_object $objnum $mntpnt1 $type1
74	make_object $objnum $mntpnt2 $type2
75}
76
77function cleanup
78{
79	zfs destroy -Rf $TESTPOOL/$TESTFS/base
80	rm $TESTDIR/zr010p*
81}
82
83log_assert "zfs receive of full send as clone should work"
84log_onexit cleanup
85log_must zfs create -o checksum=sha256 -o compression=gzip -o recordsize=512 \
86	$TESTPOOL/$TESTFS/base
87
88log_must zfs create $fs
89log_must zfs create $fs2
90mntpnt=$(get_prop mountpoint $fs)
91mntpnt2=$(get_prop mountpoint $fs2)
92
93#
94# Now, we create the two filesystems.  By creating objects with
95# different types and the same object number in each filesystem, we
96# create a situation where, when you receive the full send of each as
97# a clone of the other, we will test to ensure that the code correctly
98# handles receiving all object types onto all other object types.
99#
100
101# Receive a file onto a file (and vice versa).
102create_pair 8 $mntpnt $mntpnt2 "file" "file"
103
104# Receive a file onto a file with holes (and vice versa).
105create_pair 9 $mntpnt $mntpnt2 "file" "hole1"
106
107# Receive a file onto a directory (and vice versa).
108create_pair 10 $mntpnt $mntpnt2 "file" "directory"
109
110# Receive a file onto a missing object (and vice versa).
111create_pair 11 $mntpnt $mntpnt2 "file" "missing"
112
113# Receive a file with holes onto a file with holes (and vice versa).
114create_pair 12 $mntpnt $mntpnt2 "hole1" "hole2"
115
116# Receive a file with holes onto a directory (and vice versa).
117create_pair 13 $mntpnt $mntpnt2 "hole1" "directory"
118
119# Receive a file with holes onto a missing object (and vice versa).
120create_pair 14 $mntpnt $mntpnt2 "hole1" "missing"
121
122# Receive a directory onto a directory (and vice versa).
123create_pair 15 $mntpnt $mntpnt2 "directory" "directory"
124
125# Receive a directory onto a missing object (and vice versa).
126create_pair 16 $mntpnt $mntpnt2 "directory" "missing"
127
128# Receive a missing object onto a missing object (and vice versa).
129create_pair 17 $mntpnt $mntpnt2 "missing" "missing"
130
131# Receive a file with a different record size onto a file (and vice versa).
132log_must zfs set recordsize=128k $fs
133dd if=/dev/urandom of=$mntpnt/f18 bs=128k count=64
134touch $mntpnt2/f18
135
136# Remove objects that are intended to be missing.
137rm $mntpnt/h17 $mntpnt2/h*
138
139# Add empty objects to $fs to exercise dmu_traverse code
140for i in {1..100}; do
141	log_must touch $mntpnt/uf$i
142done
143
144log_must zfs snapshot $fs@s1
145log_must zfs snapshot $fs2@s1
146
147log_must eval "zfs send $fs@s1 > $TESTDIR/zr010p"
148log_must eval "zfs send $fs2@s1 > $TESTDIR/zr010p2"
149
150
151#
152# Test that, when we receive a full send as a clone of itself,
153# nop-write saves us all the space used by data blocks.
154#
155log_must eval "zfs receive -o origin=$fs@s1 $rfs < $TESTDIR/zr010p"
156size=$(get_prop used $rfs)
157size2=$(get_prop used $fs)
158if [[ $size -ge $(($size2 / 10)) ]] then
159        log_fail "nop-write failure; expected usage less than "\
160		"$(($size2 / 10)), but is using $size"
161fi
162log_must zfs destroy -fr $rfs
163
164# Correctness testing: receive each full send as a clone of the other fiesystem.
165log_must eval "zfs receive -o origin=$fs2@s1 $rfs < $TESTDIR/zr010p"
166mntpnt_old=$(get_prop mountpoint $fs)
167mntpnt_new=$(get_prop mountpoint $rfs)
168log_must directory_diff $mntpnt_old $mntpnt_new
169log_must zfs destroy -r $rfs
170
171log_must eval "zfs receive -o origin=$fs@s1 $rfs < $TESTDIR/zr010p2"
172mntpnt_old=$(get_prop mountpoint $fs2)
173mntpnt_new=$(get_prop mountpoint $rfs)
174log_must directory_diff $mntpnt_old $mntpnt_new
175
176log_pass "zfs receive of full send as clone works"
177