Deleted Added
full compact
1#define DEBUG 1
1/*
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 * Copyright (c) 2000
5 * Poul-Henning Kamp. All rights reserved.
6 *
7 * This code is derived from software donated to Berkeley by
8 * Jan-Simon Pendry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * @(#)kernfs_vnops.c 8.15 (Berkeley) 5/21/95
32 * From: FreeBSD: src/sys/miscfs/kernfs/kernfs_vnops.c 1.43
33 *
35 * $FreeBSD: head/sys/fs/devfs/devfs_vnops.c 76320 2001-05-06 17:40:34Z phk $
34 * $FreeBSD: head/sys/fs/devfs/devfs_vnops.c 76554 2001-05-13 20:52:40Z phk $
35 */
36
37#include <opt_devfs.h>
38#ifndef NODEVFS
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/conf.h>
43#include <sys/dirent.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/malloc.h>
47#include <sys/mount.h>
48#include <sys/namei.h>
49#include <sys/proc.h>
50#include <sys/socket.h>
51#include <sys/time.h>
52#include <sys/vnode.h>
53
54#include <fs/devfs/devfs.h>
55
56static int devfs_access __P((struct vop_access_args *ap));
57static int devfs_getattr __P((struct vop_getattr_args *ap));
58static int devfs_lookupx __P((struct vop_lookup_args *ap));
59static int devfs_print __P((struct vop_print_args *ap));
60static int devfs_read __P((struct vop_read_args *ap));
61static int devfs_readdir __P((struct vop_readdir_args *ap));
62static int devfs_readlink __P((struct vop_readlink_args *ap));
63static int devfs_reclaim __P((struct vop_reclaim_args *ap));
64static int devfs_remove __P((struct vop_remove_args *ap));
65static int devfs_revoke __P((struct vop_revoke_args *ap));
66static int devfs_setattr __P((struct vop_setattr_args *ap));
67static int devfs_symlink __P((struct vop_symlink_args *ap));
68
69int
70devfs_allocv(struct devfs_dirent *de, struct mount *mp, struct vnode **vpp, struct proc *p)
71{
72 int error;
73 struct vnode *vp;
74 dev_t dev;
75
76 if (p == NULL)
77 p = curproc; /* XXX */
78loop:
79 vp = de->de_vnode;
80 if (vp != NULL) {
81 if (vget(vp, LK_EXCLUSIVE, p ? p : curproc))
82 goto loop;
83 *vpp = vp;
84 return (0);
85 }
86 if (de->de_dirent->d_type == DT_CHR) {
87 dev = *devfs_itod(de->de_inode);
88 if (dev == NULL)
89 return (ENOENT);
90 } else {
91 dev = NODEV;
92 }
93 error = getnewvnode(VT_DEVFS, mp, devfs_vnodeop_p, &vp);
94 if (error != 0) {
95 printf("devfs_allocv: failed to allocate new vnode\n");
96 return (error);
97 }
98
99 if (de->de_dirent->d_type == DT_CHR) {
100 vp->v_type = VCHR;
101 vp = addaliasu(vp, dev->si_udev);
102 vp->v_op = devfs_specop_p;
103 } else if (de->de_dirent->d_type == DT_DIR) {
104 vp->v_type = VDIR;
105 } else if (de->de_dirent->d_type == DT_LNK) {
106 vp->v_type = VLNK;
107 } else {
108 vp->v_type = VBAD;
109 }
110 vp->v_data = de;
111 de->de_vnode = vp;
112 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
113 *vpp = vp;
114 return (0);
115}
116
117static int
118devfs_access(ap)
119 struct vop_access_args /* {
120 struct vnode *a_vp;
121 int a_mode;
122 struct ucred *a_cred;
123 struct proc *a_p;
124 } */ *ap;
125{
126 struct vnode *vp = ap->a_vp;
127 struct devfs_dirent *de;
128
129 de = vp->v_data;
130 if (vp->v_type == VDIR)
131 de = de->de_dir;
132
133 return (vaccess(vp->v_type, de->de_mode, de->de_uid, de->de_gid,
134 ap->a_mode, ap->a_cred, NULL));
135}
136
137static int
138devfs_getattr(ap)
139 struct vop_getattr_args /* {
140 struct vnode *a_vp;
141 struct vattr *a_vap;
142 struct ucred *a_cred;
143 struct proc *a_p;
144 } */ *ap;
145{
146 struct vnode *vp = ap->a_vp;
147 struct vattr *vap = ap->a_vap;
148 int error = 0;
149 struct devfs_dirent *de;
150 dev_t dev;
151
152 de = vp->v_data;
153 if (vp->v_type == VDIR)
154 de = de->de_dir;
155 bzero((caddr_t) vap, sizeof(*vap));
156 vattr_null(vap);
157 vap->va_uid = de->de_uid;
158 vap->va_gid = de->de_gid;
159 vap->va_mode = de->de_mode;
160 if (vp->v_type == VLNK)
161 vap->va_size = de->de_dirent->d_namlen;
162 else
163 vap->va_size = 0;
164 vap->va_blocksize = DEV_BSIZE;
165 vap->va_type = vp->v_type;
166
167#define fix(aa) \
168 do { \
169 if ((aa).tv_sec == 0) { \
170 (aa).tv_sec = boottime.tv_sec; \
171 (aa).tv_nsec = boottime.tv_usec * 1000; \
172 } \
173 } while (0)
174
175 if (vp->v_type != VCHR) {
176 fix(de->de_atime);
177 vap->va_atime = de->de_atime;
178 fix(de->de_mtime);
179 vap->va_mtime = de->de_mtime;
180 fix(de->de_ctime);
181 vap->va_ctime = de->de_ctime;
182 } else {
183 dev = vp->v_rdev;
184 fix(dev->si_atime);
185 vap->va_atime = dev->si_atime;
186 fix(dev->si_mtime);
187 vap->va_mtime = dev->si_mtime;
188 fix(dev->si_ctime);
189 vap->va_ctime = dev->si_ctime;
190 vap->va_rdev = dev->si_udev;
191 }
192 vap->va_gen = 0;
193 vap->va_flags = 0;
194 vap->va_bytes = 0;
195 vap->va_nlink = de->de_links;
196 vap->va_fileid = de->de_inode;
197
196#ifdef DEBUG
197 if (error)
198 printf("devfs_getattr: return error %d\n", error);
199#endif
198 return (error);
199}
200
201static int
202devfs_lookupx(ap)
203 struct vop_lookup_args /* {
204 struct vnode * a_dvp;
205 struct vnode ** a_vpp;
206 struct componentname * a_cnp;
207 } */ *ap;
208{
209 struct componentname *cnp;
210 struct vnode *dvp, **vpp;
211 struct proc *p;
212 struct devfs_dirent *de, *dd;
213 struct devfs_mount *dmp;
214 dev_t cdev;
215 int error, cloned, i, flags, nameiop;
216 char specname[SPECNAMELEN + 1], *pname;
217
218 cnp = ap->a_cnp;
219 vpp = ap->a_vpp;
220 dvp = ap->a_dvp;
221 pname = cnp->cn_nameptr;
222 p = cnp->cn_proc;
223 flags = cnp->cn_flags;
224 nameiop = cnp->cn_nameiop;
225 dmp = VFSTODEVFS(dvp->v_mount);
226 cloned = 0;
227 dd = dvp->v_data;
228
229 *vpp = NULLVP;
230
231 if (nameiop == RENAME)
232 return (EOPNOTSUPP);
233
234 if (dvp->v_type != VDIR)
235 return (ENOTDIR);
236
237 if ((flags & ISDOTDOT) && (dvp->v_flag & VROOT))
238 return (EIO);
239
240 error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, cnp->cn_proc);
241 if (error)
242 return (error);
243
244 if (cnp->cn_namelen == 1 && *pname == '.') {
245 if (nameiop != LOOKUP)
246 return (EINVAL);
247 *vpp = dvp;
248 VREF(dvp);
249 return (0);
250 }
251
252 if (flags & ISDOTDOT) {
253 if (nameiop != LOOKUP)
254 return (EINVAL);
255 VOP_UNLOCK(dvp, 0, p);
256 de = TAILQ_FIRST(&dd->de_dlist); /* "." */
257 de = TAILQ_NEXT(de, de_list); /* ".." */
258 de = de->de_dir;
259 error = devfs_allocv(de, dvp->v_mount, vpp, p);
260 if (error) {
261 vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
262 return (error);
263 }
264 if ((flags & LOCKPARENT) && (flags & ISLASTCN))
265 error = vn_lock(dvp, LK_EXCLUSIVE, p);
266 if (error)
267 vput(*vpp);
268 return (error);
269 }
270
271 devfs_populate(dmp);
272 dd = dvp->v_data;
273 TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
274 if (cnp->cn_namelen != de->de_dirent->d_namlen)
275 continue;
276 if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
277 de->de_dirent->d_namlen) != 0)
278 continue;
279 goto found;
280 }
281
282 /*
283 * OK, we didn't have an entry for the name we were asked for
284 * so we try to see if anybody can create it on demand.
285 * We need to construct the full "devname" for this device
286 * relative to "basedir" or the clone functions would not
287 * be able to tell "/dev/foo" from "/dev/bar/foo"
288 */
289 i = SPECNAMELEN;
290 specname[i] = '\0';
291 i -= cnp->cn_namelen;
292 if (i < 0)
293 goto notfound;
294 bcopy(cnp->cn_nameptr, specname + i, cnp->cn_namelen);
295 de = dd;
296 while (de != dmp->dm_basedir) {
297 i--;
298 if (i < 0)
299 goto notfound;
300 specname[i] = '/';
301 i -= de->de_dirent->d_namlen;
302 if (i < 0)
303 goto notfound;
304 bcopy(de->de_dirent->d_name, specname + i,
305 de->de_dirent->d_namlen);
306 de = TAILQ_FIRST(&de->de_dlist); /* "." */
307 de = TAILQ_NEXT(de, de_list); /* ".." */
308 de = de->de_dir;
309 }
310
311#if 0
312 printf("Finished specname: %d \"%s\"\n", i, specname + i);
313#endif
314 cdev = NODEV;
315 EVENTHANDLER_INVOKE(dev_clone, specname + i,
316 strlen(specname + i), &cdev);
317#if 0
318 printf("cloned %s -> %p %s\n", specname + i, cdev,
319 cdev == NODEV ? "NODEV" : cdev->si_name);
320#endif
321 if (cdev == NODEV)
322 goto notfound;
323
324 devfs_populate(dmp);
325 dd = dvp->v_data;
326 TAILQ_FOREACH(de, &dd->de_dlist, de_list) {
327 if (cnp->cn_namelen != de->de_dirent->d_namlen)
328 continue;
329 if (bcmp(cnp->cn_nameptr, de->de_dirent->d_name,
330 de->de_dirent->d_namlen) != 0)
331 continue;
332 goto found;
333 }
334
335notfound:
336
337 if ((nameiop == CREATE || nameiop == RENAME) &&
338 (flags & (LOCKPARENT | WANTPARENT)) && (flags & ISLASTCN)) {
339 cnp->cn_flags |= SAVENAME;
340 if (!(flags & LOCKPARENT))
341 VOP_UNLOCK(dvp, 0, p);
342 return (EJUSTRETURN);
343 }
344 return (ENOENT);
345
346
347found:
348
349 if ((cnp->cn_nameiop == DELETE) && (flags & ISLASTCN)) {
350 error = VOP_ACCESS(dvp, VWRITE, cnp->cn_cred, p);
351 if (error)
352 return (error);
353 if (*vpp == dvp) {
354 VREF(dvp);
355 *vpp = dvp;
356 return (0);
357 }
358 error = devfs_allocv(de, dvp->v_mount, vpp, p);
359 if (error)
360 return (error);
361 if (!(flags & LOCKPARENT))
362 VOP_UNLOCK(dvp, 0, p);
363 return (0);
364 }
365 error = devfs_allocv(de, dvp->v_mount, vpp, p);
366 if (error)
367 return (error);
368 if (!(flags & LOCKPARENT) || !(flags & ISLASTCN))
369 VOP_UNLOCK(dvp, 0, p);
370 return (0);
371}
372
373static int
374devfs_lookup(struct vop_lookup_args *ap)
375{
376 int j;
377 struct devfs_mount *dmp;
378
379 dmp = VFSTODEVFS(ap->a_dvp->v_mount);
380 lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc);
381 j = devfs_lookupx(ap);
382 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
383 return (j);
384}
385
386/* ARGSUSED */
387static int
388devfs_print(ap)
389 struct vop_print_args /* {
390 struct vnode *a_vp;
391 } */ *ap;
392{
393
394 printf("tag VT_DEVFS, devfs vnode\n");
395 return (0);
396}
397
398static int
399devfs_read(ap)
400 struct vop_read_args /* {
401 struct vnode *a_vp;
402 struct uio *a_uio;
403 int a_ioflag;
404 struct ucred *a_cred;
405 } */ *ap;
406{
407
408 if (ap->a_vp->v_type != VDIR)
409 return (EINVAL);
410 return (VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred, NULL, NULL, NULL));
411}
412
413static int
414devfs_readdir(ap)
415 struct vop_readdir_args /* {
416 struct vnode *a_vp;
417 struct uio *a_uio;
418 struct ucred *a_cred;
419 int *a_eofflag;
420 int *a_ncookies;
421 u_long **a_cookies;
422 } */ *ap;
423{
424 int error;
425 struct uio *uio;
426 struct dirent *dp;
427 struct devfs_dirent *dd;
428 struct devfs_dirent *de;
429 struct devfs_mount *dmp;
430 off_t off;
431
432 if (ap->a_vp->v_type != VDIR)
433 return (ENOTDIR);
434
435 if (ap->a_ncookies)
436 return (EOPNOTSUPP);
437
438 uio = ap->a_uio;
439 if (uio->uio_offset < 0)
440 return (EINVAL);
441
442 dmp = VFSTODEVFS(ap->a_vp->v_mount);
443 lockmgr(&dmp->dm_lock, LK_SHARED, 0, curproc);
444 devfs_populate(dmp);
445 error = 0;
446 de = ap->a_vp->v_data;
447 dd = TAILQ_FIRST(&de->de_dlist);
448 off = 0;
449 while (dd != NULL) {
450 if (dd->de_dirent->d_type == DT_DIR)
451 de = dd->de_dir;
452 else
453 de = dd;
454 dp = dd->de_dirent;
455 if (dp->d_reclen > uio->uio_resid)
456 break;
457 dp->d_fileno = de->de_inode;
458 if (off >= uio->uio_offset) {
459 error = uiomove((caddr_t)dp, dp->d_reclen, uio);
460 if (error)
461 break;
462 }
463 off += dp->d_reclen;
464 dd = TAILQ_NEXT(dd, de_list);
465 }
466 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
467 uio->uio_offset = off;
468 return (error);
469}
470
471static int
472devfs_readlink(ap)
473 struct vop_readlink_args /* {
474 struct vnode *a_vp;
475 struct uio *a_uio;
476 struct ucred *a_cead;
477 } */ *ap;
478{
479 int error;
480 struct devfs_dirent *de;
481
482 de = ap->a_vp->v_data;
483 error = uiomove(de->de_symlink, strlen(de->de_symlink) + 1, ap->a_uio);
484 return (error);
485}
486
487static int
488devfs_reclaim(ap)
489 struct vop_reclaim_args /* {
490 struct vnode *a_vp;
491 } */ *ap;
492{
493 struct vnode *vp = ap->a_vp;
494 struct devfs_dirent *de;
495 int i;
496
497 de = vp->v_data;
498 if (de != NULL)
499 de->de_vnode = NULL;
500 if (de != NULL && de->de_flags & DE_ORPHAN) {
501 if (de->de_symlink)
502 FREE(de->de_symlink, M_DEVFS);
503 FREE(de, M_DEVFS);
504 }
505 vp->v_data = NULL;
506 if (vp->v_rdev != NODEV && vp->v_rdev != NULL) {
507 i = vcount(vp);
508 if ((vp->v_rdev->si_flags & SI_CHEAPCLONE) && i == 0)
509 destroy_dev(vp->v_rdev);
510 }
511 return (0);
512}
513
514static int
515devfs_remove(ap)
516 struct vop_remove_args /* {
517 struct vnode *a_dvp;
518 struct vnode *a_vp;
519 struct componentname *a_cnp;
520 } */ *ap;
521{
522 struct vnode *vp = ap->a_vp;
523 struct devfs_dirent *dd;
524 struct devfs_dirent *de, **dep;
525 struct devfs_mount *dmp = VFSTODEVFS(vp->v_mount);
526
527 lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc);
528 dd = ap->a_dvp->v_data;
529 de = vp->v_data;
530 TAILQ_REMOVE(&dd->de_dlist, de, de_list);
531 dep = devfs_itode(dmp, de->de_inode);
532 if (dep != NULL)
533 *dep = DE_DELETED;
534 de->de_flags |= DE_ORPHAN;
535 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
536 return (0);
537}
538
539/*
540 * Revoke is called on a tty when a terminal session ends. The vnode
541 * is orphaned by setting v_op to deadfs so we need to let go of it
542 * as well so that we create a new one next time around.
543 */
544static int
545devfs_revoke(ap)
546 struct vop_revoke_args /* {
547 struct vnode *a_vp;
548 int a_flags;
549 } */ *ap;
550{
551 struct vnode *vp = ap->a_vp;
552 struct devfs_dirent *de;
553
554 de = vp->v_data;
555 de->de_vnode = NULL;
556 vop_revoke(ap);
557 return (0);
558}
559
560static int
561devfs_setattr(ap)
562 struct vop_setattr_args /* {
563 struct vnode *a_vp;
564 struct vattr *a_vap;
565 struct ucred *a_cred;
566 struct proc *a_p;
567 } */ *ap;
568{
569 struct devfs_dirent *de;
570 struct vattr *vap;
571 int c, error;
572 uid_t uid;
573 gid_t gid;
574
575 vap = ap->a_vap;
576 if ((vap->va_type != VNON) ||
577 (vap->va_nlink != VNOVAL) ||
578 (vap->va_fsid != VNOVAL) ||
579 (vap->va_fileid != VNOVAL) ||
580 (vap->va_blocksize != VNOVAL) ||
581 (vap->va_flags != VNOVAL && vap->va_flags != 0) ||
582 (vap->va_rdev != VNOVAL) ||
583 ((int)vap->va_bytes != VNOVAL) ||
584 (vap->va_gen != VNOVAL)) {
585 return (EINVAL);
586 }
587
588 de = ap->a_vp->v_data;
589 if (ap->a_vp->v_type == VDIR)
590 de = de->de_dir;
591
592 error = c = 0;
593 if (vap->va_uid == (uid_t)VNOVAL)
594 uid = de->de_uid;
595 else
596 uid = vap->va_uid;
597 if (vap->va_gid == (gid_t)VNOVAL)
598 gid = de->de_gid;
599 else
600 gid = vap->va_gid;
601 if (uid != de->de_uid || gid != de->de_gid) {
602 if (((ap->a_cred->cr_uid != de->de_uid) || uid != de->de_uid ||
603 (gid != de->de_gid && !groupmember(gid, ap->a_cred))) &&
604 (error = suser(ap->a_p)) != 0)
605 return (error);
606 de->de_uid = uid;
607 de->de_gid = gid;
608 c = 1;
609 }
610 if (vap->va_mode != (mode_t)VNOVAL) {
611 if ((ap->a_cred->cr_uid != de->de_uid) &&
612 (error = suser(ap->a_p)))
613 return (error);
614 de->de_mode = vap->va_mode;
615 c = 1;
616 }
617 if (vap->va_atime.tv_sec != VNOVAL) {
618 if ((ap->a_cred->cr_uid != de->de_uid) &&
619 (error = suser(ap->a_p)))
620 return (error);
621 de->de_atime = vap->va_atime;
622 c = 1;
623 }
624 if (vap->va_mtime.tv_sec != VNOVAL) {
625 if ((ap->a_cred->cr_uid != de->de_uid) &&
626 (error = suser(ap->a_p)))
627 return (error);
628 de->de_mtime = vap->va_mtime;
629 c = 1;
630 }
631
632 if (c)
633 getnanotime(&de->de_ctime);
634 return (0);
635}
636
637static int
638devfs_symlink(ap)
639 struct vop_symlink_args /* {
640 struct vnode *a_dvp;
641 struct vnode **a_vpp;
642 struct componentname *a_cnp;
643 struct vattr *a_vap;
644 char *a_target;
645 } */ *ap;
646{
647 int i, error;
648 struct devfs_dirent *dd;
649 struct devfs_dirent *de;
650 struct devfs_mount *dmp;
651
652 error = suser(ap->a_cnp->cn_proc);
653 if (error)
654 return(error);
655 dmp = VFSTODEVFS(ap->a_dvp->v_mount);
656 dd = ap->a_dvp->v_data;
657 de = devfs_newdirent(ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen);
658 de->de_uid = 0;
659 de->de_gid = 0;
660 de->de_mode = 0755;
661 de->de_inode = dmp->dm_inode++;
662 de->de_dirent->d_type = DT_LNK;
663 i = strlen(ap->a_target) + 1;
664 MALLOC(de->de_symlink, char *, i, M_DEVFS, M_WAITOK);
665 bcopy(ap->a_target, de->de_symlink, i);
666 lockmgr(&dmp->dm_lock, LK_EXCLUSIVE, 0, curproc);
667 TAILQ_INSERT_TAIL(&dd->de_dlist, de, de_list);
668 devfs_allocv(de, ap->a_dvp->v_mount, ap->a_vpp, 0);
669 lockmgr(&dmp->dm_lock, LK_RELEASE, 0, curproc);
670 return (0);
671}
672
673static vop_t **devfs_vnodeop_p;
674static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
675 { &vop_default_desc, (vop_t *) vop_defaultop },
676 { &vop_access_desc, (vop_t *) devfs_access },
677 { &vop_getattr_desc, (vop_t *) devfs_getattr },
678 { &vop_lookup_desc, (vop_t *) devfs_lookup },
679 { &vop_pathconf_desc, (vop_t *) vop_stdpathconf },
680 { &vop_print_desc, (vop_t *) devfs_print },
681 { &vop_read_desc, (vop_t *) devfs_read },
682 { &vop_readdir_desc, (vop_t *) devfs_readdir },
683 { &vop_readlink_desc, (vop_t *) devfs_readlink },
684 { &vop_reclaim_desc, (vop_t *) devfs_reclaim },
685 { &vop_remove_desc, (vop_t *) devfs_remove },
686 { &vop_revoke_desc, (vop_t *) devfs_revoke },
687 { &vop_setattr_desc, (vop_t *) devfs_setattr },
688 { &vop_symlink_desc, (vop_t *) devfs_symlink },
689 { NULL, NULL }
690};
691static struct vnodeopv_desc devfs_vnodeop_opv_desc =
692 { &devfs_vnodeop_p, devfs_vnodeop_entries };
693
694VNODEOP_SET(devfs_vnodeop_opv_desc);
695
696static vop_t **devfs_specop_p;
697static struct vnodeopv_entry_desc devfs_specop_entries[] = {
698 { &vop_default_desc, (vop_t *) spec_vnoperate },
699 { &vop_access_desc, (vop_t *) devfs_access },
700 { &vop_getattr_desc, (vop_t *) devfs_getattr },
701 { &vop_print_desc, (vop_t *) devfs_print },
702 { &vop_reclaim_desc, (vop_t *) devfs_reclaim },
703 { &vop_remove_desc, (vop_t *) devfs_remove },
704 { &vop_revoke_desc, (vop_t *) devfs_revoke },
705 { &vop_setattr_desc, (vop_t *) devfs_setattr },
706 { NULL, NULL }
707};
708static struct vnodeopv_desc devfs_specop_opv_desc =
709 { &devfs_specop_p, devfs_specop_entries };
710
711VNODEOP_SET(devfs_specop_opv_desc);
712#endif