1#!/usr/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 2008 Sun Microsystems, Inc. All rights reserved. 25# Use is subject to license terms. 26# 27 28# 29# Copyright (c) 2012 by Delphix. All rights reserved. 30# 31 32 33. $STF_SUITE/tests/functional/acl/acl_common.kshlib 34 35# 36# DESCRIPTION: 37# Verify chmod have correct behaviour on directories and files when 38# filesystem has the different aclmode setting 39# 40# STRATEGY: 41# 1. Loop super user and non-super user to run the test case. 42# 2. Create basedir and a set of subdirectores and files within it. 43# 3. Separately chmod basedir with different aclmode options, 44# combine with the variable setting of aclmode: 45# "discard", "groupmask", or "passthrough". 46# 4. Verify each directories and files have the correct access control 47# capability. 48# 49 50verify_runnable "both" 51 52function cleanup 53{ 54 # Cleanup tarfile & basedir. 55 56 (( ${#cwd} != 0 )) && cd $cwd 57 58 if [[ -f $TARFILE ]]; then 59 log_must $RM -f $TARFILE 60 fi 61 62 if [[ -d $basedir ]]; then 63 log_must $RM -rf $basedir 64 fi 65} 66 67log_assert "Verify chmod have correct behaviour to directory and file when " \ 68 "filesystem has the different aclmode setting." 69log_onexit cleanup 70 71# Define aclmode flag 72set -A aclmode_flag discard groupmask passthrough 73 74set -A ace_prefix "user:$ZFS_ACL_OTHER1" \ 75 "user:$ZFS_ACL_OTHER2" \ 76 "group:$ZFS_ACL_STAFF_GROUP" \ 77 "group:$ZFS_ACL_OTHER_GROUP" 78 79set -A argv "000" "444" "644" "777" "755" "231" "562" "413" 80 81set -A ace_file_preset "read_data" \ 82 "write_data" \ 83 "append_data" \ 84 "execute" \ 85 "read_data/write_data" \ 86 "read_data/write_data/append_data" \ 87 "write_data/append_data" \ 88 "read_data/execute" \ 89 "write_data/append_data/execute" \ 90 "read_data/write_data/append_data/execute" 91 92# Defile the based directory and file 93basedir=$TESTDIR/basedir; ofile=$basedir/ofile; odir=$basedir/odir 94nfile=$basedir/nfile; ndir=$basedir/ndir 95 96TARFILE=$TESTDIR/tarfile 97 98# Verify all the node have expected correct access control 99allnodes="$nfile $ndir" 100 101# 102# According to the original bits, the input ACE access and ACE type, return the 103# expect bits after 'chmod A0{+|=}'. 104# 105# $1 isdir indicate if the target is a directory 106# $2 bits which was make up of three bit 'rwx' 107# $3 bits_limit which was make up of three bit 'rwx' 108# $4 ACE access which is read_data, write_data or execute 109# $5 ctrl which is to determine allow or deny according to owner/group bit 110# 111function cal_bits # isdir bits bits_limit acl_access ctrl 112{ 113 typeset -i isdir=$1 114 typeset -i bits=$2 115 typeset -i bits_limit=$3 116 typeset acl_access=$4 117 typeset -i ctrl=${5:-0} 118 typeset flagr=0 flagw=0 flagx=0 119 typeset tmpstr 120 121 if (( ctrl == 0 )); then 122 if (( (( bits & 4 )) != 0 )); then 123 flagr=1 124 fi 125 if (( (( bits & 2 )) != 0 )); then 126 flagw=1 127 fi 128 if (( (( bits & 1 )) != 0 )); then 129 flagx=1 130 fi 131 else 132 #Determine ACE as per owner/group bit 133 flagr=1 134 flagw=1 135 flagx=1 136 137 if (( ((bits & 4)) != 0 )) && \ 138 (( ((bits_limit & 4)) != 0 )); then 139 flagr=0 140 fi 141 if (( ((bits & 2)) != 0 )) && \ 142 (( ((bits_limit & 2)) != 0 )); then 143 flagw=0 144 fi 145 if (( ((bits & 1)) != 0 )) && \ 146 (( ((bits_limit & 1)) != 0 )); then 147 flagx=0 148 fi 149 fi 150 if ((flagr != 0)); then 151 if [[ $acl_access == *"read_data"* ]]; then 152 if [[ $acl_access == *"allow"* && $passthrough == 0 ]]; then 153 tmpstr=${tmpstr} 154 else 155 if ((isdir == 0)); then 156 tmpstr=${tmpstr}/read_data 157 else 158 tmpstr=${tmpstr}/list_directory/read_data 159 fi 160 fi 161 fi 162 fi 163 164 if ((flagw != 0)); then 165 if [[ $acl_access == *"allow"* && $passthrough == 0 ]]; then 166 tmpstr=${tmpstr} 167 else 168 if [[ $acl_access == *"write_data"* ]]; then 169 if ((isdir == 0)); then 170 tmpstr=${tmpstr}/write_data 171 else 172 tmpstr=${tmpstr}/add_file/write_data 173 fi 174 fi 175 if [[ $acl_access == *"append_data"* ]]; then 176 if ((isdir == 0)); then 177 tmpstr=${tmpstr}/append_data 178 else 179 tmpstr=${tmpstr}/add_subdirectory/append_data 180 fi 181 fi 182 fi 183 fi 184 if ((flagx != 0)); then 185 if [[ $acl_access == *"execute"* ]]; then 186 if [[ $acl_access == *"allow"* && $passthrough == 0 ]]; then 187 tmpstr=${tmpstr} 188 else 189 tmpstr=${tmpstr}/execute 190 fi 191 fi 192 fi 193 194 tmpstr=${tmpstr#/} 195 196 $ECHO "$tmpstr" 197} 198 199# 200# To translate an ace if the node is dir 201# 202# $1 isdir indicate if the target is a directory 203# $2 acl to be translated 204# 205function translate_acl # isdir acl 206{ 207 typeset -i isdir=$1 208 typeset acl=$2 209 typeset who prefix acltemp action 210 211 if ((isdir != 0)); then 212 who=${acl%%:*} 213 prefix=$who 214 acltemp=${acl#*:} 215 acltemp=${acltemp%%:*} 216 prefix=$prefix:$acltemp 217 action=${acl##*:} 218 acl=$prefix:$(cal_bits $isdir 7 7 $acl 0):$action 219 fi 220 $ECHO "$acl" 221} 222 223# 224# To verify if a new ACL is generated as result of 225# chmod operation. 226# 227# $1 bit indicates whether owner/group bit 228# $2 newmode indicates the mode changed using chmod 229# $3 isdir indicate if the target is a directory 230# 231function check_new_acl # bit newmode isdir 232{ 233 typeset bits=$1 234 typeset mode=$2 235 typeset -i isdir=$3 236 typeset new_acl 237 typeset gbit 238 typeset ebit 239 typeset str=":" 240 gbit=$(get_substr $mode 2 1) 241 ebit=$(get_substr $mode 3 1) 242 if (( ((bits & 4)) == 0 )); then 243 if (( ((gbit & 4)) != 0 || \ 244 ((ebit & 4)) != 0 )); then 245 if ((isdir == 0)); then 246 new_acl=${new_acl}${str}read_data 247 else 248 new_acl=${new_acl}${str}list_directory/read_data 249 fi 250 str="/" 251 fi 252 fi 253 if (( ((bits & 2)) == 0 )); then 254 if (( ((gbit & 2)) != 0 || \ 255 ((ebit & 2)) != 0 )); then 256 if ((isdir == 0)); then 257 new_acl=${new_acl}${str}write_data/append_data 258 else 259 new_acl=${new_acl}${str}add_file/write_data/ 260 new_acl=${new_acl}add_subdirectory/append_data 261 fi 262 str="/" 263 fi 264 fi 265 if (( ((bits & 1)) == 0 )); then 266 if (( ((gbit & 1)) != 0 || \ 267 ((ebit & 1)) != 0 )); then 268 new_acl=${new_acl}${str}execute 269 fi 270 fi 271 $ECHO "$new_acl" 272} 273 274function build_new_acl # newmode isdir 275{ 276 typeset newmode=$1 277 typeset isdir=$2 278 typeset expect 279 if ((flag == 0)); then 280 prefix="owner@" 281 bit=$(get_substr $newmode 1 1) 282 status=$(check_new_acl $bit $newmode $isdir) 283 284 else 285 prefix="group@" 286 bit=$(get_substr $newmode 2 1) 287 status=$(check_new_acl $bit $newmode $isdir) 288 fi 289 expect=$prefix$status:deny 290 $ECHO $expect 291} 292 293# 294# According to inherited flag, verify subdirectories and files within it has 295# correct inherited access control. 296# 297function verify_aclmode # <aclmode> <node> <newmode> 298{ 299 # Define the nodes which will be affected by inherit. 300 typeset aclmode=$1 301 typeset node=$2 302 typeset newmode=$3 303 304 # count: the ACE item to fetch 305 # pass: to mark if the current ACE should apply to the target 306 # passcnt: counter, if it achieves to maxnumber, 307 # then no additional ACE should apply. 308 309 typeset -i count=0 pass=0 passcnt=0 310 typeset -i bits=0 obits=0 bits_owner=0 isdir=0 311 typeset -i total_acl 312 typeset -i acl_count=$(count_ACE $node) 313 314 ((total_acl = maxnumber + 3)) 315 316 if [[ -d $node ]]; then 317 ((isdir = 1)) 318 fi 319 320 ((i = maxnumber - 1)) 321 count=0 322 passcnt=0 323 flag=0 324 while ((i >= 0)); do 325 pass=0 326 expect1=${acls[$i]} 327 passthrough=0 328 # 329 # aclmode=passthrough, 330 # no changes will be made to the ACL other than 331 # generating the necessary ACL entries to represent 332 # the new mode of the file or directory. 333 # 334 # aclmode=discard, 335 # delete all ACL entries that don't represent 336 # the mode of the file. 337 # 338 # aclmode=groupmask, 339 # reduce user or group permissions. The permissions are 340 # reduced, such that they are no greater than the group 341 # permission bits, unless it is a user entry that has the 342 # same UID as the owner of the file or directory. 343 # Then, the ACL permissions are reduced so that they are 344 # no greater than owner permission bits. 345 # 346 347 case $aclmode in 348 passthrough) 349 if ((acl_count > total_acl)); then 350 expect1=$(build_new_acl $newmode $isdir) 351 flag=1 352 ((total_acl = total_acl + 1)) 353 ((i = i + 1)) 354 else 355 passthrough=1 356 expect1=$(translate_acl $isdir $expect1) 357 fi 358 ;; 359 groupmask) 360 if ((acl_count > total_acl)); then 361 expect1=$(build_new_acl $newmode $isdir) 362 flag=1 363 ((total_acl = total_acl + 1)) 364 ((i = i + 1)) 365 366 elif [[ $expect1 == *":allow"* ]]; then 367 who=${expect1%%:*} 368 aclaction=${expect1##*:} 369 prefix=$who 370 acltemp="" 371 reduce=0 372 # 373 # To determine the mask bits 374 # according to the entry type. 375 # 376 case $who in 377 owner@) 378 pos=1 379 ;; 380 group@) 381 pos=2 382 ;; 383 everyone@) 384 pos=3 385 ;; 386 user) 387 acltemp=${expect1#*:} 388 acltemp=${acltemp%%:*} 389 owner=$(get_owner $node) 390 group=$(get_group $node) 391 if [[ $acltemp == \ 392 $owner ]]; then 393 pos=1 394 else 395 pos=2 396 fi 397 prefix=$prefix:$acltemp 398 ;; 399 group) 400 acltemp=${expect1#*:} 401 acltemp=${acltemp%%:*} 402 pos=2 403 prefix=$prefix:$acltemp 404 reduce=1 405 ;; 406 esac 407 obits=$(get_substr $newmode $pos 1) 408 ((bits = $obits)) 409 # 410 # permission should no greater than the 411 # group permission bits 412 # 413 if ((reduce != 0)); then 414 ((bits &= \ 415 $(get_substr $newmode 2 1))) 416 # The ACL permissions are reduced so 417 # that they are no greater than owner 418 # permission bits. 419 420 ((bits_owner = \ 421 $(get_substr $newmode 1 1))) 422 ((bits &= $bits_owner)) 423 fi 424 425 if ((bits < obits)) && \ 426 [[ -n $acltemp ]]; then 427 expect2=$prefix: 428 new_bit=$(cal_bits $isdir $obits $bits_owner $expect1 1) 429 expect2=${expect2}${new_bit}:allow 430 else 431 expect2=$prefix: 432 new_bit=$(cal_bits $isdir $obits $obits $expect1 1) 433 expect2=${expect2}${new_bit}:allow 434 fi 435 priv=$(cal_bits $isdir $obits $bits_owner $expect2 0) 436 expect1=$prefix:$priv:$aclaction 437 else 438 expect1=$(translate_acl $isdir $expect1) 439 fi 440 ;; 441 discard) 442 passcnt=maxnumber 443 break 444 ;; 445 esac 446 447 if ((pass == 0)) ; then 448 # Get the first ACE to do comparison 449 450 aclcur=$(get_ACE $node $count) 451 aclcur=${aclcur#$count:} 452 if [[ -n $expect1 && $expect1 != $aclcur ]]; then 453 $LS -vd $node 454 log_fail "$aclmode $i #$count " \ 455 "ACE: $aclcur, expect to be " \ 456 "$expect1" 457 fi 458 ((count = count + 1)) 459 fi 460 ((i = i - 1)) 461 done 462 463 # 464 # If there's no any ACE be checked, it should be identify as 465 # an normal file/dir, verify it. 466 # 467 if ((passcnt == maxnumber)); then 468 if [[ -d $node ]]; then 469 compare_acls $node $odir 470 elif [[ -f $node ]]; then 471 compare_acls $node $ofile 472 fi 473 474 if [[ $? -ne 0 ]]; then 475 $LS -vd $node 476 log_fail "Unexpect acl: $node, $aclmode ($newmode)" 477 fi 478 fi 479} 480 481 482 483typeset -i maxnumber=0 484typeset acl 485typeset target 486typeset -i passthrough=0 487typeset -i flag=0 488cwd=$PWD 489cd $TESTDIR 490 491for mode in "${aclmode_flag[@]}"; do 492 493 # 494 # Set different value of aclmode 495 # 496 497 log_must $ZFS set aclmode=$mode $TESTPOOL/$TESTFS 498 499 for user in root $ZFS_ACL_STAFF1; do 500 log_must set_cur_usr $user 501 502 log_must usr_exec $MKDIR $basedir 503 504 log_must usr_exec $MKDIR $odir 505 log_must usr_exec $TOUCH $ofile 506 log_must usr_exec $MKDIR $ndir 507 log_must usr_exec $TOUCH $nfile 508 509 for obj in $allnodes; do 510 maxnumber=0 511 for preset in "${ace_file_preset[@]}"; do 512 for prefix in "${ace_prefix[@]}"; do 513 acl=$prefix:$preset 514 515 case $((maxnumber % 2)) in 516 0) 517 acl=$acl:deny 518 ;; 519 1) 520 acl=$acl:allow 521 ;; 522 esac 523 524 # 525 # Place on the target should succeed. 526 # 527 log_must usr_exec $CHMOD A+$acl $obj 528 acls[$maxnumber]=$acl 529 530 ((maxnumber = maxnumber + 1)) 531 done 532 done 533 # Archive the file and directory 534 log_must $TAR cpf@ $TARFILE $basedir 535 536 if [[ -d $obj ]]; then 537 target=$odir 538 elif [[ -f $obj ]]; then 539 target=$ofile 540 fi 541 for newmode in "${argv[@]}"; do 542 log_must usr_exec $CHMOD $newmode $obj 543 log_must usr_exec $CHMOD $newmode $target 544 log_must verify_aclmode $mode $obj $newmode 545 546 # Restore the tar archive 547 log_must $TAR xpf@ $TARFILE 548 done 549 done 550 551 log_must usr_exec $RM -rf $basedir $TARFILE 552 done 553done 554 555log_pass "Verify chmod behaviour co-op with aclmode setting passed." 556