1 /* 2 * Copyright (c) 1993 Jan-Simon Pendry 3 * Copyright (c) 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Jan-Simon Pendry. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)procfs_vnops.c 8.7 (Berkeley) 06/04/94 12 * 13 * From: 14 * $Id: procfs_vnops.c,v 3.2 1993/12/15 09:40:17 jsp Exp $ 15 */ 16 17 /* 18 * procfs vnode interface 19 */ 20 21 #include <sys/param.h> 22 #include <sys/systm.h> 23 #include <sys/time.h> 24 #include <sys/kernel.h> 25 #include <sys/file.h> 26 #include <sys/proc.h> 27 #include <sys/vnode.h> 28 #include <sys/namei.h> 29 #include <sys/malloc.h> 30 #include <sys/dirent.h> 31 #include <sys/resourcevar.h> 32 #include <miscfs/procfs/procfs.h> 33 #include <vm/vm.h> /* for PAGE_SIZE */ 34 35 /* 36 * Vnode Operations. 37 * 38 */ 39 40 /* 41 * This is a list of the valid names in the 42 * process-specific sub-directories. It is 43 * used in procfs_lookup and procfs_readdir 44 */ 45 static struct pfsnames { 46 u_short d_namlen; 47 char d_name[PROCFS_NAMELEN]; 48 pfstype d_pfstype; 49 } procent[] = { 50 #define N(s) sizeof(s)-1, s 51 /* namlen, nam, type */ 52 { N("file"), Pfile }, 53 { N("mem"), Pmem }, 54 { N("regs"), Pregs }, 55 { N("fpregs"), Pfpregs }, 56 { N("ctl"), Pctl }, 57 { N("status"), Pstatus }, 58 { N("note"), Pnote }, 59 { N("notepg"), Pnotepg }, 60 #undef N 61 }; 62 #define Nprocent (sizeof(procent)/sizeof(procent[0])) 63 64 static pid_t atopid __P((const char *, u_int)); 65 66 /* 67 * set things up for doing i/o on 68 * the pfsnode (vp). (vp) is locked 69 * on entry, and should be left locked 70 * on exit. 71 * 72 * for procfs we don't need to do anything 73 * in particular for i/o. all that is done 74 * is to support exclusive open on process 75 * memory images. 76 */ 77 procfs_open(ap) 78 struct vop_open_args /* { 79 struct vnode *a_vp; 80 int a_mode; 81 struct ucred *a_cred; 82 struct proc *a_p; 83 } */ *ap; 84 { 85 struct pfsnode *pfs = VTOPFS(ap->a_vp); 86 87 switch (pfs->pfs_type) { 88 case Pmem: 89 if (PFIND(pfs->pfs_pid) == 0) 90 return (ENOENT); /* was ESRCH, jsp */ 91 92 if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || 93 (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) 94 return (EBUSY); 95 96 97 if (ap->a_mode & FWRITE) 98 pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); 99 100 return (0); 101 102 default: 103 break; 104 } 105 106 return (0); 107 } 108 109 /* 110 * close the pfsnode (vp) after doing i/o. 111 * (vp) is not locked on entry or exit. 112 * 113 * nothing to do for procfs other than undo 114 * any exclusive open flag (see _open above). 115 */ 116 procfs_close(ap) 117 struct vop_close_args /* { 118 struct vnode *a_vp; 119 int a_fflag; 120 struct ucred *a_cred; 121 struct proc *a_p; 122 } */ *ap; 123 { 124 struct pfsnode *pfs = VTOPFS(ap->a_vp); 125 126 switch (pfs->pfs_type) { 127 case Pmem: 128 if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) 129 pfs->pfs_flags &= ~(FWRITE|O_EXCL); 130 break; 131 } 132 133 return (0); 134 } 135 136 /* 137 * do an ioctl operation on pfsnode (vp). 138 * (vp) is not locked on entry or exit. 139 */ 140 procfs_ioctl(ap) 141 struct vop_ioctl_args /* { 142 struct vnode *a_vp; 143 int a_command; 144 caddr_t a_data; 145 int a_fflag; 146 struct ucred *a_cred; 147 struct proc *a_p; 148 } */ *ap; 149 { 150 151 return (ENOTTY); 152 } 153 154 /* 155 * do block mapping for pfsnode (vp). 156 * since we don't use the buffer cache 157 * for procfs this function should never 158 * be called. in any case, it's not clear 159 * what part of the kernel ever makes use 160 * of this function. for sanity, this is the 161 * usual no-op bmap, although returning 162 * (EIO) would be a reasonable alternative. 163 */ 164 procfs_bmap(ap) 165 struct vop_bmap_args /* { 166 struct vnode *a_vp; 167 daddr_t a_bn; 168 struct vnode **a_vpp; 169 daddr_t *a_bnp; 170 } */ *ap; 171 { 172 173 if (ap->a_vpp != NULL) 174 *ap->a_vpp = ap->a_vp; 175 if (ap->a_bnp != NULL) 176 *ap->a_bnp = ap->a_bn; 177 return (0); 178 } 179 180 /* 181 * _inactive is called when the pfsnode 182 * is vrele'd and the reference count goes 183 * to zero. (vp) will be on the vnode free 184 * list, so to get it back vget() must be 185 * used. 186 * 187 * for procfs, check if the process is still 188 * alive and if it isn't then just throw away 189 * the vnode by calling vgone(). this may 190 * be overkill and a waste of time since the 191 * chances are that the process will still be 192 * there and PFIND is not free. 193 * 194 * (vp) is not locked on entry or exit. 195 */ 196 procfs_inactive(ap) 197 struct vop_inactive_args /* { 198 struct vnode *a_vp; 199 } */ *ap; 200 { 201 struct pfsnode *pfs = VTOPFS(ap->a_vp); 202 203 if (PFIND(pfs->pfs_pid) == 0) 204 vgone(ap->a_vp); 205 206 return (0); 207 } 208 209 /* 210 * _reclaim is called when getnewvnode() 211 * wants to make use of an entry on the vnode 212 * free list. at this time the filesystem needs 213 * to free any private data and remove the node 214 * from any private lists. 215 */ 216 procfs_reclaim(ap) 217 struct vop_reclaim_args /* { 218 struct vnode *a_vp; 219 } */ *ap; 220 { 221 int error; 222 223 error = procfs_freevp(ap->a_vp); 224 return (error); 225 } 226 227 /* 228 * Return POSIX pathconf information applicable to special devices. 229 */ 230 procfs_pathconf(ap) 231 struct vop_pathconf_args /* { 232 struct vnode *a_vp; 233 int a_name; 234 int *a_retval; 235 } */ *ap; 236 { 237 238 switch (ap->a_name) { 239 case _PC_LINK_MAX: 240 *ap->a_retval = LINK_MAX; 241 return (0); 242 case _PC_MAX_CANON: 243 *ap->a_retval = MAX_CANON; 244 return (0); 245 case _PC_MAX_INPUT: 246 *ap->a_retval = MAX_INPUT; 247 return (0); 248 case _PC_PIPE_BUF: 249 *ap->a_retval = PIPE_BUF; 250 return (0); 251 case _PC_CHOWN_RESTRICTED: 252 *ap->a_retval = 1; 253 return (0); 254 case _PC_VDISABLE: 255 *ap->a_retval = _POSIX_VDISABLE; 256 return (0); 257 default: 258 return (EINVAL); 259 } 260 /* NOTREACHED */ 261 } 262 263 /* 264 * _print is used for debugging. 265 * just print a readable description 266 * of (vp). 267 */ 268 procfs_print(ap) 269 struct vop_print_args /* { 270 struct vnode *a_vp; 271 } */ *ap; 272 { 273 struct pfsnode *pfs = VTOPFS(ap->a_vp); 274 275 printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n", 276 pfs->pfs_pid, 277 pfs->pfs_mode, pfs->pfs_flags); 278 } 279 280 /* 281 * _abortop is called when operations such as 282 * rename and create fail. this entry is responsible 283 * for undoing any side-effects caused by the lookup. 284 * this will always include freeing the pathname buffer. 285 */ 286 procfs_abortop(ap) 287 struct vop_abortop_args /* { 288 struct vnode *a_dvp; 289 struct componentname *a_cnp; 290 } */ *ap; 291 { 292 293 if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) 294 FREE(ap->a_cnp->cn_pnbuf, M_NAMEI); 295 return (0); 296 } 297 298 /* 299 * generic entry point for unsupported operations 300 */ 301 procfs_badop() 302 { 303 304 return (EIO); 305 } 306 307 /* 308 * Invent attributes for pfsnode (vp) and store 309 * them in (vap). 310 * Directories lengths are returned as zero since 311 * any real length would require the genuine size 312 * to be computed, and nothing cares anyway. 313 * 314 * this is relatively minimal for procfs. 315 */ 316 procfs_getattr(ap) 317 struct vop_getattr_args /* { 318 struct vnode *a_vp; 319 struct vattr *a_vap; 320 struct ucred *a_cred; 321 struct proc *a_p; 322 } */ *ap; 323 { 324 struct pfsnode *pfs = VTOPFS(ap->a_vp); 325 struct vattr *vap = ap->a_vap; 326 struct proc *procp; 327 int error; 328 329 /* first check the process still exists */ 330 switch (pfs->pfs_type) { 331 case Proot: 332 procp = 0; 333 break; 334 335 default: 336 procp = PFIND(pfs->pfs_pid); 337 if (procp == 0) 338 return (ENOENT); 339 } 340 341 error = 0; 342 343 /* start by zeroing out the attributes */ 344 VATTR_NULL(vap); 345 346 /* next do all the common fields */ 347 vap->va_type = ap->a_vp->v_type; 348 vap->va_mode = pfs->pfs_mode; 349 vap->va_fileid = pfs->pfs_fileno; 350 vap->va_flags = 0; 351 vap->va_blocksize = PAGE_SIZE; 352 vap->va_bytes = vap->va_size = 0; 353 354 /* 355 * If the process has exercised some setuid or setgid 356 * privilege, then rip away read/write permission so 357 * that only root can gain access. 358 */ 359 switch (pfs->pfs_type) { 360 case Pregs: 361 case Pfpregs: 362 case Pmem: 363 if (procp->p_flag & P_SUGID) 364 vap->va_mode &= ~((VREAD|VWRITE)| 365 ((VREAD|VWRITE)>>3)| 366 ((VREAD|VWRITE)>>6)); 367 break; 368 } 369 370 /* 371 * Make all times be current TOD. 372 * It would be possible to get the process start 373 * time from the p_stat structure, but there's 374 * no "file creation" time stamp anyway, and the 375 * p_stat structure is not addressible if u. gets 376 * swapped out for that process. 377 */ 378 microtime(&vap->va_ctime); 379 vap->va_atime = vap->va_mtime = vap->va_ctime; 380 381 /* 382 * now do the object specific fields 383 * 384 * The size could be set from struct reg, but it's hardly 385 * worth the trouble, and it puts some (potentially) machine 386 * dependent data into this machine-independent code. If it 387 * becomes important then this function should break out into 388 * a per-file stat function in the corresponding .c file. 389 */ 390 391 switch (pfs->pfs_type) { 392 case Proot: 393 vap->va_nlink = 2; 394 vap->va_uid = 0; 395 vap->va_gid = 0; 396 break; 397 398 case Pproc: 399 vap->va_nlink = 2; 400 vap->va_uid = procp->p_ucred->cr_uid; 401 vap->va_gid = procp->p_ucred->cr_gid; 402 break; 403 404 case Pfile: 405 error = EOPNOTSUPP; 406 break; 407 408 case Pmem: 409 vap->va_nlink = 1; 410 vap->va_bytes = vap->va_size = 411 ctob(procp->p_vmspace->vm_tsize + 412 procp->p_vmspace->vm_dsize + 413 procp->p_vmspace->vm_ssize); 414 vap->va_uid = procp->p_ucred->cr_uid; 415 vap->va_gid = procp->p_ucred->cr_gid; 416 break; 417 418 case Pregs: 419 case Pfpregs: 420 case Pctl: 421 case Pstatus: 422 case Pnote: 423 case Pnotepg: 424 vap->va_nlink = 1; 425 vap->va_uid = procp->p_ucred->cr_uid; 426 vap->va_gid = procp->p_ucred->cr_gid; 427 break; 428 429 default: 430 panic("procfs_getattr"); 431 } 432 433 return (error); 434 } 435 436 procfs_setattr(ap) 437 struct vop_setattr_args /* { 438 struct vnode *a_vp; 439 struct vattr *a_vap; 440 struct ucred *a_cred; 441 struct proc *a_p; 442 } */ *ap; 443 { 444 /* 445 * just fake out attribute setting 446 * it's not good to generate an error 447 * return, otherwise things like creat() 448 * will fail when they try to set the 449 * file length to 0. worse, this means 450 * that echo $note > /proc/$pid/note will fail. 451 */ 452 453 return (0); 454 } 455 456 /* 457 * implement access checking. 458 * 459 * something very similar to this code is duplicated 460 * throughout the 4bsd kernel and should be moved 461 * into kern/vfs_subr.c sometime. 462 * 463 * actually, the check for super-user is slightly 464 * broken since it will allow read access to write-only 465 * objects. this doesn't cause any particular trouble 466 * but does mean that the i/o entry points need to check 467 * that the operation really does make sense. 468 */ 469 procfs_access(ap) 470 struct vop_access_args /* { 471 struct vnode *a_vp; 472 int a_mode; 473 struct ucred *a_cred; 474 struct proc *a_p; 475 } */ *ap; 476 { 477 struct vattr *vap; 478 struct vattr vattr; 479 int error; 480 481 /* 482 * If you're the super-user, 483 * you always get access. 484 */ 485 if (ap->a_cred->cr_uid == (uid_t) 0) 486 return (0); 487 vap = &vattr; 488 if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p)) 489 return (error); 490 491 /* 492 * Access check is based on only one of owner, group, public. 493 * If not owner, then check group. If not a member of the 494 * group, then check public access. 495 */ 496 if (ap->a_cred->cr_uid != vap->va_uid) { 497 gid_t *gp; 498 int i; 499 500 (ap->a_mode) >>= 3; 501 gp = ap->a_cred->cr_groups; 502 for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++) 503 if (vap->va_gid == *gp) 504 goto found; 505 ap->a_mode >>= 3; 506 found: 507 ; 508 } 509 510 if ((vap->va_mode & ap->a_mode) == ap->a_mode) 511 return (0); 512 513 return (EACCES); 514 } 515 516 /* 517 * lookup. this is incredibly complicated in the 518 * general case, however for most pseudo-filesystems 519 * very little needs to be done. 520 * 521 * unless you want to get a migraine, just make sure your 522 * filesystem doesn't do any locking of its own. otherwise 523 * read and inwardly digest ufs_lookup(). 524 */ 525 procfs_lookup(ap) 526 struct vop_lookup_args /* { 527 struct vnode * a_dvp; 528 struct vnode ** a_vpp; 529 struct componentname * a_cnp; 530 } */ *ap; 531 { 532 struct componentname *cnp = ap->a_cnp; 533 struct vnode **vpp = ap->a_vpp; 534 struct vnode *dvp = ap->a_dvp; 535 char *pname = cnp->cn_nameptr; 536 int error = 0; 537 pid_t pid; 538 struct vnode *nvp; 539 struct pfsnode *pfs; 540 struct proc *procp; 541 pfstype pfs_type; 542 int i; 543 544 if (cnp->cn_namelen == 1 && *pname == '.') { 545 *vpp = dvp; 546 VREF(dvp); 547 /*VOP_LOCK(dvp);*/ 548 return (0); 549 } 550 551 *vpp = NULL; 552 553 pfs = VTOPFS(dvp); 554 switch (pfs->pfs_type) { 555 case Proot: 556 if (cnp->cn_flags & ISDOTDOT) 557 return (EIO); 558 559 if (CNEQ(cnp, "curproc", 7)) 560 pid = cnp->cn_proc->p_pid; 561 else 562 pid = atopid(pname, cnp->cn_namelen); 563 if (pid == NO_PID) 564 return (ENOENT); 565 566 procp = PFIND(pid); 567 if (procp == 0) 568 return (ENOENT); 569 570 error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc); 571 if (error) 572 return (error); 573 574 nvp->v_type = VDIR; 575 pfs = VTOPFS(nvp); 576 577 *vpp = nvp; 578 return (0); 579 580 case Pproc: 581 if (cnp->cn_flags & ISDOTDOT) { 582 error = procfs_root(dvp->v_mount, vpp); 583 return (error); 584 } 585 586 procp = PFIND(pfs->pfs_pid); 587 if (procp == 0) 588 return (ENOENT); 589 590 for (i = 0; i < Nprocent; i++) { 591 struct pfsnames *dp = &procent[i]; 592 593 if (cnp->cn_namelen == dp->d_namlen && 594 bcmp(pname, dp->d_name, dp->d_namlen) == 0) { 595 pfs_type = dp->d_pfstype; 596 goto found; 597 } 598 } 599 return (ENOENT); 600 601 found: 602 if (pfs_type == Pfile) { 603 nvp = procfs_findtextvp(procp); 604 if (nvp) { 605 VREF(nvp); 606 VOP_LOCK(nvp); 607 } else { 608 error = ENXIO; 609 } 610 } else { 611 error = procfs_allocvp(dvp->v_mount, &nvp, 612 pfs->pfs_pid, pfs_type); 613 if (error) 614 return (error); 615 616 nvp->v_type = VREG; 617 pfs = VTOPFS(nvp); 618 } 619 *vpp = nvp; 620 return (error); 621 622 default: 623 return (ENOTDIR); 624 } 625 } 626 627 /* 628 * readdir returns directory entries from pfsnode (vp). 629 * 630 * the strategy here with procfs is to generate a single 631 * directory entry at a time (struct pfsdent) and then 632 * copy that out to userland using uiomove. a more efficent 633 * though more complex implementation, would try to minimize 634 * the number of calls to uiomove(). for procfs, this is 635 * hardly worth the added code complexity. 636 * 637 * this should just be done through read() 638 */ 639 procfs_readdir(ap) 640 struct vop_readdir_args /* { 641 struct vnode *a_vp; 642 struct uio *a_uio; 643 struct ucred *a_cred; 644 int *a_eofflag; 645 u_long *a_cookies; 646 int a_ncookies; 647 } */ *ap; 648 { 649 struct uio *uio = ap->a_uio; 650 struct pfsdent d; 651 struct pfsdent *dp = &d; 652 struct pfsnode *pfs; 653 int error; 654 int count; 655 int i; 656 657 /* 658 * We don't allow exporting procfs mounts, and currently local 659 * requests do not need cookies. 660 */ 661 if (ap->a_ncookies) 662 panic("procfs_readdir: not hungry"); 663 664 pfs = VTOPFS(ap->a_vp); 665 666 if (uio->uio_resid < UIO_MX) 667 return (EINVAL); 668 if (uio->uio_offset & (UIO_MX-1)) 669 return (EINVAL); 670 if (uio->uio_offset < 0) 671 return (EINVAL); 672 673 error = 0; 674 count = 0; 675 i = uio->uio_offset / UIO_MX; 676 677 switch (pfs->pfs_type) { 678 /* 679 * this is for the process-specific sub-directories. 680 * all that is needed to is copy out all the entries 681 * from the procent[] table (top of this file). 682 */ 683 case Pproc: { 684 while (uio->uio_resid >= UIO_MX) { 685 struct pfsnames *dt; 686 687 if (i >= Nprocent) 688 break; 689 690 dt = &procent[i]; 691 692 dp->d_reclen = UIO_MX; 693 dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype); 694 dp->d_type = DT_REG; 695 dp->d_namlen = dt->d_namlen; 696 bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1); 697 error = uiomove((caddr_t) dp, UIO_MX, uio); 698 if (error) 699 break; 700 count += UIO_MX; 701 i++; 702 } 703 704 break; 705 706 } 707 708 /* 709 * this is for the root of the procfs filesystem 710 * what is needed is a special entry for "curproc" 711 * followed by an entry for each process on allproc 712 #ifdef PROCFS_ZOMBIE 713 * and zombproc. 714 #endif 715 */ 716 717 case Proot: { 718 int pcnt; 719 #ifdef PROCFS_ZOMBIE 720 int doingzomb = 0; 721 #endif 722 volatile struct proc *p; 723 724 p = allproc; 725 726 #define PROCFS_XFILES 1 /* number of other entries, like "curproc" */ 727 pcnt = PROCFS_XFILES; 728 729 while (p && uio->uio_resid >= UIO_MX) { 730 bzero((char *) dp, UIO_MX); 731 dp->d_type = DT_DIR; 732 dp->d_reclen = UIO_MX; 733 734 switch (i) { 735 case 0: 736 /* ship out entry for "curproc" */ 737 dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc); 738 dp->d_namlen = sprintf(dp->d_name, "curproc"); 739 break; 740 741 default: 742 if (pcnt >= i) { 743 dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc); 744 dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid); 745 } 746 747 p = p->p_next; 748 749 #ifdef PROCFS_ZOMBIE 750 if (p == 0 && doingzomb == 0) { 751 doingzomb = 1; 752 p = zombproc; 753 } 754 #endif 755 756 if (pcnt++ < i) 757 continue; 758 759 break; 760 } 761 error = uiomove((caddr_t) dp, UIO_MX, uio); 762 if (error) 763 break; 764 count += UIO_MX; 765 i++; 766 } 767 768 break; 769 770 } 771 772 default: 773 error = ENOTDIR; 774 break; 775 } 776 777 uio->uio_offset = i * UIO_MX; 778 779 return (error); 780 } 781 782 /* 783 * convert decimal ascii to pid_t 784 */ 785 static pid_t 786 atopid(b, len) 787 const char *b; 788 u_int len; 789 { 790 pid_t p = 0; 791 792 while (len--) { 793 char c = *b++; 794 if (c < '0' || c > '9') 795 return (NO_PID); 796 p = 10 * p + (c - '0'); 797 if (p > PID_MAX) 798 return (NO_PID); 799 } 800 801 return (p); 802 } 803 804 /* 805 * procfs vnode operations. 806 */ 807 int (**procfs_vnodeop_p)(); 808 struct vnodeopv_entry_desc procfs_vnodeop_entries[] = { 809 { &vop_default_desc, vn_default_error }, 810 { &vop_lookup_desc, procfs_lookup }, /* lookup */ 811 { &vop_create_desc, procfs_create }, /* create */ 812 { &vop_mknod_desc, procfs_mknod }, /* mknod */ 813 { &vop_open_desc, procfs_open }, /* open */ 814 { &vop_close_desc, procfs_close }, /* close */ 815 { &vop_access_desc, procfs_access }, /* access */ 816 { &vop_getattr_desc, procfs_getattr }, /* getattr */ 817 { &vop_setattr_desc, procfs_setattr }, /* setattr */ 818 { &vop_read_desc, procfs_read }, /* read */ 819 { &vop_write_desc, procfs_write }, /* write */ 820 { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */ 821 { &vop_select_desc, procfs_select }, /* select */ 822 { &vop_mmap_desc, procfs_mmap }, /* mmap */ 823 { &vop_fsync_desc, procfs_fsync }, /* fsync */ 824 { &vop_seek_desc, procfs_seek }, /* seek */ 825 { &vop_remove_desc, procfs_remove }, /* remove */ 826 { &vop_link_desc, procfs_link }, /* link */ 827 { &vop_rename_desc, procfs_rename }, /* rename */ 828 { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */ 829 { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */ 830 { &vop_symlink_desc, procfs_symlink }, /* symlink */ 831 { &vop_readdir_desc, procfs_readdir }, /* readdir */ 832 { &vop_readlink_desc, procfs_readlink }, /* readlink */ 833 { &vop_abortop_desc, procfs_abortop }, /* abortop */ 834 { &vop_inactive_desc, procfs_inactive }, /* inactive */ 835 { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */ 836 { &vop_lock_desc, procfs_lock }, /* lock */ 837 { &vop_unlock_desc, procfs_unlock }, /* unlock */ 838 { &vop_bmap_desc, procfs_bmap }, /* bmap */ 839 { &vop_strategy_desc, procfs_strategy }, /* strategy */ 840 { &vop_print_desc, procfs_print }, /* print */ 841 { &vop_islocked_desc, procfs_islocked }, /* islocked */ 842 { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */ 843 { &vop_advlock_desc, procfs_advlock }, /* advlock */ 844 { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */ 845 { &vop_valloc_desc, procfs_valloc }, /* valloc */ 846 { &vop_vfree_desc, procfs_vfree }, /* vfree */ 847 { &vop_truncate_desc, procfs_truncate }, /* truncate */ 848 { &vop_update_desc, procfs_update }, /* update */ 849 { (struct vnodeop_desc*)NULL, (int(*)())NULL } 850 }; 851 struct vnodeopv_desc procfs_vnodeop_opv_desc = 852 { &procfs_vnodeop_p, procfs_vnodeop_entries }; 853