fifo_vnops.c revision 1.74 1 /* $NetBSD: fifo_vnops.c,v 1.74 2014/02/07 15:29:22 hannken 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) 1990, 1993, 1995
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 * @(#)fifo_vnops.c 8.10 (Berkeley) 5/27/95
58 */
59
60 #include <sys/cdefs.h>
61 __KERNEL_RCSID(0, "$NetBSD: fifo_vnops.c,v 1.74 2014/02/07 15:29:22 hannken Exp $");
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/proc.h>
66 #include <sys/time.h>
67 #include <sys/namei.h>
68 #include <sys/vnode.h>
69 #include <sys/socket.h>
70 #include <sys/protosw.h>
71 #include <sys/socketvar.h>
72 #include <sys/stat.h>
73 #include <sys/ioctl.h>
74 #include <sys/file.h>
75 #include <sys/errno.h>
76 #include <sys/kmem.h>
77 #include <sys/un.h>
78 #include <sys/poll.h>
79 #include <sys/event.h>
80 #include <sys/condvar.h>
81
82 #include <miscfs/fifofs/fifo.h>
83 #include <miscfs/genfs/genfs.h>
84
85 /*
86 * This structure is associated with the FIFO vnode and stores
87 * the state associated with the FIFO.
88 */
89 struct fifoinfo {
90 struct socket *fi_readsock;
91 struct socket *fi_writesock;
92 kcondvar_t fi_rcv;
93 int fi_readers;
94 kcondvar_t fi_wcv;
95 int fi_writers;
96 };
97
98 /*
99 * Trivial lookup routine that always fails.
100 */
101 /* ARGSUSED */
102 static int
103 fifo_lookup(void *v)
104 {
105 struct vop_lookup_v2_args /* {
106 struct vnode *a_dvp;
107 struct vnode **a_vpp;
108 struct componentname *a_cnp;
109 } */ *ap = v;
110
111 *ap->a_vpp = NULL;
112 return (ENOTDIR);
113 }
114
115 /*
116 * Open called to set up a new instance of a fifo or
117 * to find an active instance of a fifo.
118 */
119 /* ARGSUSED */
120 static int
121 fifo_open(void *v)
122 {
123 struct vop_open_args /* {
124 struct vnode *a_vp;
125 int a_mode;
126 kauth_cred_t a_cred;
127 } */ *ap = v;
128 struct lwp *l = curlwp;
129 struct vnode *vp;
130 struct fifoinfo *fip;
131 struct socket *rso, *wso;
132 int error;
133
134 vp = ap->a_vp;
135
136 if ((fip = vp->v_fifoinfo) == NULL) {
137 fip = kmem_alloc(sizeof(*fip), KM_SLEEP);
138 vp->v_fifoinfo = fip;
139 error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0, l, NULL);
140 if (error != 0) {
141 kmem_free(fip, sizeof(*fip));
142 vp->v_fifoinfo = NULL;
143 return (error);
144 }
145 fip->fi_readsock = rso;
146 error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0, l, rso);
147 if (error != 0) {
148 (void)soclose(rso);
149 kmem_free(fip, sizeof(*fip));
150 vp->v_fifoinfo = NULL;
151 return (error);
152 }
153 fip->fi_writesock = wso;
154 solock(wso);
155 if ((error = unp_connect2(wso, rso, PRU_CONNECT2)) != 0) {
156 sounlock(wso);
157 (void)soclose(wso);
158 (void)soclose(rso);
159 kmem_free(fip, sizeof(*fip));
160 vp->v_fifoinfo = NULL;
161 return (error);
162 }
163 fip->fi_readers = 0;
164 fip->fi_writers = 0;
165 wso->so_state |= SS_CANTRCVMORE;
166 rso->so_state |= SS_CANTSENDMORE;
167 cv_init(&fip->fi_rcv, "fiford");
168 cv_init(&fip->fi_wcv, "fifowr");
169 } else {
170 wso = fip->fi_writesock;
171 rso = fip->fi_readsock;
172 solock(wso);
173 }
174
175 if (ap->a_mode & FREAD) {
176 if (fip->fi_readers++ == 0) {
177 wso->so_state &= ~SS_CANTSENDMORE;
178 cv_broadcast(&fip->fi_wcv);
179 }
180 }
181 if (ap->a_mode & FWRITE) {
182 if (fip->fi_writers++ == 0) {
183 rso->so_state &= ~SS_CANTRCVMORE;
184 cv_broadcast(&fip->fi_rcv);
185 }
186 }
187 if (ap->a_mode & FREAD) {
188 if (ap->a_mode & O_NONBLOCK) {
189 } else {
190 while (!soreadable(rso) && fip->fi_writers == 0) {
191 VOP_UNLOCK(vp);
192 error = cv_wait_sig(&fip->fi_rcv,
193 wso->so_lock);
194 sounlock(wso);
195 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
196 if (error)
197 goto bad;
198 solock(wso);
199 }
200 }
201 }
202 if (ap->a_mode & FWRITE) {
203 if (ap->a_mode & O_NONBLOCK) {
204 if (fip->fi_readers == 0) {
205 error = ENXIO;
206 sounlock(wso);
207 goto bad;
208 }
209 } else {
210 while (fip->fi_readers == 0) {
211 VOP_UNLOCK(vp);
212 error = cv_wait_sig(&fip->fi_wcv,
213 wso->so_lock);
214 sounlock(wso);
215 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
216 if (error)
217 goto bad;
218 solock(wso);
219 }
220 }
221 }
222 sounlock(wso);
223 return (0);
224 bad:
225 VOP_CLOSE(vp, ap->a_mode, ap->a_cred);
226 return (error);
227 }
228
229 /*
230 * Vnode op for read
231 */
232 /* ARGSUSED */
233 static int
234 fifo_read(void *v)
235 {
236 struct vop_read_args /* {
237 struct vnode *a_vp;
238 struct uio *a_uio;
239 int a_ioflag;
240 kauth_cred_t a_cred;
241 } */ *ap = v;
242 struct uio *uio;
243 struct socket *rso;
244 int error, sflags;
245 size_t startresid;
246
247 uio = ap->a_uio;
248 rso = ap->a_vp->v_fifoinfo->fi_readsock;
249 #ifdef DIAGNOSTIC
250 if (uio->uio_rw != UIO_READ)
251 panic("fifo_read mode");
252 #endif
253 if (uio->uio_resid == 0)
254 return (0);
255 startresid = uio->uio_resid;
256 VOP_UNLOCK(ap->a_vp);
257 sflags = (ap->a_ioflag & IO_NDELAY) ? MSG_NBIO : 0;
258 error = (*rso->so_receive)(rso, NULL, uio, NULL, NULL, &sflags);
259 /*
260 * Clear EOF indication after first such return.
261 */
262 if (error == 0 && uio->uio_resid == startresid)
263 rso->so_state &= ~SS_CANTRCVMORE;
264 if (ap->a_ioflag & IO_NDELAY) {
265 if (error == EWOULDBLOCK &&
266 ap->a_vp->v_fifoinfo->fi_writers == 0)
267 error = 0;
268 }
269 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
270 return (error);
271 }
272
273 /*
274 * Vnode op for write
275 */
276 /* ARGSUSED */
277 static int
278 fifo_write(void *v)
279 {
280 struct vop_write_args /* {
281 struct vnode *a_vp;
282 struct uio *a_uio;
283 int a_ioflag;
284 kauth_cred_t a_cred;
285 } */ *ap = v;
286 struct socket *wso;
287 int error, sflags;
288
289 wso = ap->a_vp->v_fifoinfo->fi_writesock;
290 #ifdef DIAGNOSTIC
291 if (ap->a_uio->uio_rw != UIO_WRITE)
292 panic("fifo_write mode");
293 #endif
294 VOP_UNLOCK(ap->a_vp);
295 sflags = (ap->a_ioflag & IO_NDELAY) ? MSG_NBIO : 0;
296 error = (*wso->so_send)(wso, NULL, ap->a_uio, 0, NULL, sflags, curlwp);
297 vn_lock(ap->a_vp, LK_EXCLUSIVE | LK_RETRY);
298 return (error);
299 }
300
301 /*
302 * Device ioctl operation.
303 */
304 /* ARGSUSED */
305 static int
306 fifo_ioctl(void *v)
307 {
308 struct vop_ioctl_args /* {
309 struct vnode *a_vp;
310 u_long a_command;
311 void *a_data;
312 int a_fflag;
313 kauth_cred_t a_cred;
314 struct lwp *a_l;
315 } */ *ap = v;
316 struct file filetmp;
317 int error;
318
319 if (ap->a_command == FIONBIO)
320 return (0);
321 if (ap->a_fflag & FREAD) {
322 filetmp.f_data = ap->a_vp->v_fifoinfo->fi_readsock;
323 error = soo_ioctl(&filetmp, ap->a_command, ap->a_data);
324 if (error)
325 return (error);
326 }
327 if (ap->a_fflag & FWRITE) {
328 filetmp.f_data = ap->a_vp->v_fifoinfo->fi_writesock;
329 error = soo_ioctl(&filetmp, ap->a_command, ap->a_data);
330 if (error)
331 return (error);
332 }
333 return (0);
334 }
335
336 /* ARGSUSED */
337 static int
338 fifo_poll(void *v)
339 {
340 struct vop_poll_args /* {
341 struct vnode *a_vp;
342 int a_events;
343 struct lwp *a_l;
344 } */ *ap = v;
345 struct socket *so;
346 int revents;
347
348 revents = 0;
349 if (ap->a_events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)) {
350 so = ap->a_vp->v_fifoinfo->fi_readsock;
351 if (so)
352 revents |= sopoll(so, ap->a_events);
353 }
354 if (ap->a_events & (POLLOUT | POLLWRNORM | POLLWRBAND)) {
355 so = ap->a_vp->v_fifoinfo->fi_writesock;
356 if (so)
357 revents |= sopoll(so, ap->a_events);
358 }
359
360 return (revents);
361 }
362
363 static int
364 fifo_inactive(void *v)
365 {
366 struct vop_inactive_args /* {
367 struct vnode *a_vp;
368 struct lwp *a_l;
369 } */ *ap = v;
370
371 VOP_UNLOCK(ap->a_vp);
372 return (0);
373 }
374
375 /*
376 * This is a noop, simply returning what one has been given.
377 */
378 static int
379 fifo_bmap(void *v)
380 {
381 struct vop_bmap_args /* {
382 struct vnode *a_vp;
383 daddr_t a_bn;
384 struct vnode **a_vpp;
385 daddr_t *a_bnp;
386 int *a_runp;
387 } */ *ap = v;
388
389 if (ap->a_vpp != NULL)
390 *ap->a_vpp = ap->a_vp;
391 if (ap->a_bnp != NULL)
392 *ap->a_bnp = ap->a_bn;
393 if (ap->a_runp != NULL)
394 *ap->a_runp = 0;
395 return (0);
396 }
397
398 /*
399 * Device close routine
400 */
401 /* ARGSUSED */
402 static int
403 fifo_close(void *v)
404 {
405 struct vop_close_args /* {
406 struct vnode *a_vp;
407 int a_fflag;
408 kauth_cred_t a_cred;
409 struct lwp *a_l;
410 } */ *ap = v;
411 struct vnode *vp;
412 struct fifoinfo *fip;
413 struct socket *wso, *rso;
414 int isrevoke;
415
416 vp = ap->a_vp;
417 fip = vp->v_fifoinfo;
418 isrevoke = (ap->a_fflag & (FREAD | FWRITE | FNONBLOCK)) == FNONBLOCK;
419 wso = fip->fi_writesock;
420 rso = fip->fi_readsock;
421 solock(wso);
422 if (isrevoke) {
423 if (fip->fi_readers != 0) {
424 fip->fi_readers = 0;
425 socantsendmore(wso);
426 }
427 if (fip->fi_writers != 0) {
428 fip->fi_writers = 0;
429 socantrcvmore(rso);
430 }
431 } else {
432 if ((ap->a_fflag & FREAD) && --fip->fi_readers == 0)
433 socantsendmore(wso);
434 if ((ap->a_fflag & FWRITE) && --fip->fi_writers == 0)
435 socantrcvmore(rso);
436 }
437 if ((fip->fi_readers + fip->fi_writers) == 0) {
438 sounlock(wso);
439 (void) soclose(rso);
440 (void) soclose(wso);
441 cv_destroy(&fip->fi_rcv);
442 cv_destroy(&fip->fi_wcv);
443 kmem_free(fip, sizeof(*fip));
444 vp->v_fifoinfo = NULL;
445 } else
446 sounlock(wso);
447 return (0);
448 }
449
450 /*
451 * Print out internal contents of a fifo vnode.
452 */
453 static void
454 fifo_printinfo(struct vnode *vp)
455 {
456 struct fifoinfo *fip;
457
458 fip = vp->v_fifoinfo;
459 printf(", fifo with %d readers and %d writers",
460 fip->fi_readers, fip->fi_writers);
461 }
462
463 /*
464 * Print out the contents of a fifo vnode.
465 */
466 static int
467 fifo_print(void *v)
468 {
469 struct vop_print_args /* {
470 struct vnode *a_vp;
471 } */ *ap = v;
472
473 /*
474 * We are most likely being called with the vnode belonging
475 * to some file system and this is not printed.
476 */
477 if (ap->a_vp->v_tag == VT_NON)
478 printf("tag VT_NON");
479
480 fifo_printinfo(ap->a_vp);
481 printf("\n");
482 return 0;
483 }
484
485 /*
486 * Return POSIX pathconf information applicable to fifo's.
487 */
488 static int
489 fifo_pathconf(void *v)
490 {
491 struct vop_pathconf_args /* {
492 struct vnode *a_vp;
493 int a_name;
494 register_t *a_retval;
495 } */ *ap = v;
496
497 switch (ap->a_name) {
498 case _PC_LINK_MAX:
499 *ap->a_retval = LINK_MAX;
500 return (0);
501 case _PC_PIPE_BUF:
502 *ap->a_retval = PIPE_BUF;
503 return (0);
504 case _PC_CHOWN_RESTRICTED:
505 *ap->a_retval = 1;
506 return (0);
507 case _PC_SYNC_IO:
508 *ap->a_retval = 1;
509 return (0);
510 default:
511 return (EINVAL);
512 }
513 /* NOTREACHED */
514 }
515
516 static void
517 filt_fifordetach(struct knote *kn)
518 {
519 struct socket *so;
520
521 so = (struct socket *)kn->kn_hook;
522 solock(so);
523 SLIST_REMOVE(&so->so_rcv.sb_sel.sel_klist, kn, knote, kn_selnext);
524 if (SLIST_EMPTY(&so->so_rcv.sb_sel.sel_klist))
525 so->so_rcv.sb_flags &= ~SB_KNOTE;
526 sounlock(so);
527 }
528
529 static int
530 filt_fiforead(struct knote *kn, long hint)
531 {
532 struct socket *so;
533 int rv;
534
535 so = (struct socket *)kn->kn_hook;
536 if (hint != NOTE_SUBMIT)
537 solock(so);
538 kn->kn_data = so->so_rcv.sb_cc;
539 if (so->so_state & SS_CANTRCVMORE) {
540 kn->kn_flags |= EV_EOF;
541 rv = 1;
542 } else {
543 kn->kn_flags &= ~EV_EOF;
544 rv = (kn->kn_data > 0);
545 }
546 if (hint != NOTE_SUBMIT)
547 sounlock(so);
548 return rv;
549 }
550
551 static void
552 filt_fifowdetach(struct knote *kn)
553 {
554 struct socket *so;
555
556 so = (struct socket *)kn->kn_hook;
557 solock(so);
558 SLIST_REMOVE(&so->so_snd.sb_sel.sel_klist, kn, knote, kn_selnext);
559 if (SLIST_EMPTY(&so->so_snd.sb_sel.sel_klist))
560 so->so_snd.sb_flags &= ~SB_KNOTE;
561 sounlock(so);
562 }
563
564 static int
565 filt_fifowrite(struct knote *kn, long hint)
566 {
567 struct socket *so;
568 int rv;
569
570 so = (struct socket *)kn->kn_hook;
571 if (hint != NOTE_SUBMIT)
572 solock(so);
573 kn->kn_data = sbspace(&so->so_snd);
574 if (so->so_state & SS_CANTSENDMORE) {
575 kn->kn_flags |= EV_EOF;
576 rv = 1;
577 } else {
578 kn->kn_flags &= ~EV_EOF;
579 rv = (kn->kn_data >= so->so_snd.sb_lowat);
580 }
581 if (hint != NOTE_SUBMIT)
582 sounlock(so);
583 return rv;
584 }
585
586 static const struct filterops fiforead_filtops =
587 { 1, NULL, filt_fifordetach, filt_fiforead };
588 static const struct filterops fifowrite_filtops =
589 { 1, NULL, filt_fifowdetach, filt_fifowrite };
590
591 /* ARGSUSED */
592 static int
593 fifo_kqfilter(void *v)
594 {
595 struct vop_kqfilter_args /* {
596 struct vnode *a_vp;
597 struct knote *a_kn;
598 } */ *ap = v;
599 struct socket *so;
600 struct sockbuf *sb;
601
602 so = (struct socket *)ap->a_vp->v_fifoinfo->fi_readsock;
603 switch (ap->a_kn->kn_filter) {
604 case EVFILT_READ:
605 ap->a_kn->kn_fop = &fiforead_filtops;
606 sb = &so->so_rcv;
607 break;
608 case EVFILT_WRITE:
609 ap->a_kn->kn_fop = &fifowrite_filtops;
610 sb = &so->so_snd;
611 break;
612 default:
613 return (EINVAL);
614 }
615
616 ap->a_kn->kn_hook = so;
617
618 solock(so);
619 SLIST_INSERT_HEAD(&sb->sb_sel.sel_klist, ap->a_kn, kn_selnext);
620 sb->sb_flags |= SB_KNOTE;
621 sounlock(so);
622
623 return (0);
624 }
625
626 int (**fifo_vnodeop_p)(void *);
627 const struct vnodeopv_entry_desc fifo_vnodeop_entries[] = {
628 { &vop_default_desc, vn_default_error },
629 { &vop_lookup_desc, fifo_lookup }, /* lookup */
630 { &vop_create_desc, genfs_badop }, /* create */
631 { &vop_mknod_desc, genfs_badop }, /* mknod */
632 { &vop_open_desc, fifo_open }, /* open */
633 { &vop_close_desc, fifo_close }, /* close */
634 { &vop_access_desc, genfs_ebadf }, /* access */
635 { &vop_getattr_desc, genfs_ebadf }, /* getattr */
636 { &vop_setattr_desc, genfs_ebadf }, /* setattr */
637 { &vop_read_desc, fifo_read }, /* read */
638 { &vop_write_desc, fifo_write }, /* write */
639 { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
640 { &vop_poll_desc, fifo_poll }, /* poll */
641 { &vop_kqfilter_desc, fifo_kqfilter }, /* kqfilter */
642 { &vop_revoke_desc, genfs_revoke }, /* revoke */
643 { &vop_mmap_desc, genfs_badop }, /* mmap */
644 { &vop_fsync_desc, genfs_nullop }, /* fsync */
645 { &vop_seek_desc, genfs_badop }, /* seek */
646 { &vop_remove_desc, genfs_badop }, /* remove */
647 { &vop_link_desc, genfs_badop }, /* link */
648 { &vop_rename_desc, genfs_badop }, /* rename */
649 { &vop_mkdir_desc, genfs_badop }, /* mkdir */
650 { &vop_rmdir_desc, genfs_badop }, /* rmdir */
651 { &vop_symlink_desc, genfs_badop }, /* symlink */
652 { &vop_readdir_desc, genfs_badop }, /* readdir */
653 { &vop_readlink_desc, genfs_badop }, /* readlink */
654 { &vop_abortop_desc, genfs_badop }, /* abortop */
655 { &vop_inactive_desc, fifo_inactive }, /* inactive */
656 { &vop_reclaim_desc, genfs_nullop }, /* reclaim */
657 { &vop_lock_desc, genfs_lock }, /* lock */
658 { &vop_unlock_desc, genfs_unlock }, /* unlock */
659 { &vop_bmap_desc, fifo_bmap }, /* bmap */
660 { &vop_strategy_desc, genfs_badop }, /* strategy */
661 { &vop_print_desc, fifo_print }, /* print */
662 { &vop_islocked_desc, genfs_islocked }, /* islocked */
663 { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
664 { &vop_advlock_desc, genfs_einval }, /* advlock */
665 { &vop_bwrite_desc, genfs_nullop }, /* bwrite */
666 { &vop_putpages_desc, genfs_null_putpages }, /* putpages */
667 { NULL, NULL }
668 };
669 const struct vnodeopv_desc fifo_vnodeop_opv_desc =
670 { &fifo_vnodeop_p, fifo_vnodeop_entries };
671