1#!/usr/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 by Delphix. All rights reserved.
30#
31
32
33. $STF_SUITE/tests/functional/acl/acl_common.kshlib
34
35#
36# DESCRIPTION:
37#	Verify chmod have correct behaviour on directories and files when
38#	filesystem has the different aclmode setting
39#
40# STRATEGY:
41#	1. Loop super user and non-super user to run the test case.
42#	2. Create basedir and a set of subdirectores and files within it.
43#	3. Separately chmod basedir with different aclmode options,
44#	   combine with the variable setting of aclmode:
45#	   "discard", "groupmask", or "passthrough".
46#	4. Verify each directories and files have the correct access control
47#	   capability.
48#
49
50verify_runnable "both"
51
52function cleanup
53{
54	# Cleanup tarfile & basedir.
55
56	(( ${#cwd} != 0 )) && cd $cwd
57
58	if [[ -f $TARFILE ]]; then
59		log_must $RM -f $TARFILE
60	fi
61
62	if [[ -d $basedir ]]; then
63		log_must $RM -rf $basedir
64	fi
65}
66
67log_assert "Verify chmod have correct behaviour to directory and file when " \
68	"filesystem has the different aclmode setting."
69log_onexit cleanup
70
71# Define aclmode flag
72set -A aclmode_flag discard groupmask passthrough
73
74set -A ace_prefix "user:$ZFS_ACL_OTHER1" \
75		"user:$ZFS_ACL_OTHER2" \
76		"group:$ZFS_ACL_STAFF_GROUP" \
77		"group:$ZFS_ACL_OTHER_GROUP"
78
79set -A argv  "000" "444" "644" "777" "755" "231" "562" "413"
80
81set -A ace_file_preset "read_data" \
82		"write_data" \
83		"append_data" \
84		"execute" \
85		"read_data/write_data" \
86		"read_data/write_data/append_data" \
87		"write_data/append_data" \
88		"read_data/execute" \
89		"write_data/append_data/execute" \
90		"read_data/write_data/append_data/execute"
91
92# Defile the based directory and file
93basedir=$TESTDIR/basedir;  ofile=$basedir/ofile; odir=$basedir/odir
94nfile=$basedir/nfile; ndir=$basedir/ndir
95
96TARFILE=$TESTDIR/tarfile
97
98# Verify all the node have expected correct access control
99allnodes="$nfile $ndir"
100
101#
102# According to the original bits, the input ACE access and ACE type, return the
103# expect bits after 'chmod A0{+|=}'.
104#
105# $1 isdir indicate if the target is a directory
106# $2 bits which was make up of three bit 'rwx'
107# $3 bits_limit which was make up of three bit 'rwx'
108# $4 ACE access which is read_data, write_data or execute
109# $5 ctrl which is to determine allow or deny according to owner/group bit
110#
111function cal_bits # isdir bits bits_limit acl_access ctrl
112{
113	typeset -i isdir=$1
114	typeset -i bits=$2
115	typeset -i bits_limit=$3
116	typeset acl_access=$4
117	typeset -i ctrl=${5:-0}
118	typeset flagr=0 flagw=0 flagx=0
119	typeset tmpstr
120
121	if (( ctrl == 0 )); then
122		if (( (( bits & 4 )) != 0 )); then
123			flagr=1
124		fi
125		if (( (( bits & 2 )) != 0 )); then
126			flagw=1
127		fi
128		if (( (( bits & 1 )) != 0 )); then
129			flagx=1
130		fi
131	else
132		#Determine ACE as per owner/group bit
133		flagr=1
134		flagw=1
135		flagx=1
136
137		if (( ((bits & 4)) != 0 )) && \
138			(( ((bits_limit & 4)) != 0 )); then
139			flagr=0
140		fi
141		if (( ((bits & 2)) != 0 )) && \
142			(( ((bits_limit & 2)) != 0 )); then
143			flagw=0
144		fi
145		if (( ((bits & 1)) != 0 )) && \
146			(( ((bits_limit & 1)) != 0 )); then
147			flagx=0
148		fi
149	fi
150	if ((flagr != 0)); then
151		if [[ $acl_access == *"read_data"* ]]; then
152			if [[ $acl_access == *"allow"*  && $passthrough == 0 ]]; then
153					tmpstr=${tmpstr}
154			else
155				if ((isdir == 0)); then
156					tmpstr=${tmpstr}/read_data
157				else
158					tmpstr=${tmpstr}/list_directory/read_data
159				fi
160			fi
161		fi
162	fi
163
164	if ((flagw != 0)); then
165		if [[ $acl_access == *"allow"* && $passthrough == 0 ]]; then
166			tmpstr=${tmpstr}
167		else
168			if [[ $acl_access == *"write_data"* ]]; then
169				if ((isdir == 0)); then
170					tmpstr=${tmpstr}/write_data
171				else
172					tmpstr=${tmpstr}/add_file/write_data
173				fi
174			fi
175			if [[ $acl_access == *"append_data"* ]]; then
176				if ((isdir == 0)); then
177					tmpstr=${tmpstr}/append_data
178				else
179					tmpstr=${tmpstr}/add_subdirectory/append_data
180				fi
181			fi
182		fi
183	fi
184	if ((flagx != 0)); then
185		if [[ $acl_access == *"execute"* ]]; then
186			if [[ $acl_access == *"allow"* && $passthrough == 0 ]]; then
187				tmpstr=${tmpstr}
188			else
189				tmpstr=${tmpstr}/execute
190			fi
191		fi
192	fi
193
194	tmpstr=${tmpstr#/}
195
196	$ECHO "$tmpstr"
197}
198
199#
200# To translate an ace if the node is dir
201#
202# $1 isdir indicate if the target is a directory
203# $2 acl to be translated
204#
205function translate_acl # isdir acl
206{
207	typeset -i isdir=$1
208	typeset acl=$2
209	typeset who prefix acltemp action
210
211	if ((isdir != 0)); then
212		who=${acl%%:*}
213		prefix=$who
214		acltemp=${acl#*:}
215		acltemp=${acltemp%%:*}
216		prefix=$prefix:$acltemp
217		action=${acl##*:}
218		acl=$prefix:$(cal_bits $isdir 7 7 $acl 0):$action
219	fi
220	$ECHO "$acl"
221}
222
223#
224# To verify if a new ACL is generated as result of
225# chmod operation.
226#
227# $1 bit indicates whether owner/group bit
228# $2 newmode indicates the mode changed using chmod
229# $3 isdir indicate if the target is a directory
230#
231function check_new_acl # bit newmode isdir
232{
233	typeset bits=$1
234	typeset mode=$2
235	typeset -i isdir=$3
236	typeset new_acl
237	typeset gbit
238	typeset ebit
239	typeset str=":"
240	gbit=$(get_substr $mode 2 1)
241	ebit=$(get_substr $mode 3 1)
242	if (( ((bits & 4)) == 0 )); then
243		if (( ((gbit & 4)) != 0 || \
244		    ((ebit & 4)) != 0 )); then
245			if ((isdir == 0)); then
246				new_acl=${new_acl}${str}read_data
247			else
248				new_acl=${new_acl}${str}list_directory/read_data
249			fi
250			str="/"
251		fi
252	fi
253	if (( ((bits & 2)) == 0 )); then
254		if (( ((gbit & 2)) != 0 || \
255		    ((ebit & 2)) != 0 )); then
256			if ((isdir == 0)); then
257				new_acl=${new_acl}${str}write_data/append_data
258			else
259				new_acl=${new_acl}${str}add_file/write_data/
260				new_acl=${new_acl}add_subdirectory/append_data
261			fi
262			str="/"
263		fi
264	fi
265	if (( ((bits & 1)) == 0 )); then
266		if (( ((gbit & 1)) != 0 || \
267		    ((ebit & 1)) != 0 )); then
268				new_acl=${new_acl}${str}execute
269		fi
270	fi
271	$ECHO "$new_acl"
272}
273
274function build_new_acl # newmode isdir
275{
276	typeset newmode=$1
277	typeset isdir=$2
278	typeset expect
279	if ((flag == 0)); then
280		prefix="owner@"
281		bit=$(get_substr $newmode 1 1)
282		status=$(check_new_acl $bit $newmode $isdir)
283
284	else
285		prefix="group@"
286		bit=$(get_substr $newmode 2 1)
287		status=$(check_new_acl $bit $newmode $isdir)
288	fi
289	expect=$prefix$status:deny
290	$ECHO $expect
291}
292
293#
294# According to inherited flag, verify subdirectories and files within it has
295# correct inherited access control.
296#
297function verify_aclmode # <aclmode> <node> <newmode>
298{
299	# Define the nodes which will be affected by inherit.
300	typeset aclmode=$1
301	typeset node=$2
302	typeset newmode=$3
303
304	# count: the ACE item to fetch
305	# pass: to mark if the current ACE should apply to the target
306	# passcnt: counter, if it achieves to maxnumber,
307	#	then no additional ACE should apply.
308
309	typeset -i count=0 pass=0 passcnt=0
310	typeset -i bits=0 obits=0 bits_owner=0 isdir=0
311	typeset -i total_acl
312	typeset -i acl_count=$(count_ACE $node)
313
314	((total_acl = maxnumber + 3))
315
316	if [[ -d $node ]]; then
317		((isdir = 1))
318	fi
319
320	((i = maxnumber - 1))
321	count=0
322	passcnt=0
323	flag=0
324	while ((i >= 0)); do
325		pass=0
326		expect1=${acls[$i]}
327		passthrough=0
328		#
329		# aclmode=passthrough,
330		# no changes will be made to the ACL other than
331		# generating the necessary ACL entries to represent
332		# the new mode of the file or directory.
333		#
334		# aclmode=discard,
335		# delete all ACL entries that don't represent
336		# the mode of the file.
337		#
338		# aclmode=groupmask,
339		# reduce user or group permissions.  The permissions are
340		# reduced, such that they are no greater than the group
341		# permission bits, unless it is a user entry that has the
342		# same UID as the owner of the file or directory.
343		# Then, the ACL permissions are reduced so that they are
344		# no greater than owner permission bits.
345		#
346
347		case $aclmode in
348			passthrough)
349				if ((acl_count > total_acl)); then
350					expect1=$(build_new_acl $newmode $isdir)
351					flag=1
352					((total_acl = total_acl + 1))
353					((i = i + 1))
354				else
355					passthrough=1
356					expect1=$(translate_acl $isdir $expect1)
357				fi
358				;;
359			groupmask)
360				if ((acl_count > total_acl)); then
361					expect1=$(build_new_acl $newmode $isdir)
362					flag=1
363					((total_acl = total_acl + 1))
364					((i = i + 1))
365
366				elif [[ $expect1 == *":allow"* ]]; then
367					who=${expect1%%:*}
368					aclaction=${expect1##*:}
369					prefix=$who
370					acltemp=""
371					reduce=0
372					#
373					# To determine the mask bits
374					# according to the entry type.
375					#
376					case $who in
377						owner@)
378							pos=1
379							;;
380						group@)
381							pos=2
382							;;
383						everyone@)
384							pos=3
385							;;
386						user)
387							acltemp=${expect1#*:}
388							acltemp=${acltemp%%:*}
389							owner=$(get_owner $node)
390							group=$(get_group $node)
391							if [[ $acltemp == \
392							    $owner ]]; then
393								pos=1
394							else
395								pos=2
396							fi
397							prefix=$prefix:$acltemp
398							;;
399						group)
400							acltemp=${expect1#*:}
401							acltemp=${acltemp%%:*}
402							pos=2
403							prefix=$prefix:$acltemp
404							reduce=1
405							;;
406					esac
407					obits=$(get_substr $newmode $pos 1)
408					((bits = $obits))
409					#
410					# permission should no greater than the
411					# group permission bits
412					#
413					if ((reduce != 0)); then
414						((bits &= \
415						    $(get_substr $newmode 2 1)))
416					# The ACL permissions are reduced so
417					# that they are no greater than owner
418					# permission bits.
419
420						((bits_owner = \
421						    $(get_substr $newmode 1 1)))
422						((bits &= $bits_owner))
423					fi
424
425					if ((bits < obits)) && \
426					    [[ -n $acltemp ]]; then
427						expect2=$prefix:
428						new_bit=$(cal_bits $isdir $obits $bits_owner $expect1 1)
429						expect2=${expect2}${new_bit}:allow
430					else
431						expect2=$prefix:
432						new_bit=$(cal_bits $isdir $obits $obits $expect1 1)
433						expect2=${expect2}${new_bit}:allow
434					fi
435					priv=$(cal_bits $isdir $obits $bits_owner $expect2 0)
436					expect1=$prefix:$priv:$aclaction
437				else
438					expect1=$(translate_acl $isdir $expect1)
439				fi
440				;;
441			discard)
442				passcnt=maxnumber
443				break
444				;;
445		esac
446
447		if ((pass == 0)) ; then
448			# Get the first ACE to do comparison
449
450			aclcur=$(get_ACE $node $count)
451			aclcur=${aclcur#$count:}
452			if [[ -n $expect1 && $expect1 != $aclcur ]]; then
453				$LS -vd $node
454				log_fail "$aclmode $i #$count " \
455					"ACE: $aclcur, expect to be " \
456					"$expect1"
457			fi
458		((count = count + 1))
459		fi
460		((i = i - 1))
461	done
462
463	#
464	# If there's no any ACE be checked, it should be identify as
465	# an normal file/dir, verify it.
466	#
467	if ((passcnt == maxnumber)); then
468		if [[ -d $node ]]; then
469			compare_acls $node $odir
470		elif [[	-f $node ]]; then
471			compare_acls $node $ofile
472		fi
473
474		if [[ $? -ne 0 ]]; then
475			$LS -vd $node
476			log_fail "Unexpect acl: $node, $aclmode ($newmode)"
477		fi
478	fi
479}
480
481
482
483typeset -i maxnumber=0
484typeset acl
485typeset target
486typeset -i passthrough=0
487typeset -i flag=0
488cwd=$PWD
489cd $TESTDIR
490
491for mode in "${aclmode_flag[@]}"; do
492
493	#
494	# Set different value of aclmode
495	#
496
497	log_must $ZFS set aclmode=$mode $TESTPOOL/$TESTFS
498
499	for user in root $ZFS_ACL_STAFF1; do
500		log_must set_cur_usr $user
501
502		log_must usr_exec $MKDIR $basedir
503
504		log_must usr_exec $MKDIR $odir
505		log_must usr_exec $TOUCH $ofile
506		log_must usr_exec $MKDIR $ndir
507		log_must usr_exec $TOUCH $nfile
508
509		for obj in $allnodes; do
510			maxnumber=0
511			for preset in "${ace_file_preset[@]}"; do
512				for prefix in "${ace_prefix[@]}"; do
513					acl=$prefix:$preset
514
515					case $((maxnumber % 2)) in
516						0)
517							acl=$acl:deny
518							;;
519						1)
520							acl=$acl:allow
521							;;
522					esac
523
524				#
525				# Place on the target should succeed.
526				#
527					log_must usr_exec $CHMOD A+$acl $obj
528					acls[$maxnumber]=$acl
529
530					((maxnumber = maxnumber + 1))
531				done
532			done
533			# Archive the file and directory
534			log_must $TAR cpf@ $TARFILE $basedir
535
536			if [[ -d $obj ]]; then
537				target=$odir
538			elif [[ -f $obj ]]; then
539				target=$ofile
540			fi
541			for newmode in "${argv[@]}"; do
542				log_must usr_exec $CHMOD $newmode $obj
543				log_must usr_exec $CHMOD $newmode $target
544				log_must verify_aclmode $mode $obj $newmode
545
546				# Restore the tar archive
547				log_must $TAR xpf@ $TARFILE
548			done
549		done
550
551		log_must usr_exec $RM -rf $basedir $TARFILE
552	done
553done
554
555log_pass "Verify chmod behaviour co-op with aclmode setting passed."
556