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 ((isdir != 0)); then 145 tmpstr=${tmpstr}/list_directory 146 fi 147 tmpstr=${tmpstr}/read_data 148 fi 149 fi 150 151 if ((flagw != 0)); then 152 if [[ $acl_access == *"write_data"* ]]; then 153 if ((isdir != 0)); then 154 tmpstr=${tmpstr}/add_file 155 fi 156 tmpstr=${tmpstr}/write_data 157 fi 158 if [[ $acl_access == *"append_data"* ]]; then 159 if ((isdir != 0)); then 160 tmpstr=${tmpstr}/add_subdirectory 161 fi 162 tmpstr=${tmpstr}/append_data 163 fi 164 fi 165 166 if ((flagx != 0)); then 167 if [[ $acl_access == *"execute"* ]]; then 168 tmpstr=${tmpstr}/execute 169 fi 170 fi 171 172 tmpstr=${tmpstr#/} 173 174 echo "$tmpstr" 175} 176 177# 178# To translate an ace if the node is dir 179# 180# $1 isdir indicate if the target is a directory 181# $2 acl to be translated 182# 183function translate_acl # isdir acl 184{ 185 typeset -i isdir=$1 186 typeset acl=$2 187 typeset who prefix acltemp action 188 189 if ((isdir != 0)); then 190 who=${acl%%:*} 191 prefix=$who 192 acltemp=${acl#*:} 193 acltemp=${acltemp%%:*} 194 prefix=$prefix:$acltemp 195 action=${acl##*:} 196 acl=$prefix:$(cal_bits $isdir 7 7 $acl 0):$action 197 fi 198 echo "$acl" 199} 200 201# 202# To verify if a new ACL is generated as result of 203# chmod operation. 204# 205# $1 bit indicates whether owner/group bit 206# $2 newmode indicates the mode changed using chmod 207# $3 isdir indicate if the target is a directory 208# 209function check_new_acl # bit newmode isdir 210{ 211 typeset bits=$1 212 typeset mode=$2 213 typeset -i isdir=$3 214 typeset new_acl 215 typeset gbit 216 typeset ebit 217 typeset str=":" 218 typeset dc="" 219 220 gbit=${mode:1:1} 221 ebit=${mode:2:1} 222 if (( ((bits & 4)) == 0 )); then 223 if (( ((gbit & 4)) != 0 || \ 224 ((ebit & 4)) != 0 )); then 225 if ((isdir == 0)); then 226 new_acl=${new_acl}${str}read_data 227 else 228 new_acl=${new_acl}${str}list_directory/read_data 229 fi 230 str="/" 231 fi 232 fi 233 if (( ((bits & 2)) == 0 )); then 234 if (( ((gbit & 2)) != 0 || \ 235 ((ebit & 2)) != 0 )); then 236 if ((isdir == 0)); then 237 new_acl=${new_acl}${str}write_data/append_data 238 else 239 new_acl=${new_acl}${str}add_file/write_data/ 240 new_acl=${new_acl}add_subdirectory/append_data 241 dc="/delete_child" 242 fi 243 str="/" 244 fi 245 fi 246 if (( ((bits & 1)) == 0 )); then 247 if (( ((gbit & 1)) != 0 || \ 248 ((ebit & 1)) != 0 )); then 249 new_acl=${new_acl}${str}execute 250 fi 251 fi 252 new_acl=${new_acl}${dc} 253 echo "$new_acl" 254} 255 256function build_new_acl # newmode isdir 257{ 258 typeset newmode=$1 259 typeset isdir=$2 260 typeset expect 261 if ((flag == 0)); then 262 prefix="owner@" 263 bit=${newmode:0:1} 264 status=$(check_new_acl $bit $newmode $isdir) 265 266 else 267 prefix="group@" 268 bit=${newmode:1:1} 269 status=$(check_new_acl $bit $newmode $isdir) 270 fi 271 expect=$prefix$status:deny 272 echo $expect 273} 274 275# According to inherited flag, verify subdirectories and files within it has 276# correct inherited access control. 277function verify_aclmode # <aclmode> <node> <newmode> 278{ 279 # Define the nodes which will be affected by inherit. 280 typeset aclmode=$1 281 typeset node=$2 282 typeset newmode=$3 283 284 # count: the ACE item to fetch 285 # passcnt: counter, if it achieves to maxnumber, 286 # then no additional ACE should apply. 287 288 typeset -i count=0 passcnt=0 289 typeset -i bits=0 obits=0 bits_owner=0 isdir=0 290 typeset -i total_acl 291 typeset -i acl_count=$(count_ACE $node) 292 293 ((total_acl = maxnumber + 3)) 294 295 if [[ -d $node ]]; then 296 ((isdir = 1)) 297 fi 298 299 ((i = maxnumber - 1)) 300 count=0 301 passcnt=0 302 flag=0 303 while ((i >= 0)); do 304 expect1=${acls[$i]} 305 passthrough=0 306 # 307 # aclmode=passthrough, 308 # no changes will be made to the ACL other than 309 # generating the necessary ACL entries to represent 310 # the new mode of the file or directory. 311 # 312 # aclmode=discard, 313 # delete all ACL entries that don't represent 314 # the mode of the file. 315 # 316 # aclmode=groupmask, 317 # reduce user or group permissions. The permissions are 318 # reduced, such that they are no greater than the group 319 # permission bits, unless it is a user entry that has the 320 # same UID as the owner of the file or directory. 321 # Then, the ACL permissions are reduced so that they are 322 # no greater than owner permission bits. 323 # 324 325 case $aclmode in 326 passthrough) 327 if ((acl_count > total_acl)); then 328 expect1=$(build_new_acl $newmode $isdir) 329 flag=1 330 ((total_acl = total_acl + 1)) 331 ((i = i + 1)) 332 else 333 passthrough=1 334 expect1=$(translate_acl $isdir $expect1) 335 fi 336 ;; 337 groupmask) 338 if ((acl_count > total_acl)); then 339 expect1=$(build_new_acl $newmode $isdir) 340 flag=1 341 ((total_acl = total_acl + 1)) 342 ((i = i + 1)) 343 elif [[ $expect1 == *":allow"* ]]; then 344 who=${expect1%%:*} 345 aclaction=${expect1##*:} 346 prefix=$who 347 acltemp="" 348 reduce=0 349 # To determine the mask bits 350 # according to the entry type. 351 # 352 case $who in 353 owner@) 354 pos=0 355 ;; 356 group@) 357 pos=1 358 ;; 359 everyone@) 360 pos=2 361 ;; 362 user) 363 acltemp=${expect1#*:} 364 acltemp=${acltemp%%:*} 365 owner=$(get_owner $node) 366 group=$(get_group $node) 367 if [[ $acltemp == $owner ]]; then 368 pos=0 369 else 370 pos=1 371 fi 372 prefix=$prefix:$acltemp 373 ;; 374 group) 375 acltemp=${expect1#*:} 376 acltemp=${acltemp%%:*} 377 pos=1 378 prefix=$prefix:$acltemp 379 reduce=1 380 ;; 381 esac 382 383 obits=${newmode:$pos:1} 384 ((bits = $obits)) 385 # permission should be no greater than the 386 # group permission bits 387 if ((reduce != 0)); then 388 ((bits &= ${newmode:1:1})) 389 # The ACL permissions are reduced so 390 # that they are no greater than owner 391 # permission bits. 392 ((bits_owner = ${newmode:0:1})) 393 ((bits &= $bits_owner)) 394 fi 395 396 if ((bits < obits)) && [[ -n $acltemp ]]; then 397 expect2=$prefix: 398 new_bit=$(cal_bits $isdir $obits \ 399 $bits_owner $expect1 0) 400 expect2=${expect2}${new_bit}:allow 401 else 402 expect2=$prefix: 403 new_bit=$(cal_bits $isdir $obits \ 404 $obits $expect1 0) 405 expect2=${expect2}${new_bit}:allow 406 fi 407 408 priv=$(cal_bits $isdir $obits $bits_owner \ 409 $expect2 0) 410 expect1=$prefix:$priv:$aclaction 411 else 412 expect1=$(translate_acl $isdir $expect1) 413 fi 414 ;; 415 discard) 416 passcnt=maxnumber 417 break 418 ;; 419 esac 420 421 # Get the first ACE to do comparison 422 aclcur=$(get_ACE $node $count) 423 aclcur=${aclcur#$count:} 424 if [[ -n $expect1 && $expect1 != $aclcur ]]; then 425 ls -vd $node 426 log_fail "$aclmode $i #$count " \ 427 "ACE: $aclcur, expect to be " \ 428 "$expect1" 429 fi 430 ((count = count + 1)) 431 ((i = i - 1)) 432 done 433 434 # 435 # If there's no any ACE be checked, it should be identify as 436 # an normal file/dir, verify it. 437 # 438 if ((passcnt == maxnumber)); then 439 if [[ -d $node ]]; then 440 compare_acls $node $odir 441 elif [[ -f $node ]]; then 442 compare_acls $node $ofile 443 fi 444 445 if [[ $? -ne 0 ]]; then 446 ls -vd $node 447 log_fail "Unexpect acl: $node, $aclmode ($newmode)" 448 fi 449 fi 450} 451 452 453 454typeset -i maxnumber=0 455typeset acl 456typeset target 457typeset -i passthrough=0 458typeset -i flag=0 459 460cd $TESTDIR 461for mode in "${aclmode_flag[@]}"; do 462 log_must zfs set aclmode=$mode $TESTPOOL/$TESTFS 463 464 for user in root $ZFS_ACL_STAFF1; do 465 log_must set_cur_usr $user 466 467 log_must usr_exec mkdir $basedir 468 469 log_must usr_exec mkdir $odir 470 log_must usr_exec touch $ofile 471 log_must usr_exec mkdir $ndir 472 log_must usr_exec touch $nfile 473 474 for obj in $allnodes; do 475 maxnumber=0 476 for preset in "${ace_file_preset[@]}"; do 477 for prefix in "${ace_prefix[@]}"; do 478 acl=$prefix:$preset 479 480 case $((maxnumber % 2)) in 481 0) 482 acl=$acl:deny 483 ;; 484 1) 485 acl=$acl:allow 486 ;; 487 esac 488 489 log_must usr_exec chmod A+$acl $obj 490 acls[$maxnumber]=$acl 491 492 ((maxnumber = maxnumber + 1)) 493 done 494 done 495 # Archive the file and directory 496 log_must tar cpf@ $TARFILE ${basedir#$TESTDIR/} 497 498 if [[ -d $obj ]]; then 499 target=$odir 500 elif [[ -f $obj ]]; then 501 target=$ofile 502 fi 503 for newmode in "${argv[@]}"; do 504 log_must usr_exec chmod $newmode $obj 505 log_must usr_exec chmod $newmode $target 506 log_must verify_aclmode $mode $obj $newmode 507 log_must tar xpf@ $TARFILE 508 done 509 done 510 511 log_must usr_exec rm -rf $basedir $TARFILE 512 done 513done 514 515log_pass "Verify chmod behaviour co-op with aclmode setting passed" 516