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 }