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.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
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]
24 # Copyright 2009 Sun Microsystems, Inc. All rights reserved.
25 # Use is subject to license terms.
28 # Copyright (c) 2013, 2016 by Delphix. All rights reserved.
31 .
$STF_SUITE/include
/libtest.shlib
32 .
$STF_SUITE/tests
/functional
/inheritance
/inherit.kshlib
36 # Test that properties are correctly inherited using 'zfs set',
37 # 'zfs inherit' and 'zfs inherit -r'.
40 # 1) Read a configX.cfg file and create the specified datasets
41 # 2) Read a stateX.cfg file and execute the commands within it
42 # and verify that the properties have the correct values
43 # 3) Repeat steps 1-2 for each configX and stateX files found.
46 verify_runnable
"global"
48 log_assert
"Test properties are inherited correctly"
51 # Simple function to create specified datasets.
53 function create_dataset
{ #name type disks
58 if [[ $type == "POOL" ]]; then
59 create_pool
"$dataset" "$disks"
60 elif [[ $type == "CTR" ]]; then
61 log_must zfs create
$dataset
62 log_must zfs
set canmount
=off
$dataset
63 elif [[ $type == "FS" ]]; then
64 log_must zfs create
$dataset
66 log_fail
"Unrecognised type $type"
73 # Function to walk through all the properties in a
74 # dataset, setting them to a 'local' value if required.
76 function init_props
{ #dataset init_code
84 # Though the effect of '-' and 'default' is the same we
85 # call them out via a log_note to aid in debugging the
88 if [[ $init_code == "-" ]]; then
89 log_note
"Leaving properties for $dataset unchanged."
90 [[ $def_recordsize == 0 ]] && \
91 update_recordsize
$dataset $init_code
93 elif [[ $init_code == "default" ]]; then
94 log_note
"Leaving properties for $dataset at default values."
95 [[ $def_recordsize == 0 ]] && \
96 update_recordsize
$dataset $init_code
98 elif [[ $init_code == "local" ]]; then
99 log_note
"Setting properties for $dataset to local values."
100 while (( i
< ${#prop[*]} )); do
101 if [[ ${prop[i]} == "recordsize" ]]; then
102 update_recordsize
$dataset $init_code
104 if [[ ${prop[i]} == "mountpoint" ]]; then
105 set_n_verify_prop
${prop[i]} \
106 ${local_val[((i/2))]}.
$dir $dataset
108 set_n_verify_prop
${prop[i]} \
109 ${local_val[((i/2))]} $dataset
116 log_fail
"Unrecognised init code $init_code"
121 # We enter this function either to update the recordsize value
122 # in the default array, or to update the local value array.
124 function update_recordsize
{ #dataset init_code
131 # First need to find where the recordsize property is
132 # located in the arrays
134 while (( idx
< ${#prop[*]} )); do
135 [[ ${prop[idx]} == "recordsize" ]] && break
141 record_val
=`get_prop recordsize $dataset`
142 if [[ $init_code == "-" ||
$init_code == "default" ]]; then
143 def_val
[idx
]=$record_val
145 elif [[ $init_code == "local" ]]; then
146 log_must zfs
set recordsize
=$record_val $dataset
147 local_val
[idx
]=$record_val
152 # The mountpoint property is slightly different from other properties and
153 # so is handled here. For all other properties if they are set to a specific
154 # value at a higher level in the data hierarchy (i.e. checksum=on) then that
155 # value propogates down the hierarchy unchanged, with the source field being
156 # set to 'inherited from <higher dataset>'.
158 # The mountpoint property is different in that while the value propogates
159 # down the hierarchy, the value at each level is determined by a combination
160 # of the top-level value and the current level in the hierarchy.
162 # For example consider the case where we have a pool (called pool1), containing
163 # a dataset (ctr) which in turn contains a filesystem (fs). If we set the
164 # mountpoint of the pool to '/mnt2' then the mountpoints for the dataset and
165 # filesystem are '/mnt2/ctr' and /mnt2/ctr/fs' respectively, with the 'source'
166 # field being set to 'inherited from pool1'.
168 # So at the filesystem level to calculate what our mountpoint property should
169 # be set to we walk back up the hierarchy sampling the mountpoint property at
170 # each level and forming up the expected mountpoint value piece by piece until
171 # we reach the level specified in the 'source' field, which in this example is
172 # the top-level pool.
174 function get_mntpt_val
#dataset src index
183 if [[ $src == "local" ]]; then
184 # Extract mount points specific to datasets
185 if [[ $dataset == "TESTPOOL" ]]; then
186 mntpt
=${local_val[idx]}.1
187 elif [[ $dataset == "TESTPOOL/TESTCTR" ]]; then
188 mntpt
=${local_val[idx]}.2
190 mntpt
=${local_val[idx]}.3
192 elif [[ $src == "default" ]]; then
195 # Walk back up the hierarchy building up the
196 # expected mountpoint property value.
197 obj_name
=${dataset##*/}
199 while [[ $src != $dataset ]]; do
202 mnt_val
=`get_prop mountpoint $dset`
204 mod_prop_val
=${mnt_val##*/}
205 new_path
="/"$mod_prop_val$new_path
209 mntpt
=$new_path"/"$obj_name
215 # Simple function to verify that a property has the
218 function verify_prop_val
#property dataset src index
229 prop_val
=`get_prop $prop $dataset`
231 # mountpoint property is handled as a special case
232 if [[ $prop == "mountpoint" ]]; then
233 exp_val
=`get_mntpt_val $dataset $src $idx`
235 if [[ $src == "local" ]]; then
236 exp_val
=${local_val[idx]}
237 elif [[ $src == "default" ]]; then
238 exp_val
=${def_val[idx]}
241 # We are inheriting the value from somewhere
244 exp_val
=`get_prop $prop $src`
248 if [[ $prop_val != $exp_val ]]; then
249 # After putback PSARC/2008/231 Apr,09,2008,
250 # the default value of aclinherit has changed to be
251 # 'restricted' instead of 'secure',
252 # but the old interface of 'secure' still exist
254 if [[ $prop != "aclinherit" || \
255 $exp_val != "secure" || \
256 $prop_val != "restricted" ]]; then
258 log_fail
"$prop of $dataset is [$prop_val] rather "\
265 # Function to read the configX.cfg files and create the specified
268 function scan_config
{ #config-file
269 typeset config_file
=$1
274 typeset
-i mount_dir
=1
276 grep "^[^#]" $config_file |
{
277 while read name
type init
; do
278 create_dataset
$name $type $DISK
279 init_props
$name $init $mount_dir
280 ((mount_dir
= mount_dir
+ 1))
286 # Function to check an exit flag, calling log_fail if that exit flag
287 # is non-zero. Can be used from code that runs in a tight loop, which
288 # would otherwise result in a lot of journal output.
290 function check_failure
{ # int status, error message to use
292 typeset
-i exit_flag
=$1
295 if [[ $exit_flag -ne 0 ]]; then
296 log_fail
"$error_message"
302 # Main function. Executes the commands specified in the stateX.cfg
303 # files and then verifies that all the properties have the correct
304 # values and 'source' fields.
306 function scan_state
{ #state-file
307 typeset state_file
=$1
311 log_note
"Reading state from $state_file"
313 while ((i
< ${#prop[*]})); do
314 grep "^[^#]" $state_file |
{
315 while IFS
=: read target op
; do
317 # The user can if they wish specify that no
318 # operation be performed (by specifying '-'
319 # rather than a command). This is not as
320 # useless as it sounds as it allows us to
321 # verify that the dataset hierarchy has been
322 # set up correctly as specified in the
323 # configX.cfg file (which includes 'set'ting
324 # properties at a higher level and checking
325 # that they propogate down to the lower levels.
327 # Note in a few places here, we use
328 # check_failure, rather than log_must - this
329 # substantially reduces journal output.
331 if [[ $op == "-" ]]; then
332 log_note
"No operation specified"
334 export __ZFS_POOL_RESTRICT
="TESTPOOL"
335 log_must zfs unmount
-a
336 unset __ZFS_POOL_RESTRICT
338 for p
in ${prop[i]} ${prop[((i+1))]}; do
341 check_failure
$ret "zfs $op $p \
345 for check_obj
in $list; do
346 read init_src final_src
348 for p
in ${prop[i]} ${prop[((i+1))]}; do
349 # check_failure to keep journal small
350 verify_prop_src
$check_obj $p \
353 check_failure
$ret "verify" \
354 "_prop_src $check_obj $p" \
357 # Again, to keep journal size down.
358 verify_prop_val
$p $check_obj \
361 check_failure
$ret "verify" \
362 "_prop_val $check_obj $p" \
374 # Note that we keep this list relatively short so that this test doesn't
375 # time out (after taking more than 10 minutes).
377 set -A prop
"checksum" "" \
381 "recordsize" "recsize" \
388 # Note except for the mountpoint default value (which is handled in
389 # the routine itself), each property specified in the 'prop' array
390 # above must have a corresponding entry in the two arrays below.
393 set -A def_val
"on" "off" "on" \
395 "" "hidden" "discard" \
398 set -A local_val
"off" "on" "off" \
400 "$TESTDIR" "visible" "groupmask" \
404 # Global flag indicating whether the default record size had been
407 typeset def_recordsize
=0
409 set -A config_files $
(ls $STF_SUITE/tests
/functional
/inheritance
/config
*[1-9]*.cfg
)
410 set -A state_files $
(ls $STF_SUITE/tests
/functional
/inheritance
/state
*.cfg
)
413 # Global list of datasets created.
419 if [[ ${#config_files[*]} != ${#state_files[*]} ]]; then
420 log_fail
"Must have the same number of config files " \
421 " (${#config_files[*]}) and state files ${#state_files[*]}"
424 while ((k
< ${#config_files[*]})); do
425 default_cleanup_noexit
428 log_note
"Testing configuration ${config_files[k]}"
430 scan_config
${config_files[k]}
431 scan_state
${state_files[k]}
436 log_pass
"Properties correctly inherited as expected"