1#!/usr/local/bin/ksh93 -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 2009 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26
27. $STF_SUITE/tests/acl/acl_common.kshlib
28. $STF_SUITE/tests/acl/cifs/cifs.kshlib
29
30#################################################################################
31#
32# __stc_assertion_start
33#
34# ID: cifs_attr_003_pos
35#
36# DESCRIPTION:
37#	Verify the DOS attributes (Readonly, Hidden, Archive, System)
38#	and BSD'ish attributes (Immutable, nounlink, and appendonly)
39#	will provide the proper access limitation as expected.
40#
41#	Readonly means that the content of a file can't be modified, but
42#	timestamps, mode and so on can.
43#
44#	Archive - Indicates if a file should be included in the next backup
45#	of the file system.  ZFS will set this bit whenever a file is
46#	modified.
47#
48#	Hidden and System (ZFS does nothing special with these, other than
49#	letting a user/application set them.
50#
51#	Immutable (The data can't, change nor can mode, ACL, size and so on)
52#	The only attribute that can be updated is the access time.
53#
54#	Nonunlink - Sort of like immutable except that a file/dir can't be
55#	removed.
56#	This will also effect a rename operation, since that involes a
57#	remove.
58#
59#	Appendonly - File can only be appended to.
60#
61#	nodump, settable, opaque (These are for the MacOS port) we will
62#	allow them to be set, but have no semantics tied to them.
63#
64# STRATEGY:
65#	1. Loop super user and non-super user to run the test case.
66#	2. Create basedir and a set of subdirectores and files within it.
67#	3. Set the file/dir with each kind of special attribute.
68#	4. Verify the access limitation works as expected.
69#
70# TESTABILITY: explicit
71#
72# TEST_AUTOMATION_LEVEL: automated
73#
74# CODING_STATUS: COMPLETED (2007-11-05)
75#
76# __stc_assertion_end
77#
78################################################################################
79
80verify_runnable "both"
81
82if ! cifs_supported ; then
83	log_unsupported "CIFS not supported on current system."
84fi
85
86test_requires ZFS_ACL ZFS_XATTR
87
88function cleanup
89{
90	if [[ -n $gobject ]]; then
91		destroy_object $gobject
92	fi
93
94	for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
95		mtpt=$(get_prop mountpoint $fs)
96		log_must $RM -rf $mtpt/file.* $mtpt/dir.*
97	done
98}
99
100#
101# Set the special attribute to the given node
102#
103# $1: The given node (file/dir)
104# $2: The special attribute to be set
105#
106function set_attribute
107{
108	typeset object=$1
109	typeset attr=$2
110
111	if [[ -z $attr ]]; then
112		attr="AHRSadimu"
113		if [[ -f $object ]]; then
114			attr="${attr}q"
115		fi
116	fi
117
118	$CHMOD S+c${attr} $object
119	return $?
120}
121
122#
123# Clear the special attribute to the given node
124#
125# $1: The given node (file/dir)
126# $2: The special attribute to be cleared
127#
128function clear_attribute
129{
130	typeset object=$1
131	typeset attr=$2
132
133	if [[ -z $attr ]]; then
134		if is_global_zone ; then
135			attr="AHRSadimu"
136			if [[ -f $object ]]; then
137				attr="${attr}q"
138			fi
139		else
140			attr="AHRS"
141		fi
142	fi
143
144	$CHMOD S-c${attr} $object
145	return $?
146}
147
148#
149# A wrapper function to call test function according to the given attr
150#
151# $1: The given node (file/dir)
152# $2: The special attribute to be test
153#
154function test_wrapper
155{
156	typeset object=$1
157	typeset attr=$2
158
159	if [[ -z $object || -z $attr ]]; then
160		log_fail "Object($object), Attr($attr) not defined."
161	fi
162
163	case $attr in
164		R)	func=test_readonly
165			;;
166		i)	func=test_immutable
167			;;
168		u)	func=test_nounlink
169			;;
170		a)	func=test_appendonly
171			;;
172	esac
173
174	if [[ -n $func ]]; then
175		$func $object
176	fi
177}
178
179#
180# Invoke the function and verify whether its return code as expected
181#
182# $1: Expect value
183# $2-$n: Function and args need to be invoked
184#
185function verify_expect
186{
187	typeset -i expect=$1
188	typeset status
189
190	shift
191
192	"$@" > /dev/null 2>&1
193	status=$?
194	if  [[ $status -eq 0 ]]; then
195		if (( expect != 0 )); then
196			log_fail "$@ unexpect return 0"
197		fi
198	else
199		if (( expect == 0 )); then
200			log_fail "$@ unexpect return $status"
201		fi
202	fi
203}
204
205#
206# Unit testing function against overwrite file
207#
208# $1: The given file node
209# $2: Execute user
210# $3: Expect value, default to be zero
211#
212function unit_writefile
213{
214	typeset object=$1
215	typeset user=$2
216	typeset expect=${3:-0}
217
218	if [[ -f $object ]]; then
219		verify_expect $expect $CHG_USR_EXEC $user \
220			$CP $TESTFILE $object
221		verify_expect $expect $CHG_USR_EXEC $user \
222			$EVAL "$ECHO '$TESTSTR' > $object"
223	fi
224}
225
226#
227# Unit testing function against write new stuffs into a directory
228#
229# $1: The given directory node
230# $2: Execute user
231# $3: Expect value, default to be zero
232#
233function unit_writedir
234{
235	typeset object=$1
236	typeset user=$2
237	typeset expect=${3:-0}
238
239	if [[ -d $object ]]; then
240		verify_expect $expect $CHG_USR_EXEC $user \
241			$CP $TESTFILE $object
242		verify_expect $expect $CHG_USR_EXEC $user \
243			$MKDIR -p $object/$TESTDIR
244	fi
245}
246
247function unit_appenddata
248{
249	typeset object=$1
250	typeset user=$2
251	typeset expect=${3:-0}
252
253	if [[ ! -d $object ]]; then
254		verify_expect $expect $CHG_USR_EXEC $user \
255			$EVAL "$ECHO '$TESTSTR' >> $object"
256	fi
257}
258
259#
260# Unit testing function against delete content from a directory
261#
262# $1: The given node, dir
263# $2: Execute user
264# $3: Expect value, default to be zero
265#
266function unit_deletecontent
267{
268	typeset object=$1
269	typeset user=$2
270	typeset expect=${3:-0}
271
272	if [[ -d $object ]]; then
273		for target in $object/${TESTFILE##*/} $object/$TESTDIR ; do
274			if [[ -e $target ]]; then
275				verify_expect $expect $CHG_USR_EXEC $user \
276					$EVAL "$MV $target $target.new"
277				verify_expect $expect $CHG_USR_EXEC $user \
278					$EVAL "$ECHO y | $RM -r $target.new"
279			fi
280		done
281	fi
282}
283
284#
285# Unit testing function against delete a node
286#
287# $1: The given node, file/dir
288# $2: Execute user
289# $3: Expect value, default to be zero
290#
291function unit_deletedata
292{
293	typeset object=$1
294	typeset user=$2
295	typeset expect=${3:-0}
296
297	verify_expect $expect $CHG_USR_EXEC $user \
298		$EVAL "$ECHO y | $RM -r $object"
299
300}
301
302#
303# Unit testing function against write xattr to a node
304#
305# $1: The given node, file/dir
306# $2: Execute user
307# $3: Expect value, default to be zero
308#
309function unit_writexattr
310{
311	typeset object=$1
312	typeset user=$2
313	typeset expect=${3:-0}
314
315	verify_expect $expect $CHG_USR_EXEC $user \
316		$RUNAT $object "$CP $TESTFILE $TESTATTR"
317	verify_expect $expect $CHG_USR_EXEC $user \
318		$EVAL "$RUNAT $object \"$ECHO '$TESTSTR' > $TESTATTR\""
319	verify_expect $expect $CHG_USR_EXEC $user \
320		$EVAL "$RUNAT $object \"$ECHO '$TESTSTR' >> $TESTATTR\""
321	if [[ $expect -eq 0 ]]; then
322		verify_expect $expect $CHG_USR_EXEC $user \
323			$RUNAT $object "$RM -f $TESTATTR"
324	fi
325}
326
327#
328# Unit testing function against modify accesstime of a node
329#
330# $1: The given node, file/dir
331# $2: Execute user
332# $3: Expect value, default to be zero
333#
334function unit_accesstime
335{
336	typeset object=$1
337	typeset user=$2
338	typeset expect=${3:-0}
339
340	if [[ -d $object ]]; then
341		verify_expect $expect $CHG_USR_EXEC $user $LS $object
342	else
343		verify_expect $expect $CHG_USR_EXEC $user $CAT $object
344	fi
345}
346
347#
348# Unit testing function against modify updatetime of a node
349#
350# $1: The given node, file/dir
351# $2: Execute user
352# $3: Expect value, default to be zero
353#
354function unit_updatetime
355{
356	typeset object=$1
357	typeset user=$2
358	typeset expect=${3:-0}
359
360	verify_expect $expect $CHG_USR_EXEC $user $TOUCH $object
361	verify_expect $expect $CHG_USR_EXEC $user $TOUCH -a $object
362	verify_expect $expect $CHG_USR_EXEC $user $TOUCH -m $object
363}
364
365#
366# Unit testing function against write acl of a node
367#
368# $1: The given node, file/dir
369# $2: Execute user
370# $3: Expect value, default to be zero
371#
372function unit_writeacl
373{
374	typeset object=$1
375	typeset user=$2
376	typeset expect=${3:-0}
377
378	verify_expect $expect $CHG_USR_EXEC $user chmod A+$TESTACL $object
379	verify_expect $expect $CHG_USR_EXEC $user chmod A+$TESTACL $object
380	verify_expect $expect $CHG_USR_EXEC $user chmod A0- $object
381	verify_expect $expect $CHG_USR_EXEC $user chmod A0- $object
382	oldmode=$(get_mode $object)
383	verify_expect $expect $CHG_USR_EXEC $user chmod $TESTMODE $object
384}
385
386#
387# Testing function to verify the given node is readonly
388#
389# $1: The given node, file/dir
390#
391function test_readonly
392{
393	typeset object=$1
394
395	if [[ -z $object ]]; then
396		log_fail "Object($object) not defined."
397	fi
398
399	log_note "Testing readonly of $object"
400
401	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
402		if [[ -d $object ]]; then
403			log_must usr_exec chmod \
404				A+user:$user:${ace_dir}:allow $object
405		else
406			log_must usr_exec chmod \
407				A+user:$user:${ace_file}:allow $object
408		fi
409
410		log_must set_attribute $object "R"
411
412		unit_writefile $object $user 1
413		unit_writedir $object $user
414		unit_appenddata $object $user 1
415
416		if [[ -d $object ]]; then
417			unit_writexattr $object $user
418		else
419			unit_writexattr $object $user 1
420		fi
421
422		unit_accesstime $object $user
423		unit_updatetime $object $user
424		unit_writeacl $object $user
425		unit_deletecontent $object $user
426		unit_deletedata $object $user
427
428		if [[ -d $object ]] ;then
429			create_object "dir" $object $ZFS_ACL_CUR_USER
430		else
431			create_object "file" $object $ZFS_ACL_CUR_USER
432		fi
433	done
434}
435
436#
437# Testing function to verify the given node is immutable
438#
439# $1: The given node, file/dir
440#
441function test_immutable
442{
443	typeset object=$1
444
445	if [[ -z $object ]]; then
446		log_fail "Object($object) not defined."
447	fi
448
449	log_note "Testing immutable of $object"
450
451	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
452		if [[ -d $object ]]; then
453			log_must usr_exec chmod \
454				A+user:$user:${ace_dir}:allow $object
455		else
456			log_must usr_exec chmod \
457				A+user:$user:${ace_file}:allow $object
458		fi
459		log_must set_attribute $object "i"
460
461		unit_writefile $object $user 1
462		unit_writedir $object $user 1
463		unit_appenddata $object $user 1
464		unit_writexattr $object $user 1
465		unit_accesstime $object $user
466		unit_updatetime $object $user 1
467		unit_writeacl $object $user 1
468		unit_deletecontent $object $user 1
469		unit_deletedata $object $user 1
470
471		if [[ -d $object ]] ;then
472			create_object "dir" $object $ZFS_ACL_CUR_USER
473		else
474			create_object "file" $object $ZFS_ACL_CUR_USER
475		fi
476	done
477}
478
479#
480# Testing function to verify the given node is nounlink
481#
482# $1: The given node, file/dir
483#
484function test_nounlink
485{
486	typeset object=$1
487
488	if [[ -z $object ]]; then
489		log_fail "Object($object) not defined."
490	fi
491
492	$ECHO "Testing nounlink of $object"
493
494	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
495		if [[ -d $object ]]; then
496			log_must usr_exec chmod \
497				A+user:$user:${ace_dir}:allow $object
498		else
499			log_must usr_exec chmod \
500				A+user:$user:${ace_file}:allow $object
501		fi
502		log_must set_attribute $object "u"
503
504		unit_writefile $object $user
505		unit_writedir $object $user
506		unit_appenddata $object $user
507		unit_writexattr $object $user
508		unit_accesstime $object $user
509		unit_updatetime $object $user
510		unit_writeacl $object $user
511		unit_deletecontent $object $user 1
512		unit_deletedata $object $user 1
513
514		if [[ -d $object ]] ;then
515			create_object "dir" $object $ZFS_ACL_CUR_USER
516		else
517			create_object "file" $object $ZFS_ACL_CUR_USER
518		fi
519	done
520}
521
522#
523# Testing function to verify the given node is appendonly
524#
525# $1: The given node, file/dir
526#
527function test_appendonly
528{
529	typeset object=$1
530
531	if [[ -z $object ]]; then
532		log_fail "Object($object) not defined."
533	fi
534
535	log_note "Testing appendonly of $object"
536
537	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
538		if [[ -d $object ]]; then
539			log_must usr_exec chmod \
540				A+user:$user:${ace_dir}:allow $object
541		else
542			log_must usr_exec chmod \
543				A+user:$user:${ace_file}:allow $object
544		fi
545		log_must set_attribute $object "a"
546
547		unit_writefile $object $user 1
548		unit_writedir $object $user
549		unit_appenddata $object $user
550		unit_writexattr $object $user
551		unit_accesstime $object $user
552		unit_updatetime $object $user
553		unit_writeacl $object $user
554		unit_deletecontent $object $user
555		unit_deletedata $object $user
556
557		if [[ -d $object ]] ;then
558			create_object "dir" $object $ZFS_ACL_CUR_USER
559		else
560			create_object "file" $object $ZFS_ACL_CUR_USER
561		fi
562	done
563}
564
565FILES="file.0 file.1"
566DIRS="dir.0 dir.1"
567XATTRS="attr.0 attr.1"
568FS="$TESTPOOL $TESTPOOL/$TESTFS"
569
570if is_global_zone ; then
571	ATTRS="R i u a"
572else
573	ATTRS="R"
574fi
575
576TESTFILE=$TMPDIR/tfile
577TESTDIR=tdir
578TESTATTR=tattr
579TESTACL=user:$ZFS_ACL_OTHER1:write_data:allow
580TESTMODE=777
581TESTSTR="ZFS test suites"
582
583ace_file="write_data/append_data/write_xattr/write_acl/write_attributes"
584ace_dir="add_file/add_subdirectory/${ace_file}"
585
586log_assert "Verify DOS & BSD'ish attributes will provide the " \
587	"access limitation as expected."
588log_onexit cleanup
589
590$ECHO "$TESTSTR" > $TESTFILE
591
592typeset gobject
593typeset gattr
594for gattr in $ATTRS ; do
595	for fs in $FS ; do
596		mtpt=$(get_prop mountpoint $fs)
597		$CHMOD 777 $mtpt
598		for user in root $ZFS_ACL_STAFF1; do
599			log_must set_cur_usr $user
600			for file in $FILES ; do
601				gobject=$mtpt/$file
602				create_object "file" $gobject $ZFS_ACL_CUR_USER
603				test_wrapper $gobject $gattr
604				destroy_object $gobject
605			done
606
607			for dir in $DIRS ; do
608				gobject=$mtpt/$dir
609				create_object "dir" $gobject $ZFS_ACL_CUR_USER
610				test_wrapper $gobject $gattr
611				destroy_object $gobject
612			done
613		done
614	done
615done
616
617log_pass "DOS & BSD'ish attributes provide the access limitation as expected."
618