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