1#!/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 2009 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# 31 32. $STF_SUITE/tests/functional/acl/acl_common.kshlib 33. $STF_SUITE/tests/functional/acl/cifs/cifs.kshlib 34 35# 36# DESCRIPTION: 37# Verify the DOS attributes (Readonly, Hidden, Archive, System) 38# and BSD'ish attributes (Immutable, nounlink, and appendonly) 39# will provide the proper access limitation as expected. 40# 41# Readonly means that the content of a file can't be modified, but 42# timestamps, mode and so on can. 43# 44# Archive - Indicates if a file should be included in the next backup 45# of the file system. ZFS will set this bit whenever a file is 46# modified. 47# 48# Hidden and System (ZFS does nothing special with these, other than 49# letting a user/application set them. 50# 51# Immutable (The data can't, change nor can mode, ACL, size and so on) 52# The only attribute that can be updated is the access time. 53# 54# Nonunlink - Sort of like immutable except that a file/dir can't be 55# removed. 56# This will also effect a rename operation, since that involes a 57# remove. 58# 59# Appendonly - File can only be appended to. 60# 61# nodump, settable, opaque (These are for the MacOS port) we will 62# allow them to be set, but have no semantics tied to them. 63# 64# STRATEGY: 65# 1. Loop super user and non-super user to run the test case. 66# 2. Create basedir and a set of subdirectores and files within it. 67# 3. Set the file/dir with each kind of special attribute. 68# 4. Verify the access limitation works as expected. 69# 70 71verify_runnable "both" 72 73function cleanup 74{ 75 if [[ -n $gobject ]]; then 76 destroy_object $gobject 77 fi 78 79 for fs in $TESTPOOL/$TESTFS $TESTPOOL ; do 80 mtpt=$(get_prop mountpoint $fs) 81 log_must rm -rf $mtpt/file.* $mtpt/dir.* 82 done 83 84 [[ -f $TESTFILE ]] && rm $TESTFILE 85} 86 87# 88# Set the special attribute to the given node 89# 90# $1: The given node (file/dir) 91# $2: The special attribute to be set 92# 93function set_attribute 94{ 95 typeset object=$1 96 typeset attr=$2 97 98 if [[ -z $attr ]]; then 99 attr="AHRSadimu" 100 if [[ -f $object ]]; then 101 attr="${attr}q" 102 fi 103 fi 104 chmod S+c${attr} $object 105 return $? 106} 107 108# 109# Clear the special attribute to the given node 110# 111# $1: The given node (file/dir) 112# $2: The special attribute to be cleared 113# 114function clear_attribute 115{ 116 typeset object=$1 117 typeset attr=$2 118 119 if [[ -z $attr ]]; then 120 if is_global_zone ; then 121 attr="AHRSadimu" 122 if [[ -f $object ]]; then 123 attr="${attr}q" 124 fi 125 else 126 attr="AHRS" 127 fi 128 fi 129 130 chmod S-c${attr} $object 131 return $? 132} 133 134# 135# A wrapper function to call test function according to the given attr 136# 137# $1: The given node (file/dir) 138# $2: The special attribute to be test 139# 140function test_wrapper 141{ 142 typeset object=$1 143 typeset attr=$2 144 145 if [[ -z $object || -z $attr ]]; then 146 log_fail "Object($object), Attr($attr) not defined." 147 fi 148 149 case $attr in 150 R) func=test_readonly 151 ;; 152 i) func=test_immutable 153 ;; 154 u) func=test_nounlink 155 ;; 156 a) func=test_appendonly 157 ;; 158 esac 159 160 if [[ -n $func ]]; then 161 $func $object 162 fi 163} 164 165# 166# Invoke the function and verify whether its return code as expected 167# 168# $1: Expect value 169# $2-$n: Function and args need to be invoked 170# 171function verify_expect 172{ 173 typeset -i expect=$1 174 typeset status 175 176 shift 177 178 "$@" > /dev/null 2>&1 179 status=$? 180 if [[ $status -eq 0 ]]; then 181 if ((expect != 0)); then 182 log_fail "$@ unexpect return 0" 183 fi 184 else 185 if ((expect == 0)); then 186 log_fail "$@ unexpect return $status" 187 fi 188 fi 189} 190 191# 192# Unit testing function against overwrite file 193# 194# $1: The given file node 195# $2: Execute user 196# $3: Expect value, default to be zero 197# 198function unit_writefile 199{ 200 typeset object=$1 201 typeset user=$2 202 typeset expect=${3:-0} 203 if [[ -f $object ]]; then 204 verify_expect $expect chg_usr_exec $user \ 205 cp $TESTFILE $object 206 verify_expect $expect chg_usr_exec $user \ 207 "echo '$TESTSTR' > $object" 208 fi 209} 210 211# 212# Unit testing function against write new stuffs into a directory 213# 214# $1: The given directory node 215# $2: Execute user 216# $3: Expect value, default to be zero 217# 218function unit_writedir 219{ 220 typeset object=$1 221 typeset user=$2 222 typeset expect=${3:-0} 223 224 if [[ -d $object ]]; then 225 verify_expect $expect chg_usr_exec $user \ 226 cp $TESTFILE $object 227 verify_expect $expect chg_usr_exec $user \ 228 mkdir -p $object/$TESTDIR 229 fi 230} 231 232function unit_appenddata 233{ 234 typeset object=$1 235 typeset user=$2 236 typeset expect=${3:-0} 237 238 if [[ ! -d $object ]]; then 239 verify_expect $expect chg_usr_exec $user \ 240 "echo '$TESTSTR' >> $object" 241 fi 242} 243 244# 245# Unit testing function against delete content from a directory 246# 247# $1: The given node, dir 248# $2: Execute user 249# $3: Expect value, default to be zero 250# 251function unit_deletecontent 252{ 253 typeset object=$1 254 typeset user=$2 255 typeset expect=${3:-0} 256 257 if [[ -d $object ]]; then 258 for target in $object/${TESTFILE##*/} $object/$TESTDIR ; do 259 if [[ -e $target ]]; then 260 verify_expect $expect chg_usr_exec $user \ 261 "mv $target $target.new" 262 verify_expect $expect chg_usr_exec $user \ 263 "echo y | rm -r $target.new" 264 fi 265 done 266 fi 267} 268 269# 270# Unit testing function against delete a node 271# 272# $1: The given node, file/dir 273# $2: Execute user 274# $3: Expect value, default to be zero 275# 276function unit_deletedata 277{ 278 typeset object=$1 279 typeset user=$2 280 typeset expect=${3:-0} 281 282 verify_expect $expect chg_usr_exec $user \ 283 "echo y | rm -r $object" 284 285} 286 287# 288# Unit testing function against write xattr to a node 289# 290# $1: The given node, file/dir 291# $2: Execute user 292# $3: Expect value, default to be zero 293# 294function unit_writexattr 295{ 296 typeset object=$1 297 typeset user=$2 298 typeset expect=${3:-0} 299 300 verify_expect $expect chg_usr_exec $user \ 301 runat $object "cp $TESTFILE $TESTATTR" 302 verify_expect $expect chg_usr_exec $user \ 303 "runat $object \"echo '$TESTSTR' > $TESTATTR\"" 304 verify_expect $expect chg_usr_exec $user \ 305 "runat $object \"echo '$TESTSTR' >> $TESTATTR\"" 306 if [[ $expect -eq 0 ]]; then 307 verify_expect $expect chg_usr_exec $user \ 308 runat $object "rm -f $TESTATTR" 309 fi 310} 311 312# 313# Unit testing function against modify accesstime of a node 314# 315# $1: The given node, file/dir 316# $2: Execute user 317# $3: Expect value, default to be zero 318# 319function unit_accesstime 320{ 321 typeset object=$1 322 typeset user=$2 323 typeset expect=${3:-0} 324 325 if [[ -d $object ]]; then 326 verify_expect $expect chg_usr_exec $user ls $object 327 else 328 verify_expect $expect chg_usr_exec $user cat $object 329 fi 330} 331 332# 333# Unit testing function against modify updatetime of a node 334# 335# $1: The given node, file/dir 336# $2: Execute user 337# $3: Expect value, default to be zero 338# 339function unit_updatetime 340{ 341 typeset object=$1 342 typeset user=$2 343 typeset expect=${3:-0} 344 typeset immutable_expect=${4:-$expect} 345 verify_expect $expect chg_usr_exec $user touch $object 346 verify_expect $immutable_expect chg_usr_exec $user touch -a $object 347 verify_expect $expect chg_usr_exec $user touch -m $object 348} 349 350# 351# Unit testing function against write acl of a node 352# 353# $1: The given node, file/dir 354# $2: Execute user 355# $3: Expect value, default to be zero 356# 357function unit_writeacl 358{ 359 typeset object=$1 360 typeset user=$2 361 typeset expect=${3:-0} 362 363 verify_expect $expect chg_usr_exec $user chmod A+$TESTACL $object 364 verify_expect $expect chg_usr_exec $user chmod A+$TESTACL $object 365 verify_expect $expect chg_usr_exec $user chmod A0- $object 366 verify_expect $expect chg_usr_exec $user chmod A0- $object 367 oldmode=$(get_mode $object) 368 verify_expect $expect chg_usr_exec $user chmod $TESTMODE $object 369} 370 371# 372# Testing function to verify the given node is readonly 373# 374# $1: The given node, file/dir 375# 376function test_readonly 377{ 378 typeset object=$1 379 typeset exp 380 381 if [[ -z $object ]]; then 382 log_fail "Object($object) not defined." 383 fi 384 385 log_note "Testing readonly of $object" 386 387 for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do 388 if [[ -d $object ]]; then 389 log_must usr_exec chmod \ 390 A+user:$user:${ace_dir}:allow $object 391 else 392 log_must usr_exec chmod \ 393 A+user:$user:${ace_file}:allow $object 394 fi 395 396 log_must set_attribute $object "R" 397 398 # As with mode bits, root can bypass. 399 if [[ "$user" == "root" ]]; then 400 exp=0 401 else 402 exp=1 403 fi 404 405 unit_writefile $object $user $exp 406 unit_writedir $object $user 407 unit_appenddata $object $user $exp 408 409 if [[ -d $object ]]; then 410 unit_writexattr $object $user 411 else 412 unit_writexattr $object $user $exp 413 fi 414 415 unit_accesstime $object $user 416 unit_updatetime $object $user 417 unit_writeacl $object $user 418 unit_deletecontent $object $user 419 unit_deletedata $object $user 420 421 if [[ -d $object ]] ;then 422 create_object "dir" $object $ZFS_ACL_CUR_USER 423 else 424 create_object "file" $object $ZFS_ACL_CUR_USER 425 fi 426 done 427} 428 429# 430# Testing function to verify the given node is immutable 431# 432# $1: The given node, file/dir 433# 434function test_immutable 435{ 436 typeset object=$1 437 438 if [[ -z $object ]]; then 439 log_fail "Object($object) not defined." 440 fi 441 442 log_note "Testing immutable of $object" 443 444 for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do 445 if [[ -d $object ]]; then 446 log_must usr_exec chmod \ 447 A+user:$user:${ace_dir}:allow $object 448 else 449 log_must usr_exec chmod \ 450 A+user:$user:${ace_file}:allow $object 451 fi 452 log_must set_attribute $object "i" 453 454 unit_writefile $object $user 1 455 unit_writedir $object $user 1 456 unit_appenddata $object $user 1 457 unit_writexattr $object $user 1 458 unit_accesstime $object $user 459 unit_updatetime $object $user 1 0 460 unit_writeacl $object $user 1 461 unit_deletecontent $object $user 1 462 unit_deletedata $object $user 1 463 464 if [[ -d $object ]] ;then 465 create_object "dir" $object $ZFS_ACL_CUR_USER 466 else 467 create_object "file" $object $ZFS_ACL_CUR_USER 468 fi 469 done 470} 471 472# 473# Testing function to verify the given node is nounlink 474# 475# $1: The given node, file/dir 476# 477function test_nounlink 478{ 479 typeset object=$1 480 481 if [[ -z $object ]]; then 482 log_fail "Object($object) not defined." 483 fi 484 485 echo "Testing nounlink of $object" 486 487 for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do 488 if [[ -d $object ]]; then 489 log_must usr_exec chmod \ 490 A+user:$user:${ace_dir}:allow $object 491 else 492 log_must usr_exec chmod \ 493 A+user:$user:${ace_file}:allow $object 494 fi 495 log_must set_attribute $object "u" 496 497 unit_writefile $object $user 498 unit_writedir $object $user 499 unit_appenddata $object $user 500 unit_writexattr $object $user 501 unit_accesstime $object $user 502 unit_updatetime $object $user 503 unit_writeacl $object $user 504 unit_deletecontent $object $user 1 505 unit_deletedata $object $user 1 506 507 if [[ -d $object ]] ;then 508 create_object "dir" $object $ZFS_ACL_CUR_USER 509 else 510 create_object "file" $object $ZFS_ACL_CUR_USER 511 fi 512 done 513} 514 515# 516# Testing function to verify the given node is appendonly 517# 518# $1: The given node, file/dir 519# 520function test_appendonly 521{ 522 typeset object=$1 523 524 if [[ -z $object ]]; then 525 log_fail "Object($object) not defined." 526 fi 527 528 log_note "Testing appendonly of $object" 529 530 for user in $ZFS_ACL_CUR_USER root $ZFS_ACL_STAFF2; do 531 if [[ -d $object ]]; then 532 log_must usr_exec chmod \ 533 A+user:$user:${ace_dir}:allow $object 534 else 535 log_must usr_exec chmod \ 536 A+user:$user:${ace_file}:allow $object 537 fi 538 log_must set_attribute $object "a" 539 540 unit_writefile $object $user 1 541 unit_writedir $object $user 542 unit_appenddata $object $user 543 unit_writexattr $object $user 544 unit_accesstime $object $user 545 unit_updatetime $object $user 546 unit_writeacl $object $user 547 unit_deletecontent $object $user 548 unit_deletedata $object $user 549 550 if [[ -d $object ]] ;then 551 create_object "dir" $object $ZFS_ACL_CUR_USER 552 else 553 create_object "file" $object $ZFS_ACL_CUR_USER 554 fi 555 done 556} 557 558FILES="file.0 file.1" 559DIRS="dir.0 dir.1" 560XATTRS="attr.0 attr.1" 561FS="$TESTPOOL $TESTPOOL/$TESTFS" 562 563if is_global_zone ; then 564 ATTRS="R i u a" 565else 566 ATTRS="R" 567fi 568 569TESTFILE=/tmp/tfile 570TESTDIR=tdir 571TESTATTR=tattr 572TESTACL=user:$ZFS_ACL_OTHER1:write_data:allow 573TESTMODE=777 574TESTSTR="ZFS test suites" 575 576ace_file="write_data/append_data/write_xattr/write_acl/write_attributes" 577ace_dir="add_file/add_subdirectory/${ace_file}" 578 579log_assert "Verify DOS & BSD'ish attributes will provide the " \ 580 "access limitation as expected." 581log_onexit cleanup 582 583echo "$TESTSTR" > $TESTFILE 584 585typeset gobject 586typeset gattr 587for gattr in $ATTRS ; do 588 for fs in $FS ; do 589 mtpt=$(get_prop mountpoint $fs) 590 chmod 777 $mtpt 591 for user in root $ZFS_ACL_STAFF1; do 592 log_must set_cur_usr $user 593 for file in $FILES ; do 594 gobject=$mtpt/$file 595 create_object "file" $gobject $ZFS_ACL_CUR_USER 596 test_wrapper $gobject $gattr 597 destroy_object $gobject 598 done 599 600 for dir in $DIRS ; do 601 gobject=$mtpt/$dir 602 create_object "dir" $gobject $ZFS_ACL_CUR_USER 603 test_wrapper $gobject $gattr 604 destroy_object $gobject 605 done 606 done 607 done 608done 609 610log_pass "DOS & BSD'ish attributes provide the access limitation as expected." 611