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