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 2006 Sun Microsystems, Inc. All rights reserved.
24 # Use is subject to license terms.
25 #
26 # acltools.tcl - utility procedures used for NFSv4 ACL tests
27 #
28
29 #--------------------------------------------------------------------
30 # aclmask()
31 #
32 # Convert an ACL access_mask defined by strings into numeric form.
33 # Returns the numeric value of the access_mask.
34 #
35 proc aclmask { mask_list } {
36 set mask_val 0
37 foreach entry $mask_list {
38 switch -exact -- $entry {
39 ACE4_READ_DATA { set mask_val [expr $mask_val | 0x00000001] }
40 ACE4_LIST_DIRECTORY { set mask_val [expr $mask_val | 0x00000001] }
41 ACE4_WRITE_DATA {set mask_val [expr $mask_val | 0x00000002] }
42 ACE4_ADD_FILE {set mask_val [expr $mask_val | 0x00000002] }
43 ACE4_APPEND_DATA {set mask_val [expr $mask_val | 0x00000004] }
44 ACE4_ADD_SUBDIRECTORY {set mask_val [expr $mask_val | 0x00000004] }
45 ACE4_READ_NAMED_ATTRS {set mask_val [expr $mask_val | 0x00000008] }
46 ACE4_WRITE_NAMED_ATTRS {set mask_val [expr $mask_val | 0x00000010] }
47 ACE4_EXECUTE {set mask_val [expr $mask_val | 0x00000020] }
48 ACE4_DELETE_CHILD {set mask_val [expr $mask_val | 0x00000040] }
49 ACE4_READ_ATTRIBUTES {set mask_val [expr $mask_val | 0x00000080] }
50 ACE4_WRITE_ATTRIBUTES {set mask_val [expr $mask_val | 0x00000100] }
51 ACE4_DELETE {set mask_val [expr $mask_val | 0x00010000] }
52 ACE4_READ_ACL {set mask_val [expr $mask_val | 0x00020000] }
53 ACE4_WRITE_ACL {set mask_val [expr $mask_val | 0x00040000] }
54 ACE4_WRITE_OWNER {set mask_val [expr $mask_val | 0x00080000] }
55 ACE4_SYNCHRONIZE {set mask_val [expr $mask_val | 0x00100000] }
56 ACE4_GENERIC_READ {set mask_val [expr $mask_val | 0x00120081] }
57 ACE4_GENERIC_WRITE {set mask_val [expr $mask_val | 0x00160106] }
58 ACE4_GENERIC_EXECUTE {set mask_val [expr $mask_val | 0x001200A0] }
59 ACE4_MASK_UNDEFINED {set mask_val [expr $mask_val | 0x80000000] }
60
61 default { puts $entry }
62 }
63 }
64 return [format "%x" $mask_val]
65 }
66
67
68 #--------------------------------------------------------------------
69 # de_aclmask()
70 # decode an ACL access_mask from numeric into human readable form.
71 #
72 proc de_aclmask { mask_val } {
73
74 set num [format "0x%s" $mask_val]
75
76 if { [expr $num & 0x00000001] == 1} {
77 append mask_str "ACE4_READ_DATA "
78 }
79 if { [expr $num & 0x00000002] } {
80 append mask_str "ACE4_WRITE_DATA "
81 }
82 if { [expr $num & 0x00000004] } {
83 append mask_str "ACE4_APPEND_DATA "
84 }
85 if { [expr $num & 0x00000008] } {
86 append mask_str "ACE4_READ_NAMED_ATTRS "
87 }
88 if { [expr $num & 0x00000010] } {
89 append mask_str "ACE4_WRITE_NAMED_ATTRS "
90 }
91 if { [expr $num & 0x00000020] } {
92 append mask_str "ACE4_EXECUTE "
93 }
94 if { [expr $num & 0x00000040] } {
95 append mask_str "ACE4_DELETE_CHILD "
96 }
97 if { [expr $num & 0x00000080] } {
98 append mask_str "ACE4_READ_ATTRIBUTES "
99 }
100 if { [expr $num & 0x00000100] } {
101 append mask_str "ACE4_WRITE_ATTRIBUTES "
102 }
103 if { [expr $num & 0x00010000] } {
104 append mask_str "ACE4_DELETE "
105 }
106 if { [expr $num & 0x00020000] } {
107 append mask_str "ACE4_READ_ACL "
108 }
109 if { [expr $num & 0x00040000] } {
110 append mask_str "ACE4_WRITE_ACL "
111 }
112 if { [expr $num & 0x00080000] } {
113 append mask_str "ACE4_WRITE_OWNER "
114 }
115 if { [expr $num & 0x00100000] } {
116 append mask_str "ACE4_SYNCHRONIZE "
117 }
118 if { [expr $num & 0x00120081] } {
119 append mask_str "ACE4_GENERIC_READ "
120 }
121 if { [expr $num & 0x00160106] } {
122 append mask_str "ACE4_GENERIC_WRITE "
123 }
124 if { [expr $num & 0x001200A0] } {
125 append mask_str "ACE4_GENERIC_EXECUTE "
126 }
127 if { [expr $num & 0x80000000] } {
128 append mask_str "ACE4_MASK_UNDEFINED "
129 }
130
131 return $mask_str
132 }
133
134
135 #--------------------------------------------------------------------
136 # de_acltype()
137 # Decode ACL type numeric value to string.
138 #
139 proc de_acltype { type_val } {
140
141 set num [format "0x%s" $type_val]
142
143 if { [expr $num & 0x00000000] == 1} {
144 set type_str "ACE4_ACCESS_ALLOWED_ACE_TYPE"
145 }
146 if { [expr $num & 0x00000001] == 1} {
147 set type_str "ACE4_ACCESS_DENIED_ACE_TYPE"
148 }
149 if { [expr $num & 0x00000002] == 1} {
150 set type_str "ACE4_SYSTEM_AUDIT_ACE_TYPE"
151 }
152 if { [expr $num & 0x00000003] == 1} {
153 set type_str "ACE4_SYSTEM_ALARM_ACE_TYPE"
154 }
155
156 return $type_str
157 }
158
159 #--------------------------------------------------------------------
160 # de_aclflg()
161 # Decode ACL flag numeric value to string.
162 #
163 proc de_aclflag { flag_val } {
164 set num [format "0x%s" $type_val]
165
166 if { [expr $num & 0x00000001] == 1} {
167 append flag_str "ACE4_FILE_INHERIT_ACE "
168 }
169 if { [expr $num & 0x00000002] == 1} {
170 append flag_str "ACE4_DIRECTORY_INHERIT_ACE "
171 }
172 if { [expr $num & 0x00000004] == 1} {
173 append flag_str "ACE4_NO_PROPAGATE_INHERIT_ACE "
174 }
175 if { [expr $num & 0x00000008] == 1} {
176 append flag_str "ACE4_INHERIT_ONLY_ACE "
177 }
178 if { [expr $num & 0x00000010] == 1} {
179 append flag_str "ACE4_SUCCESSFUL_ACCESS_ACE_FLAG "
180 }
181 if { [expr $num & 0x00000020] == 1} {
182 append flag_str "ACE4_FAILED_ACCESS_ACE_FLAG "
183 }
184 if { [expr $num & 0x00000040] == 1} {
185 append flag_str "ACE4_IDENTIFIER_GROUP "
186 }
187
188 return $flag_str
189 }
190
191 #--------------------------------------------------------------------
192 # extract_acl_list()
193 # Given a string from a Getattr acl command (which contains the
194 # ACL entries and extraneous text), extract the actual
195 # ACL entries and return in list form.
196 #
197 proc extract_acl_list { acl_str } {
198 set acl_tmp_list [split $acl_str "\{\}"]
199 set acl_tmp_list_ln [ expr [llength $acl_tmp_list] - 4]
200
201 for { set i 7} {$i < $acl_tmp_list_ln} { incr i 2} {
202 set acl_elm [lindex $acl_tmp_list $i]
203 lappend acl_list $acl_elm
204 }
205
206 return $acl_list
207 }
208
209 #--------------------------------------------------------------------
210 # compare_acl_lists()
211 # Compare either two full lists of ACL's or sub-fields within the
212 # ACL list. Return 0 if they are identical, otherwise return non-zero.
213 #
214 proc compare_acl_lists {list1 list2 {field ALL } } {
215 set list1_ln [ llength $list1]
216 set list2_ln [ llength $list1]
217
218 # Determine are we to match on sub-field or on entire
219 # list.
220 # ACL sub-fields are always in the following order:
221 # <type><flag><access mask><who>
222 #
223 switch -exact $field {
224 TYPE { set pos 0; set field_match TRUE}
225 FLAG { set pos 1; set field_match TRUE}
226 MASK { set pos 2; set field_match TRUE}
227 WHO { set pos 3; set field_match TRUE}
228 default { set pos 0; set field_match FALSE}
229 }
230
231 # Sanity check - both lists should have the same number of elements.
232 if { $list1_ln != $list2_ln} {
233 putmsg stderr 0 "lists have different number of elements"
234 return 1
235 }
236
237 # If we are just comparing one sub-field then we extract
238 # that, otherwise we just compare the entire block.
239 if { $field_match == "TRUE"} {
240 for { set i 0} {$i < $list1_ln} { incr i 1} {
241 if { [lindex [lindex $list1 $i] $pos] !=
242 [lindex [lindex $list2 $i] $pos] } {
243 putmsg stderr 0 "element $i doesn't match !"
244 putmsg stderr 0 "[lindex [lindex $list1 $i] $pos]"
245 putmsg stderr 0 "[lindex [lindex $list2 $i] $pos]"
246 return 2
247 }
248 }
249 } else {
250 for { set i 0} {$i < $list1_ln} { incr i 1} {
251 if { [lindex $list1 $i] != [lindex $list2 $i] } {
252 putmsg stderr 0 "element $i doesn't match !"
253 putmsg stderr 0 "[lindex $list1 $i] : [lindex $list2 $i]"
254 return 2
255 }
256 }
257 }
258
259 return 0
260 }
261
262 #--------------------------------------------------------------------
263 # Convert a Dir ACL list to a File one by removing the DELETE_CHILD
264 # entry from the ACL mask if it exists.
265 #
266 proc dir2file_aclmask { dir_acl } {
267 set list1_ln [ llength $dir_acl]
268 set pos 2
269
270 set new_list $dir_acl
271
272 for { set i 0} {$i < $list1_ln} { incr i 1} {
273 set block [lindex $new_list $i]
274 set el [lindex $block $pos]
275 set elm [format "0x%s" $el]
276
277 if { [expr $elm & 0x00000040] } {
278 set new_el [format "%x" [expr $elm ^ 0x00000040]]
279 set new_block [lreplace $block $pos $pos $new_el]
280 set new_list [lreplace $new_list $i $i $new_block]
281 }
282 }
283
284 return $new_list
285 }
286
287
288 #--------------------------------------------------------------------
289 # restore_perms()
290 # Restore all the permissions on a file or directory. Usually
291 # called after a test has removed one of the perms.
292 #
293 proc restore_perms { test_fh field target} {
294
295 set expcode "OK"
296 set sid {0 0}
297
298 # get the initial ACL settings.
299 set initial_acl [compound {Putfh $test_fh; \
300 Getattr acl }]
301
302 ckres "Getattr acl" $status $expcode $initial_acl 1
303
304 #
305 # Break the string returned from the Geattr acl command into
306 # a list and then extract the actual ACL settings.
307 #
308 set acl_list [extract_acl_list $initial_acl]
309
310 # Create the new ACL settings by replacing the appropriate entries.
311 #
312 # Order of entries in the list is as follows:
313 # <OWNER><OWNER><GROUP><GROUP><GROUP><EVERYONE><EVERYONE>
314 #
315
316 # Determine which fields to replace.
317 switch -exact $field {
318 OWNER {
319 if { $target == "FILE" } {
320 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
321 ACE4_WRITE_ATTRIBUTES ACE4_READ_ACL ACE4_WRITE_ACL \
322 ACE4_READ_DATA ACE4_APPEND_DATA \
323 ACE4_WRITE_DATA ACE4_EXECUTE \
324 ACE4_SYNCHRONIZE } ]
325 } else {
326 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
327 ACE4_READ_ACL ACE4_WRITE_ACL \
328 ACE4_WRITE_ATTRIBUTES ACE4_LIST_DIRECTORY \
329 ACE4_ADD_SUBDIRECTORY ACE4_ADD_FILE \
330 ACE4_EXECUTE \
331 ACE4_DELETE_CHILD ACE4_SYNCHRONIZE } ]
332 }
333
334 set deny_mask 0
335
336 set acl_list [lreplace $acl_list 0 0 "0 0 $allow_mask OWNER\@"]
337 set acl_list [lreplace $acl_list 1 1 "1 0 $deny_mask OWNER\@"]
338 }
339 GROUP {
340 if { $target == "FILE" } {
341 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
342 ACE4_READ_ACL ACE4_READ_DATA ACE4_APPEND_DATA \
343 ACE4_WRITE_DATA ACE4_EXECUTE ACE4_SYNCHRONIZE } ]
344 } else {
345 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
346 ACE4_LIST_DIRECTORY ACE4_ADD_SUBDIRECTORY ACE4_ADD_FILE \
347 ACE4_READ_ACL ACE4_EXECUTE ACE4_DELETE_CHILD ACE4_SYNCHRONIZE } ]
348 }
349 set deny_mask [ aclmask { ACE4_WRITE_ATTRIBUTES ACE4_WRITE_ACL} ]
350
351 set acl_list [lreplace $acl_list 2 2 "1 40 $deny_mask GROUP\@"]
352 set acl_list [lreplace $acl_list 3 3 "0 40 $allow_mask GROUP\@"]
353 set acl_list [lreplace $acl_list 4 4 "1 40 $deny_mask GROUP\@"]
354 }
355 OTHER {
356 if { $target == "FILE" } {
357 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
358 ACE4_READ_ACL ACE4_READ_DATA ACE4_APPEND_DATA \
359 ACE4_WRITE_DATA ACE4_EXECUTE ACE4_SYNCHRONIZE } ]
360 } else {
361 set allow_mask [ aclmask { ACE4_READ_ATTRIBUTES \
362 ACE4_LIST_DIRECTORY ACE4_ADD_SUBDIRECTORY ACE4_ADD_FILE \
363 ACE4_READ_ACL ACE4_EXECUTE ACE4_DELETE_CHILD ACE4_SYNCHRONIZE } ]
364 }
365
366 set deny_mask [ aclmask { ACE4_WRITE_ATTRIBUTES ACE4_WRITE_ACL} ]
367
368 global IsZFS
369 if $IsZFS {
370 set acl_list [lreplace $acl_list 4 4 "0 0 $allow_mask EVERYONE\@"]
371 set acl_list [lreplace $acl_list 5 5 "1 0 $deny_mask EVERYONE\@"]
372 } else {
373 set acl_list [lreplace $acl_list 5 5 "0 0 $allow_mask EVERYONE\@"]
374 set acl_list [lreplace $acl_list 6 6 "1 0 $deny_mask EVERYONE\@"]
375 }
376 }
377 }
378
379 # Set the new ACL values.
380 set res [compound {Putfh $test_fh; \
381 Setattr $sid { {acl \
382 { $acl_list } } } } ]
383
384 ckres "Setattr acl" $status $expcode $res 1
385
386 # Re-read ACL values
387 set res2 [compound {Putfh $test_fh; \
388 Getattr acl }]
389
390 ckres "Getattr acl again" $status $expcode $res2 1
391
392 set new_acl_list [extract_acl_list $res2]
393
394 if { [compare_acl_lists $new_acl_list $acl_list] != 0} {
395 putmsg stderr 0 \
396 "\t Test FAIL: lists do not match."
397 } else {
398 putmsg stdout 0 "\t Test PASS"
399 }
400
401 puts ""
402 }
403
404
405 #--------------------------------------------------------------------
406 # remove_dir_entries()
407 # Given directory handled and list of contents (files and
408 # sub-dirs) remove the contents so the parent directory can
409 # itself be removed.
410 #
411 # This can be used as a building block in the future for a generic
412 # 'rm -rf' type routine which just removes a directory even when it
413 # is not empty.
414 #
415 proc remove_dir_entries { handle contents_list } {
416 foreach entry $contents_list {
417 set result [compound {Putfh $handle; Remove $entry}]
418
419 if {$status != "OK"} {
420 putmsg stderr 0 "\t WARNING: cleanup to remove tmp entry ($entry)"
421 putmsg stderr 0 "\t failed : status=$status; please cleanup "
422 putmsg stderr 0 "\t manually."
423 putmsg stderr 1 "\t res=($result)"
424 putmsg stderr 1 " "
425 }
426 }
427 }