1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2008-2010 Edward Tomasz Napierała <trasz@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * ACL support routines specific to NFSv4 access control lists. These are 31 * utility routines for code common across file systems implementing NFSv4 32 * ACLs. 33 */ 34 35 #ifdef _KERNEL 36 #include <sys/cdefs.h> 37 #if 0 38 __FBSDID("$FreeBSD: head/sys/kern/subr_acl_nfs4.c 341827 2018-12-11 19:32:16Z mjg $"); 39 #endif 40 __KERNEL_RCSID(0, "$NetBSD: subr_acl_nfs4.c,v 1.1 2020/05/16 18:31:50 christos Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/kernel.h> 44 #include <sys/module.h> 45 #include <sys/systm.h> 46 #include <sys/mount.h> 47 #include <sys/vnode.h> 48 #include <sys/errno.h> 49 #include <sys/stat.h> 50 #include <sys/sysctl.h> 51 #include <sys/acl.h> 52 #include <sys/kauth.h> 53 54 static void acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode); 55 56 57 #else 58 59 #include <errno.h> 60 #include <assert.h> 61 #include <sys/acl.h> 62 #include <sys/stat.h> 63 #define KASSERT(a) assert(a) 64 65 #endif /* !_KERNEL */ 66 67 #if 0 68 static int 69 _acl_entry_matches(struct acl_entry *ae, acl_tag_t tag, acl_perm_t perm, 70 acl_entry_type_t entry_type) 71 { 72 if (ae->ae_tag != tag) 73 return (0); 74 75 if (ae->ae_id != ACL_UNDEFINED_ID) 76 return (0); 77 78 if (ae->ae_perm != perm) 79 return (0); 80 81 if (ae->ae_entry_type != entry_type) 82 return (0); 83 84 if (ae->ae_flags != 0) 85 return (0); 86 87 return (1); 88 } 89 #endif 90 91 static struct acl_entry * 92 _acl_append(struct acl *aclp, acl_tag_t tag, acl_perm_t perm, 93 acl_entry_type_t entry_type) 94 { 95 struct acl_entry *ae; 96 97 KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 98 99 ae = &(aclp->acl_entry[aclp->acl_cnt]); 100 aclp->acl_cnt++; 101 102 ae->ae_tag = tag; 103 ae->ae_id = ACL_UNDEFINED_ID; 104 ae->ae_perm = perm; 105 ae->ae_entry_type = entry_type; 106 ae->ae_flags = 0; 107 108 return (ae); 109 } 110 111 #if 0 112 static struct acl_entry * 113 _acl_duplicate_entry(struct acl *aclp, unsigned int entry_index) 114 { 115 size_t i; 116 117 KASSERT(aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 118 119 for (i = aclp->acl_cnt; i > entry_index; i--) 120 aclp->acl_entry[i] = aclp->acl_entry[i - 1]; 121 122 aclp->acl_cnt++; 123 124 return (&(aclp->acl_entry[entry_index + 1])); 125 } 126 #endif 127 128 129 #ifdef _KERNEL 130 void 131 acl_nfs4_sync_acl_from_mode(struct acl *aclp, mode_t mode, 132 int file_owner_id) 133 { 134 135 acl_nfs4_trivial_from_mode(aclp, mode); 136 } 137 #endif /* _KERNEL */ 138 139 void 140 __acl_nfs4_sync_mode_from_acl(mode_t *_mode, const struct acl *aclp) 141 { 142 size_t i; 143 mode_t old_mode = *_mode, mode = 0, seen = 0; 144 const struct acl_entry *ae; 145 146 KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES); 147 148 /* 149 * NFSv4 Minor Version 1, draft-ietf-nfsv4-minorversion1-03.txt 150 * 151 * 3.16.6.1. Recomputing mode upon SETATTR of ACL 152 */ 153 154 for (i = 0; i < aclp->acl_cnt; i++) { 155 ae = &(aclp->acl_entry[i]); 156 157 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 158 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 159 continue; 160 161 if (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) 162 continue; 163 164 if (ae->ae_tag == ACL_USER_OBJ) { 165 if ((ae->ae_perm & ACL_READ_DATA) && 166 ((seen & S_IRUSR) == 0)) { 167 seen |= S_IRUSR; 168 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 169 mode |= S_IRUSR; 170 } 171 if ((ae->ae_perm & ACL_WRITE_DATA) && 172 ((seen & S_IWUSR) == 0)) { 173 seen |= S_IWUSR; 174 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 175 mode |= S_IWUSR; 176 } 177 if ((ae->ae_perm & ACL_EXECUTE) && 178 ((seen & S_IXUSR) == 0)) { 179 seen |= S_IXUSR; 180 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 181 mode |= S_IXUSR; 182 } 183 } else if (ae->ae_tag == ACL_GROUP_OBJ) { 184 if ((ae->ae_perm & ACL_READ_DATA) && 185 ((seen & S_IRGRP) == 0)) { 186 seen |= S_IRGRP; 187 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 188 mode |= S_IRGRP; 189 } 190 if ((ae->ae_perm & ACL_WRITE_DATA) && 191 ((seen & S_IWGRP) == 0)) { 192 seen |= S_IWGRP; 193 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 194 mode |= S_IWGRP; 195 } 196 if ((ae->ae_perm & ACL_EXECUTE) && 197 ((seen & S_IXGRP) == 0)) { 198 seen |= S_IXGRP; 199 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 200 mode |= S_IXGRP; 201 } 202 } else if (ae->ae_tag == ACL_EVERYONE) { 203 if (ae->ae_perm & ACL_READ_DATA) { 204 if ((seen & S_IRUSR) == 0) { 205 seen |= S_IRUSR; 206 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 207 mode |= S_IRUSR; 208 } 209 if ((seen & S_IRGRP) == 0) { 210 seen |= S_IRGRP; 211 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 212 mode |= S_IRGRP; 213 } 214 if ((seen & S_IROTH) == 0) { 215 seen |= S_IROTH; 216 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 217 mode |= S_IROTH; 218 } 219 } 220 if (ae->ae_perm & ACL_WRITE_DATA) { 221 if ((seen & S_IWUSR) == 0) { 222 seen |= S_IWUSR; 223 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 224 mode |= S_IWUSR; 225 } 226 if ((seen & S_IWGRP) == 0) { 227 seen |= S_IWGRP; 228 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 229 mode |= S_IWGRP; 230 } 231 if ((seen & S_IWOTH) == 0) { 232 seen |= S_IWOTH; 233 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 234 mode |= S_IWOTH; 235 } 236 } 237 if (ae->ae_perm & ACL_EXECUTE) { 238 if ((seen & S_IXUSR) == 0) { 239 seen |= S_IXUSR; 240 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 241 mode |= S_IXUSR; 242 } 243 if ((seen & S_IXGRP) == 0) { 244 seen |= S_IXGRP; 245 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 246 mode |= S_IXGRP; 247 } 248 if ((seen & S_IXOTH) == 0) { 249 seen |= S_IXOTH; 250 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW) 251 mode |= S_IXOTH; 252 } 253 } 254 } 255 } 256 257 *_mode = mode | (old_mode & ACL_PRESERVE_MASK); 258 } 259 260 /* 261 * Populate the ACL with entries inherited from parent_aclp. 262 */ 263 static void 264 acl_nfs4_inherit_entries(const struct acl *parent_aclp, 265 struct acl *child_aclp, mode_t mode, int file_owner_id, 266 int is_directory) 267 { 268 int flags, tag; 269 size_t i; 270 const struct acl_entry *parent_entry; 271 struct acl_entry *ae; 272 273 KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES); 274 275 for (i = 0; i < parent_aclp->acl_cnt; i++) { 276 parent_entry = &(parent_aclp->acl_entry[i]); 277 flags = parent_entry->ae_flags; 278 tag = parent_entry->ae_tag; 279 280 /* 281 * Don't inherit owner@, group@, or everyone@ entries. 282 */ 283 if (tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || 284 tag == ACL_EVERYONE) 285 continue; 286 287 /* 288 * Entry is not inheritable at all. 289 */ 290 if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 291 ACL_ENTRY_FILE_INHERIT)) == 0) 292 continue; 293 294 /* 295 * We're creating a file, but ae is not inheritable 296 * by files. 297 */ 298 if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 299 continue; 300 301 /* 302 * Entry is inheritable only by files, but has NO_PROPAGATE 303 * flag set, and we're creating a directory, so it wouldn't 304 * propagate to any file in that directory anyway. 305 */ 306 if (is_directory && 307 (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 308 (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 309 continue; 310 311 /* 312 * Entry qualifies for being inherited. 313 */ 314 KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 315 ae = &(child_aclp->acl_entry[child_aclp->acl_cnt]); 316 *ae = *parent_entry; 317 child_aclp->acl_cnt++; 318 319 ae->ae_flags &= ~ACL_ENTRY_INHERIT_ONLY; 320 ae->ae_flags |= ACL_ENTRY_INHERITED; 321 322 /* 323 * If the type of the ACE is neither ALLOW nor DENY, 324 * then leave it as it is and proceed to the next one. 325 */ 326 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 327 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 328 continue; 329 330 /* 331 * If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if 332 * the object being created is not a directory, then clear 333 * the following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 334 * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 335 * ACL_ENTRY_INHERIT_ONLY. 336 */ 337 if (ae->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 338 !is_directory) { 339 ae->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 340 ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 341 ACL_ENTRY_INHERIT_ONLY); 342 } 343 344 /* 345 * If the object is a directory and ACL_ENTRY_FILE_INHERIT 346 * is set, but ACL_ENTRY_DIRECTORY_INHERIT is not set, ensure 347 * that ACL_ENTRY_INHERIT_ONLY is set. 348 */ 349 if (is_directory && 350 (ae->ae_flags & ACL_ENTRY_FILE_INHERIT) && 351 ((ae->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 352 ae->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 353 } 354 355 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW && 356 (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) == 0) { 357 /* 358 * Some permissions must never be inherited. 359 */ 360 ae->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER | 361 ACL_WRITE_NAMED_ATTRS | ACL_WRITE_ATTRIBUTES); 362 363 /* 364 * Others must be masked according to the file mode. 365 */ 366 if ((mode & S_IRGRP) == 0) 367 ae->ae_perm &= ~ACL_READ_DATA; 368 if ((mode & S_IWGRP) == 0) 369 ae->ae_perm &= 370 ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 371 if ((mode & S_IXGRP) == 0) 372 ae->ae_perm &= ~ACL_EXECUTE; 373 } 374 } 375 } 376 377 /* 378 * Calculate inherited ACL in a manner compatible with PSARC/2010/029. 379 * It's also being used to calculate a trivial ACL, by inheriting from 380 * a NULL ACL. 381 */ 382 static void 383 acl_nfs4_compute_inherited_acl_psarc(const struct acl *parent_aclp, 384 struct acl *aclp, mode_t mode, int file_owner_id, int is_directory) 385 { 386 acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 387 acl_perm_t user_allow, group_allow, everyone_allow; 388 389 KASSERT(aclp->acl_cnt == 0); 390 391 user_allow = group_allow = everyone_allow = ACL_READ_ACL | 392 ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 393 user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 394 ACL_WRITE_NAMED_ATTRS; 395 396 if (mode & S_IRUSR) 397 user_allow |= ACL_READ_DATA; 398 if (mode & S_IWUSR) 399 user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 400 if (mode & S_IXUSR) 401 user_allow |= ACL_EXECUTE; 402 403 if (mode & S_IRGRP) 404 group_allow |= ACL_READ_DATA; 405 if (mode & S_IWGRP) 406 group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 407 if (mode & S_IXGRP) 408 group_allow |= ACL_EXECUTE; 409 410 if (mode & S_IROTH) 411 everyone_allow |= ACL_READ_DATA; 412 if (mode & S_IWOTH) 413 everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 414 if (mode & S_IXOTH) 415 everyone_allow |= ACL_EXECUTE; 416 417 user_deny = ((group_allow | everyone_allow) & ~user_allow); 418 group_deny = everyone_allow & ~group_allow; 419 user_allow_first = group_deny & ~user_deny; 420 421 if (user_allow_first != 0) 422 _acl_append(aclp, ACL_USER_OBJ, user_allow_first, 423 ACL_ENTRY_TYPE_ALLOW); 424 if (user_deny != 0) 425 _acl_append(aclp, ACL_USER_OBJ, user_deny, 426 ACL_ENTRY_TYPE_DENY); 427 if (group_deny != 0) 428 _acl_append(aclp, ACL_GROUP_OBJ, group_deny, 429 ACL_ENTRY_TYPE_DENY); 430 431 if (parent_aclp != NULL) 432 acl_nfs4_inherit_entries(parent_aclp, aclp, mode, 433 file_owner_id, is_directory); 434 435 _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 436 _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 437 _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 438 } 439 440 #ifdef _KERNEL 441 void 442 acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 443 struct acl *child_aclp, mode_t mode, int file_owner_id, 444 int is_directory) 445 { 446 447 acl_nfs4_compute_inherited_acl_psarc(parent_aclp, child_aclp, 448 mode, file_owner_id, is_directory); 449 } 450 #endif /* _KERNEL */ 451 452 /* 453 * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 454 * Note that this results in an ACL different from (but semantically 455 * equal to) the "canonical six" trivial ACL computed using algorithm 456 * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 457 */ 458 static void 459 acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 460 { 461 462 aclp->acl_cnt = 0; 463 acl_nfs4_compute_inherited_acl_psarc(NULL, aclp, mode, -1, -1); 464 } 465 466 #ifndef _KERNEL 467 /* 468 * This routine is used by libc to implement acl_strip_np(3) 469 * and acl_is_trivial_np(3). 470 */ 471 void 472 __acl_nfs4_trivial_from_mode_libc(struct acl *aclp, int mode, int canonical_six) 473 { 474 475 aclp->acl_cnt = 0; 476 acl_nfs4_trivial_from_mode(aclp, mode); 477 } 478 #endif /* !_KERNEL */ 479 480 #ifdef _KERNEL 481 static int 482 _acls_are_equal(const struct acl *a, const struct acl *b) 483 { 484 int i; 485 const struct acl_entry *entrya, *entryb; 486 487 if (a->acl_cnt != b->acl_cnt) 488 return (0); 489 490 for (i = 0; i < b->acl_cnt; i++) { 491 entrya = &(a->acl_entry[i]); 492 entryb = &(b->acl_entry[i]); 493 494 if (entrya->ae_tag != entryb->ae_tag || 495 entrya->ae_id != entryb->ae_id || 496 entrya->ae_perm != entryb->ae_perm || 497 entrya->ae_entry_type != entryb->ae_entry_type || 498 entrya->ae_flags != entryb->ae_flags) 499 return (0); 500 } 501 502 return (1); 503 } 504 505 /* 506 * This routine is used to determine whether to remove extended attribute 507 * that stores ACL contents. 508 */ 509 int 510 acl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 511 { 512 int trivial; 513 mode_t tmpmode = 0; 514 struct acl *tmpaclp; 515 516 if (aclp->acl_cnt > 6) 517 return (0); 518 519 /* 520 * Compute the mode from the ACL, then compute new ACL from that mode. 521 * If the ACLs are identical, then the ACL is trivial. 522 * 523 * XXX: I guess there is a faster way to do this. However, even 524 * this slow implementation significantly speeds things up 525 * for files that don't have non-trivial ACLs - it's critical 526 * for performance to not use EA when they are not needed. 527 * 528 * First try the PSARC/2010/029 semantics. 529 */ 530 tmpaclp = acl_alloc(KM_SLEEP); 531 __acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 532 acl_nfs4_trivial_from_mode(tmpaclp, tmpmode); 533 trivial = _acls_are_equal(aclp, tmpaclp); 534 if (trivial) { 535 acl_free(tmpaclp); 536 return (trivial); 537 } 538 539 /* 540 * Check if it's a draft-ietf-nfsv4-minorversion1-03.txt trivial ACL. 541 */ 542 acl_free(tmpaclp); 543 544 return (trivial); 545 } 546 547 int 548 acl_nfs4_check(const struct acl *aclp, int is_directory) 549 { 550 size_t i; 551 const struct acl_entry *ae; 552 553 /* 554 * The spec doesn't seem to say anything about ACL validity. 555 * It seems there is not much to do here. There is even no need 556 * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 557 * entries, as there can be several of them and that's perfectly 558 * valid. There can be none of them too. Really. 559 */ 560 561 if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 562 return (EINVAL); 563 564 for (i = 0; i < aclp->acl_cnt; i++) { 565 ae = &(aclp->acl_entry[i]); 566 567 switch (ae->ae_tag) { 568 case ACL_USER_OBJ: 569 case ACL_GROUP_OBJ: 570 case ACL_EVERYONE: 571 if (ae->ae_id != ACL_UNDEFINED_ID) 572 return (EINVAL); 573 break; 574 575 case ACL_USER: 576 case ACL_GROUP: 577 if (ae->ae_id == ACL_UNDEFINED_ID) 578 return (EINVAL); 579 break; 580 581 default: 582 return (EINVAL); 583 } 584 585 if ((ae->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 586 return (EINVAL); 587 588 /* 589 * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 590 */ 591 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 592 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 593 return (EINVAL); 594 595 if ((ae->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 596 return (EINVAL); 597 598 /* Disallow unimplemented flags. */ 599 if (ae->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 600 ACL_ENTRY_FAILED_ACCESS)) 601 return (EINVAL); 602 603 /* Disallow flags not allowed for ordinary files. */ 604 if (!is_directory) { 605 if (ae->ae_flags & (ACL_ENTRY_FILE_INHERIT | 606 ACL_ENTRY_DIRECTORY_INHERIT | 607 ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 608 return (EINVAL); 609 } 610 } 611 612 return (0); 613 } 614 #endif 615