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