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