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.2 2024/01/19 19:07:38 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 54static 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 68static 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 91static 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 112static 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 130void 131acl_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 139void 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 */ 263static void 264/*ARGSUSED*/ 265acl_nfs4_inherit_entries(const struct acl *parent_aclp, 266 struct acl *child_aclp, mode_t mode, int file_owner_id, 267 int is_directory) 268{ 269 int flags, tag; 270 size_t i; 271 const struct acl_entry *parent_entry; 272 struct acl_entry *ae; 273 274 KASSERT(parent_aclp->acl_cnt <= ACL_MAX_ENTRIES); 275 276 for (i = 0; i < parent_aclp->acl_cnt; i++) { 277 parent_entry = &(parent_aclp->acl_entry[i]); 278 flags = parent_entry->ae_flags; 279 tag = parent_entry->ae_tag; 280 281 /* 282 * Don't inherit owner@, group@, or everyone@ entries. 283 */ 284 if (tag == ACL_USER_OBJ || tag == ACL_GROUP_OBJ || 285 tag == ACL_EVERYONE) 286 continue; 287 288 /* 289 * Entry is not inheritable at all. 290 */ 291 if ((flags & (ACL_ENTRY_DIRECTORY_INHERIT | 292 ACL_ENTRY_FILE_INHERIT)) == 0) 293 continue; 294 295 /* 296 * We're creating a file, but ae is not inheritable 297 * by files. 298 */ 299 if (!is_directory && (flags & ACL_ENTRY_FILE_INHERIT) == 0) 300 continue; 301 302 /* 303 * Entry is inheritable only by files, but has NO_PROPAGATE 304 * flag set, and we're creating a directory, so it wouldn't 305 * propagate to any file in that directory anyway. 306 */ 307 if (is_directory && 308 (flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0 && 309 (flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)) 310 continue; 311 312 /* 313 * Entry qualifies for being inherited. 314 */ 315 KASSERT(child_aclp->acl_cnt + 1 <= ACL_MAX_ENTRIES); 316 ae = &(child_aclp->acl_entry[child_aclp->acl_cnt]); 317 *ae = *parent_entry; 318 child_aclp->acl_cnt++; 319 320 ae->ae_flags &= ~ACL_ENTRY_INHERIT_ONLY; 321 ae->ae_flags |= ACL_ENTRY_INHERITED; 322 323 /* 324 * If the type of the ACE is neither ALLOW nor DENY, 325 * then leave it as it is and proceed to the next one. 326 */ 327 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 328 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 329 continue; 330 331 /* 332 * If the ACL_ENTRY_NO_PROPAGATE_INHERIT is set, or if 333 * the object being created is not a directory, then clear 334 * the following flags: ACL_ENTRY_NO_PROPAGATE_INHERIT, 335 * ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT, 336 * ACL_ENTRY_INHERIT_ONLY. 337 */ 338 if (ae->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT || 339 !is_directory) { 340 ae->ae_flags &= ~(ACL_ENTRY_NO_PROPAGATE_INHERIT | 341 ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | 342 ACL_ENTRY_INHERIT_ONLY); 343 } 344 345 /* 346 * If the object is a directory and ACL_ENTRY_FILE_INHERIT 347 * is set, but ACL_ENTRY_DIRECTORY_INHERIT is not set, ensure 348 * that ACL_ENTRY_INHERIT_ONLY is set. 349 */ 350 if (is_directory && 351 (ae->ae_flags & ACL_ENTRY_FILE_INHERIT) && 352 ((ae->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT) == 0)) { 353 ae->ae_flags |= ACL_ENTRY_INHERIT_ONLY; 354 } 355 356 if (ae->ae_entry_type == ACL_ENTRY_TYPE_ALLOW && 357 (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) == 0) { 358 /* 359 * Some permissions must never be inherited. 360 */ 361 ae->ae_perm &= ~(ACL_WRITE_ACL | ACL_WRITE_OWNER | 362 ACL_WRITE_NAMED_ATTRS | ACL_WRITE_ATTRIBUTES); 363 364 /* 365 * Others must be masked according to the file mode. 366 */ 367 if ((mode & S_IRGRP) == 0) 368 ae->ae_perm &= ~ACL_READ_DATA; 369 if ((mode & S_IWGRP) == 0) 370 ae->ae_perm &= 371 ~(ACL_WRITE_DATA | ACL_APPEND_DATA); 372 if ((mode & S_IXGRP) == 0) 373 ae->ae_perm &= ~ACL_EXECUTE; 374 } 375 } 376} 377 378/* 379 * Calculate inherited ACL in a manner compatible with PSARC/2010/029. 380 * It's also being used to calculate a trivial ACL, by inheriting from 381 * a NULL ACL. 382 */ 383static void 384acl_nfs4_compute_inherited_acl_psarc(const struct acl *parent_aclp, 385 struct acl *aclp, mode_t mode, int file_owner_id, int is_directory) 386{ 387 acl_perm_t user_allow_first = 0, user_deny = 0, group_deny = 0; 388 acl_perm_t user_allow, group_allow, everyone_allow; 389 390 KASSERT(aclp->acl_cnt == 0); 391 392 user_allow = group_allow = everyone_allow = ACL_READ_ACL | 393 ACL_READ_ATTRIBUTES | ACL_READ_NAMED_ATTRS | ACL_SYNCHRONIZE; 394 user_allow |= ACL_WRITE_ACL | ACL_WRITE_OWNER | ACL_WRITE_ATTRIBUTES | 395 ACL_WRITE_NAMED_ATTRS; 396 397 if (mode & S_IRUSR) 398 user_allow |= ACL_READ_DATA; 399 if (mode & S_IWUSR) 400 user_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 401 if (mode & S_IXUSR) 402 user_allow |= ACL_EXECUTE; 403 404 if (mode & S_IRGRP) 405 group_allow |= ACL_READ_DATA; 406 if (mode & S_IWGRP) 407 group_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 408 if (mode & S_IXGRP) 409 group_allow |= ACL_EXECUTE; 410 411 if (mode & S_IROTH) 412 everyone_allow |= ACL_READ_DATA; 413 if (mode & S_IWOTH) 414 everyone_allow |= (ACL_WRITE_DATA | ACL_APPEND_DATA); 415 if (mode & S_IXOTH) 416 everyone_allow |= ACL_EXECUTE; 417 418 user_deny = ((group_allow | everyone_allow) & ~user_allow); 419 group_deny = everyone_allow & ~group_allow; 420 user_allow_first = group_deny & ~user_deny; 421 422 if (user_allow_first != 0) 423 _acl_append(aclp, ACL_USER_OBJ, user_allow_first, 424 ACL_ENTRY_TYPE_ALLOW); 425 if (user_deny != 0) 426 _acl_append(aclp, ACL_USER_OBJ, user_deny, 427 ACL_ENTRY_TYPE_DENY); 428 if (group_deny != 0) 429 _acl_append(aclp, ACL_GROUP_OBJ, group_deny, 430 ACL_ENTRY_TYPE_DENY); 431 432 if (parent_aclp != NULL) 433 acl_nfs4_inherit_entries(parent_aclp, aclp, mode, 434 file_owner_id, is_directory); 435 436 _acl_append(aclp, ACL_USER_OBJ, user_allow, ACL_ENTRY_TYPE_ALLOW); 437 _acl_append(aclp, ACL_GROUP_OBJ, group_allow, ACL_ENTRY_TYPE_ALLOW); 438 _acl_append(aclp, ACL_EVERYONE, everyone_allow, ACL_ENTRY_TYPE_ALLOW); 439} 440 441#ifdef _KERNEL 442void 443acl_nfs4_compute_inherited_acl(const struct acl *parent_aclp, 444 struct acl *child_aclp, mode_t mode, int file_owner_id, 445 int is_directory) 446{ 447 448 acl_nfs4_compute_inherited_acl_psarc(parent_aclp, child_aclp, 449 mode, file_owner_id, is_directory); 450} 451#endif /* _KERNEL */ 452 453/* 454 * Calculate trivial ACL in a manner compatible with PSARC/2010/029. 455 * Note that this results in an ACL different from (but semantically 456 * equal to) the "canonical six" trivial ACL computed using algorithm 457 * described in draft-ietf-nfsv4-minorversion1-03.txt, 3.16.6.2. 458 */ 459static void 460acl_nfs4_trivial_from_mode(struct acl *aclp, mode_t mode) 461{ 462 463 aclp->acl_cnt = 0; 464 acl_nfs4_compute_inherited_acl_psarc(NULL, aclp, mode, -1, -1); 465} 466 467#ifndef _KERNEL 468/* 469 * This routine is used by libc to implement acl_strip_np(3) 470 * and acl_is_trivial_np(3). 471 */ 472void 473/*ARGSUSED*/ 474__acl_nfs4_trivial_from_mode_libc(struct acl *aclp, int mode, int canonical_six) 475{ 476 477 aclp->acl_cnt = 0; 478 acl_nfs4_trivial_from_mode(aclp, mode); 479} 480#endif /* !_KERNEL */ 481 482#ifdef _KERNEL 483static int 484_acls_are_equal(const struct acl *a, const struct acl *b) 485{ 486 int i; 487 const struct acl_entry *entrya, *entryb; 488 489 if (a->acl_cnt != b->acl_cnt) 490 return (0); 491 492 for (i = 0; i < b->acl_cnt; i++) { 493 entrya = &(a->acl_entry[i]); 494 entryb = &(b->acl_entry[i]); 495 496 if (entrya->ae_tag != entryb->ae_tag || 497 entrya->ae_id != entryb->ae_id || 498 entrya->ae_perm != entryb->ae_perm || 499 entrya->ae_entry_type != entryb->ae_entry_type || 500 entrya->ae_flags != entryb->ae_flags) 501 return (0); 502 } 503 504 return (1); 505} 506 507/* 508 * This routine is used to determine whether to remove extended attribute 509 * that stores ACL contents. 510 */ 511int 512acl_nfs4_is_trivial(const struct acl *aclp, int file_owner_id) 513{ 514 int trivial; 515 mode_t tmpmode = 0; 516 struct acl *tmpaclp; 517 518 if (aclp->acl_cnt > 6) 519 return (0); 520 521 /* 522 * Compute the mode from the ACL, then compute new ACL from that mode. 523 * If the ACLs are identical, then the ACL is trivial. 524 * 525 * XXX: I guess there is a faster way to do this. However, even 526 * this slow implementation significantly speeds things up 527 * for files that don't have non-trivial ACLs - it's critical 528 * for performance to not use EA when they are not needed. 529 * 530 * First try the PSARC/2010/029 semantics. 531 */ 532 tmpaclp = acl_alloc(KM_SLEEP); 533 __acl_nfs4_sync_mode_from_acl(&tmpmode, aclp); 534 acl_nfs4_trivial_from_mode(tmpaclp, tmpmode); 535 trivial = _acls_are_equal(aclp, tmpaclp); 536 if (trivial) { 537 acl_free(tmpaclp); 538 return (trivial); 539 } 540 541 /* 542 * Check if it's a draft-ietf-nfsv4-minorversion1-03.txt trivial ACL. 543 */ 544 acl_free(tmpaclp); 545 546 return (trivial); 547} 548 549int 550acl_nfs4_check(const struct acl *aclp, int is_directory) 551{ 552 size_t i; 553 const struct acl_entry *ae; 554 555 /* 556 * The spec doesn't seem to say anything about ACL validity. 557 * It seems there is not much to do here. There is even no need 558 * to count "owner@" or "everyone@" (ACL_USER_OBJ and ACL_EVERYONE) 559 * entries, as there can be several of them and that's perfectly 560 * valid. There can be none of them too. Really. 561 */ 562 563 if (aclp->acl_cnt > ACL_MAX_ENTRIES || aclp->acl_cnt <= 0) 564 return (EINVAL); 565 566 for (i = 0; i < aclp->acl_cnt; i++) { 567 ae = &(aclp->acl_entry[i]); 568 569 switch (ae->ae_tag) { 570 case ACL_USER_OBJ: 571 case ACL_GROUP_OBJ: 572 case ACL_EVERYONE: 573 if (ae->ae_id != ACL_UNDEFINED_ID) 574 return (EINVAL); 575 break; 576 577 case ACL_USER: 578 case ACL_GROUP: 579 if (ae->ae_id == ACL_UNDEFINED_ID) 580 return (EINVAL); 581 break; 582 583 default: 584 return (EINVAL); 585 } 586 587 if ((ae->ae_perm | ACL_NFS4_PERM_BITS) != ACL_NFS4_PERM_BITS) 588 return (EINVAL); 589 590 /* 591 * Disallow ACL_ENTRY_TYPE_AUDIT and ACL_ENTRY_TYPE_ALARM for now. 592 */ 593 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 594 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 595 return (EINVAL); 596 597 if ((ae->ae_flags | ACL_FLAGS_BITS) != ACL_FLAGS_BITS) 598 return (EINVAL); 599 600 /* Disallow unimplemented flags. */ 601 if (ae->ae_flags & (ACL_ENTRY_SUCCESSFUL_ACCESS | 602 ACL_ENTRY_FAILED_ACCESS)) 603 return (EINVAL); 604 605 /* Disallow flags not allowed for ordinary files. */ 606 if (!is_directory) { 607 if (ae->ae_flags & (ACL_ENTRY_FILE_INHERIT | 608 ACL_ENTRY_DIRECTORY_INHERIT | 609 ACL_ENTRY_NO_PROPAGATE_INHERIT | ACL_ENTRY_INHERIT_ONLY)) 610 return (EINVAL); 611 } 612 } 613 614 return (0); 615} 616#endif 617