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 2009 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. $STF_SUITE/tests/functional/acl/cifs/cifs.kshlib
34
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
71verify_runnable "both"
72
73function cleanup
74{
75	if [[ -n $gobject ]]; then
76		destroy_object $gobject
77	fi
78
79	for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do
80		mtpt=$(get_prop mountpoint $fs)
81		log_must rm -rf $mtpt/file.* $mtpt/dir.*
82	done
83
84	[[ -f $TESTFILE ]] && rm $TESTFILE
85}
86
87#
88# Set the special attribute to the given node
89#
90# $1: The given node (file/dir)
91# $2: The special attribute to be set
92#
93function set_attribute
94{
95	typeset object=$1
96	typeset attr=$2
97
98	if [[ -z $attr ]]; then
99		attr="AHRSadimu"
100		if [[ -f $object ]]; then
101			attr="${attr}q"
102		fi
103	fi
104	chmod S+c${attr} $object
105	return $?
106}
107
108#
109# Clear the special attribute to the given node
110#
111# $1: The given node (file/dir)
112# $2: The special attribute to be cleared
113#
114function clear_attribute
115{
116	typeset object=$1
117	typeset attr=$2
118
119	if [[ -z $attr ]]; then
120		if is_global_zone ; then
121			attr="AHRSadimu"
122			if [[ -f $object ]]; then
123				attr="${attr}q"
124			fi
125		else
126			attr="AHRS"
127		fi
128	fi
129
130	chmod S-c${attr} $object
131	return $?
132}
133
134#
135# A wrapper function to call test function according to the given attr
136#
137# $1: The given node (file/dir)
138# $2: The special attribute to be test
139#
140function test_wrapper
141{
142	typeset object=$1
143	typeset attr=$2
144
145	if [[ -z $object || -z $attr ]]; then
146		log_fail "Object($object), Attr($attr) not defined."
147	fi
148
149	case $attr in
150		R)	func=test_readonly
151			;;
152		i)	func=test_immutable
153			;;
154		u)	func=test_nounlink
155			;;
156		a)	func=test_appendonly
157			;;
158	esac
159
160	if [[ -n $func ]]; then
161		$func $object
162	fi
163}
164
165#
166# Invoke the function and verify whether its return code as expected
167#
168# $1: Expect value
169# $2-$n: Function and args need to be invoked
170#
171function verify_expect
172{
173	typeset -i expect=$1
174	typeset status
175
176	shift
177
178	"$@" > /dev/null 2>&1
179	status=$?
180	if  [[ $status -eq 0 ]]; then
181		if ((expect != 0)); then
182			log_fail "$@ unexpect return 0"
183		fi
184	else
185		if ((expect == 0)); then
186			log_fail "$@ unexpect return $status"
187		fi
188	fi
189}
190
191#
192# Unit testing function against overwrite file
193#
194# $1: The given file node
195# $2: Execute user
196# $3: Expect value, default to be zero
197#
198function unit_writefile
199{
200	typeset object=$1
201	typeset user=$2
202	typeset expect=${3:-0}
203	if [[ -f $object ]]; then
204		verify_expect $expect chg_usr_exec $user \
205		    cp $TESTFILE $object
206		verify_expect $expect chg_usr_exec $user \
207		    "echo '$TESTSTR' > $object"
208	fi
209}
210
211#
212# Unit testing function against write new stuffs into a directory
213#
214# $1: The given directory node
215# $2: Execute user
216# $3: Expect value, default to be zero
217#
218function unit_writedir
219{
220	typeset object=$1
221	typeset user=$2
222	typeset expect=${3:-0}
223
224	if [[ -d $object ]]; then
225		verify_expect $expect chg_usr_exec $user \
226		    cp $TESTFILE $object
227		verify_expect $expect chg_usr_exec $user \
228		    mkdir -p $object/$TESTDIR
229	fi
230}
231
232function unit_appenddata
233{
234	typeset object=$1
235	typeset user=$2
236	typeset expect=${3:-0}
237
238	if [[ ! -d $object ]]; then
239		verify_expect $expect chg_usr_exec $user \
240		    "echo '$TESTSTR' >> $object"
241	fi
242}
243
244#
245# Unit testing function against delete content from a directory
246#
247# $1: The given node, dir
248# $2: Execute user
249# $3: Expect value, default to be zero
250#
251function unit_deletecontent
252{
253	typeset object=$1
254	typeset user=$2
255	typeset expect=${3:-0}
256
257	if [[ -d $object ]]; then
258		for target in $object/${TESTFILE##*/} $object/$TESTDIR ; do
259			if [[ -e $target ]]; then
260				verify_expect $expect chg_usr_exec $user \
261				    "mv $target $target.new"
262				verify_expect $expect chg_usr_exec $user \
263				    "echo y | rm -r $target.new"
264			fi
265		done
266	fi
267}
268
269#
270# Unit testing function against delete a node
271#
272# $1: The given node, file/dir
273# $2: Execute user
274# $3: Expect value, default to be zero
275#
276function unit_deletedata
277{
278	typeset object=$1
279	typeset user=$2
280	typeset expect=${3:-0}
281
282	verify_expect $expect chg_usr_exec $user \
283	    "echo y | rm -r $object"
284
285}
286
287#
288# Unit testing function against write xattr to a node
289#
290# $1: The given node, file/dir
291# $2: Execute user
292# $3: Expect value, default to be zero
293#
294function unit_writexattr
295{
296	typeset object=$1
297	typeset user=$2
298	typeset expect=${3:-0}
299
300	verify_expect $expect chg_usr_exec $user \
301	    runat $object "cp $TESTFILE $TESTATTR"
302	verify_expect $expect chg_usr_exec $user \
303	    "runat $object \"echo '$TESTSTR' > $TESTATTR\""
304	verify_expect $expect chg_usr_exec $user \
305	    "runat $object \"echo '$TESTSTR' >> $TESTATTR\""
306	if [[ $expect -eq 0 ]]; then
307		verify_expect $expect chg_usr_exec $user \
308		    runat $object "rm -f $TESTATTR"
309	fi
310}
311
312#
313# Unit testing function against modify accesstime of a node
314#
315# $1: The given node, file/dir
316# $2: Execute user
317# $3: Expect value, default to be zero
318#
319function unit_accesstime
320{
321	typeset object=$1
322	typeset user=$2
323	typeset expect=${3:-0}
324
325	if [[ -d $object ]]; then
326		verify_expect $expect chg_usr_exec $user ls $object
327	else
328		verify_expect $expect chg_usr_exec $user cat $object
329	fi
330}
331
332#
333# Unit testing function against modify updatetime of a node
334#
335# $1: The given node, file/dir
336# $2: Execute user
337# $3: Expect value, default to be zero
338#
339function unit_updatetime
340{
341	typeset object=$1
342	typeset user=$2
343	typeset expect=${3:-0}
344	typeset immutable_expect=${4:-$expect}
345	verify_expect $expect chg_usr_exec $user touch $object
346	verify_expect $immutable_expect chg_usr_exec $user touch -a $object
347	verify_expect $expect chg_usr_exec $user touch -m $object
348}
349
350#
351# Unit testing function against write acl of a node
352#
353# $1: The given node, file/dir
354# $2: Execute user
355# $3: Expect value, default to be zero
356#
357function unit_writeacl
358{
359	typeset object=$1
360	typeset user=$2
361	typeset expect=${3:-0}
362
363	verify_expect $expect chg_usr_exec $user chmod A+$TESTACL $object
364	verify_expect $expect chg_usr_exec $user chmod A+$TESTACL $object
365	verify_expect $expect chg_usr_exec $user chmod A0- $object
366	verify_expect $expect chg_usr_exec $user chmod A0- $object
367	oldmode=$(get_mode $object)
368	verify_expect $expect chg_usr_exec $user chmod $TESTMODE $object
369}
370
371#
372# Testing function to verify the given node is readonly
373#
374# $1: The given node, file/dir
375#
376function test_readonly
377{
378	typeset object=$1
379	typeset exp
380
381	if [[ -z $object ]]; then
382		log_fail "Object($object) not defined."
383	fi
384
385	log_note "Testing readonly of $object"
386
387	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
388		if [[ -d $object ]]; then
389			log_must usr_exec chmod \
390			    A+user:$user:${ace_dir}:allow $object
391		else
392			log_must usr_exec chmod \
393			    A+user:$user:${ace_file}:allow $object
394		fi
395
396		log_must set_attribute $object "R"
397
398		# As with mode bits, root can bypass.
399		if [[ "$user" == "root" ]]; then
400			exp=0
401		else
402			exp=1
403		fi
404
405		unit_writefile $object $user $exp
406		unit_writedir $object $user
407		unit_appenddata $object $user $exp
408
409		if [[ -d $object ]]; then
410			unit_writexattr $object $user
411		else
412			unit_writexattr $object $user $exp
413		fi
414
415		unit_accesstime $object $user
416		unit_updatetime $object $user
417		unit_writeacl $object $user
418		unit_deletecontent $object $user
419		unit_deletedata $object $user
420
421		if [[ -d $object ]] ;then
422			create_object "dir" $object $ZFS_ACL_CUR_USER
423		else
424			create_object "file" $object $ZFS_ACL_CUR_USER
425		fi
426	done
427}
428
429#
430# Testing function to verify the given node is immutable
431#
432# $1: The given node, file/dir
433#
434function test_immutable
435{
436	typeset object=$1
437
438	if [[ -z $object ]]; then
439		log_fail "Object($object) not defined."
440	fi
441
442	log_note "Testing immutable of $object"
443
444	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
445		if [[ -d $object ]]; then
446			log_must usr_exec chmod \
447			    A+user:$user:${ace_dir}:allow $object
448		else
449			log_must usr_exec chmod \
450			    A+user:$user:${ace_file}:allow $object
451		fi
452		log_must set_attribute $object "i"
453
454		unit_writefile $object $user 1
455		unit_writedir $object $user 1
456		unit_appenddata $object $user 1
457		unit_writexattr $object $user 1
458		unit_accesstime $object $user
459		unit_updatetime $object $user 1 0
460		unit_writeacl $object $user 1
461		unit_deletecontent $object $user 1
462		unit_deletedata $object $user 1
463
464		if [[ -d $object ]] ;then
465			create_object "dir" $object $ZFS_ACL_CUR_USER
466		else
467			create_object "file" $object $ZFS_ACL_CUR_USER
468		fi
469	done
470}
471
472#
473# Testing function to verify the given node is nounlink
474#
475# $1: The given node, file/dir
476#
477function test_nounlink
478{
479	typeset object=$1
480
481	if [[ -z $object ]]; then
482		log_fail "Object($object) not defined."
483	fi
484
485	echo "Testing nounlink of $object"
486
487	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
488		if [[ -d $object ]]; then
489			log_must usr_exec chmod \
490			    A+user:$user:${ace_dir}:allow $object
491		else
492			log_must usr_exec chmod \
493			    A+user:$user:${ace_file}:allow $object
494		fi
495		log_must set_attribute $object "u"
496
497		unit_writefile $object $user
498		unit_writedir $object $user
499		unit_appenddata $object $user
500		unit_writexattr $object $user
501		unit_accesstime $object $user
502		unit_updatetime $object $user
503		unit_writeacl $object $user
504		unit_deletecontent $object $user 1
505		unit_deletedata $object $user 1
506
507		if [[ -d $object ]] ;then
508			create_object "dir" $object $ZFS_ACL_CUR_USER
509		else
510			create_object "file" $object $ZFS_ACL_CUR_USER
511		fi
512	done
513}
514
515#
516# Testing function to verify the given node is appendonly
517#
518# $1: The given node, file/dir
519#
520function test_appendonly
521{
522	typeset object=$1
523
524	if [[ -z $object ]]; then
525		log_fail "Object($object) not defined."
526	fi
527
528	log_note "Testing appendonly of $object"
529
530	for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do
531		if [[ -d $object ]]; then
532			log_must usr_exec chmod \
533			    A+user:$user:${ace_dir}:allow $object
534		else
535			log_must usr_exec chmod \
536			    A+user:$user:${ace_file}:allow $object
537		fi
538		log_must set_attribute $object "a"
539
540		unit_writefile $object $user 1
541		unit_writedir $object $user
542		unit_appenddata $object $user
543		unit_writexattr $object $user
544		unit_accesstime $object $user
545		unit_updatetime $object $user
546		unit_writeacl $object $user
547		unit_deletecontent $object $user
548		unit_deletedata $object $user
549
550		if [[ -d $object ]] ;then
551			create_object "dir" $object $ZFS_ACL_CUR_USER
552		else
553			create_object "file" $object $ZFS_ACL_CUR_USER
554		fi
555	done
556}
557
558FILES="file.0 file.1"
559DIRS="dir.0 dir.1"
560XATTRS="attr.0 attr.1"
561FS="$TESTPOOL $TESTPOOL/$TESTFS"
562
563if is_global_zone ; then
564	ATTRS="R i u a"
565else
566	ATTRS="R"
567fi
568
569TESTFILE=/tmp/tfile
570TESTDIR=tdir
571TESTATTR=tattr
572TESTACL=user:$ZFS_ACL_OTHER1:write_data:allow
573TESTMODE=777
574TESTSTR="ZFS test suites"
575
576ace_file="write_data/append_data/write_xattr/write_acl/write_attributes"
577ace_dir="add_file/add_subdirectory/${ace_file}"
578
579log_assert "Verify DOS & BSD'ish attributes will provide the " \
580    "access limitation as expected."
581log_onexit cleanup
582
583echo "$TESTSTR" > $TESTFILE
584
585typeset gobject
586typeset gattr
587for gattr in $ATTRS ; do
588	for fs in $FS ; do
589		mtpt=$(get_prop mountpoint $fs)
590		chmod 777 $mtpt
591		for user in root $ZFS_ACL_STAFF1; do
592			log_must set_cur_usr $user
593			for file in $FILES ; do
594				gobject=$mtpt/$file
595				create_object "file" $gobject $ZFS_ACL_CUR_USER
596				test_wrapper $gobject $gattr
597				destroy_object $gobject
598			done
599
600			for dir in $DIRS ; do
601				gobject=$mtpt/$dir
602				create_object "dir" $gobject $ZFS_ACL_CUR_USER
603				test_wrapper $gobject $gattr
604				destroy_object $gobject
605			done
606		done
607	done
608done
609
610log_pass "DOS & BSD'ish attributes provide the access limitation as expected."
611