1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21
22#
23# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26
27#
28# Copyright (c) 2016 by Delphix. All rights reserved.
29#
30
31. $STF_SUITE/tests/functional/acl/acl.cfg
32. $STF_SUITE/include/libtest.shlib
33
34#
35# Get the given file/directory access mode
36#
37# $1 object -- file or directroy
38#
39function get_mode #<obj>
40{
41	typeset obj=$1
42	if (( ${#obj} == 0 )); then
43		return 1
44	fi
45
46	ls -ld $obj | awk '{print $1}'
47}
48
49#
50# Get the given file/directory ACL
51#
52# $1 object -- file or directroy
53#
54function get_acl #<obj>
55{
56        typeset obj=$1
57	if (( ${#obj} == 0 )); then
58		return 1
59	fi
60
61	ls -vd $obj | nawk '(NR != 1) {print $0}'
62}
63
64#
65# Get the given file/directory ACL
66#
67# $1 object -- file or directroy
68#
69function get_compact_acl #<obj>
70{
71        typeset obj=$1
72	if (( ${#obj} == 0 )); then
73		return 1
74	fi
75
76	ls -Vd $obj | nawk '(NR != 1) {print $0}'
77}
78
79#
80# Check the given two files/directories have the same ACLs
81#
82# Return 0, if source object acl is equal to target object acl.
83#
84# $1 source object
85# $2 target object
86#
87function compare_acls #<src> <tgt>
88{
89        typeset src=$1
90        typeset tgt=$2
91
92	(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
93	[[ $src == $tgt ]] && return 0
94
95	typeset tmpsrc=/tmp/compare_acls.src.$$
96	typeset tmptgt=/tmp/compare_acls.tgt.$$
97
98	get_acl $src > $tmpsrc
99	get_acl $tgt > $tmptgt
100	typeset -i ret=0
101	diff $tmpsrc $tmptgt > /dev/null 2>&1
102	ret=$?
103	rm -f $tmpsrc $tmptgt
104
105	if (( ret != 0 )); then
106		return $ret
107	fi
108
109	get_compact_acl $src > $tmpsrc
110	get_compact_acl $tgt > $tmptgt
111	diff $tmpsrc $tmptgt > /dev/null 2>&1
112	ret=$?
113	rm -f $tmpsrc $tmptgt
114
115	return $ret
116}
117
118#
119# Check that the given two objects have the same modes.
120# Return 0, if their modes are equal with each other. Otherwise, return 1.
121#
122# $1 source object
123# $2 target object
124#
125function compare_modes #<src> <tgt>
126{
127        typeset src=$1
128        typeset tgt=$2
129        typeset -i i=0
130        set -A mode
131
132	(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
133	[[ $src == $tgt ]] && return 0
134
135	typeset obj
136        for obj in $src $tgt
137        do
138                mode[i]=$(get_mode $obj)
139
140                (( i = i + 1 ))
141        done
142
143        [[ ${mode[0]} != ${mode[1]} ]] && return 1
144
145        return 0
146}
147
148#
149# Check that the given two objects have the same xattrs.
150# Return 0, if their xattrs are equal with each other. Otherwise, return 1.
151#
152# $1 source object
153# $2 target object
154#
155function compare_xattrs #<src> <tgt>
156{
157        typeset src=$1
158        typeset tgt=$2
159
160	(( ${#src} == 0 || ${#tgt} == 0 )) && return 1
161	[[ $src == $tgt ]] && return 0
162
163	typeset tmpsrc=/tmp/compare_xattrs.src.$$
164	typeset tmptgt=/tmp/compare_xattrs.tgt.$$
165
166	get_xattr $src > $tmpsrc
167	get_xattr $tgt > $tmptgt
168	typeset -i ret=0
169	diff $tmpsrc $tmptgt > /dev/null 2>&1
170	ret=$?
171	rm -f $tmpsrc $tmptgt
172
173        return $ret
174}
175
176#
177# Check '+' is set for a given file/directory with 'ls [-l]' command
178#
179# $1 object -- file or directory.
180#
181function plus_sign_check_l #<obj>
182{
183	typeset obj=$1
184	if (( ${#obj} == 0 )); then
185		return 1
186	fi
187
188	ls -ld $obj | awk '{print $1}' | grep "+\>" > /dev/null
189
190        return $?
191}
192
193#
194# Check '+' is set for a given file/directory with 'ls [-v]' command
195#
196# $1 object -- file or directory.
197#
198function plus_sign_check_v #<obj>
199{
200	typeset obj=$1
201	if (( ${#obj} == 0 )); then
202		return 1
203	fi
204
205	ls -vd $obj | nawk '(NR == 1) {print $1}' | grep "+\>" > /dev/null
206
207        return $?
208}
209
210#
211# A wrapper function of c program
212#
213# $1 legal login name
214# $2-n commands and options
215#
216function chgusr_exec #<login_name> <commands> [...]
217{
218	chg_usr_exec $@
219	return $?
220}
221
222#
223# Export the current user for the following usr_exec operating.
224#
225# $1 legal login name
226#
227function set_cur_usr #<login_name>
228{
229	export ZFS_ACL_CUR_USER=$1
230}
231
232#
233# Run commands by $ZFS_ACL_CUR_USER
234#
235# $1-n commands and options
236#
237function usr_exec #<commands> [...]
238{
239	chg_usr_exec "$ZFS_ACL_CUR_USER" $@
240	return $?
241}
242
243#
244# Count how many ACEs for the speficied file or directory.
245#
246# $1 file or directroy name
247#
248function count_ACE #<file or dir name>
249{
250	if [[ ! -e $1 ]]; then
251		log_note "Need input file or directroy name."
252		return 1
253	fi
254
255	ls -vd $1 | nawk 'BEGIN {count=0}
256			(NR != 1)&&(/[0-9]:/) {count++}
257			END {print count}'
258
259	return 0
260}
261
262#
263# Get specified number ACE content of specified file or directory.
264#
265# $1 file or directory name
266# $2 specified number
267#
268function get_ACE #<file or dir name> <specified number> <verbose|compact>
269{
270	if [[ ! -e $1 || $2 -ge $(count_ACE $1) ]]; then
271		return 1
272	fi
273
274	typeset file=$1
275	typeset -i num=$2
276	typeset format=${3:-verbose}
277	typeset -i next_num=-1
278
279        typeset tmpfile=/tmp/tmp_get_ACE.$$
280        typeset line=""
281	typeset args
282
283	case $format in
284		verbose) args="-vd"
285			;;
286		compact) args="-Vd"
287			;;
288		*) log_fail "Invalid parameter as ($format), " \
289			"only verbose|compact is supported."
290			;;
291	esac
292
293	ls $args $file > $tmpfile
294	(( $? != 0 )) && log_fail "FAIL: ls $args $file > $tmpfile"
295	while read line; do
296		[[ -z $line ]] && continue
297		if [[ $args == -vd ]]; then
298			if [[ $line == "$num":* ]]; then
299				(( next_num = num + 1 ))
300			fi
301			if [[ $line == "$next_num":* ]]; then
302				break
303			fi
304			if (( next_num != -1 )); then
305				print -n $line
306			fi
307		else
308			if (( next_num == num )); then
309				print -n $line
310			fi
311			(( next_num += 1 ))
312		fi
313	done < $tmpfile
314
315	rm -f $tmpfile
316	(( $? != 0 )) && log_fail "FAIL: rm -f $tmpfile"
317}
318
319#
320# Cleanup exist user/group.
321#
322function cleanup_user_group
323{
324	del_user $ZFS_ACL_ADMIN
325
326	del_user $ZFS_ACL_STAFF1
327	del_user $ZFS_ACL_STAFF2
328	del_group $ZFS_ACL_STAFF_GROUP
329
330	del_user $ZFS_ACL_OTHER1
331	del_user $ZFS_ACL_OTHER2
332	del_group $ZFS_ACL_OTHER_GROUP
333
334	return 0
335}
336
337#
338# Clean up testfile and test directory
339#
340function cleanup
341{
342	if [[ -d $TESTDIR ]]; then
343		cd $TESTDIR
344		rm -rf $TESTDIR/*
345	fi
346}
347
348#
349# According to specified access or acl_spec, do relevant operating by using the
350# specified user.
351#
352# $1 specified user
353# $2 node
354# $3 acl_spec or access
355#
356function rwx_node #user node acl_spec|access
357{
358	typeset user=$1
359	typeset node=$2
360	typeset acl_spec=$3
361
362	if [[ $user == "" || $node == "" || $acl_spec == "" ]]; then
363		log_note "node or acl_spec are not defined."
364		return 1
365	fi
366
367	if [[ -d $node ]]; then
368		case $acl_spec in
369		*:read_data:*|read_data)
370			chgusr_exec $user ls -l $node > /dev/null 2>&1
371			return $? ;;
372		*:write_data:*|write_data)
373			if [[ -f ${node}/tmpfile ]]; then
374				log_must rm -f ${node}/tmpfile
375			fi
376			chgusr_exec $user touch ${node}/tmpfile > \
377				/dev/null 2>&1
378			return $? ;;
379		*"execute:"*|execute)
380			chgusr_exec $user find $node > /dev/null 2>&1
381			return $? ;;
382		esac
383	else
384		case $acl_spec in
385		*:read_data:*|read_data)
386			chgusr_exec $user cat $node > /dev/null 2>&1
387			return $? ;;
388		*:write_data:*|write_data)
389			chgusr_exec $user dd if=/usr/bin/ls of=$node > \
390				/dev/null 2>&1
391			return $? ;;
392		*"execute:"*|execute)
393			ZFS_ACL_ERR_STR=$(chgusr_exec $user $node 2>&1)
394			return $? ;;
395		esac
396	fi
397}
398
399#
400# Get the given file/directory xattr
401#
402# $1 object -- file or directroy
403#
404function get_xattr #<obj>
405{
406        typeset obj=$1
407	typeset xattr
408	if (( ${#obj} == 0 )); then
409		return 1
410	fi
411
412	for xattr in `runat $obj ls | \
413		/usr/bin/egrep -v -e SUNWattr_ro -e SUNWattr_rw` ; do
414		runat $obj sum $xattr
415	done
416}
417
418#
419# Get the owner of a file/directory
420#
421function get_owner #node
422{
423	typeset node=$1
424	typeset value
425
426	if [[ -z $node ]]; then
427		log_fail "node are not defined."
428	fi
429
430	if [[ -d $node ]]; then
431		value=$(ls -dl $node | awk '{print $3}')
432	elif [[ -e $node ]]; then
433		value=$(ls -l $node | awk '{print $3}')
434	fi
435
436	echo $value
437}
438
439#
440# Get the group of a file/directory
441#
442function get_group #node
443{
444	typeset node=$1
445	typeset value
446
447	if [[ -z $node ]]; then
448		log_fail "node are not defined."
449	fi
450
451	if [[ -d $node ]]; then
452		value=$(ls -dl $node | awk '{print $4}')
453	elif [[ -e $node ]]; then
454		value=$(ls -l $node | awk '{print $4}')
455	fi
456
457	echo $value
458}
459
460
461#
462# Get the group name that a UID belongs to
463#
464function get_user_group #uid
465{
466	typeset uid=$1
467	typeset value
468
469	if [[ -z $uid ]]; then
470		log_fail "UID not defined."
471	fi
472
473	value=$(id $uid)
474
475	if [[ $? -eq 0 ]]; then
476		value=${value##*\(}
477		value=${value%%\)*}
478		echo $value
479	else
480		log_fail "Invalid UID (uid)."
481	fi
482}
483
484#
485# Get the specified item of the specified string
486#
487# $1:	Item number, count from 0.
488# $2-n: strings
489#
490function getitem
491{
492	typeset -i n=$1
493	shift
494
495	(( n += 1 ))
496	eval echo \${$n}
497}
498
499#
500# This function calculate the specified directory files checksum and write
501# to the specified array.
502#
503# $1 directory in which the files will be cksum.
504# $2 file array name which was used to store file cksum information.
505# $3 attribute array name which was used to store attribute information.
506#
507function cksum_files #<dir> <file_array_name> <attribute_array_name>
508{
509	typeset dir=$1
510	typeset farr_name=$2
511	typeset aarr_name=$3
512
513	[[ ! -d $dir ]] && return
514	typeset oldpwd=$PWD
515	cd $dir
516	typeset files=$(ls file*)
517
518	typeset -i i=0
519	typeset -i n=0
520	while (( i < NUM_FILE )); do
521		typeset f=$(getitem $i $files)
522		eval $farr_name[$i]=\$\(\cksum $f\)
523
524		typeset -i j=0
525		while (( j < NUM_ATTR )); do
526			eval $aarr_name[$n]=\$\(\runat \$f \cksum \
527				attribute.$j\)
528
529			(( j += 1 ))
530			(( n += 1 ))
531		done
532
533		(( i += 1 ))
534	done
535
536	cd $oldpwd
537}
538
539#
540# This function compare two cksum results array.
541#
542# $1 The array name which stored the cksum before operation.
543# $2 The array name which stored the cksum after operation.
544#
545function compare_cksum #<array1> <array2>
546{
547	typeset before=$1
548	typeset after=$2
549	eval typeset -i count=\${#$before[@]}
550
551	typeset -i i=0
552	while (( i < count )); do
553		eval typeset var1=\${$before[$i]}
554		eval typeset var2=\${$after[$i]}
555
556		if [[ $var1 != $var2 ]]; then
557			return 1
558		fi
559
560		(( i += 1 ))
561	done
562
563	return 0
564}
565
566#
567# This function calculate all the files cksum information in current directory
568# and output them to the specified file.
569#
570# $1 directory from which the files will be cksum.
571# $2 cksum output file
572#
573function record_cksum #<outfile>
574{
575	typeset dir=$1
576	typeset outfile=$2
577
578	[[ ! -d ${outfile%/*} ]] && usr_exec mkdir -p ${outfile%/*}
579
580	usr_exec cd $dir ; find . -depth -type f -exec cksum {} \\\; | \
581	    sort > $outfile
582	usr_exec cd $dir ; find . -depth -type f -xattr -exec runat {} \
583		cksum attribute* \\\; | sort >> $outfile
584}
585
586#
587# The function create_files creates the directories and files that the script
588# will operate on to test extended attribute functionality.
589#
590# $1 The base directory in which to create directories and files.
591#
592function create_files #<directory>
593{
594	typeset basedir=$1
595
596	[[ ! -d $basedir ]] && usr_exec mkdir -m 777 $basedir
597	[[ ! -d $RES_DIR  ]] && usr_exec mkdir -m 777 $RES_DIR
598	[[ ! -d $INI_DIR ]] && usr_exec mkdir -m 777 $INI_DIR
599	[[ ! -d $TST_DIR ]] && usr_exec mkdir -m 777 $TST_DIR
600	[[ ! -d $TMP_DIR  ]] && usr_exec mkdir -m 777 $TMP_DIR
601
602	#
603	# Create the original file and its attribute files.
604	#
605	[[ ! -a $RES_DIR/file ]] && \
606		usr_exec file_write -o create -f $RES_DIR/file \
607			-b 1024 -d 0 -c 1
608	[[ ! -a $RES_DIR/attribute ]] && \
609		usr_exec cp $RES_DIR/file $RES_DIR/attribute
610
611	typeset oldpwd=$PWD
612	cd $INI_DIR
613
614	typeset -i i=0
615	while (( i < NUM_FILE )); do
616		typeset dstfile=$INI_DIR/file.$$.$i
617		usr_exec cp $RES_DIR/file $dstfile
618
619		typeset -i j=0
620		while (( j < NUM_ATTR )); do
621			usr_exec runat $dstfile \
622				cp $RES_DIR/attribute ./attribute.$j
623			(( j += 1 ))
624		done
625
626		(( i += 1 ))
627	done
628
629	cd $oldpwd
630}
631