xref: /dragonfly/sys/vfs/isofs/cd9660/cd9660_vfsops.c (revision 8a7bdfea)
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_vfsops.c	8.18 (Berkeley) 5/22/95
39  * $FreeBSD: src/sys/isofs/cd9660/cd9660_vfsops.c,v 1.74.2.7 2002/04/08 09:39:29 bde Exp $
40  * $DragonFly: src/sys/vfs/isofs/cd9660/cd9660_vfsops.c,v 1.45 2008/01/05 14:02:41 swildner Exp $
41  */
42 
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/proc.h>
46 #include <sys/nlookup.h>
47 #include <sys/kernel.h>
48 #include <sys/vnode.h>
49 #include <sys/mount.h>
50 #include <sys/buf.h>
51 #include <sys/cdio.h>
52 #include <sys/conf.h>
53 #include <sys/fcntl.h>
54 #include <sys/malloc.h>
55 #include <sys/stat.h>
56 #include <sys/syslog.h>
57 
58 #include <vm/vm_zone.h>
59 
60 #include "iso.h"
61 #include "iso_rrip.h"
62 #include "cd9660_node.h"
63 #include "cd9660_mount.h"
64 
65 extern struct vop_ops cd9660_vnode_vops;
66 extern struct vop_ops cd9660_spec_vops;
67 extern struct vop_ops cd9660_fifo_vops;
68 
69 MALLOC_DEFINE(M_ISOFSMNT, "ISOFS mount", "ISOFS mount structure");
70 MALLOC_DEFINE(M_ISOFSNODE, "ISOFS node", "ISOFS vnode private part");
71 
72 static int cd9660_mount (struct mount *, char *, caddr_t, struct ucred *);
73 static int cd9660_unmount (struct mount *, int);
74 static int cd9660_root (struct mount *, struct vnode **);
75 static int cd9660_statfs (struct mount *, struct statfs *, struct ucred *);
76 static int cd9660_vget (struct mount *, ino_t, struct vnode **);
77 static int cd9660_fhtovp (struct mount *, struct fid *, struct vnode **);
78 static int cd9660_checkexp (struct mount *, struct sockaddr *,
79 	    int *, struct ucred **);
80 static int cd9660_vptofh (struct vnode *, struct fid *);
81 
82 static struct vfsops cd9660_vfsops = {
83 	.vfs_mount =    	cd9660_mount,
84 	.vfs_unmount =  	cd9660_unmount,
85 	.vfs_root =      	cd9660_root,
86 	.vfs_statfs =   	cd9660_statfs,
87 	.vfs_sync =     	vfs_stdsync,
88 	.vfs_vget =     	cd9660_vget,
89 	.vfs_fhtovp =   	cd9660_fhtovp,
90 	.vfs_checkexp =  	cd9660_checkexp,
91 	.vfs_vptofh =   	cd9660_vptofh,
92 	.vfs_init =     	cd9660_init,
93 	.vfs_uninit =    	cd9660_uninit,
94 };
95 VFS_SET(cd9660_vfsops, cd9660, VFCF_READONLY);
96 MODULE_VERSION(cd9660, 1);
97 
98 
99 /*
100  * Called by vfs_mountroot when iso is going to be mounted as root.
101  */
102 
103 static int iso_get_ssector (cdev_t dev);
104 static int iso_mountfs (struct vnode *devvp, struct mount *mp,
105 			    struct iso_args *argp);
106 
107 /*
108  * Try to find the start of the last data track on this CD-ROM.  This
109  * is used to mount the last session of a multi-session CD.  Bail out
110  * and return 0 if we fail, this is always a safe bet.
111  */
112 static int
113 iso_get_ssector(cdev_t dev)
114 {
115 	struct ioc_toc_header h;
116 	struct ioc_read_toc_single_entry t;
117 	int i;
118 
119 	if (dev_dioctl(dev, CDIOREADTOCHEADER, (caddr_t)&h, FREAD, proc0.p_ucred) != 0)
120 		return 0;
121 
122 	for (i = h.ending_track; i >= 0; i--) {
123 		t.address_format = CD_LBA_FORMAT;
124 		t.track = i;
125 		if (dev_dioctl(dev, CDIOREADTOCENTRY, (caddr_t)&t, FREAD, proc0.p_ucred) != 0) {
126 			return 0;
127 		}
128 		if ((t.entry.control & 4) != 0)
129 			/* found a data track */
130 			break;
131 	}
132 
133 	if (i < 0)
134 		return 0;
135 
136 	return ntohl(t.entry.addr.lba);
137 }
138 
139 static int
140 iso_mountroot(struct mount *mp)
141 {
142 	struct iso_args args;
143 	struct vnode *rootvp;
144 	int error;
145 
146 	if ((error = bdevvp(rootdev, &rootvp))) {
147 		kprintf("iso_mountroot: can't find rootvp\n");
148 		return (error);
149 	}
150 	args.flags = ISOFSMNT_ROOT;
151 
152 	vn_lock(rootvp, LK_EXCLUSIVE | LK_RETRY);
153 	error = VOP_OPEN(rootvp, FREAD, FSCRED, NULL);
154 	vn_unlock(rootvp);
155 	if (error)
156 		return (error);
157 
158 	args.ssector = iso_get_ssector(rootdev);
159 
160 	VOP_CLOSE(rootvp, FREAD);
161 
162 	if (bootverbose)
163 		kprintf("iso_mountroot(): using session at block %d\n",
164 		       args.ssector);
165 	if ((error = iso_mountfs(rootvp, mp, &args)) != 0)
166 		return (error);
167 
168 	cd9660_statfs(mp, &mp->mnt_stat, proc0.p_ucred);
169 	return (0);
170 }
171 
172 /*
173  * VFS Operations.
174  *
175  * mount system call
176  */
177 static int
178 cd9660_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred)
179 {
180 	struct vnode *devvp;
181 	struct iso_args args;
182 	size_t size;
183 	int error;
184 	mode_t accessmode;
185 	struct iso_mnt *imp = 0;
186 	struct nlookupdata nd;
187 
188 	if ((mp->mnt_flag & (MNT_ROOTFS|MNT_UPDATE)) == MNT_ROOTFS) {
189 		return (iso_mountroot(mp));
190 	}
191 	if ((error = copyin(data, (caddr_t)&args, sizeof (struct iso_args))))
192 		return (error);
193 
194 	if ((mp->mnt_flag & MNT_RDONLY) == 0)
195 		return (EROFS);
196 
197 	/*
198 	 * If updating, check whether changing from read-only to
199 	 * read/write; if there is no device name, that's all we do.
200 	 */
201 	if (mp->mnt_flag & MNT_UPDATE) {
202 		imp = VFSTOISOFS(mp);
203 		if (args.fspec == 0)
204 			return (vfs_export(mp, &imp->im_export, &args.export));
205 	}
206 	/*
207 	 * Not an update, or updating the name: look up the name
208 	 * and verify that it refers to a sensible block device.
209 	 */
210 	devvp = NULL;
211 	error = nlookup_init(&nd, args.fspec, UIO_USERSPACE, NLC_FOLLOW);
212 	if (error == 0)
213 		error = nlookup(&nd);
214 	if (error == 0)
215 		error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
216 	nlookup_done(&nd);
217 	if (error)
218 		return (error);
219 
220 	if (!vn_isdisk(devvp, &error)) {
221 		vrele(devvp);
222 		return (error);
223 	}
224 
225 	/*
226 	 * Verify that user has necessary permissions on the device,
227 	 * or has superuser abilities
228 	 */
229 	accessmode = VREAD;
230 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
231 	error = VOP_ACCESS(devvp, accessmode, cred);
232 	if (error)
233 		error = suser_cred(cred, 0);
234 	if (error) {
235 		vput(devvp);
236 		return (error);
237 	}
238 	vn_unlock(devvp);
239 
240 	if ((mp->mnt_flag & MNT_UPDATE) == 0) {
241 		error = iso_mountfs(devvp, mp, &args);
242 	} else {
243 		if (devvp != imp->im_devvp)
244 			error = EINVAL;	/* needs translation */
245 		else
246 			vrele(devvp);
247 	}
248 	if (error) {
249 		vrele(devvp);
250 		return error;
251 	}
252 	imp = VFSTOISOFS(mp);
253 	copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
254 	    &size);
255 	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
256 	cd9660_statfs(mp, &mp->mnt_stat, cred);
257 	return 0;
258 }
259 
260 /*
261  * Common code for mount and mountroot
262  */
263 static int
264 iso_mountfs(struct vnode *devvp, struct mount *mp, struct iso_args *argp)
265 {
266 	struct iso_mnt *isomp = NULL;
267 	struct buf *bp = NULL;
268 	struct buf *pribp = NULL, *supbp = NULL;
269 	cdev_t dev;
270 	int error = EINVAL;
271 	int needclose = 0;
272 	int high_sierra = 0;
273 	int iso_bsize;
274 	int iso_blknum;
275 	int joliet_level;
276 	struct iso_volume_descriptor *vdp = 0;
277 	struct iso_primary_descriptor *pri = NULL;
278 	struct iso_sierra_primary_descriptor *pri_sierra = NULL;
279 	struct iso_supplementary_descriptor *sup = NULL;
280 	struct iso_directory_record *rootp;
281 	int logical_block_size;
282 
283 	if (!(mp->mnt_flag & MNT_RDONLY))
284 		return EROFS;
285 
286 	/*
287 	 * Disallow multiple mounts of the same device.
288 	 * Disallow mounting of a device that is currently in use
289 	 * Flush out any old buffers remaining from a previous use.
290 	 */
291 	if ((error = vfs_mountedon(devvp)))
292 		return error;
293 	if (count_udev(devvp->v_umajor, devvp->v_uminor) > 0)
294 		return EBUSY;
295 	if ((error = vinvalbuf(devvp, V_SAVE, 0, 0)))
296 		return (error);
297 
298 	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
299 	error = VOP_OPEN(devvp, FREAD, FSCRED, NULL);
300 	vn_unlock(devvp);
301 	if (error)
302 		return error;
303 	dev = devvp->v_rdev;
304 	if (dev->si_iosize_max != 0)
305 		mp->mnt_iosize_max = dev->si_iosize_max;
306 	if (mp->mnt_iosize_max > MAXPHYS)
307 		mp->mnt_iosize_max = MAXPHYS;
308 
309 	needclose = 1;
310 
311 	/* This is the "logical sector size".  The standard says this
312 	 * should be 2048 or the physical sector size on the device,
313 	 * whichever is greater.  For now, we'll just use a constant.
314 	 */
315 	iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
316 
317 	joliet_level = 0;
318 	for (iso_blknum = 16 + argp->ssector;
319 	     iso_blknum < 100 + argp->ssector;
320 	     iso_blknum++) {
321 		if ((error = bread(devvp, (off_t)iso_blknum * iso_bsize,
322 				  iso_bsize, &bp)) != 0)
323 			goto out;
324 
325 		vdp = (struct iso_volume_descriptor *)bp->b_data;
326 		if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) {
327 			if (bcmp (vdp->id_sierra, ISO_SIERRA_ID,
328 				  sizeof vdp->id) != 0) {
329 				error = EINVAL;
330 				goto out;
331 			} else
332 				high_sierra = 1;
333 		}
334 		switch (isonum_711 (high_sierra? vdp->type_sierra: vdp->type)){
335 		case ISO_VD_PRIMARY:
336 			if (pribp == NULL) {
337 				pribp = bp;
338 				bp = NULL;
339 				pri = (struct iso_primary_descriptor *)vdp;
340 				pri_sierra =
341 				  (struct iso_sierra_primary_descriptor *)vdp;
342 			}
343 			break;
344 
345 		case ISO_VD_SUPPLEMENTARY:
346 			if (supbp == NULL) {
347 				supbp = bp;
348 				bp = NULL;
349 				sup = (struct iso_supplementary_descriptor *)vdp;
350 
351 				if (!(argp->flags & ISOFSMNT_NOJOLIET)) {
352 					if (bcmp(sup->escape, "%/@", 3) == 0)
353 						joliet_level = 1;
354 					if (bcmp(sup->escape, "%/C", 3) == 0)
355 						joliet_level = 2;
356 					if (bcmp(sup->escape, "%/E", 3) == 0)
357 						joliet_level = 3;
358 
359 					if ((isonum_711 (sup->flags) & 1) &&
360 					    (argp->flags & ISOFSMNT_BROKENJOLIET) == 0)
361 						joliet_level = 0;
362 				}
363 			}
364 			break;
365 
366 		case ISO_VD_END:
367 			goto vd_end;
368 
369 		default:
370 			break;
371 		}
372 		if (bp) {
373 			brelse(bp);
374 			bp = NULL;
375 		}
376 	}
377  vd_end:
378 	if (bp) {
379 		brelse(bp);
380 		bp = NULL;
381 	}
382 
383 	if (pri == NULL) {
384 		error = EINVAL;
385 		goto out;
386 	}
387 
388 	logical_block_size =
389 		isonum_723 (high_sierra?
390 			    pri_sierra->logical_block_size:
391 			    pri->logical_block_size);
392 
393 	if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
394 	    || (logical_block_size & (logical_block_size - 1)) != 0) {
395 		error = EINVAL;
396 		goto out;
397 	}
398 
399 	rootp = (struct iso_directory_record *)
400 		(high_sierra?
401 		 pri_sierra->root_directory_record:
402 		 pri->root_directory_record);
403 
404 	isomp = kmalloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK | M_ZERO);
405 	isomp->logical_block_size = logical_block_size;
406 	isomp->volume_space_size =
407 		isonum_733 (high_sierra?
408 			    pri_sierra->volume_space_size:
409 			    pri->volume_space_size);
410 	isomp->joliet_level = 0;
411 	/*
412 	 * Since an ISO9660 multi-session CD can also access previous
413 	 * sessions, we have to include them into the space consider-
414 	 * ations.  This doesn't yield a very accurate number since
415 	 * parts of the old sessions might be inaccessible now, but we
416 	 * can't do much better.  This is also important for the NFS
417 	 * filehandle validation.
418 	 */
419 	isomp->volume_space_size += argp->ssector;
420 	bcopy (rootp, isomp->root, sizeof isomp->root);
421 	isomp->root_extent = isonum_733 (rootp->extent);
422 	isomp->root_size = isonum_733 (rootp->size);
423 
424 	isomp->im_bmask = logical_block_size - 1;
425 	isomp->im_bshift = ffs(logical_block_size) - 1;
426 
427 	pribp->b_flags |= B_AGE;
428 	brelse(pribp);
429 	pribp = NULL;
430 
431 	mp->mnt_data = (qaddr_t)isomp;
432 	mp->mnt_stat.f_fsid.val[0] = dev2udev(dev);
433 	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
434 	mp->mnt_maxsymlinklen = 0;
435 	mp->mnt_flag |= MNT_LOCAL;
436 	isomp->im_mountp = mp;
437 	isomp->im_dev = dev;
438 	isomp->im_devvp = devvp;
439 
440 	dev->si_mountpoint = mp;
441 
442 	/* Check the Rock Ridge Extention support */
443 	if (!(argp->flags & ISOFSMNT_NORRIP)) {
444 		if ((error = bread(isomp->im_devvp,
445 				  lblktooff(isomp, isomp->root_extent + isonum_711(rootp->ext_attr_length)),
446 				  isomp->logical_block_size, &bp)) != 0)
447 		    goto out;
448 
449 		rootp = (struct iso_directory_record *)bp->b_data;
450 
451 		if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) {
452 		    argp->flags	 |= ISOFSMNT_NORRIP;
453 		} else {
454 		    argp->flags	 &= ~ISOFSMNT_GENS;
455 		}
456 
457 		/*
458 		 * The contents are valid,
459 		 * but they will get reread as part of another vnode, so...
460 		 */
461 		bp->b_flags |= B_AGE;
462 		brelse(bp);
463 		bp = NULL;
464 	}
465 	isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS |
466 					 ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET);
467 
468 	if (high_sierra) {
469 		/* this effectively ignores all the mount flags */
470 		log(LOG_INFO, "cd9660: High Sierra Format\n");
471 		isomp->iso_ftype = ISO_FTYPE_HIGH_SIERRA;
472 	} else {
473 		switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) {
474 		  default:
475 			  isomp->iso_ftype = ISO_FTYPE_DEFAULT;
476 			  break;
477 		  case ISOFSMNT_GENS|ISOFSMNT_NORRIP:
478 			  isomp->iso_ftype = ISO_FTYPE_9660;
479 			  break;
480 		  case 0:
481 			  log(LOG_INFO, "cd9660: RockRidge Extension\n");
482 			  isomp->iso_ftype = ISO_FTYPE_RRIP;
483 			  break;
484 		}
485 	}
486 
487 	/* Decide whether to use the Joliet descriptor */
488 
489 	if (isomp->iso_ftype != ISO_FTYPE_RRIP && joliet_level) {
490 		log(LOG_INFO, "cd9660: Joliet Extension (Level %d)\n", joliet_level);
491 		rootp = (struct iso_directory_record *)
492 			sup->root_directory_record;
493 		bcopy (rootp, isomp->root, sizeof isomp->root);
494 		isomp->root_extent = isonum_733 (rootp->extent);
495 		isomp->root_size = isonum_733 (rootp->size);
496 		isomp->joliet_level = joliet_level;
497 		supbp->b_flags |= B_AGE;
498 	}
499 
500 	if (supbp) {
501 		brelse(supbp);
502 		supbp = NULL;
503 	}
504 
505 	vfs_add_vnodeops(mp, &cd9660_vnode_vops, &mp->mnt_vn_norm_ops);
506 	vfs_add_vnodeops(mp, &cd9660_spec_vops, &mp->mnt_vn_spec_ops);
507 	vfs_add_vnodeops(mp, &cd9660_fifo_vops, &mp->mnt_vn_fifo_ops);
508 
509 	return 0;
510 out:
511 	dev->si_mountpoint = NULL;
512 	if (bp)
513 		brelse(bp);
514 	if (pribp)
515 		brelse(pribp);
516 	if (supbp)
517 		brelse(supbp);
518 	if (needclose)
519 		VOP_CLOSE(devvp, FREAD);
520 	if (isomp) {
521 		kfree((caddr_t)isomp, M_ISOFSMNT);
522 		mp->mnt_data = (qaddr_t)0;
523 	}
524 	return error;
525 }
526 
527 /*
528  * unmount system call
529  */
530 static int
531 cd9660_unmount(struct mount *mp, int mntflags)
532 {
533 	struct iso_mnt *isomp;
534 	int error, flags = 0;
535 
536 	if (mntflags & MNT_FORCE)
537 		flags |= FORCECLOSE;
538 #if 0
539 	mntflushbuf(mp, 0);
540 	if (mntinvalbuf(mp))
541 		return EBUSY;
542 #endif
543 	if ((error = vflush(mp, 0, flags)))
544 		return (error);
545 
546 	isomp = VFSTOISOFS(mp);
547 
548 	isomp->im_devvp->v_rdev->si_mountpoint = NULL;
549 	error = VOP_CLOSE(isomp->im_devvp, FREAD);
550 	vrele(isomp->im_devvp);
551 	kfree((caddr_t)isomp, M_ISOFSMNT);
552 	mp->mnt_data = (qaddr_t)0;
553 	mp->mnt_flag &= ~MNT_LOCAL;
554 	return (error);
555 }
556 
557 /*
558  * Return root of a filesystem
559  */
560 static int
561 cd9660_root(struct mount *mp, struct vnode **vpp)
562 {
563 	struct iso_mnt *imp = VFSTOISOFS(mp);
564 	struct iso_directory_record *dp =
565 	    (struct iso_directory_record *)imp->root;
566 	ino_t ino = isodirino(dp, imp);
567 
568 	/*
569 	 * With RRIP we must use the `.' entry of the root directory.
570 	 * Simply tell vget, that it's a relocated directory.
571 	 */
572 	return (cd9660_vget_internal(mp, ino, vpp,
573 	    imp->iso_ftype == ISO_FTYPE_RRIP, dp));
574 }
575 
576 /*
577  * Get file system statistics.
578  */
579 int
580 cd9660_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred)
581 {
582 	struct iso_mnt *isomp;
583 
584 	isomp = VFSTOISOFS(mp);
585 
586 	sbp->f_bsize = isomp->logical_block_size;
587 	sbp->f_iosize = sbp->f_bsize;	/* XXX */
588 	sbp->f_blocks = isomp->volume_space_size;
589 	sbp->f_bfree = 0; /* total free blocks */
590 	sbp->f_bavail = 0; /* blocks free for non superuser */
591 	sbp->f_files =	0; /* total files */
592 	sbp->f_ffree = 0; /* free file nodes */
593 	if (sbp != &mp->mnt_stat) {
594 		sbp->f_type = mp->mnt_vfc->vfc_typenum;
595 		bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
596 	}
597 	return 0;
598 }
599 
600 /*
601  * File handle to vnode
602  *
603  * Have to be really careful about stale file handles:
604  * - check that the inode number is in range
605  * - call iget() to get the locked inode
606  * - check for an unallocated inode (i_mode == 0)
607  * - check that the generation number matches
608  */
609 
610 struct ifid {
611 	ushort	ifid_len;
612 	ushort	ifid_pad;
613 	int	ifid_ino;
614 	long	ifid_start;
615 };
616 
617 /* ARGSUSED */
618 int
619 cd9660_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
620 {
621 	struct ifid *ifhp = (struct ifid *)fhp;
622 	struct iso_node *ip;
623 	struct vnode *nvp;
624 	int error;
625 
626 #ifdef	ISOFS_DBG
627 	kprintf("fhtovp: ino %d, start %ld\n",
628 	       ifhp->ifid_ino, ifhp->ifid_start);
629 #endif
630 
631 	if ((error = VFS_VGET(mp, ifhp->ifid_ino, &nvp)) != 0) {
632 		*vpp = NULLVP;
633 		return (error);
634 	}
635 	ip = VTOI(nvp);
636 	if (ip->inode.iso_mode == 0) {
637 		vput(nvp);
638 		*vpp = NULLVP;
639 		return (ESTALE);
640 	}
641 	*vpp = nvp;
642 	return (0);
643 }
644 
645 int
646 cd9660_checkexp(struct mount *mp, struct sockaddr *nam, int *exflagsp,
647 		struct ucred **credanonp)
648 {
649 	struct netcred *np;
650 	struct iso_mnt *imp;
651 
652 	imp = VFSTOISOFS(mp);
653 
654 	/*
655 	 * Get the export permission structure for this <mp, client> tuple.
656 	 */
657 	np = vfs_export_lookup(mp, &imp->im_export, nam);
658 	if (np == NULL)
659 		return (EACCES);
660 
661 	*exflagsp = np->netc_exflags;
662 	*credanonp = &np->netc_anon;
663 	return (0);
664 }
665 
666 int
667 cd9660_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
668 {
669 
670 	/*
671 	 * XXXX
672 	 * It would be nice if we didn't always set the `relocated' flag
673 	 * and force the extra read, but I don't want to think about fixing
674 	 * that right now.
675 	 */
676 	return (cd9660_vget_internal(mp, ino, vpp,
677 #if 0
678 	    VFSTOISOFS(mp)->iso_ftype == ISO_FTYPE_RRIP,
679 #else
680 	    0,
681 #endif
682 	    (struct iso_directory_record *)0));
683 }
684 
685 int
686 cd9660_vget_internal(struct mount *mp, ino_t ino, struct vnode **vpp,
687 		     int relocated, struct iso_directory_record *isodir)
688 {
689 	struct iso_mnt *imp;
690 	struct iso_node *ip;
691 	struct buf *bp;
692 	struct vnode *vp;
693 	cdev_t dev;
694 	int error;
695 
696 	imp = VFSTOISOFS(mp);
697 	dev = imp->im_dev;
698 again:
699 	if ((*vpp = cd9660_ihashget(dev, ino)) != NULLVP)
700 		return (0);
701 
702 	/* Allocate a new vnode/iso_node. */
703 	error = getnewvnode(VT_ISOFS, mp, &vp, 0, 0);
704 	if (error) {
705 		*vpp = NULLVP;
706 		return (error);
707 	}
708 	MALLOC(ip, struct iso_node *, sizeof(struct iso_node), M_ISOFSNODE,
709 	    M_WAITOK | M_ZERO);
710 	ip->i_vnode = vp;
711 	ip->i_dev = dev;
712 	ip->i_number = ino;
713 
714 	/*
715 	 * Insert it into the inode hash table and check for a collision.
716 	 * If a collision occurs, throw away the vnode and try again.
717 	 */
718 	if (cd9660_ihashins(ip) != 0) {
719 		kprintf("debug: cd9660 ihashins collision, retrying\n");
720 		vx_put(vp);
721 		kfree(ip, M_ISOFSNODE);
722 		goto again;
723 	}
724 	vp->v_data = ip;
725 
726 	if (isodir == NULL) {
727 		int lbn, off;
728 
729 		lbn = lblkno(imp, ino);
730 		if (lbn >= imp->volume_space_size) {
731 			vx_put(vp);
732 			kprintf("fhtovp: lbn exceed volume space %d\n", lbn);
733 			return (ESTALE);
734 		}
735 
736 		off = blkoff(imp, ino);
737 		if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
738 			vx_put(vp);
739 			kprintf("fhtovp: crosses block boundary %d\n",
740 			       off + ISO_DIRECTORY_RECORD_SIZE);
741 			return (ESTALE);
742 		}
743 
744 		error = bread(imp->im_devvp,
745 			      lblktooff(imp, lbn),
746 			      imp->logical_block_size, &bp);
747 		if (error) {
748 			vx_put(vp);
749 			brelse(bp);
750 			kprintf("fhtovp: bread error %d\n",error);
751 			return (error);
752 		}
753 		isodir = (struct iso_directory_record *)(bp->b_data + off);
754 
755 		if (off + isonum_711(isodir->length) >
756 		    imp->logical_block_size) {
757 			vx_put(vp);
758 			if (bp != NULL)
759 				brelse(bp);
760 			kprintf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
761 			       off +isonum_711(isodir->length), off,
762 			       isonum_711(isodir->length));
763 			return (ESTALE);
764 		}
765 
766 #if 0
767 		if (isonum_733(isodir->extent) +
768 		    isonum_711(isodir->ext_attr_length) != ifhp->ifid_start) {
769 			if (bp != NULL)
770 				brelse(bp);
771 			kprintf("fhtovp: file start miss %d vs %d\n",
772 			       isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length),
773 			       ifhp->ifid_start);
774 			return (ESTALE);
775 		}
776 #endif
777 	} else
778 		bp = 0;
779 
780 	ip->i_mnt = imp;
781 	ip->i_devvp = imp->im_devvp;
782 	vref(ip->i_devvp);
783 
784 	if (relocated) {
785 		/*
786 		 * On relocated directories we must
787 		 * read the `.' entry out of a dir.
788 		 */
789 		ip->iso_start = ino >> imp->im_bshift;
790 		if (bp != NULL)
791 			brelse(bp);
792 		if ((error = cd9660_devblkatoff(vp, (off_t)0, NULL, &bp)) != 0) {
793 			vx_put(vp);
794 			return (error);
795 		}
796 		isodir = (struct iso_directory_record *)bp->b_data;
797 	}
798 
799 	ip->iso_extent = isonum_733(isodir->extent);
800 	ip->i_size = isonum_733(isodir->size);
801 	ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
802 
803 	/*
804 	 * Setup time stamp, attribute
805 	 */
806 	vp->v_type = VNON;
807 	switch (imp->iso_ftype) {
808 	default:	/* ISO_FTYPE_9660 */
809 	    {
810 		struct buf *bp2;
811 		int off;
812 		if ((imp->im_flags & ISOFSMNT_EXTATT)
813 		    && (off = isonum_711(isodir->ext_attr_length)))
814 			cd9660_devblkatoff(vp, (off_t)-(off << imp->im_bshift), NULL,
815 				     &bp2);
816 		else
817 			bp2 = NULL;
818 		cd9660_defattr(isodir, ip, bp2, ISO_FTYPE_9660);
819 		cd9660_deftstamp(isodir, ip, bp2, ISO_FTYPE_9660);
820 		if (bp2)
821 			brelse(bp2);
822 		break;
823 	    }
824 	case ISO_FTYPE_RRIP:
825 		cd9660_rrip_analyze(isodir, ip, imp);
826 		break;
827 	}
828 
829 	if (bp != NULL)
830 		brelse(bp);
831 
832 	/*
833 	 * Initialize the associated vnode
834 	 */
835 	vp->v_type = IFTOVT(ip->inode.iso_mode);
836 
837 	switch (vp->v_type) {
838 	case VFIFO:
839 		vp->v_ops = &mp->mnt_vn_fifo_ops;
840 		break;
841 	case VCHR:
842 	case VBLK:
843 		vp->v_ops = &mp->mnt_vn_spec_ops;
844 		addaliasu(vp, umajor(ip->inode.iso_rdev),
845 			  uminor(ip->inode.iso_rdev));
846 		break;
847 	case VREG:
848 	case VDIR:
849 		vinitvmio(vp, ip->i_size);
850 		break;
851 	default:
852 		break;
853 	}
854 
855 	if (ip->iso_extent == imp->root_extent)
856 		vp->v_flag |= VROOT;
857 
858 	/*
859 	 * Return the locked and refd vp
860 	 */
861 	*vpp = vp;
862 	return (0);
863 }
864 
865 /*
866  * Vnode pointer to File handle
867  */
868 /* ARGSUSED */
869 int
870 cd9660_vptofh(struct vnode *vp, struct fid *fhp)
871 {
872 	struct iso_node *ip = VTOI(vp);
873 	struct ifid *ifhp;
874 
875 	ifhp = (struct ifid *)fhp;
876 	ifhp->ifid_len = sizeof(struct ifid);
877 
878 	ifhp->ifid_ino = ip->i_number;
879 	ifhp->ifid_start = ip->iso_start;
880 
881 #ifdef	ISOFS_DBG
882 	kprintf("vptofh: ino %d, start %ld\n",
883 	       ifhp->ifid_ino,ifhp->ifid_start);
884 #endif
885 	return 0;
886 }
887