xref: /freebsd/sys/fs/cd9660/cd9660_vnops.c (revision 3e0f6b97b257a96f7275e4442204263e44b16686)
1 /*-
2  * Copyright (c) 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley
6  * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
7  * Support code is derived from software contributed to Berkeley
8  * by Atsushi Murai (amurai@spec.co.jp).
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. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	@(#)cd9660_vnops.c	8.19 (Berkeley) 5/27/95
39  * $FreeBSD$
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/namei.h>
45 #include <sys/resourcevar.h>
46 #include <sys/kernel.h>
47 #include <sys/file.h>
48 #include <sys/stat.h>
49 #include <sys/buf.h>
50 #include <sys/proc.h>
51 #include <sys/conf.h>
52 #include <sys/mount.h>
53 #include <sys/vnode.h>
54 #include <miscfs/specfs/specdev.h>
55 #include <miscfs/fifofs/fifo.h>
56 #include <sys/malloc.h>
57 #include <sys/dir.h>
58 #include <sys/unistd.h>
59 
60 #include <isofs/cd9660/iso.h>
61 #include <isofs/cd9660/cd9660_node.h>
62 #include <isofs/cd9660/iso_rrip.h>
63 
64 static int cd9660_setattr __P((struct vop_setattr_args *));
65 static int cd9660_open __P((struct vop_open_args *));
66 static int cd9660_close __P((struct vop_close_args *));
67 static int cd9660_access __P((struct vop_access_args *));
68 static int cd9660_getattr __P((struct vop_getattr_args *));
69 static int cd9660_read __P((struct vop_read_args *));
70 static int cd9660_ioctl __P((struct vop_ioctl_args *));
71 static int cd9660_select __P((struct vop_select_args *));
72 static int cd9660_mmap __P((struct vop_mmap_args *));
73 static int cd9660_seek __P((struct vop_seek_args *));
74 struct isoreaddir;
75 static int iso_uiodir __P((struct isoreaddir *idp, struct dirent *dp,
76 			   off_t off));
77 static int iso_shipdir __P((struct isoreaddir *idp));
78 static int cd9660_readdir __P((struct vop_readdir_args *));
79 static int cd9660_readlink __P((struct vop_readlink_args *ap));
80 static int cd9660_abortop __P((struct vop_abortop_args *));
81 static int cd9660_lock __P((struct vop_lock_args *));
82 static int cd9660_unlock __P((struct vop_unlock_args *));
83 static int cd9660_strategy __P((struct vop_strategy_args *));
84 static int cd9660_print __P((struct vop_print_args *));
85 static int cd9660_islocked __P((struct vop_islocked_args *));
86 
87 #if 0
88 /*
89  * Mknod vnode call
90  *  Actually remap the device number
91  */
92 static int
93 cd9660_mknod(ndp, vap, cred, p)
94 	struct nameidata *ndp;
95 	struct ucred *cred;
96 	struct vattr *vap;
97 	struct proc *p;
98 {
99 #ifndef	ISODEVMAP
100 	free(ndp->ni_pnbuf, M_NAMEI);
101 	vput(ndp->ni_dvp);
102 	vput(ndp->ni_vp);
103 	return (EINVAL);
104 #else
105 	register struct vnode *vp;
106 	struct iso_node *ip;
107 	struct iso_dnode *dp;
108 	int error;
109 
110 	vp = ndp->ni_vp;
111 	ip = VTOI(vp);
112 
113 	if (ip->i_mnt->iso_ftype != ISO_FTYPE_RRIP
114 	    || vap->va_type != vp->v_type
115 	    || (vap->va_type != VCHR && vap->va_type != VBLK)) {
116 		free(ndp->ni_pnbuf, M_NAMEI);
117 		vput(ndp->ni_dvp);
118 		vput(ndp->ni_vp);
119 		return (EINVAL);
120 	}
121 
122 	dp = iso_dmap(ip->i_dev,ip->i_number,1);
123 	if (ip->inode.iso_rdev == vap->va_rdev || vap->va_rdev == VNOVAL) {
124 		/* same as the unmapped one, delete the mapping */
125 		remque(dp);
126 		FREE(dp,M_CACHE);
127 	} else
128 		/* enter new mapping */
129 		dp->d_dev = vap->va_rdev;
130 
131 	/*
132 	 * Remove inode so that it will be reloaded by iget and
133 	 * checked to see if it is an alias of an existing entry
134 	 * in the inode cache.
135 	 */
136 	vput(vp);
137 	vp->v_type = VNON;
138 	vgone(vp);
139 	return (0);
140 #endif
141 }
142 #endif
143 
144 /*
145  * Setattr call. Only allowed for block and character special devices.
146  */
147 int
148 cd9660_setattr(ap)
149 	struct vop_setattr_args /* {
150 		struct vnodeop_desc *a_desc;
151 		struct vnode *a_vp;
152 		struct vattr *a_vap;
153 		struct ucred *a_cred;
154 		struct proc *a_p;
155 	} */ *ap;
156 {
157 	struct vnode *vp = ap->a_vp;
158 	struct vattr *vap = ap->a_vap;
159 
160   	if (vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL ||
161 	    vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL ||
162 	    vap->va_mtime.tv_sec != VNOVAL || vap->va_mode != (mode_t)VNOVAL)
163 		return (EROFS);
164 	if (vap->va_size != (u_quad_t)VNOVAL) {
165  		switch (vp->v_type) {
166  		case VDIR:
167  			return (EISDIR);
168 		case VLNK:
169 		case VREG:
170 			return (EROFS);
171  		case VCHR:
172  		case VBLK:
173  		case VSOCK:
174  		case VFIFO:
175 			return (0);
176 		}
177 	}
178 	return (0);
179 }
180 
181 /*
182  * Open called.
183  *
184  * Nothing to do.
185  */
186 /* ARGSUSED */
187 static int
188 cd9660_open(ap)
189 	struct vop_open_args /* {
190 		struct vnode *a_vp;
191 		int  a_mode;
192 		struct ucred *a_cred;
193 		struct proc *a_p;
194 	} */ *ap;
195 {
196 	return (0);
197 }
198 
199 /*
200  * Close called
201  *
202  * Update the times on the inode on writeable file systems.
203  */
204 /* ARGSUSED */
205 static int
206 cd9660_close(ap)
207 	struct vop_close_args /* {
208 		struct vnode *a_vp;
209 		int  a_fflag;
210 		struct ucred *a_cred;
211 		struct proc *a_p;
212 	} */ *ap;
213 {
214 	return (0);
215 }
216 
217 /*
218  * Check mode permission on inode pointer. Mode is READ, WRITE or EXEC.
219  * The mode is shifted to select the owner/group/other fields. The
220  * super user is granted all permissions.
221  */
222 /* ARGSUSED */
223 static int
224 cd9660_access(ap)
225 	struct vop_access_args /* {
226 		struct vnode *a_vp;
227 		int  a_mode;
228 		struct ucred *a_cred;
229 		struct proc *a_p;
230 	} */ *ap;
231 {
232 	struct vnode *vp = ap->a_vp;
233 	struct iso_node *ip = VTOI(vp);
234 	struct ucred *cred = ap->a_cred;
235 	mode_t mask, mode = ap->a_mode;
236 	gid_t *gp;
237 	int i;
238 
239 	/*
240 	 * Disallow write attempts unless the file is a socket,
241 	 * fifo, or a block or character device resident on the
242 	 * file system.
243 	 */
244 	if (mode & VWRITE) {
245 		switch (vp->v_type) {
246 		case VDIR:
247 		case VLNK:
248 		case VREG:
249 			return (EROFS);
250 		}
251 	}
252 
253 	/* User id 0 always gets access. */
254 	if (cred->cr_uid == 0)
255 		return (0);
256 
257 	mask = 0;
258 
259 	/* Otherwise, check the owner. */
260 	if (cred->cr_uid == ip->inode.iso_uid) {
261 		if (mode & VEXEC)
262 			mask |= S_IXUSR;
263 		if (mode & VREAD)
264 			mask |= S_IRUSR;
265 		if (mode & VWRITE)
266 			mask |= S_IWUSR;
267 		return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES);
268 	}
269 
270 	/* Otherwise, check the groups. */
271 	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
272 		if (ip->inode.iso_gid == *gp) {
273 			if (mode & VEXEC)
274 				mask |= S_IXGRP;
275 			if (mode & VREAD)
276 				mask |= S_IRGRP;
277 			if (mode & VWRITE)
278 				mask |= S_IWGRP;
279 			return ((ip->inode.iso_mode & mask) == mask ?
280 			    0 : EACCES);
281 		}
282 
283 	/* Otherwise, check everyone else. */
284 	if (mode & VEXEC)
285 		mask |= S_IXOTH;
286 	if (mode & VREAD)
287 		mask |= S_IROTH;
288 	if (mode & VWRITE)
289 		mask |= S_IWOTH;
290 	return ((ip->inode.iso_mode & mask) == mask ? 0 : EACCES);
291 }
292 
293 static int
294 cd9660_getattr(ap)
295 	struct vop_getattr_args /* {
296 		struct vnode *a_vp;
297 		struct vattr *a_vap;
298 		struct ucred *a_cred;
299 		struct proc *a_p;
300 	} */ *ap;
301 
302 {
303 	struct vnode *vp = ap->a_vp;
304 	register struct vattr *vap = ap->a_vap;
305 	register struct iso_node *ip = VTOI(vp);
306 
307 	vap->va_fsid	= ip->i_dev;
308 	vap->va_fileid	= ip->i_number;
309 
310 	vap->va_mode	= ip->inode.iso_mode;
311 	vap->va_nlink	= ip->inode.iso_links;
312 	vap->va_uid	= ip->inode.iso_uid;
313 	vap->va_gid	= ip->inode.iso_gid;
314 	vap->va_atime	= ip->inode.iso_atime;
315 	vap->va_mtime	= ip->inode.iso_mtime;
316 	vap->va_ctime	= ip->inode.iso_ctime;
317 	vap->va_rdev	= ip->inode.iso_rdev;
318 
319 	vap->va_size	= (u_quad_t) ip->i_size;
320 	if (ip->i_size == 0 && (vap->va_mode & S_IFMT) == S_IFLNK) {
321 		struct vop_readlink_args rdlnk;
322 		struct iovec aiov;
323 		struct uio auio;
324 		char *cp;
325 
326 		MALLOC(cp, char *, MAXPATHLEN, M_TEMP, M_WAITOK);
327 		aiov.iov_base = cp;
328 		aiov.iov_len = MAXPATHLEN;
329 		auio.uio_iov = &aiov;
330 		auio.uio_iovcnt = 1;
331 		auio.uio_offset = 0;
332 		auio.uio_rw = UIO_READ;
333 		auio.uio_segflg = UIO_SYSSPACE;
334 		auio.uio_procp = ap->a_p;
335 		auio.uio_resid = MAXPATHLEN;
336 		rdlnk.a_uio = &auio;
337 		rdlnk.a_vp = ap->a_vp;
338 		rdlnk.a_cred = ap->a_cred;
339 		if (cd9660_readlink(&rdlnk) == 0)
340 			vap->va_size = MAXPATHLEN - auio.uio_resid;
341 		FREE(cp, M_TEMP);
342 	}
343 	vap->va_flags	= 0;
344 	vap->va_gen = 1;
345 	vap->va_blocksize = ip->i_mnt->logical_block_size;
346 	vap->va_bytes	= (u_quad_t) ip->i_size;
347 	vap->va_type	= vp->v_type;
348 	vap->va_filerev	= 0;
349 	return (0);
350 }
351 
352 #if ISO_DEFAULT_BLOCK_SIZE >= PAGE_SIZE
353 #ifdef DEBUG
354 extern int doclusterread;
355 #else
356 #define doclusterread 1
357 #endif
358 #else
359 /* XXX until cluster routines can handle block sizes less than one page */
360 #if defined(__FreeBSD__)
361 #define doclusterread 1
362 #else
363 #define doclusterread 0
364 #endif
365 #endif
366 
367 /*
368  * Vnode op for reading.
369  */
370 static int
371 cd9660_read(ap)
372 	struct vop_read_args /* {
373 		struct vnode *a_vp;
374 		struct uio *a_uio;
375 		int a_ioflag;
376 		struct ucred *a_cred;
377 	} */ *ap;
378 {
379 	struct vnode *vp = ap->a_vp;
380 	register struct uio *uio = ap->a_uio;
381 	register struct iso_node *ip = VTOI(vp);
382 	register struct iso_mnt *imp;
383 	struct buf *bp;
384 	daddr_t lbn, rablock;
385 	off_t diff;
386 	int rasize, error = 0;
387 	long size, n, on;
388 
389 	if (uio->uio_resid == 0)
390 		return (0);
391 	if (uio->uio_offset < 0)
392 		return (EINVAL);
393 	ip->i_flag |= IN_ACCESS;
394 	imp = ip->i_mnt;
395 	do {
396 		lbn = lblkno(imp, uio->uio_offset);
397 		on = blkoff(imp, uio->uio_offset);
398 		n = min((u_int)(imp->logical_block_size - on),
399 			uio->uio_resid);
400 		diff = (off_t)ip->i_size - uio->uio_offset;
401 		if (diff <= 0)
402 			return (0);
403 		if (diff < n)
404 			n = diff;
405 		size = blksize(imp, ip, lbn);
406 		rablock = lbn + 1;
407 		if (doclusterread) {
408 			if (lblktosize(imp, rablock) <= ip->i_size)
409 				error = cluster_read(vp, (off_t)ip->i_size,
410 				         lbn, size, NOCRED, uio->uio_resid,
411 					 (ap->a_ioflag >> 16), &bp);
412 			else
413 				error = bread(vp, lbn, size, NOCRED, &bp);
414 		} else {
415 			if (vp->v_lastr + 1 == lbn &&
416 			    lblktosize(imp, rablock) < ip->i_size) {
417 				rasize = blksize(imp, ip, rablock);
418 				error = breadn(vp, lbn, size, &rablock,
419 					       &rasize, 1, NOCRED, &bp);
420 			} else
421 				error = bread(vp, lbn, size, NOCRED, &bp);
422 		}
423 		vp->v_lastr = lbn;
424 		n = min(n, size - bp->b_resid);
425 		if (error) {
426 			brelse(bp);
427 			return (error);
428 		}
429 
430 		error = uiomove(bp->b_data + on, (int)n, uio);
431 		brelse(bp);
432 	} while (error == 0 && uio->uio_resid > 0 && n != 0);
433 	return (error);
434 }
435 
436 /* ARGSUSED */
437 static int
438 cd9660_ioctl(ap)
439 	struct vop_ioctl_args /* {
440 		struct vnode *a_vp;
441 		u_long a_command;
442 		caddr_t  a_data;
443 		int  a_fflag;
444 		struct ucred *a_cred;
445 		struct proc *a_p;
446 	} */ *ap;
447 {
448 	printf("You did ioctl for isofs !!\n");
449 	return (ENOTTY);
450 }
451 
452 /* ARGSUSED */
453 static int
454 cd9660_select(ap)
455 	struct vop_select_args /* {
456 		struct vnode *a_vp;
457 		int  a_which;
458 		int  a_fflags;
459 		struct ucred *a_cred;
460 		struct proc *a_p;
461 	} */ *ap;
462 {
463 
464 	/*
465 	 * We should really check to see if I/O is possible.
466 	 */
467 	return (1);
468 }
469 
470 /*
471  * Mmap a file
472  *
473  * NB Currently unsupported.
474  */
475 /* ARGSUSED */
476 static int
477 cd9660_mmap(ap)
478 	struct vop_mmap_args /* {
479 		struct vnode *a_vp;
480 		int  a_fflags;
481 		struct ucred *a_cred;
482 		struct proc *a_p;
483 	} */ *ap;
484 {
485 
486 	return (EINVAL);
487 }
488 
489 /*
490  * Seek on a file
491  *
492  * Nothing to do, so just return.
493  */
494 /* ARGSUSED */
495 static int
496 cd9660_seek(ap)
497 	struct vop_seek_args /* {
498 		struct vnode *a_vp;
499 		off_t  a_oldoff;
500 		off_t  a_newoff;
501 		struct ucred *a_cred;
502 	} */ *ap;
503 {
504 
505 	return (0);
506 }
507 
508 /*
509  * Structure for reading directories
510  */
511 struct isoreaddir {
512 	struct dirent saveent;
513 	struct dirent assocent;
514 	struct dirent current;
515 	off_t saveoff;
516 	off_t assocoff;
517 	off_t curroff;
518 	struct uio *uio;
519 	off_t uio_off;
520 	int eofflag;
521 	u_long *cookies;
522 	int ncookies;
523 };
524 
525 int
526 iso_uiodir(idp,dp,off)
527 	struct isoreaddir *idp;
528 	struct dirent *dp;
529 	off_t off;
530 {
531 	int error;
532 
533 	dp->d_name[dp->d_namlen] = 0;
534 	dp->d_reclen = DIRSIZ(dp);
535 
536 	if (idp->uio->uio_resid < dp->d_reclen) {
537 		idp->eofflag = 0;
538 		return (-1);
539 	}
540 
541 	if (idp->cookies) {
542 		if (idp->ncookies <= 0) {
543 			idp->eofflag = 0;
544 			return (-1);
545 		}
546 
547 		*idp->cookies++ = off;
548 		--idp->ncookies;
549 	}
550 
551 	if (error = uiomove((caddr_t) dp,dp->d_reclen,idp->uio))
552 		return (error);
553 	idp->uio_off = off;
554 	return (0);
555 }
556 
557 int
558 iso_shipdir(idp)
559 	struct isoreaddir *idp;
560 {
561 	struct dirent *dp;
562 	int cl, sl, assoc;
563 	int error;
564 	char *cname, *sname;
565 
566 	cl = idp->current.d_namlen;
567 	cname = idp->current.d_name;
568 assoc = (cl > 1) && (*cname == ASSOCCHAR);
569 	if (assoc) {
570 		cl--;
571 		cname++;
572 	}
573 
574 	dp = &idp->saveent;
575 	sname = dp->d_name;
576 	if (!(sl = dp->d_namlen)) {
577 		dp = &idp->assocent;
578 		sname = dp->d_name + 1;
579 		sl = dp->d_namlen - 1;
580 	}
581 	if (sl > 0) {
582 		if (sl != cl
583 		    || bcmp(sname,cname,sl)) {
584 			if (idp->assocent.d_namlen) {
585 				if (error = iso_uiodir(idp,&idp->assocent,idp->assocoff))
586 					return (error);
587 				idp->assocent.d_namlen = 0;
588 			}
589 			if (idp->saveent.d_namlen) {
590 				if (error = iso_uiodir(idp,&idp->saveent,idp->saveoff))
591 					return (error);
592 				idp->saveent.d_namlen = 0;
593 			}
594 		}
595 	}
596 	idp->current.d_reclen = DIRSIZ(&idp->current);
597 	if (assoc) {
598 		idp->assocoff = idp->curroff;
599 		bcopy(&idp->current,&idp->assocent,idp->current.d_reclen);
600 	} else {
601 		idp->saveoff = idp->curroff;
602 		bcopy(&idp->current,&idp->saveent,idp->current.d_reclen);
603 	}
604 	return (0);
605 }
606 
607 /*
608  * Vnode op for readdir
609  */
610 static int
611 cd9660_readdir(ap)
612 	struct vop_readdir_args /* {
613 		struct vnode *a_vp;
614 		struct uio *a_uio;
615 		struct ucred *a_cred;
616 		int *a_eofflag;
617 		int *a_ncookies;
618 		u_long *a_cookies;
619 	} */ *ap;
620 {
621 	register struct uio *uio = ap->a_uio;
622 	struct isoreaddir *idp;
623 	struct vnode *vdp = ap->a_vp;
624 	struct iso_node *dp;
625 	struct iso_mnt *imp;
626 	struct buf *bp = NULL;
627 	struct iso_directory_record *ep;
628 	int entryoffsetinblock;
629 	doff_t endsearch;
630 	u_long bmask;
631 	int error = 0;
632 	int reclen;
633 	u_short namelen;
634 	int ncookies = 0;
635 	u_long *cookies = NULL;
636 
637 	dp = VTOI(vdp);
638 	imp = dp->i_mnt;
639 	bmask = imp->im_bmask;
640 
641 	MALLOC(idp, struct isoreaddir *, sizeof(*idp), M_TEMP, M_WAITOK);
642 	idp->saveent.d_namlen = idp->assocent.d_namlen = 0;
643 	/*
644 	 * XXX
645 	 * Is it worth trying to figure out the type?
646 	 */
647 	idp->saveent.d_type = idp->assocent.d_type = idp->current.d_type =
648 	    DT_UNKNOWN;
649 	idp->uio = uio;
650 	if (ap->a_ncookies == NULL) {
651 		idp->cookies = NULL;
652 	} else {
653 		/*
654 		 * Guess the number of cookies needed.
655 		 */
656 		ncookies = uio->uio_resid / 16;
657 		MALLOC(cookies, u_long *, ncookies * sizeof(u_int), M_TEMP,
658 		    M_WAITOK);
659 		idp->cookies = cookies;
660 		idp->ncookies = ncookies;
661 	}
662 	idp->eofflag = 1;
663 	idp->curroff = uio->uio_offset;
664 
665 	if ((entryoffsetinblock = idp->curroff & bmask) &&
666 	    (error = VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp))) {
667 		FREE(idp, M_TEMP);
668 		return (error);
669 	}
670 	endsearch = dp->i_size;
671 
672 	while (idp->curroff < endsearch) {
673 		/*
674 		 * If offset is on a block boundary,
675 		 * read the next directory block.
676 		 * Release previous if it exists.
677 		 */
678 		if ((idp->curroff & bmask) == 0) {
679 			if (bp != NULL)
680 				brelse(bp);
681 			if (error =
682 			    VOP_BLKATOFF(vdp, (off_t)idp->curroff, NULL, &bp))
683 				break;
684 			entryoffsetinblock = 0;
685 		}
686 		/*
687 		 * Get pointer to next entry.
688 		 */
689 		ep = (struct iso_directory_record *)
690 			((char *)bp->b_data + entryoffsetinblock);
691 
692 		reclen = isonum_711(ep->length);
693 		if (reclen == 0) {
694 			/* skip to next block, if any */
695 			idp->curroff =
696 			    (idp->curroff & ~bmask) + imp->logical_block_size;
697 			continue;
698 		}
699 
700 		if (reclen < ISO_DIRECTORY_RECORD_SIZE) {
701 			error = EINVAL;
702 			/* illegal entry, stop */
703 			break;
704 		}
705 
706 		if (entryoffsetinblock + reclen > imp->logical_block_size) {
707 			error = EINVAL;
708 			/* illegal directory, so stop looking */
709 			break;
710 		}
711 
712 		idp->current.d_namlen = isonum_711(ep->name_len);
713 
714 		if (reclen < ISO_DIRECTORY_RECORD_SIZE + idp->current.d_namlen) {
715 			error = EINVAL;
716 			/* illegal entry, stop */
717 			break;
718 		}
719 
720 		if (isonum_711(ep->flags)&2)
721 			idp->current.d_fileno = isodirino(ep, imp);
722 		else
723 			idp->current.d_fileno = dbtob(bp->b_blkno) +
724 				entryoffsetinblock;
725 
726 		idp->curroff += reclen;
727 
728 		switch (imp->iso_ftype) {
729 		case ISO_FTYPE_RRIP:
730 			cd9660_rrip_getname(ep,idp->current.d_name, &namelen,
731 					   &idp->current.d_fileno,imp);
732 			idp->current.d_namlen = (u_char)namelen;
733 			if (idp->current.d_namlen)
734 				error = iso_uiodir(idp,&idp->current,idp->curroff);
735 			break;
736 		default: /* ISO_FTYPE_DEFAULT || ISO_FTYPE_9660 || ISO_FTYPE_HIGH_SIERRA*/
737 			strcpy(idp->current.d_name,"..");
738 			switch (ep->name[0]) {
739 			case 0:
740 				idp->current.d_namlen = 1;
741 				error = iso_uiodir(idp,&idp->current,idp->curroff);
742 				break;
743 			case 1:
744 				idp->current.d_namlen = 2;
745 				error = iso_uiodir(idp,&idp->current,idp->curroff);
746 				break;
747 			default:
748 				isofntrans(ep->name,idp->current.d_namlen,
749 					   idp->current.d_name, &namelen,
750 					   imp->iso_ftype == ISO_FTYPE_9660,
751 					   isonum_711(ep->flags)&4);
752 				idp->current.d_namlen = (u_char)namelen;
753 				if (imp->iso_ftype == ISO_FTYPE_DEFAULT)
754 					error = iso_shipdir(idp);
755 				else
756 					error = iso_uiodir(idp,&idp->current,idp->curroff);
757 				break;
758 			}
759 		}
760 		if (error)
761 			break;
762 
763 		entryoffsetinblock += reclen;
764 	}
765 
766 	if (!error && imp->iso_ftype == ISO_FTYPE_DEFAULT) {
767 		idp->current.d_namlen = 0;
768 		error = iso_shipdir(idp);
769 	}
770 	if (error < 0)
771 		error = 0;
772 
773 	if (ap->a_ncookies != NULL) {
774 		if (error)
775 			free(cookies, M_TEMP);
776 		else {
777 			/*
778 			 * Work out the number of cookies actually used.
779 			 */
780 			*ap->a_ncookies = ncookies - idp->ncookies;
781 			*ap->a_cookies = cookies;
782 		}
783 	}
784 
785 	if (bp)
786 		brelse (bp);
787 
788 	uio->uio_offset = idp->uio_off;
789 	*ap->a_eofflag = idp->eofflag;
790 
791 	FREE(idp, M_TEMP);
792 
793 	return (error);
794 }
795 
796 /*
797  * Return target name of a symbolic link
798  * Shouldn't we get the parent vnode and read the data from there?
799  * This could eventually result in deadlocks in cd9660_lookup.
800  * But otherwise the block read here is in the block buffer two times.
801  */
802 typedef struct iso_directory_record ISODIR;
803 typedef struct iso_node		    ISONODE;
804 typedef struct iso_mnt		    ISOMNT;
805 static int
806 cd9660_readlink(ap)
807 	struct vop_readlink_args /* {
808 		struct vnode *a_vp;
809 		struct uio *a_uio;
810 		struct ucred *a_cred;
811 	} */ *ap;
812 {
813 	ISONODE	*ip;
814 	ISODIR	*dirp;
815 	ISOMNT	*imp;
816 	struct	buf *bp;
817 	struct	uio *uio;
818 	u_short	symlen;
819 	int	error;
820 	char	*symname;
821 
822 	ip  = VTOI(ap->a_vp);
823 	imp = ip->i_mnt;
824 	uio = ap->a_uio;
825 
826 	if (imp->iso_ftype != ISO_FTYPE_RRIP)
827 		return (EINVAL);
828 
829 	/*
830 	 * Get parents directory record block that this inode included.
831 	 */
832 	error = bread(imp->im_devvp,
833 		      (ip->i_number >> imp->im_bshift) <<
834 		      (imp->im_bshift - DEV_BSHIFT),
835 		      imp->logical_block_size, NOCRED, &bp);
836 	if (error) {
837 		brelse(bp);
838 		return (EINVAL);
839 	}
840 
841 	/*
842 	 * Setup the directory pointer for this inode
843 	 */
844 	dirp = (ISODIR *)(bp->b_data + (ip->i_number & imp->im_bmask));
845 
846 	/*
847 	 * Just make sure, we have a right one....
848 	 *   1: Check not cross boundary on block
849 	 */
850 	if ((ip->i_number & imp->im_bmask) + isonum_711(dirp->length)
851 	    > (unsigned)imp->logical_block_size) {
852 		brelse(bp);
853 		return (EINVAL);
854 	}
855 
856 	/*
857 	 * Now get a buffer
858 	 * Abuse a namei buffer for now.
859 	 */
860 	if (uio->uio_segflg == UIO_SYSSPACE)
861 		symname = uio->uio_iov->iov_base;
862 	else
863 		MALLOC(symname, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
864 
865 	/*
866 	 * Ok, we just gathering a symbolic name in SL record.
867 	 */
868 	if (cd9660_rrip_getsymname(dirp, symname, &symlen, imp) == 0) {
869 		if (uio->uio_segflg != UIO_SYSSPACE)
870 			FREE(symname, M_NAMEI);
871 		brelse(bp);
872 		return (EINVAL);
873 	}
874 	/*
875 	 * Don't forget before you leave from home ;-)
876 	 */
877 	brelse(bp);
878 
879 	/*
880 	 * return with the symbolic name to caller's.
881 	 */
882 	if (uio->uio_segflg != UIO_SYSSPACE) {
883 		error = uiomove(symname, symlen, uio);
884 		FREE(symname, M_NAMEI);
885 		return (error);
886 	}
887 	uio->uio_resid -= symlen;
888 	uio->uio_iov->iov_base += symlen;
889 	uio->uio_iov->iov_len -= symlen;
890 	return (0);
891 }
892 
893 /*
894  * Ufs abort op, called after namei() when a CREATE/DELETE isn't actually
895  * done. If a buffer has been saved in anticipation of a CREATE, delete it.
896  */
897 static int
898 cd9660_abortop(ap)
899 	struct vop_abortop_args /* {
900 		struct vnode *a_dvp;
901 		struct componentname *a_cnp;
902 	} */ *ap;
903 {
904 	if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
905 		FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
906 	return (0);
907 }
908 
909 /*
910  * Lock an inode.
911  */
912 static int
913 cd9660_lock(ap)
914 	struct vop_lock_args /* {
915 		struct vnode *a_vp;
916 		int a_flags;
917 		struct proc *a_p;
918 	} */ *ap;
919 {
920 	struct vnode *vp = ap->a_vp;
921 
922 	return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags, &vp->v_interlock,
923 		ap->a_p));
924 }
925 
926 /*
927  * Unlock an inode.
928  */
929 static int
930 cd9660_unlock(ap)
931 	struct vop_unlock_args /* {
932 		struct vnode *a_vp;
933 		int a_flags;
934 		struct proc *a_p;
935 	} */ *ap;
936 {
937 	struct vnode *vp = ap->a_vp;
938 
939 	return (lockmgr(&VTOI(vp)->i_lock, ap->a_flags | LK_RELEASE,
940 		&vp->v_interlock, ap->a_p));
941 }
942 
943 /*
944  * Calculate the logical to physical mapping if not done already,
945  * then call the device strategy routine.
946  */
947 static int
948 cd9660_strategy(ap)
949 	struct vop_strategy_args /* {
950 		struct buf *a_bp;
951 	} */ *ap;
952 {
953 	register struct buf *bp = ap->a_bp;
954 	register struct vnode *vp = bp->b_vp;
955 	register struct iso_node *ip;
956 	int error;
957 
958 	ip = VTOI(vp);
959 	if (vp->v_type == VBLK || vp->v_type == VCHR)
960 		panic("cd9660_strategy: spec");
961 	if (bp->b_blkno == bp->b_lblkno) {
962 		if ((error =
963 		    VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL))) {
964 			bp->b_error = error;
965 			bp->b_flags |= B_ERROR;
966 			biodone(bp);
967 			return (error);
968 		}
969 		if ((long)bp->b_blkno == -1)
970 			clrbuf(bp);
971 	}
972 	if ((long)bp->b_blkno == -1) {
973 		biodone(bp);
974 		return (0);
975 	}
976 	vp = ip->i_devvp;
977 	bp->b_dev = vp->v_rdev;
978 	VOCALL (vp->v_op, VOFFSET(vop_strategy), ap);
979 	return (0);
980 }
981 
982 /*
983  * Print out the contents of an inode.
984  */
985 static int
986 cd9660_print(ap)
987 	struct vop_print_args /* {
988 		struct vnode *a_vp;
989 	} */ *ap;
990 {
991 
992 	printf("tag VT_ISOFS, isofs vnode\n");
993 	return (0);
994 }
995 
996 /*
997  * Check for a locked inode.
998  */
999 int
1000 cd9660_islocked(ap)
1001 	struct vop_islocked_args /* {
1002 		struct vnode *a_vp;
1003 	} */ *ap;
1004 {
1005 
1006 	return (lockstatus(&VTOI(ap->a_vp)->i_lock));
1007 }
1008 
1009 /*
1010  * Return POSIX pathconf information applicable to cd9660 filesystems.
1011  */
1012 int
1013 cd9660_pathconf(ap)
1014 	struct vop_pathconf_args /* {
1015 		struct vnode *a_vp;
1016 		int a_name;
1017 		register_t *a_retval;
1018 	} */ *ap;
1019 {
1020 
1021 	switch (ap->a_name) {
1022 	case _PC_LINK_MAX:
1023 		*ap->a_retval = 1;
1024 		return (0);
1025 	case _PC_NAME_MAX:
1026 		if (VTOI(ap->a_vp)->i_mnt->iso_ftype == ISO_FTYPE_RRIP)
1027 			*ap->a_retval = NAME_MAX;
1028 		else
1029 			*ap->a_retval = 37;
1030 		return (0);
1031 	case _PC_PATH_MAX:
1032 		*ap->a_retval = PATH_MAX;
1033 		return (0);
1034 	case _PC_PIPE_BUF:
1035 		*ap->a_retval = PIPE_BUF;
1036 		return (0);
1037 	case _PC_CHOWN_RESTRICTED:
1038 		*ap->a_retval = 1;
1039 		return (0);
1040 	case _PC_NO_TRUNC:
1041 		*ap->a_retval = 1;
1042 		return (0);
1043 	default:
1044 		return (EINVAL);
1045 	}
1046 	/* NOTREACHED */
1047 }
1048 
1049 /*
1050  * Global vfs data structures for isofs
1051  */
1052 #define cd9660_create \
1053 	((int (*) __P((struct  vop_create_args *)))eopnotsupp)
1054 #define cd9660_mknod ((int (*) __P((struct  vop_mknod_args *)))eopnotsupp)
1055 #define cd9660_write ((int (*) __P((struct  vop_write_args *)))eopnotsupp)
1056 #ifdef NFS
1057 #define	 cd9660_lease_check lease_check
1058 #else
1059 #define	 cd9660_lease_check ((int (*) __P((struct vop_lease_args *)))nullop)
1060 #endif
1061 #define cd9660_fsync ((int (*) __P((struct  vop_fsync_args *)))nullop)
1062 #define cd9660_remove \
1063 	((int (*) __P((struct  vop_remove_args *)))eopnotsupp)
1064 #define cd9660_link ((int (*) __P((struct  vop_link_args *)))eopnotsupp)
1065 #define cd9660_rename \
1066 	((int (*) __P((struct  vop_rename_args *)))eopnotsupp)
1067 #define cd9660_mkdir ((int (*) __P((struct  vop_mkdir_args *)))eopnotsupp)
1068 #define cd9660_rmdir ((int (*) __P((struct  vop_rmdir_args *)))eopnotsupp)
1069 #define cd9660_symlink \
1070 	((int (*) __P((struct vop_symlink_args *)))eopnotsupp)
1071 #define cd9660_advlock \
1072 	((int (*) __P((struct vop_advlock_args *)))eopnotsupp)
1073 #define cd9660_valloc ((int(*) __P(( \
1074 		struct vnode *pvp, \
1075 		int mode, \
1076 		struct ucred *cred, \
1077 		struct vnode **vpp))) eopnotsupp)
1078 #define cd9660_vfree ((int (*) __P((struct  vop_vfree_args *)))eopnotsupp)
1079 #define cd9660_truncate \
1080 	((int (*) __P((struct  vop_truncate_args *)))eopnotsupp)
1081 #define cd9660_update \
1082 	((int (*) __P((struct  vop_update_args *)))eopnotsupp)
1083 #define cd9660_bwrite \
1084 	((int (*) __P((struct  vop_bwrite_args *)))eopnotsupp)
1085 
1086 /*
1087  * Global vfs data structures for cd9660
1088  */
1089 vop_t **cd9660_vnodeop_p;
1090 struct vnodeopv_entry_desc cd9660_vnodeop_entries[] = {
1091 
1092 	{ &vop_default_desc, (vop_t *)vn_default_error },
1093 	{ &vop_lookup_desc, (vop_t *)cd9660_lookup },	/* lookup */
1094 	{ &vop_create_desc, (vop_t *)cd9660_create },	/* create */
1095 	{ &vop_mknod_desc, (vop_t *)cd9660_mknod },	/* mknod */
1096 	{ &vop_open_desc, (vop_t *)cd9660_open },	/* open */
1097 	{ &vop_close_desc, (vop_t *)cd9660_close },	/* close */
1098 	{ &vop_access_desc, (vop_t *)cd9660_access },	/* access */
1099 	{ &vop_getattr_desc, (vop_t *)cd9660_getattr },	/* getattr */
1100 	{ &vop_setattr_desc, (vop_t *)cd9660_setattr },	/* setattr */
1101 	{ &vop_read_desc, (vop_t *)cd9660_read },	/* read */
1102 	{ &vop_write_desc, (vop_t *)cd9660_write },	/* write */
1103 	{ &vop_lease_desc, (vop_t *)cd9660_lease_check },/* lease */
1104 	{ &vop_ioctl_desc, (vop_t *)cd9660_ioctl },	/* ioctl */
1105 	{ &vop_select_desc, (vop_t *)cd9660_select },	/* select */
1106 	{ &vop_revoke_desc, (vop_t *)cd9660_revoke },	/* revoke */
1107 	{ &vop_mmap_desc, (vop_t *)cd9660_mmap },	/* mmap */
1108 	{ &vop_fsync_desc, (vop_t *)cd9660_fsync },	/* fsync */
1109 	{ &vop_seek_desc, (vop_t *)cd9660_seek },	/* seek */
1110 	{ &vop_remove_desc, (vop_t *)cd9660_remove },	/* remove */
1111 	{ &vop_link_desc, (vop_t *)cd9660_link },	/* link */
1112 	{ &vop_rename_desc, (vop_t *)cd9660_rename },	/* rename */
1113 	{ &vop_mkdir_desc, (vop_t *)cd9660_mkdir },	/* mkdir */
1114 	{ &vop_rmdir_desc, (vop_t *)cd9660_rmdir },	/* rmdir */
1115 	{ &vop_symlink_desc, (vop_t *)cd9660_symlink },	/* symlink */
1116 	{ &vop_readdir_desc, (vop_t *)cd9660_readdir },	/* readdir */
1117 	{ &vop_readlink_desc, (vop_t *)cd9660_readlink },/* readlink */
1118 	{ &vop_abortop_desc, (vop_t *)cd9660_abortop },	/* abortop */
1119 	{ &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */
1120 	{ &vop_reclaim_desc, (vop_t *)cd9660_reclaim },	/* reclaim */
1121 	{ &vop_lock_desc, (vop_t *)cd9660_lock },	/* lock */
1122 	{ &vop_unlock_desc, (vop_t *)cd9660_unlock },	/* unlock */
1123 	{ &vop_bmap_desc, (vop_t *)cd9660_bmap },	/* bmap */
1124 	{ &vop_strategy_desc, (vop_t *)cd9660_strategy },/* strategy */
1125 	{ &vop_print_desc, (vop_t *)cd9660_print },	/* print */
1126 	{ &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */
1127 	{ &vop_pathconf_desc, (vop_t *)cd9660_pathconf },/* pathconf */
1128 	{ &vop_advlock_desc, (vop_t *)cd9660_advlock },	/* advlock */
1129 	{ &vop_blkatoff_desc, (vop_t *)cd9660_blkatoff },/* blkatoff */
1130 	{ &vop_valloc_desc, (vop_t *)cd9660_valloc },	/* valloc */
1131 	{ &vop_vfree_desc, (vop_t *)cd9660_vfree },	/* vfree */
1132 	{ &vop_truncate_desc, (vop_t *)cd9660_truncate },/* truncate */
1133 	{ &vop_update_desc, (vop_t *)cd9660_update },	/* update */
1134 	{ &vop_bwrite_desc, (vop_t *)vn_bwrite },
1135 	{ NULL, NULL }
1136 };
1137 static struct vnodeopv_desc cd9660_vnodeop_opv_desc =
1138 	{ &cd9660_vnodeop_p, cd9660_vnodeop_entries };
1139 VNODEOP_SET(cd9660_vnodeop_opv_desc);
1140 
1141 /*
1142  * Special device vnode ops
1143  */
1144 vop_t **cd9660_specop_p;
1145 struct vnodeopv_entry_desc cd9660_specop_entries[] = {
1146 	{ &vop_default_desc, (vop_t *)vn_default_error },
1147 	{ &vop_lookup_desc, (vop_t *)spec_lookup },	/* lookup */
1148 	{ &vop_create_desc, (vop_t *)spec_create },	/* create */
1149 	{ &vop_mknod_desc, (vop_t *)spec_mknod },	/* mknod */
1150 	{ &vop_open_desc, (vop_t *)spec_open },		/* open */
1151 	{ &vop_close_desc, (vop_t *)spec_close },	/* close */
1152 	{ &vop_access_desc, (vop_t *)cd9660_access },	/* access */
1153 	{ &vop_getattr_desc, (vop_t *)cd9660_getattr },	/* getattr */
1154 	{ &vop_setattr_desc, (vop_t *)cd9660_setattr },	/* setattr */
1155 	{ &vop_read_desc, (vop_t *)spec_read },		/* read */
1156 	{ &vop_write_desc, (vop_t *)spec_write },	/* write */
1157 	{ &vop_lease_desc, (vop_t *)spec_lease_check },	/* lease */
1158 	{ &vop_ioctl_desc, (vop_t *)spec_ioctl },	/* ioctl */
1159 	{ &vop_select_desc, (vop_t *)spec_select },	/* select */
1160 	{ &vop_revoke_desc, (vop_t *)spec_revoke },	/* revoke */
1161 	{ &vop_mmap_desc, (vop_t *)spec_mmap },		/* mmap */
1162 	{ &vop_fsync_desc, (vop_t *)spec_fsync },	/* fsync */
1163 	{ &vop_seek_desc, (vop_t *)spec_seek },		/* seek */
1164 	{ &vop_remove_desc, (vop_t *)spec_remove },	/* remove */
1165 	{ &vop_link_desc, (vop_t *)spec_link },		/* link */
1166 	{ &vop_rename_desc, (vop_t *)spec_rename },	/* rename */
1167 	{ &vop_mkdir_desc, (vop_t *)spec_mkdir },	/* mkdir */
1168 	{ &vop_rmdir_desc, (vop_t *)spec_rmdir },	/* rmdir */
1169 	{ &vop_symlink_desc, (vop_t *)spec_symlink },	/* symlink */
1170 	{ &vop_readdir_desc, (vop_t *)spec_readdir },	/* readdir */
1171 	{ &vop_readlink_desc, (vop_t *)spec_readlink },	/* readlink */
1172 	{ &vop_abortop_desc, (vop_t *)spec_abortop },	/* abortop */
1173 	{ &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */
1174 	{ &vop_reclaim_desc, (vop_t *)cd9660_reclaim },	/* reclaim */
1175 	{ &vop_lock_desc, (vop_t *)cd9660_lock },	/* lock */
1176 	{ &vop_unlock_desc, (vop_t *)cd9660_unlock },	/* unlock */
1177 	{ &vop_bmap_desc, (vop_t *)spec_bmap },		/* bmap */
1178 	{ &vop_strategy_desc, (vop_t *)spec_strategy },	/* strategy */
1179 	{ &vop_print_desc, (vop_t *)cd9660_print },	/* print */
1180 	{ &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */
1181 	{ &vop_pathconf_desc, (vop_t *)spec_pathconf },	/* pathconf */
1182 	{ &vop_advlock_desc, (vop_t *)spec_advlock },	/* advlock */
1183 	{ &vop_blkatoff_desc, (vop_t *)spec_blkatoff },	/* blkatoff */
1184 	{ &vop_valloc_desc, (vop_t *)spec_valloc },	/* valloc */
1185 	{ &vop_vfree_desc, (vop_t *)spec_vfree },	/* vfree */
1186 	{ &vop_truncate_desc, (vop_t *)spec_truncate },	/* truncate */
1187 	{ &vop_update_desc, (vop_t *)cd9660_update },	/* update */
1188 	{ &vop_bwrite_desc, (vop_t *)vn_bwrite },
1189 	{ NULL, NULL }
1190 };
1191 static struct vnodeopv_desc cd9660_specop_opv_desc =
1192 	{ &cd9660_specop_p, cd9660_specop_entries };
1193 VNODEOP_SET(cd9660_specop_opv_desc);
1194 
1195 vop_t **cd9660_fifoop_p;
1196 struct vnodeopv_entry_desc cd9660_fifoop_entries[] = {
1197 	{ &vop_default_desc, (vop_t *)vn_default_error },
1198 	{ &vop_lookup_desc, (vop_t *)fifo_lookup },	/* lookup */
1199 	{ &vop_create_desc, (vop_t *)fifo_create },	/* create */
1200 	{ &vop_mknod_desc, (vop_t *)fifo_mknod },	/* mknod */
1201 	{ &vop_open_desc, (vop_t *)fifo_open },		/* open */
1202 	{ &vop_close_desc, (vop_t *)fifo_close },	/* close */
1203 	{ &vop_access_desc, (vop_t *)cd9660_access },	/* access */
1204 	{ &vop_getattr_desc, (vop_t *)cd9660_getattr },	/* getattr */
1205 	{ &vop_setattr_desc, (vop_t *)cd9660_setattr },	/* setattr */
1206 	{ &vop_read_desc, (vop_t *)fifo_read },		/* read */
1207 	{ &vop_write_desc, (vop_t *)fifo_write },	/* write */
1208 	{ &vop_lease_desc, (vop_t *)fifo_lease_check },	/* lease */
1209 	{ &vop_ioctl_desc, (vop_t *)fifo_ioctl },	/* ioctl */
1210 	{ &vop_select_desc, (vop_t *)fifo_select },	/* select */
1211 	{ &vop_revoke_desc, (vop_t *)fifo_revoke },	/* revoke */
1212 	{ &vop_mmap_desc, (vop_t *)fifo_mmap },		/* mmap */
1213 	{ &vop_fsync_desc, (vop_t *)fifo_fsync },	/* fsync */
1214 	{ &vop_seek_desc, (vop_t *)fifo_seek },		/* seek */
1215 	{ &vop_remove_desc, (vop_t *)fifo_remove },	/* remove */
1216 	{ &vop_link_desc, (vop_t *)fifo_link }	,	/* link */
1217 	{ &vop_rename_desc, (vop_t *)fifo_rename },	/* rename */
1218 	{ &vop_mkdir_desc, (vop_t *)fifo_mkdir },	/* mkdir */
1219 	{ &vop_rmdir_desc, (vop_t *)fifo_rmdir },	/* rmdir */
1220 	{ &vop_symlink_desc, (vop_t *)fifo_symlink },	/* symlink */
1221 	{ &vop_readdir_desc, (vop_t *)fifo_readdir },	/* readdir */
1222 	{ &vop_readlink_desc, (vop_t *)fifo_readlink },	/* readlink */
1223 	{ &vop_abortop_desc, (vop_t *)fifo_abortop },	/* abortop */
1224 	{ &vop_inactive_desc, (vop_t *)cd9660_inactive },/* inactive */
1225 	{ &vop_reclaim_desc, (vop_t *)cd9660_reclaim },	/* reclaim */
1226 	{ &vop_lock_desc, (vop_t *)cd9660_lock },	/* lock */
1227 	{ &vop_unlock_desc, (vop_t *)cd9660_unlock },	/* unlock */
1228 	{ &vop_bmap_desc, (vop_t *)fifo_bmap },		/* bmap */
1229 	{ &vop_strategy_desc, (vop_t *)fifo_strategy },	/* strategy */
1230 	{ &vop_print_desc, (vop_t *)cd9660_print },	/* print */
1231 	{ &vop_islocked_desc, (vop_t *)cd9660_islocked },/* islocked */
1232 	{ &vop_pathconf_desc, (vop_t *)fifo_pathconf },	/* pathconf */
1233 	{ &vop_advlock_desc, (vop_t *)fifo_advlock },	/* advlock */
1234 	{ &vop_blkatoff_desc, (vop_t *)fifo_blkatoff },	/* blkatoff */
1235 	{ &vop_valloc_desc, (vop_t *)fifo_valloc },	/* valloc */
1236 	{ &vop_vfree_desc, (vop_t *)fifo_vfree },	/* vfree */
1237 	{ &vop_truncate_desc, (vop_t *)fifo_truncate },	/* truncate */
1238 	{ &vop_update_desc, (vop_t *)cd9660_update },	/* update */
1239 	{ &vop_bwrite_desc, (vop_t *)vn_bwrite },
1240 	{ NULL, NULL }
1241 };
1242 static struct vnodeopv_desc cd9660_fifoop_opv_desc =
1243 	{ &cd9660_fifoop_p, cd9660_fifoop_entries };
1244 
1245 VNODEOP_SET(cd9660_fifoop_opv_desc);
1246