1 /* $NetBSD: genfs_vnops.c,v 1.210 2020/09/05 16:30:12 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2008 The NetBSD Foundation, Inc. 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright (c) 1982, 1986, 1989, 1993 31 * The Regents of the University of California. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. Neither the name of the University nor the names of its contributors 42 * may be used to endorse or promote products derived from this software 43 * without specific prior written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 * 57 */ 58 59 #include <sys/cdefs.h> 60 __KERNEL_RCSID(0, "$NetBSD: genfs_vnops.c,v 1.210 2020/09/05 16:30:12 riastradh Exp $"); 61 62 #include <sys/param.h> 63 #include <sys/systm.h> 64 #include <sys/proc.h> 65 #include <sys/kernel.h> 66 #include <sys/mount.h> 67 #include <sys/fstrans.h> 68 #include <sys/namei.h> 69 #include <sys/vnode_impl.h> 70 #include <sys/fcntl.h> 71 #include <sys/kmem.h> 72 #include <sys/poll.h> 73 #include <sys/mman.h> 74 #include <sys/file.h> 75 #include <sys/kauth.h> 76 #include <sys/stat.h> 77 #include <sys/extattr.h> 78 79 #include <miscfs/genfs/genfs.h> 80 #include <miscfs/genfs/genfs_node.h> 81 #include <miscfs/specfs/specdev.h> 82 83 static void filt_genfsdetach(struct knote *); 84 static int filt_genfsread(struct knote *, long); 85 static int filt_genfsvnode(struct knote *, long); 86 87 int 88 genfs_poll(void *v) 89 { 90 struct vop_poll_args /* { 91 struct vnode *a_vp; 92 int a_events; 93 struct lwp *a_l; 94 } */ *ap = v; 95 96 return (ap->a_events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)); 97 } 98 99 int 100 genfs_seek(void *v) 101 { 102 struct vop_seek_args /* { 103 struct vnode *a_vp; 104 off_t a_oldoff; 105 off_t a_newoff; 106 kauth_cred_t cred; 107 } */ *ap = v; 108 109 if (ap->a_newoff < 0) 110 return (EINVAL); 111 112 return (0); 113 } 114 115 int 116 genfs_abortop(void *v) 117 { 118 struct vop_abortop_args /* { 119 struct vnode *a_dvp; 120 struct componentname *a_cnp; 121 } */ *ap = v; 122 123 (void)ap; 124 125 return (0); 126 } 127 128 int 129 genfs_fcntl(void *v) 130 { 131 struct vop_fcntl_args /* { 132 struct vnode *a_vp; 133 u_int a_command; 134 void *a_data; 135 int a_fflag; 136 kauth_cred_t a_cred; 137 struct lwp *a_l; 138 } */ *ap = v; 139 140 if (ap->a_command == F_SETFL) 141 return (0); 142 else 143 return (EOPNOTSUPP); 144 } 145 146 /*ARGSUSED*/ 147 int 148 genfs_badop(void *v) 149 { 150 151 panic("genfs: bad op"); 152 } 153 154 /*ARGSUSED*/ 155 int 156 genfs_nullop(void *v) 157 { 158 159 return (0); 160 } 161 162 /*ARGSUSED*/ 163 int 164 genfs_einval(void *v) 165 { 166 167 return (EINVAL); 168 } 169 170 /* 171 * Called when an fs doesn't support a particular vop. 172 * This takes care to vrele, vput, or vunlock passed in vnodes 173 * and calls VOP_ABORTOP for a componentname (in non-rename VOP). 174 */ 175 int 176 genfs_eopnotsupp(void *v) 177 { 178 struct vop_generic_args /* 179 struct vnodeop_desc *a_desc; 180 / * other random data follows, presumably * / 181 } */ *ap = v; 182 struct vnodeop_desc *desc = ap->a_desc; 183 struct vnode *vp, *vp_last = NULL; 184 int flags, i, j, offset_cnp, offset_vp; 185 186 KASSERT(desc->vdesc_offset != VOP_LOOKUP_DESCOFFSET); 187 KASSERT(desc->vdesc_offset != VOP_ABORTOP_DESCOFFSET); 188 189 /* 190 * Abort any componentname that lookup potentially left state in. 191 * 192 * As is logical, componentnames for VOP_RENAME are handled by 193 * the caller of VOP_RENAME. Yay, rename! 194 */ 195 if (desc->vdesc_offset != VOP_RENAME_DESCOFFSET && 196 (offset_vp = desc->vdesc_vp_offsets[0]) != VDESC_NO_OFFSET && 197 (offset_cnp = desc->vdesc_componentname_offset) != VDESC_NO_OFFSET){ 198 struct componentname *cnp; 199 struct vnode *dvp; 200 201 dvp = *VOPARG_OFFSETTO(struct vnode **, offset_vp, ap); 202 cnp = *VOPARG_OFFSETTO(struct componentname **, offset_cnp, ap); 203 204 VOP_ABORTOP(dvp, cnp); 205 } 206 207 flags = desc->vdesc_flags; 208 for (i = 0; i < VDESC_MAX_VPS; flags >>=1, i++) { 209 if ((offset_vp = desc->vdesc_vp_offsets[i]) == VDESC_NO_OFFSET) 210 break; /* stop at end of list */ 211 if ((j = flags & VDESC_VP0_WILLPUT)) { 212 vp = *VOPARG_OFFSETTO(struct vnode **, offset_vp, ap); 213 214 /* Skip if NULL */ 215 if (!vp) 216 continue; 217 218 switch (j) { 219 case VDESC_VP0_WILLPUT: 220 /* Check for dvp == vp cases */ 221 if (vp == vp_last) 222 vrele(vp); 223 else { 224 vput(vp); 225 vp_last = vp; 226 } 227 break; 228 case VDESC_VP0_WILLRELE: 229 vrele(vp); 230 break; 231 } 232 } 233 } 234 235 return (EOPNOTSUPP); 236 } 237 238 /*ARGSUSED*/ 239 int 240 genfs_ebadf(void *v) 241 { 242 243 return (EBADF); 244 } 245 246 /* ARGSUSED */ 247 int 248 genfs_enoioctl(void *v) 249 { 250 251 return (EPASSTHROUGH); 252 } 253 254 255 /* 256 * Eliminate all activity associated with the requested vnode 257 * and with all vnodes aliased to the requested vnode. 258 */ 259 int 260 genfs_revoke(void *v) 261 { 262 struct vop_revoke_args /* { 263 struct vnode *a_vp; 264 int a_flags; 265 } */ *ap = v; 266 267 #ifdef DIAGNOSTIC 268 if ((ap->a_flags & REVOKEALL) == 0) 269 panic("genfs_revoke: not revokeall"); 270 #endif 271 vrevoke(ap->a_vp); 272 return (0); 273 } 274 275 /* 276 * Lock the node (for deadfs). 277 */ 278 int 279 genfs_deadlock(void *v) 280 { 281 struct vop_lock_args /* { 282 struct vnode *a_vp; 283 int a_flags; 284 } */ *ap = v; 285 vnode_t *vp = ap->a_vp; 286 vnode_impl_t *vip = VNODE_TO_VIMPL(vp); 287 int flags = ap->a_flags; 288 krw_t op; 289 290 if (! ISSET(flags, LK_RETRY)) 291 return ENOENT; 292 293 if (ISSET(flags, LK_DOWNGRADE)) { 294 rw_downgrade(&vip->vi_lock); 295 } else if (ISSET(flags, LK_UPGRADE)) { 296 KASSERT(ISSET(flags, LK_NOWAIT)); 297 if (!rw_tryupgrade(&vip->vi_lock)) { 298 return EBUSY; 299 } 300 } else if ((flags & (LK_EXCLUSIVE | LK_SHARED)) != 0) { 301 op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER); 302 if (ISSET(flags, LK_NOWAIT)) { 303 if (!rw_tryenter(&vip->vi_lock, op)) 304 return EBUSY; 305 } else { 306 rw_enter(&vip->vi_lock, op); 307 } 308 } 309 VSTATE_ASSERT_UNLOCKED(vp, VS_RECLAIMED); 310 return 0; 311 } 312 313 /* 314 * Unlock the node (for deadfs). 315 */ 316 int 317 genfs_deadunlock(void *v) 318 { 319 struct vop_unlock_args /* { 320 struct vnode *a_vp; 321 } */ *ap = v; 322 vnode_t *vp = ap->a_vp; 323 vnode_impl_t *vip = VNODE_TO_VIMPL(vp); 324 325 rw_exit(&vip->vi_lock); 326 327 return 0; 328 } 329 330 /* 331 * Lock the node. 332 */ 333 int 334 genfs_lock(void *v) 335 { 336 struct vop_lock_args /* { 337 struct vnode *a_vp; 338 int a_flags; 339 } */ *ap = v; 340 vnode_t *vp = ap->a_vp; 341 vnode_impl_t *vip = VNODE_TO_VIMPL(vp); 342 int flags = ap->a_flags; 343 krw_t op; 344 345 if (ISSET(flags, LK_DOWNGRADE)) { 346 rw_downgrade(&vip->vi_lock); 347 } else if (ISSET(flags, LK_UPGRADE)) { 348 KASSERT(ISSET(flags, LK_NOWAIT)); 349 if (!rw_tryupgrade(&vip->vi_lock)) { 350 return EBUSY; 351 } 352 } else if ((flags & (LK_EXCLUSIVE | LK_SHARED)) != 0) { 353 op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER); 354 if (ISSET(flags, LK_NOWAIT)) { 355 if (!rw_tryenter(&vip->vi_lock, op)) 356 return EBUSY; 357 } else { 358 rw_enter(&vip->vi_lock, op); 359 } 360 } 361 VSTATE_ASSERT_UNLOCKED(vp, VS_ACTIVE); 362 return 0; 363 } 364 365 /* 366 * Unlock the node. 367 */ 368 int 369 genfs_unlock(void *v) 370 { 371 struct vop_unlock_args /* { 372 struct vnode *a_vp; 373 } */ *ap = v; 374 vnode_t *vp = ap->a_vp; 375 vnode_impl_t *vip = VNODE_TO_VIMPL(vp); 376 377 rw_exit(&vip->vi_lock); 378 379 return 0; 380 } 381 382 /* 383 * Return whether or not the node is locked. 384 */ 385 int 386 genfs_islocked(void *v) 387 { 388 struct vop_islocked_args /* { 389 struct vnode *a_vp; 390 } */ *ap = v; 391 vnode_t *vp = ap->a_vp; 392 vnode_impl_t *vip = VNODE_TO_VIMPL(vp); 393 394 if (rw_write_held(&vip->vi_lock)) 395 return LK_EXCLUSIVE; 396 397 if (rw_read_held(&vip->vi_lock)) 398 return LK_SHARED; 399 400 return 0; 401 } 402 403 /* 404 * Stubs to use when there is no locking to be done on the underlying object. 405 */ 406 int 407 genfs_nolock(void *v) 408 { 409 410 return (0); 411 } 412 413 int 414 genfs_nounlock(void *v) 415 { 416 417 return (0); 418 } 419 420 int 421 genfs_noislocked(void *v) 422 { 423 424 return (0); 425 } 426 427 int 428 genfs_mmap(void *v) 429 { 430 431 return (0); 432 } 433 434 /* 435 * VOP_PUTPAGES() for vnodes which never have pages. 436 */ 437 438 int 439 genfs_null_putpages(void *v) 440 { 441 struct vop_putpages_args /* { 442 struct vnode *a_vp; 443 voff_t a_offlo; 444 voff_t a_offhi; 445 int a_flags; 446 } */ *ap = v; 447 struct vnode *vp = ap->a_vp; 448 449 KASSERT(vp->v_uobj.uo_npages == 0); 450 rw_exit(vp->v_uobj.vmobjlock); 451 return (0); 452 } 453 454 void 455 genfs_node_init(struct vnode *vp, const struct genfs_ops *ops) 456 { 457 struct genfs_node *gp = VTOG(vp); 458 459 rw_init(&gp->g_glock); 460 gp->g_op = ops; 461 } 462 463 void 464 genfs_node_destroy(struct vnode *vp) 465 { 466 struct genfs_node *gp = VTOG(vp); 467 468 rw_destroy(&gp->g_glock); 469 } 470 471 void 472 genfs_size(struct vnode *vp, off_t size, off_t *eobp, int flags) 473 { 474 int bsize; 475 476 bsize = 1 << vp->v_mount->mnt_fs_bshift; 477 *eobp = (size + bsize - 1) & ~(bsize - 1); 478 } 479 480 static void 481 filt_genfsdetach(struct knote *kn) 482 { 483 struct vnode *vp = (struct vnode *)kn->kn_hook; 484 485 mutex_enter(vp->v_interlock); 486 SLIST_REMOVE(&vp->v_klist, kn, knote, kn_selnext); 487 mutex_exit(vp->v_interlock); 488 } 489 490 static int 491 filt_genfsread(struct knote *kn, long hint) 492 { 493 struct vnode *vp = (struct vnode *)kn->kn_hook; 494 int rv; 495 496 /* 497 * filesystem is gone, so set the EOF flag and schedule 498 * the knote for deletion. 499 */ 500 switch (hint) { 501 case NOTE_REVOKE: 502 KASSERT(mutex_owned(vp->v_interlock)); 503 kn->kn_flags |= (EV_EOF | EV_ONESHOT); 504 return (1); 505 case 0: 506 mutex_enter(vp->v_interlock); 507 kn->kn_data = vp->v_size - ((file_t *)kn->kn_obj)->f_offset; 508 rv = (kn->kn_data != 0); 509 mutex_exit(vp->v_interlock); 510 return rv; 511 default: 512 KASSERT(mutex_owned(vp->v_interlock)); 513 kn->kn_data = vp->v_size - ((file_t *)kn->kn_obj)->f_offset; 514 return (kn->kn_data != 0); 515 } 516 } 517 518 static int 519 filt_genfswrite(struct knote *kn, long hint) 520 { 521 struct vnode *vp = (struct vnode *)kn->kn_hook; 522 523 /* 524 * filesystem is gone, so set the EOF flag and schedule 525 * the knote for deletion. 526 */ 527 switch (hint) { 528 case NOTE_REVOKE: 529 KASSERT(mutex_owned(vp->v_interlock)); 530 kn->kn_flags |= (EV_EOF | EV_ONESHOT); 531 return (1); 532 case 0: 533 mutex_enter(vp->v_interlock); 534 kn->kn_data = 0; 535 mutex_exit(vp->v_interlock); 536 return 1; 537 default: 538 KASSERT(mutex_owned(vp->v_interlock)); 539 kn->kn_data = 0; 540 return 1; 541 } 542 } 543 544 static int 545 filt_genfsvnode(struct knote *kn, long hint) 546 { 547 struct vnode *vp = (struct vnode *)kn->kn_hook; 548 int fflags; 549 550 switch (hint) { 551 case NOTE_REVOKE: 552 KASSERT(mutex_owned(vp->v_interlock)); 553 kn->kn_flags |= EV_EOF; 554 if ((kn->kn_sfflags & hint) != 0) 555 kn->kn_fflags |= hint; 556 return (1); 557 case 0: 558 mutex_enter(vp->v_interlock); 559 fflags = kn->kn_fflags; 560 mutex_exit(vp->v_interlock); 561 break; 562 default: 563 KASSERT(mutex_owned(vp->v_interlock)); 564 if ((kn->kn_sfflags & hint) != 0) 565 kn->kn_fflags |= hint; 566 fflags = kn->kn_fflags; 567 break; 568 } 569 570 return (fflags != 0); 571 } 572 573 static const struct filterops genfsread_filtops = { 574 .f_isfd = 1, 575 .f_attach = NULL, 576 .f_detach = filt_genfsdetach, 577 .f_event = filt_genfsread, 578 }; 579 580 static const struct filterops genfswrite_filtops = { 581 .f_isfd = 1, 582 .f_attach = NULL, 583 .f_detach = filt_genfsdetach, 584 .f_event = filt_genfswrite, 585 }; 586 587 static const struct filterops genfsvnode_filtops = { 588 .f_isfd = 1, 589 .f_attach = NULL, 590 .f_detach = filt_genfsdetach, 591 .f_event = filt_genfsvnode, 592 }; 593 594 int 595 genfs_kqfilter(void *v) 596 { 597 struct vop_kqfilter_args /* { 598 struct vnode *a_vp; 599 struct knote *a_kn; 600 } */ *ap = v; 601 struct vnode *vp; 602 struct knote *kn; 603 604 vp = ap->a_vp; 605 kn = ap->a_kn; 606 switch (kn->kn_filter) { 607 case EVFILT_READ: 608 kn->kn_fop = &genfsread_filtops; 609 break; 610 case EVFILT_WRITE: 611 kn->kn_fop = &genfswrite_filtops; 612 break; 613 case EVFILT_VNODE: 614 kn->kn_fop = &genfsvnode_filtops; 615 break; 616 default: 617 return (EINVAL); 618 } 619 620 kn->kn_hook = vp; 621 622 mutex_enter(vp->v_interlock); 623 SLIST_INSERT_HEAD(&vp->v_klist, kn, kn_selnext); 624 mutex_exit(vp->v_interlock); 625 626 return (0); 627 } 628 629 void 630 genfs_node_wrlock(struct vnode *vp) 631 { 632 struct genfs_node *gp = VTOG(vp); 633 634 rw_enter(&gp->g_glock, RW_WRITER); 635 } 636 637 void 638 genfs_node_rdlock(struct vnode *vp) 639 { 640 struct genfs_node *gp = VTOG(vp); 641 642 rw_enter(&gp->g_glock, RW_READER); 643 } 644 645 int 646 genfs_node_rdtrylock(struct vnode *vp) 647 { 648 struct genfs_node *gp = VTOG(vp); 649 650 return rw_tryenter(&gp->g_glock, RW_READER); 651 } 652 653 void 654 genfs_node_unlock(struct vnode *vp) 655 { 656 struct genfs_node *gp = VTOG(vp); 657 658 rw_exit(&gp->g_glock); 659 } 660 661 int 662 genfs_node_wrlocked(struct vnode *vp) 663 { 664 struct genfs_node *gp = VTOG(vp); 665 666 return rw_write_held(&gp->g_glock); 667 } 668 669 static int 670 groupmember(gid_t gid, kauth_cred_t cred) 671 { 672 int ismember; 673 int error = kauth_cred_ismember_gid(cred, gid, &ismember); 674 if (error) 675 return error; 676 if (kauth_cred_getegid(cred) == gid || ismember) 677 return 0; 678 return -1; 679 } 680 681 /* 682 * Common filesystem object access control check routine. Accepts a 683 * vnode, cred, uid, gid, mode, acl, requested access mode. 684 * Returns 0 on success, or an errno on failure. 685 */ 686 int 687 genfs_can_access(vnode_t *vp, kauth_cred_t cred, uid_t file_uid, gid_t file_gid, 688 mode_t file_mode, struct acl *acl, accmode_t accmode) 689 { 690 accmode_t dac_granted; 691 int error; 692 693 KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0); 694 KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE)); 695 696 /* 697 * Look for a normal, non-privileged way to access the file/directory 698 * as requested. If it exists, go with that. 699 */ 700 701 dac_granted = 0; 702 703 /* Check the owner. */ 704 if (kauth_cred_geteuid(cred) == file_uid) { 705 dac_granted |= VADMIN; 706 if (file_mode & S_IXUSR) 707 dac_granted |= VEXEC; 708 if (file_mode & S_IRUSR) 709 dac_granted |= VREAD; 710 if (file_mode & S_IWUSR) 711 dac_granted |= (VWRITE | VAPPEND); 712 713 goto privchk; 714 } 715 716 /* Otherwise, check the groups (first match) */ 717 /* Otherwise, check the groups. */ 718 error = groupmember(file_gid, cred); 719 if (error > 0) 720 return error; 721 if (error == 0) { 722 if (file_mode & S_IXGRP) 723 dac_granted |= VEXEC; 724 if (file_mode & S_IRGRP) 725 dac_granted |= VREAD; 726 if (file_mode & S_IWGRP) 727 dac_granted |= (VWRITE | VAPPEND); 728 729 goto privchk; 730 } 731 732 /* Otherwise, check everyone else. */ 733 if (file_mode & S_IXOTH) 734 dac_granted |= VEXEC; 735 if (file_mode & S_IROTH) 736 dac_granted |= VREAD; 737 if (file_mode & S_IWOTH) 738 dac_granted |= (VWRITE | VAPPEND); 739 740 privchk: 741 if ((accmode & dac_granted) == accmode) 742 return 0; 743 744 return (accmode & VADMIN) ? EPERM : EACCES; 745 } 746 747 /* 748 * Implement a version of genfs_can_access() that understands POSIX.1e ACL 749 * semantics; 750 * the access ACL has already been prepared for evaluation by the file system 751 * and is passed via 'uid', 'gid', and 'acl'. Return 0 on success, else an 752 * errno value. 753 */ 754 int 755 genfs_can_access_acl_posix1e(vnode_t *vp, kauth_cred_t cred, uid_t file_uid, 756 gid_t file_gid, mode_t file_mode, struct acl *acl, accmode_t accmode) 757 { 758 struct acl_entry *acl_other, *acl_mask; 759 accmode_t dac_granted; 760 accmode_t acl_mask_granted; 761 int group_matched, i; 762 int error; 763 764 KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND)) == 0); 765 KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE)); 766 767 /* 768 * The owner matches if the effective uid associated with the 769 * credential matches that of the ACL_USER_OBJ entry. While we're 770 * doing the first scan, also cache the location of the ACL_MASK and 771 * ACL_OTHER entries, preventing some future iterations. 772 */ 773 acl_mask = acl_other = NULL; 774 for (i = 0; i < acl->acl_cnt; i++) { 775 struct acl_entry *ae = &acl->acl_entry[i]; 776 switch (ae->ae_tag) { 777 case ACL_USER_OBJ: 778 if (kauth_cred_geteuid(cred) != file_uid) 779 break; 780 dac_granted = 0; 781 dac_granted |= VADMIN; 782 if (ae->ae_perm & ACL_EXECUTE) 783 dac_granted |= VEXEC; 784 if (ae->ae_perm & ACL_READ) 785 dac_granted |= VREAD; 786 if (ae->ae_perm & ACL_WRITE) 787 dac_granted |= (VWRITE | VAPPEND); 788 goto out; 789 790 case ACL_MASK: 791 acl_mask = ae; 792 break; 793 794 case ACL_OTHER: 795 acl_other = ae; 796 break; 797 798 default: 799 break; 800 } 801 } 802 803 /* 804 * An ACL_OTHER entry should always exist in a valid access ACL. If 805 * it doesn't, then generate a serious failure. For now, this means 806 * a debugging message and EPERM, but in the future should probably 807 * be a panic. 808 */ 809 if (acl_other == NULL) { 810 /* 811 * XXX This should never happen 812 */ 813 printf("%s: ACL_OTHER missing\n", __func__); 814 return EPERM; 815 } 816 817 /* 818 * Checks against ACL_USER, ACL_GROUP_OBJ, and ACL_GROUP fields are 819 * masked by an ACL_MASK entry, if any. As such, first identify the 820 * ACL_MASK field, then iterate through identifying potential user 821 * matches, then group matches. If there is no ACL_MASK, assume that 822 * the mask allows all requests to succeed. 823 */ 824 if (acl_mask != NULL) { 825 acl_mask_granted = 0; 826 if (acl_mask->ae_perm & ACL_EXECUTE) 827 acl_mask_granted |= VEXEC; 828 if (acl_mask->ae_perm & ACL_READ) 829 acl_mask_granted |= VREAD; 830 if (acl_mask->ae_perm & ACL_WRITE) 831 acl_mask_granted |= (VWRITE | VAPPEND); 832 } else 833 acl_mask_granted = VEXEC | VREAD | VWRITE | VAPPEND; 834 835 /* 836 * Check ACL_USER ACL entries. There will either be one or no 837 * matches; if there is one, we accept or rejected based on the 838 * match; otherwise, we continue on to groups. 839 */ 840 for (i = 0; i < acl->acl_cnt; i++) { 841 struct acl_entry *ae = &acl->acl_entry[i]; 842 switch (ae->ae_tag) { 843 case ACL_USER: 844 if (kauth_cred_geteuid(cred) != ae->ae_id) 845 break; 846 dac_granted = 0; 847 if (ae->ae_perm & ACL_EXECUTE) 848 dac_granted |= VEXEC; 849 if (ae->ae_perm & ACL_READ) 850 dac_granted |= VREAD; 851 if (ae->ae_perm & ACL_WRITE) 852 dac_granted |= (VWRITE | VAPPEND); 853 dac_granted &= acl_mask_granted; 854 goto out; 855 } 856 } 857 858 /* 859 * Group match is best-match, not first-match, so find a "best" 860 * match. Iterate across, testing each potential group match. Make 861 * sure we keep track of whether we found a match or not, so that we 862 * know if we should try again with any available privilege, or if we 863 * should move on to ACL_OTHER. 864 */ 865 group_matched = 0; 866 for (i = 0; i < acl->acl_cnt; i++) { 867 struct acl_entry *ae = &acl->acl_entry[i]; 868 switch (ae->ae_tag) { 869 case ACL_GROUP_OBJ: 870 error = groupmember(file_gid, cred); 871 if (error > 0) 872 return error; 873 if (error) 874 break; 875 dac_granted = 0; 876 if (ae->ae_perm & ACL_EXECUTE) 877 dac_granted |= VEXEC; 878 if (ae->ae_perm & ACL_READ) 879 dac_granted |= VREAD; 880 if (ae->ae_perm & ACL_WRITE) 881 dac_granted |= (VWRITE | VAPPEND); 882 dac_granted &= acl_mask_granted; 883 884 if ((accmode & dac_granted) == accmode) 885 return 0; 886 887 group_matched = 1; 888 break; 889 890 case ACL_GROUP: 891 error = groupmember(ae->ae_id, cred); 892 if (error > 0) 893 return error; 894 if (error) 895 break; 896 dac_granted = 0; 897 if (ae->ae_perm & ACL_EXECUTE) 898 dac_granted |= VEXEC; 899 if (ae->ae_perm & ACL_READ) 900 dac_granted |= VREAD; 901 if (ae->ae_perm & ACL_WRITE) 902 dac_granted |= (VWRITE | VAPPEND); 903 dac_granted &= acl_mask_granted; 904 905 if ((accmode & dac_granted) == accmode) 906 return 0; 907 908 group_matched = 1; 909 break; 910 911 default: 912 break; 913 } 914 } 915 916 if (group_matched == 1) { 917 /* 918 * There was a match, but it did not grant rights via pure 919 * DAC. Try again, this time with privilege. 920 */ 921 for (i = 0; i < acl->acl_cnt; i++) { 922 struct acl_entry *ae = &acl->acl_entry[i]; 923 switch (ae->ae_tag) { 924 case ACL_GROUP_OBJ: 925 error = groupmember(file_gid, cred); 926 if (error > 0) 927 return error; 928 if (error) 929 break; 930 dac_granted = 0; 931 if (ae->ae_perm & ACL_EXECUTE) 932 dac_granted |= VEXEC; 933 if (ae->ae_perm & ACL_READ) 934 dac_granted |= VREAD; 935 if (ae->ae_perm & ACL_WRITE) 936 dac_granted |= (VWRITE | VAPPEND); 937 dac_granted &= acl_mask_granted; 938 goto out; 939 940 case ACL_GROUP: 941 error = groupmember(ae->ae_id, cred); 942 if (error > 0) 943 return error; 944 if (error) 945 break; 946 dac_granted = 0; 947 if (ae->ae_perm & ACL_EXECUTE) 948 dac_granted |= VEXEC; 949 if (ae->ae_perm & ACL_READ) 950 dac_granted |= VREAD; 951 if (ae->ae_perm & ACL_WRITE) 952 dac_granted |= (VWRITE | VAPPEND); 953 dac_granted &= acl_mask_granted; 954 955 goto out; 956 default: 957 break; 958 } 959 } 960 /* 961 * Even with privilege, group membership was not sufficient. 962 * Return failure. 963 */ 964 dac_granted = 0; 965 goto out; 966 } 967 968 /* 969 * Fall back on ACL_OTHER. ACL_MASK is not applied to ACL_OTHER. 970 */ 971 dac_granted = 0; 972 if (acl_other->ae_perm & ACL_EXECUTE) 973 dac_granted |= VEXEC; 974 if (acl_other->ae_perm & ACL_READ) 975 dac_granted |= VREAD; 976 if (acl_other->ae_perm & ACL_WRITE) 977 dac_granted |= (VWRITE | VAPPEND); 978 979 out: 980 if ((accmode & dac_granted) == accmode) 981 return 0; 982 return (accmode & VADMIN) ? EPERM : EACCES; 983 } 984 985 static struct { 986 accmode_t accmode; 987 int mask; 988 } accmode2mask[] = { 989 { VREAD, ACL_READ_DATA }, 990 { VWRITE, ACL_WRITE_DATA }, 991 { VAPPEND, ACL_APPEND_DATA }, 992 { VEXEC, ACL_EXECUTE }, 993 { VREAD_NAMED_ATTRS, ACL_READ_NAMED_ATTRS }, 994 { VWRITE_NAMED_ATTRS, ACL_WRITE_NAMED_ATTRS }, 995 { VDELETE_CHILD, ACL_DELETE_CHILD }, 996 { VREAD_ATTRIBUTES, ACL_READ_ATTRIBUTES }, 997 { VWRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES }, 998 { VDELETE, ACL_DELETE }, 999 { VREAD_ACL, ACL_READ_ACL }, 1000 { VWRITE_ACL, ACL_WRITE_ACL }, 1001 { VWRITE_OWNER, ACL_WRITE_OWNER }, 1002 { VSYNCHRONIZE, ACL_SYNCHRONIZE }, 1003 { 0, 0 }, 1004 }; 1005 1006 static int 1007 _access_mask_from_accmode(accmode_t accmode) 1008 { 1009 int access_mask = 0, i; 1010 1011 for (i = 0; accmode2mask[i].accmode != 0; i++) { 1012 if (accmode & accmode2mask[i].accmode) 1013 access_mask |= accmode2mask[i].mask; 1014 } 1015 1016 /* 1017 * VAPPEND is just a modifier for VWRITE; if the caller asked 1018 * for 'VAPPEND | VWRITE', we want to check for ACL_APPEND_DATA only. 1019 */ 1020 if (access_mask & ACL_APPEND_DATA) 1021 access_mask &= ~ACL_WRITE_DATA; 1022 1023 return (access_mask); 1024 } 1025 1026 /* 1027 * Return 0, iff access is allowed, 1 otherwise. 1028 */ 1029 static int 1030 _acl_denies(const struct acl *aclp, int access_mask, kauth_cred_t cred, 1031 int file_uid, int file_gid, int *denied_explicitly) 1032 { 1033 int i, error; 1034 const struct acl_entry *ae; 1035 1036 if (denied_explicitly != NULL) 1037 *denied_explicitly = 0; 1038 1039 KASSERT(aclp->acl_cnt <= ACL_MAX_ENTRIES); 1040 1041 for (i = 0; i < aclp->acl_cnt; i++) { 1042 ae = &(aclp->acl_entry[i]); 1043 1044 if (ae->ae_entry_type != ACL_ENTRY_TYPE_ALLOW && 1045 ae->ae_entry_type != ACL_ENTRY_TYPE_DENY) 1046 continue; 1047 if (ae->ae_flags & ACL_ENTRY_INHERIT_ONLY) 1048 continue; 1049 switch (ae->ae_tag) { 1050 case ACL_USER_OBJ: 1051 if (kauth_cred_geteuid(cred) != file_uid) 1052 continue; 1053 break; 1054 case ACL_USER: 1055 if (kauth_cred_geteuid(cred) != ae->ae_id) 1056 continue; 1057 break; 1058 case ACL_GROUP_OBJ: 1059 error = groupmember(file_gid, cred); 1060 if (error > 0) 1061 return error; 1062 if (error != 0) 1063 continue; 1064 break; 1065 case ACL_GROUP: 1066 error = groupmember(ae->ae_id, cred); 1067 if (error > 0) 1068 return error; 1069 if (error != 0) 1070 continue; 1071 break; 1072 default: 1073 KASSERT(ae->ae_tag == ACL_EVERYONE); 1074 } 1075 1076 if (ae->ae_entry_type == ACL_ENTRY_TYPE_DENY) { 1077 if (ae->ae_perm & access_mask) { 1078 if (denied_explicitly != NULL) 1079 *denied_explicitly = 1; 1080 return (1); 1081 } 1082 } 1083 1084 access_mask &= ~(ae->ae_perm); 1085 if (access_mask == 0) 1086 return (0); 1087 } 1088 1089 if (access_mask == 0) 1090 return (0); 1091 1092 return (1); 1093 } 1094 1095 int 1096 genfs_can_access_acl_nfs4(vnode_t *vp, kauth_cred_t cred, uid_t file_uid, 1097 gid_t file_gid, mode_t file_mode, struct acl *aclp, accmode_t accmode) 1098 { 1099 int denied, explicitly_denied, access_mask, is_directory, 1100 must_be_owner = 0; 1101 file_mode = 0; 1102 1103 KASSERT((accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | VAPPEND | 1104 VEXPLICIT_DENY | VREAD_NAMED_ATTRS | VWRITE_NAMED_ATTRS | 1105 VDELETE_CHILD | VREAD_ATTRIBUTES | VWRITE_ATTRIBUTES | VDELETE | 1106 VREAD_ACL | VWRITE_ACL | VWRITE_OWNER | VSYNCHRONIZE)) == 0); 1107 KASSERT((accmode & VAPPEND) == 0 || (accmode & VWRITE)); 1108 1109 if (accmode & VADMIN) 1110 must_be_owner = 1; 1111 1112 /* 1113 * Ignore VSYNCHRONIZE permission. 1114 */ 1115 accmode &= ~VSYNCHRONIZE; 1116 1117 access_mask = _access_mask_from_accmode(accmode); 1118 1119 if (vp && vp->v_type == VDIR) 1120 is_directory = 1; 1121 else 1122 is_directory = 0; 1123 1124 /* 1125 * File owner is always allowed to read and write the ACL 1126 * and basic attributes. This is to prevent a situation 1127 * where user would change ACL in a way that prevents him 1128 * from undoing the change. 1129 */ 1130 if (kauth_cred_geteuid(cred) == file_uid) 1131 access_mask &= ~(ACL_READ_ACL | ACL_WRITE_ACL | 1132 ACL_READ_ATTRIBUTES | ACL_WRITE_ATTRIBUTES); 1133 1134 /* 1135 * Ignore append permission for regular files; use write 1136 * permission instead. 1137 */ 1138 if (!is_directory && (access_mask & ACL_APPEND_DATA)) { 1139 access_mask &= ~ACL_APPEND_DATA; 1140 access_mask |= ACL_WRITE_DATA; 1141 } 1142 1143 denied = _acl_denies(aclp, access_mask, cred, file_uid, file_gid, 1144 &explicitly_denied); 1145 1146 if (must_be_owner) { 1147 if (kauth_cred_geteuid(cred) != file_uid) 1148 denied = EPERM; 1149 } 1150 1151 /* 1152 * For VEXEC, ensure that at least one execute bit is set for 1153 * non-directories. We have to check the mode here to stay 1154 * consistent with execve(2). See the test in 1155 * exec_check_permissions(). 1156 */ 1157 __acl_nfs4_sync_mode_from_acl(&file_mode, aclp); 1158 if (!denied && !is_directory && (accmode & VEXEC) && 1159 (file_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) 1160 denied = EACCES; 1161 1162 if (!denied) 1163 return (0); 1164 1165 /* 1166 * Access failed. Iff it was not denied explicitly and 1167 * VEXPLICIT_DENY flag was specified, allow access. 1168 */ 1169 if ((accmode & VEXPLICIT_DENY) && explicitly_denied == 0) 1170 return (0); 1171 1172 accmode &= ~VEXPLICIT_DENY; 1173 1174 if (accmode & (VADMIN_PERMS | VDELETE_CHILD | VDELETE)) 1175 denied = EPERM; 1176 else 1177 denied = EACCES; 1178 1179 return (denied); 1180 } 1181 1182 /* 1183 * Common routine to check if chmod() is allowed. 1184 * 1185 * Policy: 1186 * - You must own the file, and 1187 * - You must not set the "sticky" bit (meaningless, see chmod(2)) 1188 * - You must be a member of the group if you're trying to set the 1189 * SGIDf bit 1190 * 1191 * vp - vnode of the file-system object 1192 * cred - credentials of the invoker 1193 * cur_uid, cur_gid - current uid/gid of the file-system object 1194 * new_mode - new mode for the file-system object 1195 * 1196 * Returns 0 if the change is allowed, or an error value otherwise. 1197 */ 1198 int 1199 genfs_can_chmod(vnode_t *vp, kauth_cred_t cred, uid_t cur_uid, 1200 gid_t cur_gid, mode_t new_mode) 1201 { 1202 int error; 1203 1204 /* 1205 * To modify the permissions on a file, must possess VADMIN 1206 * for that file. 1207 */ 1208 if ((error = VOP_ACCESSX(vp, VWRITE_ACL, cred)) != 0) 1209 return (error); 1210 1211 /* 1212 * Unprivileged users can't set the sticky bit on files. 1213 */ 1214 if ((vp->v_type != VDIR) && (new_mode & S_ISTXT)) 1215 return (EFTYPE); 1216 1217 /* 1218 * If the invoker is trying to set the SGID bit on the file, 1219 * check group membership. 1220 */ 1221 if (new_mode & S_ISGID) { 1222 int ismember; 1223 1224 error = kauth_cred_ismember_gid(cred, cur_gid, 1225 &ismember); 1226 if (error || !ismember) 1227 return (EPERM); 1228 } 1229 1230 /* 1231 * Deny setting setuid if we are not the file owner. 1232 */ 1233 if ((new_mode & S_ISUID) && cur_uid != kauth_cred_geteuid(cred)) 1234 return (EPERM); 1235 1236 return (0); 1237 } 1238 1239 /* 1240 * Common routine to check if chown() is allowed. 1241 * 1242 * Policy: 1243 * - You must own the file, and 1244 * - You must not try to change ownership, and 1245 * - You must be member of the new group 1246 * 1247 * vp - vnode 1248 * cred - credentials of the invoker 1249 * cur_uid, cur_gid - current uid/gid of the file-system object 1250 * new_uid, new_gid - target uid/gid of the file-system object 1251 * 1252 * Returns 0 if the change is allowed, or an error value otherwise. 1253 */ 1254 int 1255 genfs_can_chown(vnode_t *vp, kauth_cred_t cred, uid_t cur_uid, 1256 gid_t cur_gid, uid_t new_uid, gid_t new_gid) 1257 { 1258 int error, ismember; 1259 1260 /* 1261 * To modify the ownership of a file, must possess VADMIN for that 1262 * file. 1263 */ 1264 if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred)) != 0) 1265 return (error); 1266 1267 /* 1268 * You can only change ownership of a file if: 1269 * You own the file and... 1270 */ 1271 if (kauth_cred_geteuid(cred) == cur_uid) { 1272 /* 1273 * You don't try to change ownership, and... 1274 */ 1275 if (new_uid != cur_uid) 1276 return (EPERM); 1277 1278 /* 1279 * You don't try to change group (no-op), or... 1280 */ 1281 if (new_gid == cur_gid) 1282 return (0); 1283 1284 /* 1285 * Your effective gid is the new gid, or... 1286 */ 1287 if (kauth_cred_getegid(cred) == new_gid) 1288 return (0); 1289 1290 /* 1291 * The new gid is one you're a member of. 1292 */ 1293 ismember = 0; 1294 error = kauth_cred_ismember_gid(cred, new_gid, 1295 &ismember); 1296 if (!error && ismember) 1297 return (0); 1298 } 1299 1300 return (EPERM); 1301 } 1302 1303 int 1304 genfs_can_chtimes(vnode_t *vp, kauth_cred_t cred, uid_t owner_uid, 1305 u_int vaflags) 1306 { 1307 int error; 1308 /* 1309 * Grant permission if the caller is the owner of the file, or 1310 * the super-user, or has ACL_WRITE_ATTRIBUTES permission on 1311 * on the file. If the time pointer is null, then write 1312 * permission on the file is also sufficient. 1313 * 1314 * From NFSv4.1, draft 21, 6.2.1.3.1, Discussion of Mask Attributes: 1315 * A user having ACL_WRITE_DATA or ACL_WRITE_ATTRIBUTES 1316 * will be allowed to set the times [..] to the current 1317 * server time. 1318 */ 1319 if ((error = VOP_ACCESSX(vp, VWRITE_ATTRIBUTES, cred)) != 0) 1320 return (vaflags & VA_UTIMES_NULL) == 0 ? EPERM : EACCES; 1321 1322 /* Must be owner, or... */ 1323 if (kauth_cred_geteuid(cred) == owner_uid) 1324 return (0); 1325 1326 /* set the times to the current time, and... */ 1327 if ((vaflags & VA_UTIMES_NULL) == 0) 1328 return (EPERM); 1329 1330 /* have write access. */ 1331 error = VOP_ACCESS(vp, VWRITE, cred); 1332 if (error) 1333 return (error); 1334 1335 return (0); 1336 } 1337 1338 /* 1339 * Common routine to check if chflags() is allowed. 1340 * 1341 * Policy: 1342 * - You must own the file, and 1343 * - You must not change system flags, and 1344 * - You must not change flags on character/block devices. 1345 * 1346 * vp - vnode 1347 * cred - credentials of the invoker 1348 * owner_uid - uid of the file-system object 1349 * changing_sysflags - true if the invoker wants to change system flags 1350 */ 1351 int 1352 genfs_can_chflags(vnode_t *vp, kauth_cred_t cred, 1353 uid_t owner_uid, bool changing_sysflags) 1354 { 1355 1356 /* The user must own the file. */ 1357 if (kauth_cred_geteuid(cred) != owner_uid) { 1358 return EPERM; 1359 } 1360 1361 if (changing_sysflags) { 1362 return EPERM; 1363 } 1364 1365 /* 1366 * Unprivileged users cannot change the flags on devices, even if they 1367 * own them. 1368 */ 1369 if (vp->v_type == VCHR || vp->v_type == VBLK) { 1370 return EPERM; 1371 } 1372 1373 return 0; 1374 } 1375 1376 /* 1377 * Common "sticky" policy. 1378 * 1379 * When a directory is "sticky" (as determined by the caller), this 1380 * function may help implementing the following policy: 1381 * - Renaming a file in it is only possible if the user owns the directory 1382 * or the file being renamed. 1383 * - Deleting a file from it is only possible if the user owns the 1384 * directory or the file being deleted. 1385 */ 1386 int 1387 genfs_can_sticky(vnode_t *vp, kauth_cred_t cred, uid_t dir_uid, uid_t file_uid) 1388 { 1389 if (kauth_cred_geteuid(cred) != dir_uid && 1390 kauth_cred_geteuid(cred) != file_uid) 1391 return EPERM; 1392 1393 return 0; 1394 } 1395 1396 int 1397 genfs_can_extattr(vnode_t *vp, kauth_cred_t cred, accmode_t accmode, 1398 int attrnamespace) 1399 { 1400 /* 1401 * Kernel-invoked always succeeds. 1402 */ 1403 if (cred == NOCRED) 1404 return 0; 1405 1406 switch (attrnamespace) { 1407 case EXTATTR_NAMESPACE_SYSTEM: 1408 return kauth_authorize_system(cred, KAUTH_SYSTEM_FS_EXTATTR, 1409 0, vp->v_mount, NULL, NULL); 1410 case EXTATTR_NAMESPACE_USER: 1411 return VOP_ACCESS(vp, accmode, cred); 1412 default: 1413 return EPERM; 1414 } 1415 } 1416 1417 int 1418 genfs_access(void *v) 1419 { 1420 struct vop_access_args *ap = v; 1421 1422 KASSERT((ap->a_accmode & ~(VEXEC | VWRITE | VREAD | VADMIN | 1423 VAPPEND)) == 0); 1424 1425 return VOP_ACCESSX(ap->a_vp, ap->a_accmode, ap->a_cred); 1426 } 1427 1428 int 1429 genfs_accessx(void *v) 1430 { 1431 struct vop_accessx_args *ap = v; 1432 int error; 1433 accmode_t accmode = ap->a_accmode; 1434 error = vfs_unixify_accmode(&accmode); 1435 if (error != 0) 1436 return error; 1437 1438 if (accmode == 0) 1439 return 0; 1440 1441 return VOP_ACCESS(ap->a_vp, accmode, ap->a_cred); 1442 } 1443 1444 /* 1445 * genfs_pathconf: 1446 * 1447 * Standard implementation of POSIX pathconf, to get information about limits 1448 * for a filesystem. 1449 * Override per filesystem for the case where the filesystem has smaller 1450 * limits. 1451 */ 1452 int 1453 genfs_pathconf(void *v) 1454 { 1455 struct vop_pathconf_args *ap = v; 1456 1457 switch (ap->a_name) { 1458 case _PC_PATH_MAX: 1459 *ap->a_retval = PATH_MAX; 1460 return 0; 1461 case _PC_ACL_EXTENDED: 1462 case _PC_ACL_NFS4: 1463 *ap->a_retval = 0; 1464 return 0; 1465 default: 1466 return EINVAL; 1467 } 1468 } 1469