1#!/bin/ksh -p
2# SPDX-License-Identifier: CDDL-1.0
3#
4# CDDL HEADER START
5#
6# This file and its contents are supplied under the terms of the
7# Common Development and Distribution License ("CDDL"), version 1.0.
8# You may only use this file in accordance with the terms of version
9# 1.0 of the CDDL.
10#
11# A full copy of the text of the CDDL should have accompanied this
12# source.  A copy of the CDDL is also available via the Internet at
13# http://www.illumos.org/license/CDDL.
14#
15# CDDL HEADER END
16#
17
18#
19# Copyright 2017, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
20#
21
22. $STF_SUITE/include/libtest.shlib
23. $STF_SUITE/tests/functional/cli_root/zfs_set/zfs_set_common.kshlib
24
25#
26# DESCRIPTION:
27# Verify ZFS property override (-o) and exclude (-x) options work when
28# receiving a send stream
29#
30# STRATEGY:
31# 1. Create a filesystem with children.
32# 2. Snapshot the filesystems.
33# 3. Create various send streams (full, incremental, replication) and verify
34#    we can both override and exclude native and user properties.
35#
36
37verify_runnable "both"
38
39function cleanup
40{
41	log_must rm -f $streamfile_full
42	log_must rm -f $streamfile_incr
43	log_must rm -f $streamfile_repl
44	log_must rm -f $streamfile_trun
45	destroy_dataset "$orig" "-rf"
46	destroy_dataset "$dest" "-rf"
47}
48
49log_assert "ZFS receive property override and exclude options work as expected."
50log_onexit cleanup
51
52orig=$TESTPOOL/$TESTFS1
53origsub=$orig/sub
54dest=$TESTPOOL/$TESTFS2
55destsub=$dest/sub
56typeset userprop=$(valid_user_property 8)
57typeset userval=$(user_property_value 8)
58typeset streamfile_full=$TESTDIR/streamfile_full.$$
59typeset streamfile_incr=$TESTDIR/streamfile_incr.$$
60typeset streamfile_repl=$TESTDIR/streamfile_repl.$$
61typeset streamfile_trun=$TESTDIR/streamfile_trun.$$
62
63#
64# 3.1 Verify we can't specify the same property in multiple -o or -x options
65#     or an invalid value was specified.
66#
67# Create a full send stream
68log_must zfs create $orig
69log_must zfs snapshot $orig@snap1
70log_must eval "zfs send $orig@snap1 > $streamfile_full"
71# Verify we reject invalid options
72log_mustnot eval "zfs recv $dest -o atime < $streamfile_full"
73log_mustnot eval "zfs recv $dest -x atime=off < $streamfile_full"
74log_mustnot eval "zfs recv $dest -o atime=off -x atime < $streamfile_full"
75log_mustnot eval "zfs recv $dest -o atime=off -o atime=on < $streamfile_full"
76log_mustnot eval "zfs recv $dest -x atime -x atime < $streamfile_full"
77log_mustnot eval "zfs recv $dest -o version=1 < $streamfile_full"
78log_mustnot eval "zfs recv $dest -x version < $streamfile_full"
79log_mustnot eval "zfs recv $dest -x normalization < $streamfile_full"
80# Verify we also reject invalid ZVOL options
81log_must zfs create -V 32K -s $orig/zvol
82log_must eval "zfs send $orig@snap1 > $streamfile_full"
83log_mustnot eval "zfs recv $dest -x volsize < $streamfile_full"
84log_mustnot eval "zfs recv $dest -o volsize=32K < $streamfile_full"
85# Cleanup
86block_device_wait
87log_must_busy zfs destroy -r -f $orig
88
89#
90# 3.2 Verify -o property=value works on streams without properties.
91#
92# Create a full send stream
93log_must zfs create $orig
94log_must zfs snapshot $orig@snap1
95log_must eval "zfs send $orig@snap1 > $streamfile_full"
96# Receive the full stream, override some properties
97log_must eval "zfs recv -o compression=on -o '$userprop:dest'='$userval' "\
98	"$dest < $streamfile_full"
99log_must eval "check_prop_source $dest compression on local"
100log_must eval "check_prop_source $dest '$userprop:dest' '$userval' local"
101# Cleanup
102log_must zfs destroy -r -f $orig
103log_must zfs destroy -r -f $dest
104
105#
106# 3.3 Verify -o property=value and -x work on both native and user properties
107#     for an incremental replication send stream.
108#
109# Create a dataset tree and receive it
110log_must zfs create $orig
111log_must zfs create $origsub
112log_must zfs snapshot -r $orig@snap1
113log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
114log_must eval "zfs recv $dest < $streamfile_repl"
115# Fill the datasets with properties and create an incremental replication stream
116log_must zfs snapshot -r $orig@snap2
117log_must zfs snapshot -r $orig@snap3
118log_must eval "zfs set copies=2 $orig"
119log_must eval "zfs set '$userprop:orig'='$userval' $orig"
120log_must eval "zfs set '$userprop:orig'='$userval' $origsub"
121log_must eval "zfs set '$userprop:snap'='$userval' $orig@snap1"
122log_must eval "zfs set '$userprop:snap'='$userval' $origsub@snap3"
123log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
124# Sets various combination of override and exclude options
125log_must eval "zfs recv -F -o atime=off -o '$userprop:dest2'='$userval' "\
126	"-o quota=123456789 -o checksum=sha512 -x compression "\
127        "-x '$userprop:orig' -x '$userprop:snap3' $dest < $streamfile_incr"
128# Verify we can correctly override and exclude properties
129log_must eval "check_prop_source $dest copies 2 received"
130log_must eval "check_prop_source $dest atime off local"
131log_must eval "check_prop_source $dest '$userprop:dest2' '$userval' local"
132log_must eval "check_prop_source $dest quota 123456789 local"
133log_must eval "check_prop_source $dest checksum sha512 local"
134log_must eval "check_prop_inherit $destsub copies $dest"
135log_must eval "check_prop_inherit $destsub atime $dest"
136log_must eval "check_prop_inherit $destsub checksum $dest"
137log_must eval "check_prop_inherit $destsub '$userprop:dest2' $dest"
138log_must eval "check_prop_source $destsub quota 0 default"
139log_must eval "check_prop_source $destsub compression on default"
140log_must eval "check_prop_missing $dest '$userprop:orig'"
141log_must eval "check_prop_missing $destsub '$userprop:orig'"
142log_must eval "check_prop_source " \
143	"$dest@snap1 '$userprop:snap' '$userval' received"
144log_must eval "check_prop_source " \
145	"$destsub@snap3 '$userprop:snap' '$userval' received"
146log_must eval "check_prop_missing $dest@snap3 '$userprop:snap3'"
147log_must eval "check_prop_missing $destsub@snap3 '$userprop:snap3'"
148# Cleanup
149log_must zfs destroy -r -f $orig
150log_must zfs destroy -r -f $dest
151
152#
153# 3.4 Verify '-x property' does not remove existing local properties and a
154#     modified sent property is received and updated to the new value but can
155#     still be excluded.
156#
157# Create a dataset tree
158log_must zfs create $orig
159log_must zfs create $origsub
160log_must zfs snapshot -r $orig@snap1
161log_must eval "zfs set copies=2 $orig"
162log_must eval "zfs set '$userprop:orig'='oldval' $orig"
163log_must eval "zfs set '$userprop:orig'='oldsubval' $origsub"
164log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
165log_must eval "zfs receive $dest < $streamfile_repl"
166log_must eval "check_prop_source $dest copies 2 received"
167log_must eval "check_prop_inherit $destsub copies $dest"
168log_must eval "check_prop_source $dest '$userprop:orig' 'oldval' received"
169log_must eval "check_prop_source $destsub '$userprop:orig' 'oldsubval' received"
170# Set new custom properties on both source and destination
171log_must eval "zfs set copies=3 $orig"
172log_must eval "zfs set '$userprop:orig'='newval' $orig"
173log_must eval "zfs set '$userprop:orig'='newsubval' $origsub"
174log_must eval "zfs set compression=gzip $dest"
175log_must eval "zfs set '$userprop:dest'='localval' $dest"
176# Receive the new stream, verify we preserve locally set properties
177log_must zfs snapshot -r $orig@snap2
178log_must zfs snapshot -r $orig@snap3
179log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
180log_must eval "zfs recv -F -x copies -x compression -x '$userprop:orig' " \
181	"-x '$userprop:dest' $dest < $streamfile_incr"
182log_must eval "check_prop_source $dest '$userprop:dest' 'localval' local"
183log_must eval "check_prop_received $dest '$userprop:orig' 'newval'"
184log_must eval "check_prop_received $destsub '$userprop:orig' 'newsubval'"
185log_must eval "check_prop_missing $dest '$userprop:orig'"
186log_must eval "check_prop_missing $destsub '$userprop:orig'"
187log_must eval "check_prop_source $dest copies 1 default"
188log_must eval "check_prop_received $dest copies 3"
189log_must eval "check_prop_source $destsub copies 1 default"
190log_must eval "check_prop_received $destsub copies '-'"
191log_must eval "check_prop_source $dest compression gzip local"
192log_must eval "check_prop_inherit $destsub compression $dest"
193# Cleanup
194log_must zfs destroy -r -f $orig
195log_must zfs destroy -r -f $dest
196
197#
198# 3.5 Verify we can exclude non-inheritable properties from a send stream
199#
200# Create a dataset tree and replication stream
201log_must zfs create $orig
202log_must zfs create $origsub
203log_must zfs snapshot -r $orig@snap1
204log_must eval "zfs set quota=123456789 $orig"
205log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
206# Receive the stream excluding non-inheritable properties
207log_must eval "zfs recv -F -x quota $dest < $streamfile_repl"
208log_must eval "check_prop_source $dest quota 0 default"
209log_must eval "check_prop_source $destsub quota 0 default"
210# Set some non-inheritable properties on the destination, verify we keep them
211log_must eval "zfs set quota=123456789 $dest"
212log_must eval "zfs set canmount=off $destsub"
213log_must zfs snapshot -r $orig@snap2
214log_must zfs snapshot -r $orig@snap3
215log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
216log_must eval "zfs recv -F -x quota -x canmount $dest < $streamfile_incr"
217log_must eval "check_prop_source $dest quota 123456789 local"
218log_must eval "check_prop_source $destsub quota 0 default"
219log_must eval "check_prop_source $destsub canmount off local"
220# Cleanup
221log_must zfs destroy -r -f $orig
222log_must zfs destroy -r -f $dest
223
224#
225# 3.6 Verify we correctly restore existing properties on a failed receive
226#
227# Receive a "clean" dataset tree
228log_must zfs create $orig
229log_must zfs create $origsub
230log_must zfs snapshot -r $orig@snap1
231log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
232log_must eval "zfs receive $dest < $streamfile_repl"
233# Set custom properties on the destination
234log_must eval "zfs set atime=off $dest"
235log_must eval "zfs set quota=123456789 $dest"
236log_must eval "zfs set '$userprop:orig'='$userval' $dest"
237log_must eval "zfs set '$userprop:origsub'='$userval' $destsub"
238# Create a truncated incremental replication stream
239mntpnt=$(get_prop mountpoint $orig)
240log_must eval "dd if=/dev/urandom of=$mntpnt/file bs=1024k count=10"
241log_must zfs snapshot -r $orig@snap2
242log_must zfs snapshot -r $orig@snap3
243log_must eval "zfs send -R -I $orig@snap1 $orig@snap3 > $streamfile_incr"
244log_must eval "dd if=$streamfile_incr of=$streamfile_trun bs=1024k count=9"
245# Receive the truncated stream, verify original properties are kept
246log_mustnot eval "zfs recv -F -o copies=3 -o quota=987654321 "\
247	"-o '$userprop:new'='badval' $dest < $streamfile_trun"
248log_must eval "check_prop_source $dest copies 1 default"
249log_must eval "check_prop_source $destsub copies 1 default"
250log_must eval "check_prop_source $dest atime off local"
251log_must eval "check_prop_inherit $destsub atime $dest"
252log_must eval "check_prop_source $dest quota 123456789 local"
253log_must eval "check_prop_source $destsub quota 0 default"
254log_must eval "check_prop_source $dest '$userprop:orig' '$userval' local"
255log_must eval "check_prop_inherit $destsub '$userprop:orig' $dest"
256log_must eval "check_prop_source $destsub '$userprop:origsub' '$userval' local"
257log_must eval "check_prop_missing $dest '$userprop:new'"
258# Cleanup
259log_must zfs destroy -r -f $orig
260log_must zfs destroy -r -f $dest
261
262#
263# 3.7 Verify we can receive a send stream excluding but not overriding
264#     properties invalid for the dataset type, in which case only the
265#     appropriate properties are set on the destination.
266log_must zfs create -V 128K -s $orig
267log_must zfs snapshot $orig@snap1
268log_must eval "zfs send $orig@snap1 > $streamfile_full"
269log_mustnot eval "zfs receive -o atime=off $dest < $streamfile_full"
270log_mustnot eval "zfs receive -o atime=off -x canmount $dest < $streamfile_full"
271log_must eval "zfs receive -x atime -x canmount $dest < $streamfile_full"
272log_must eval "check_prop_source $dest type volume -"
273log_must eval "check_prop_source $dest atime - -"
274log_must eval "check_prop_source $dest canmount - -"
275log_must_busy zfs destroy -r -f $orig
276log_must_busy zfs destroy -r -f $dest
277# Recursive sends also accept (and ignore) such overrides
278log_must zfs create $orig
279log_must zfs create -V 128K -s $origsub
280log_must zfs snapshot -r $orig@snap1
281log_must eval "zfs send -R $orig@snap1 > $streamfile_repl"
282log_must eval "zfs receive -o atime=off $dest < $streamfile_repl"
283log_must eval "check_prop_source $dest type filesystem -"
284log_must eval "check_prop_source $dest atime off local"
285log_must eval "check_prop_source $destsub type volume -"
286log_must eval "check_prop_source $destsub atime - -"
287# Cleanup
288block_device_wait
289log_must_busy zfs destroy -r -f $orig
290log_must_busy zfs destroy -r -f $dest
291
292#
293# 3.8 Verify 'zfs recv -x|-o' works correctly when used in conjunction with -d
294#     and -e options.
295#
296log_must zfs create -p $orig/1/2/3/4
297log_must eval "zfs set copies=2 $orig"
298log_must eval "zfs set atime=on $orig"
299log_must eval "zfs set '$userprop:orig'='oldval' $orig"
300log_must zfs snapshot -r $orig@snap1
301log_must eval "zfs send -R $orig/1/2@snap1 > $streamfile_repl"
302# Verify 'zfs recv -e'
303log_must zfs create $dest
304log_must eval "zfs receive -e -o copies=3 -x atime "\
305	"-o '$userprop:orig'='newval' $dest < $streamfile_repl"
306log_must datasetexists $dest/2/3/4
307log_must eval "check_prop_source $dest/2 copies 3 local"
308log_must eval "check_prop_inherit $dest/2/3/4 copies $dest/2"
309log_must eval "check_prop_source $dest/2/3/4 atime on default"
310log_must eval "check_prop_source $dest/2 '$userprop:orig' 'newval' local"
311log_must eval "check_prop_inherit $dest/2/3/4 '$userprop:orig' $dest/2"
312log_must zfs destroy -r -f $dest
313# Verify 'zfs recv -d'
314log_must zfs create $dest
315typeset fs="$(echo $orig | awk -F'/' '{print $NF}')"
316log_must eval "zfs receive -d -o copies=3 -x atime "\
317	"-o '$userprop:orig'='newval' $dest < $streamfile_repl"
318log_must datasetexists $dest/$fs/1/2/3/4
319log_must eval "check_prop_source $dest/$fs/1/2 copies 3 local"
320log_must eval "check_prop_inherit $dest/$fs/1/2/3/4 copies $dest/$fs/1/2"
321log_must eval "check_prop_source $dest/$fs/1/2/3/4 atime on default"
322log_must eval "check_prop_source $dest/$fs/1/2 '$userprop:orig' 'newval' local"
323log_must eval "check_prop_inherit $dest/$fs/1/2/3/4 '$userprop:orig' $dest/$fs/1/2"
324# We don't need to cleanup here
325
326log_pass "ZFS receive property override and exclude options passed."
327