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 http://www.opensolaris.org/os/licensing.
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 2008 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27
28#
29# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
30#
31
32. $STF_SUITE/tests/functional/acl/acl_common.kshlib
33
34#
35# DESCRIPTION:
36#	Verify chmod have correct behaviour to directory and file when
37#	filesystem has the different aclinherit setting
38#
39# STRATEGY:
40#	1. Loop super user and non-super user to run the test case.
41#	2. Create basedir and a set of subdirectores and files within it.
42#	3. Separately chmod basedir with different inherite options,
43#	combine with the variable setting of aclinherit:
44#		"discard", "noallow", "restricted" or "passthrough".
45#	4. Then create nested directories and files like the following.
46#
47#                     ofile
48#                     odir
49#          chmod -->  basedir -|
50#                              |_ nfile1
51#                              |_ ndir1 _
52#                                        |_ nfile2
53#                                        |_ ndir2 _
54#                                                  |_ nfile3
55#                                                  |_ ndir3
56#
57#	5. Verify each directories and files have the correct access control
58#	   capability.
59#
60
61verify_runnable "both"
62
63function cleanup
64{
65	typeset dir
66
67	# Cleanup basedir, compared file and dir.
68
69	if [[ -f $ofile ]]; then
70		log_must rm -f $ofile
71	fi
72
73	for dir in $odir $basedir ; do
74		if [[ -d $dir ]]; then
75			log_must rm -rf $dir
76		fi
77	done
78	log_must zfs set aclmode=discard $TESTPOOL/$TESTFS
79}
80
81log_assert "Verify chmod have correct behaviour to directory and file when " \
82	"filesystem has the different aclinherit setting."
83log_onexit cleanup
84
85# Define inherit flag
86typeset aclinherit_flag=(discard noallow restricted passthrough)
87typeset object_flag=(file_inherit dir_inherit file_inherit/dir_inherit)
88typeset strategy_flag=("" inherit_only no_propagate inherit_only/no_propagate)
89
90typeset ace_prefix1="user:$ZFS_ACL_OTHER1"
91typeset ace_prefix2="user:$ZFS_ACL_OTHER2"
92
93# Defile the based directory and file
94basedir=$TESTDIR/basedir;  ofile=$TESTDIR/ofile; odir=$TESTDIR/odir
95
96# Define the files and directories will be created after chmod
97ndir1=$basedir/ndir1; ndir2=$ndir1/ndir2; ndir3=$ndir2/ndir3
98nfile1=$basedir/nfile1; nfile2=$ndir1/nfile2; nfile3=$ndir2/nfile3
99
100# Verify all the node have expected correct access control
101allnodes="$ndir1 $ndir2 $ndir3 $nfile1 $nfile2 $nfile3"
102
103#
104# According to inherited flag, verify subdirectories and files within it has
105# correct inherited access control.
106#
107function verify_inherit #<aclinherit> <object> [strategy]
108{
109	# Define the nodes which will be affected by inherit.
110	typeset inherit_nodes
111	typeset inherit=$1
112	typeset obj=$2
113	typeset str=$3
114	typeset str1="/inherited:"
115
116	# count: the ACE item to fetch
117	# pass: to mark if the current ACE should apply to the target
118	# maxnumber: predefine as 4
119	# passcnt: counter, if it achieves to maxnumber,
120	#	then no additional ACE should apply.
121	# isinherit: indicate if the current target is in the inherit list.
122
123	typeset -i count=0 pass=0 passcnt=0 isinherit=0 maxnumber=4
124
125	log_must usr_exec mkdir -p $ndir3
126	log_must usr_exec touch $nfile1 $nfile2 $nfile3
127
128	# Get the files which inherited ACE.
129	if [[ $obj == *"file_inherit"* ]]; then
130		inherit_nodes="$inherit_nodes $nfile1"
131
132		if [[ $str != *"no_propagate"* ]]; then
133			inherit_nodes="$inherit_nodes $nfile2 $nfile3"
134		fi
135	fi
136	# Get the directores which inherited ACE.
137	if [[ $obj == *"dir_inherit"* ]]; then
138		inherit_nodes="$inherit_nodes $ndir1"
139
140		if [[ $str != *"no_propagate"* ]]; then
141			inherit_nodes="$inherit_nodes $ndir2 $ndir3"
142		fi
143	fi
144
145	for node in $allnodes; do
146		if [[ " $inherit_nodes " == *" $node "* ]]; then
147			isinherit=1
148		else
149			isinherit=0
150		fi
151
152		i=0
153		count=0
154		passcnt=0
155		while ((i < maxnumber)); do
156			pass=0
157			eval expect1=\$acl$i
158
159		#
160		# aclinherit=passthrough,
161		# inherit all inheritable ACL entries without any
162		# modifications made to the ACL entries when they
163		# are inherited.
164		#
165		# aclinherit=restricted,
166		# any inheritable ACL entries will remove
167		# write_acl and write_owner permissions when the ACL entry is
168		# inherited.
169		#
170		# aclinherit=noallow,
171		# only inherit inheritable ACE that specify "deny" permissions
172		#
173		# aclinherit=discard
174		# will not inherit any ACL entries
175		#
176
177			case $inherit in
178				passthrough)
179					;;
180				restricted)
181					[[ $expect1 == *":allow" ]] && \
182						eval expect1=\$acls$i
183					;;
184				noallow)
185					if [[ $expect1 == *":allow" ]]; then
186						pass=1
187						((passcnt = passcnt + 1))
188					fi
189					;;
190				discard)
191					passcnt=maxnumber
192					break
193					;;
194			esac
195			propagate=0
196			# verify ACE's only for inherited nodes
197			if ((pass == 0 && isinherit == 1)); then
198				aclaction=${expect1##*:}
199
200				if [[ $expect1 == *"propagate"* ]]; then
201					# strip strategy flags from the expect
202					# value
203					acltemp=${expect1%/*}
204					expect1=${acltemp}
205					propagate=1
206				fi
207				acltemp=${expect1%:*}
208				if [[ -d $node ]]; then
209					if [[ $expect1 == *"inherit_only"* \
210					    && $propagate == 0 ]]; then
211						# prepare expect value for
212						# "inherit_only" nodes
213						acltemp_subdir=${expect1%/*}
214						expect1=${acltemp_subdir}${str1}
215						expect1=${expect1}${aclaction}
216
217					elif [[ $propagate == 1 ]]; then
218						# prepare expect value for
219						# "propagate" nodes
220						expect1=${acltemp}:inherited:
221						expect1=${expect1}${aclaction}
222
223					else
224						# prepare expect value for nodes
225						# with no starategy flags
226						expect1=${acltemp}${str1}
227						expect1=${expect1}${aclaction}
228					fi
229
230				elif [[ -f $node ]]; then
231					acltemp_subfile=${expect1%file*}
232					expect1=${acltemp_subfile}inherited:
233					expect1=${expect1}${aclaction}
234				fi
235
236				# Get the first ACE to do comparison
237
238				aclcur=$(get_ACE $node $count)
239				aclcur=${aclcur#$count:}
240				if [[ -n $expect1 && $expect1 != $aclcur ]]; then
241					ls -vd $basedir
242					ls -vd $node
243					log_fail "$inherit $i #$count " \
244						"ACE: $aclcur, expect to be " \
245						"$expect1"
246				fi
247
248				((count = count + 1))
249			fi
250			((i = i + 1))
251		done
252
253		#
254		# If there's no any ACE be checked, it should be identify as
255		# an normal file/dir, verify it.
256		#
257
258		if ((passcnt == maxnumber)); then
259			if [[ -d $node ]]; then
260				compare_acls $node $odir
261			elif [[	-f $node ]]; then
262				compare_acls $node $ofile
263			fi
264
265			if [[ $? -ne 0 ]]; then
266				ls -vd $basedir
267				ls -vd $node
268				log_fail "Unexpect acl: $node, $inherit ($str)"
269			fi
270		fi
271	done
272}
273
274typeset -i i=0
275typeset acl0 acl1 acl2 acl3
276typeset acls0 acls1 acls2 acls3
277
278#
279# Set aclmode=passthrough to make sure
280# the acl will not change during chmod.
281# A general testing should verify the combination of
282# aclmode/aclinherit works well,
283# here we just simple test them separately.
284#
285
286log_must zfs set aclmode=passthrough $TESTPOOL/$TESTFS
287
288for inherit in "${aclinherit_flag[@]}"; do
289
290	#
291	# Set different value of aclinherit
292	#
293
294	log_must zfs set aclinherit=$inherit $TESTPOOL/$TESTFS
295
296	for user in root $ZFS_ACL_STAFF1; do
297		log_must set_cur_usr $user
298
299		for obj in "${object_flag[@]}"; do
300			for str in "${strategy_flag[@]}"; do
301				typeset inh_opt=$obj
302				((${#str} != 0)) && inh_opt=$inh_opt/$str
303
304				#
305				# Prepare 4 ACES, which should include :
306				# deny -> to verify "noallow"
307				# write_acl/write_owner -> to verify "restricted"
308				#
309
310				acl0=${ace_prefix1}":read_xattr/write_acl"
311				acl0=${acl0}"/write_owner:$inh_opt:deny"
312				acl1=${ace_prefix2}":read_xattr/write_acl/"
313				acl1=${acl1}"write_owner:$inh_opt:allow"
314				acl2="$ace_prefix1:read_xattr:$inh_opt:deny"
315				acl3="$ace_prefix2:read_xattr:$inh_opt:allow"
316
317				#
318				# The ACE filtered by write_acl/write_owner
319				#
320
321				acls0="$ace_prefix1:read_xattr:$inh_opt:deny"
322				acls1="$ace_prefix2:read_xattr:$inh_opt:allow"
323				acls2=$acl2
324				acls3=$acl3
325
326
327				#
328				# Create basedir and tmp dir/file
329				# for comparison.
330				#
331
332				log_note "$user: chmod $acl $basedir"
333				log_must usr_exec mkdir $basedir
334				log_must usr_exec mkdir $odir
335				log_must usr_exec touch $ofile
336
337				i=3
338				while ((i >= 0)); do
339					eval acl=\$acl$i
340
341				#
342				# Place on a directory should succeed.
343				#
344					log_must usr_exec chmod A+$acl $basedir
345
346					((i = i - 1))
347				done
348
349				verify_inherit $inherit $obj $str
350
351				log_must usr_exec rm -rf $ofile $odir $basedir
352			done
353		done
354	done
355done
356
357log_pass "Verify chmod inherit behaviour co-op with aclinherit setting passed."
358