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