ISOFS => CD9660
[unix-history] / usr / src / sys / isofs / cd9660 / cd9660_vfsops.c
CommitLineData
5fc285c5
KM
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 * %sccs.include.redist.c%
11 *
12 * @(#)cd9660_vfsops.c 8.1 (Berkeley) %G%
13 */
14
15#include <sys/param.h>
16#include <sys/systm.h>
17#include <sys/namei.h>
18#include <sys/proc.h>
19#include <sys/kernel.h>
20#include <sys/vnode.h>
21#include <miscfs/specfs/specdev.h>
22#include <sys/mount.h>
23#include <sys/buf.h>
24#include <sys/file.h>
25#include <sys/dkbad.h>
26#include <sys/disklabel.h>
27#include <sys/ioctl.h>
28#include <sys/errno.h>
29#include <sys/malloc.h>
30
31#include <isofs/cd9660/iso.h>
32#include <isofs/cd9660/isofs_node.h>
33
34extern int enodev ();
35
36struct vfsops isofs_vfsops = {
37 isofs_mount,
38 isofs_start,
39 isofs_unmount,
40 isofs_root,
41 isofs_quotactl,
42 isofs_statfs,
43 isofs_sync,
44 isofs_vget,
45 isofs_fhtovp,
46 isofs_vptofh,
47 isofs_init,
48};
49
50/*
51 * Called by vfs_mountroot when iso is going to be mounted as root.
52 *
53 * Name is updated by mount(8) after booting.
54 */
55#define ROOTNAME "root_device"
56
57static iso_mountfs();
58
59isofs_mountroot()
60{
61 register struct mount *mp;
62 extern struct vnode *rootvp;
63 struct proc *p = curproc; /* XXX */
64 struct iso_mnt *imp;
65 register struct fs *fs;
66 u_int size;
67 int error;
68 struct iso_args args;
69
70 /*
71 * Get vnodes for swapdev and rootdev.
72 */
73 if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
74 panic("isofs_mountroot: can't setup bdevvp's");
75
76 mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
77 bzero((char *)mp, (u_long)sizeof(struct mount));
78 mp->mnt_op = &isofs_vfsops;
79 mp->mnt_flag = MNT_RDONLY;
80 args.flags = ISOFSMNT_ROOT;
81 if (error = iso_mountfs(rootvp, mp, p, &args)) {
82 free(mp, M_MOUNT);
83 return (error);
84 }
85 if (error = vfs_lock(mp)) {
86 (void)isofs_unmount(mp, 0, p);
87 free(mp, M_MOUNT);
88 return (error);
89 }
90 TAILQ_INSERT_TAIL(&mountlist, mp, mnt_list);
91 mp->mnt_flag |= MNT_ROOTFS;
92 mp->mnt_vnodecovered = NULLVP;
93 imp = VFSTOISOFS(mp);
94 bzero(imp->im_fsmnt, sizeof(imp->im_fsmnt));
95 imp->im_fsmnt[0] = '/';
96 bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
97 MNAMELEN);
98 (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
99 &size);
100 bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
101 (void) isofs_statfs(mp, &mp->mnt_stat, p);
102 vfs_unlock(mp);
103 return (0);
104}
105
106/*
107 * Flag to allow forcible unmounting.
108 */
109int iso_doforce = 1;
110
111/*
112 * VFS Operations.
113 *
114 * mount system call
115 */
116isofs_mount(mp, path, data, ndp, p)
117 register struct mount *mp;
118 char *path;
119 caddr_t data;
120 struct nameidata *ndp;
121 struct proc *p;
122{
123 struct vnode *devvp;
124 struct iso_args args;
125 u_int size;
126 int error;
127 struct iso_mnt *imp;
128
129 if (error = copyin(data, (caddr_t)&args, sizeof (struct iso_args)))
130 return (error);
131
132 if ((mp->mnt_flag & MNT_RDONLY) == 0)
133 return (EROFS);
134
135 /*
136 * If updating, check whether changing from read-only to
137 * read/write; if there is no device name, that's all we do.
138 */
139 if (mp->mnt_flag & MNT_UPDATE) {
140 imp = VFSTOISOFS(mp);
141 if (args.fspec == 0)
142 return (vfs_export(mp, &imp->im_export, &args.export));
143 }
144 /*
145 * Not an update, or updating the name: look up the name
146 * and verify that it refers to a sensible block device.
147 */
148 NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
149 if (error = namei(ndp))
150 return (error);
151 devvp = ndp->ni_vp;
152
153 if (devvp->v_type != VBLK) {
154 vrele(devvp);
155 return ENOTBLK;
156 }
157 if (major(devvp->v_rdev) >= nblkdev) {
158 vrele(devvp);
159 return ENXIO;
160 }
161 if ((mp->mnt_flag & MNT_UPDATE) == 0)
162 error = iso_mountfs(devvp, mp, p, &args);
163 else {
164 if (devvp != imp->im_devvp)
165 error = EINVAL; /* needs translation */
166 else
167 vrele(devvp);
168 }
169 if (error) {
170 vrele(devvp);
171 return error;
172 }
173 imp = VFSTOISOFS(mp);
174 (void) copyinstr(path, imp->im_fsmnt, sizeof(imp->im_fsmnt)-1, &size);
175 bzero(imp->im_fsmnt + size, sizeof(imp->im_fsmnt) - size);
176 bcopy((caddr_t)imp->im_fsmnt, (caddr_t)mp->mnt_stat.f_mntonname,
177 MNAMELEN);
178 (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
179 &size);
180 bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
181 (void) isofs_statfs(mp, &mp->mnt_stat, p);
182 return 0;
183}
184
185/*
186 * Common code for mount and mountroot
187 */
188static iso_mountfs(devvp, mp, p, argp)
189 register struct vnode *devvp;
190 struct mount *mp;
191 struct proc *p;
192 struct iso_args *argp;
193{
194 register struct iso_mnt *isomp = (struct iso_mnt *)0;
195 struct buf *bp = NULL;
196 dev_t dev = devvp->v_rdev;
197 caddr_t base, space;
198 int havepart = 0, blks;
199 int error = EINVAL, i, size;
200 int needclose = 0;
201 int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
202 extern struct vnode *rootvp;
203 int j;
204 int iso_bsize;
205 int iso_blknum;
206 struct iso_volume_descriptor *vdp;
207 struct iso_primary_descriptor *pri;
208 struct iso_directory_record *rootp;
209 int logical_block_size;
210
211 if (!ronly)
212 return EROFS;
213
214 /*
215 * Disallow multiple mounts of the same device.
216 * Disallow mounting of a device that is currently in use
217 * (except for root, which might share swap device for miniroot).
218 * Flush out any old buffers remaining from a previous use.
219 */
220 if (error = vfs_mountedon(devvp))
221 return error;
222 if (vcount(devvp) > 1 && devvp != rootvp)
223 return EBUSY;
224 if (error = vinvalbuf(devvp, V_SAVE, p->p_ucred, p, 0, 0))
225 return (error);
226
227 if (error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p))
228 return error;
229 needclose = 1;
230
231 /* This is the "logical sector size". The standard says this
232 * should be 2048 or the physical sector size on the device,
233 * whichever is greater. For now, we'll just use a constant.
234 */
235 iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
236
237 for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
238 if (error = bread (devvp, btodb(iso_blknum * iso_bsize),
239 iso_bsize, NOCRED, &bp))
240 goto out;
241
242 vdp = (struct iso_volume_descriptor *)bp->b_un.b_addr;
243 if (bcmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) != 0) {
244 error = EINVAL;
245 goto out;
246 }
247
248 if (isonum_711 (vdp->type) == ISO_VD_END) {
249 error = EINVAL;
250 goto out;
251 }
252
253 if (isonum_711 (vdp->type) == ISO_VD_PRIMARY)
254 break;
255 brelse(bp);
256 }
257
258 if (isonum_711 (vdp->type) != ISO_VD_PRIMARY) {
259 error = EINVAL;
260 goto out;
261 }
262
263 pri = (struct iso_primary_descriptor *)vdp;
264
265 logical_block_size = isonum_723 (pri->logical_block_size);
266
267 if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
268 || (logical_block_size & (logical_block_size - 1)) != 0) {
269 error = EINVAL;
270 goto out;
271 }
272
273 rootp = (struct iso_directory_record *)pri->root_directory_record;
274
275 isomp = (struct iso_mnt *)malloc(sizeof *isomp,M_ISOFSMNT,M_WAITOK);
276 isomp->logical_block_size = logical_block_size;
277 isomp->volume_space_size = isonum_733 (pri->volume_space_size);
278 bcopy (rootp, isomp->root, sizeof isomp->root);
279 isomp->root_extent = isonum_733 (rootp->extent);
280 isomp->root_size = isonum_733 (rootp->size);
281
282 isomp->im_bmask = logical_block_size - 1;
283 isomp->im_bshift = 0;
284 while ((1 << isomp->im_bshift) < isomp->logical_block_size)
285 isomp->im_bshift++;
286
287 bp->b_flags |= B_AGE;
288 brelse(bp);
289 bp = NULL;
290
291 mp->mnt_data = (qaddr_t)isomp;
292 mp->mnt_stat.f_fsid.val[0] = (long)dev;
293 mp->mnt_stat.f_fsid.val[1] = MOUNT_ISOFS;
294 mp->mnt_maxsymlinklen = 0;
295 mp->mnt_flag |= MNT_LOCAL;
296 isomp->im_mountp = mp;
297 isomp->im_dev = dev;
298 isomp->im_devvp = devvp;
299
300 devvp->v_specflags |= SI_MOUNTEDON;
301
302 /* Check the Rock Ridge Extention support */
303 if (!(argp->flags & ISOFSMNT_NORRIP)) {
304 if (error = bread (isomp->im_devvp,
305 (isomp->root_extent + isonum_711(rootp->ext_attr_length))
306 * isomp->logical_block_size / DEV_BSIZE,
307 isomp->logical_block_size,NOCRED,&bp))
308 goto out;
309
310 rootp = (struct iso_directory_record *)bp->b_un.b_addr;
311
312 if ((isomp->rr_skip = isofs_rrip_offset(rootp,isomp)) < 0) {
313 argp->flags |= ISOFSMNT_NORRIP;
314 } else {
315 argp->flags &= ~ISOFSMNT_GENS;
316 }
317
318 /*
319 * The contents are valid,
320 * but they will get reread as part of another vnode, so...
321 */
322 bp->b_flags |= B_AGE;
323 brelse(bp);
324 bp = NULL;
325 }
326 isomp->im_flags = argp->flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS|ISOFSMNT_EXTATT);
327 switch (isomp->im_flags&(ISOFSMNT_NORRIP|ISOFSMNT_GENS)) {
328 default:
329 isomp->iso_ftype = ISO_FTYPE_DEFAULT;
330 break;
331 case ISOFSMNT_GENS|ISOFSMNT_NORRIP:
332 isomp->iso_ftype = ISO_FTYPE_9660;
333 break;
334 case 0:
335 isomp->iso_ftype = ISO_FTYPE_RRIP;
336 break;
337 }
338
339 return 0;
340out:
341 if (bp)
342 brelse(bp);
343 if (needclose)
344 (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, NOCRED, p);
345 if (isomp) {
346 free((caddr_t)isomp, M_ISOFSMNT);
347 mp->mnt_data = (qaddr_t)0;
348 }
349 return error;
350}
351
352/*
353 * Make a filesystem operational.
354 * Nothing to do at the moment.
355 */
356/* ARGSUSED */
357isofs_start(mp, flags, p)
358 struct mount *mp;
359 int flags;
360 struct proc *p;
361{
362 return 0;
363}
364
365/*
366 * unmount system call
367 */
368int
369isofs_unmount(mp, mntflags, p)
370 struct mount *mp;
371 int mntflags;
372 struct proc *p;
373{
374 register struct iso_mnt *isomp;
375 int i, error, ronly, flags = 0;
376
377 if (mntflags & MNT_FORCE) {
378 if (!iso_doforce || (mp->mnt_flag & MNT_ROOTFS))
379 return (EINVAL);
380 flags |= FORCECLOSE;
381 }
382#if 0
383 mntflushbuf(mp, 0);
384 if (mntinvalbuf(mp))
385 return EBUSY;
386#endif
387 if (error = vflush(mp, NULLVP, flags))
388 return (error);
389
390 isomp = VFSTOISOFS(mp);
391
392#ifdef ISODEVMAP
393 if (isomp->iso_ftype == ISO_FTYPE_RRIP)
394 iso_dunmap(isomp->im_dev);
395#endif
396
397 isomp->im_devvp->v_specflags &= ~SI_MOUNTEDON;
398 error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED, p);
399 vrele(isomp->im_devvp);
400 free((caddr_t)isomp, M_ISOFSMNT);
401 mp->mnt_data = (qaddr_t)0;
402 mp->mnt_flag &= ~MNT_LOCAL;
403 return (error);
404}
405
406/*
407 * Return root of a filesystem
408 */
409isofs_root(mp, vpp)
410 struct mount *mp;
411 struct vnode **vpp;
412{
413 register struct iso_node *ip;
414 struct iso_node tip, *nip;
415 struct vnode tvp;
416 int error;
417 struct iso_mnt *imp = VFSTOISOFS (mp);
418 struct iso_directory_record *dp;
419
420 tvp.v_mount = mp;
421 tvp.v_data = &tip;
422 ip = VTOI(&tvp);
423 ip->i_vnode = &tvp;
424 ip->i_dev = imp->im_dev;
425 ip->i_diroff = 0;
426 dp = (struct iso_directory_record *)imp->root;
427 isodirino(&ip->i_number,dp,imp);
428
429 /*
430 * With RRIP we must use the `.' entry of the root directory.
431 * Simply tell iget, that it's a relocated directory.
432 */
433 error = iso_iget(ip,ip->i_number,
434 imp->iso_ftype == ISO_FTYPE_RRIP,
435 &nip,dp);
436 if (error)
437 return error;
438 *vpp = ITOV(nip);
439 return 0;
440}
441
442/*
443 * Do operations associated with quotas, not supported
444 */
445/* ARGSUSED */
446int
447isofs_quotactl(mp, cmd, uid, arg, p)
448 struct mount *mp;
449 int cmd;
450 uid_t uid;
451 caddr_t arg;
452 struct proc *p;
453{
454
455 return (EOPNOTSUPP);
456}
457
458/*
459 * Get file system statistics.
460 */
461isofs_statfs(mp, sbp, p)
462 struct mount *mp;
463 register struct statfs *sbp;
464 struct proc *p;
465{
466 register struct iso_mnt *isomp;
467 register struct fs *fs;
468
469 isomp = VFSTOISOFS(mp);
470
471 sbp->f_type = MOUNT_ISOFS;
472 sbp->f_bsize = isomp->logical_block_size;
473 sbp->f_iosize = sbp->f_bsize; /* XXX */
474 sbp->f_blocks = isomp->volume_space_size;
475 sbp->f_bfree = 0; /* total free blocks */
476 sbp->f_bavail = 0; /* blocks free for non superuser */
477 sbp->f_files = 0; /* total files */
478 sbp->f_ffree = 0; /* free file nodes */
479 if (sbp != &mp->mnt_stat) {
480 bcopy((caddr_t)mp->mnt_stat.f_mntonname,
481 (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
482 bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
483 (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
484 }
485 /* Use the first spare for flags: */
486 sbp->f_spare[0] = isomp->im_flags;
487 return 0;
488}
489
490/* ARGSUSED */
491int
492isofs_sync(mp, waitfor, cred, p)
493 struct mount *mp;
494 int waitfor;
495 struct ucred *cred;
496 struct proc *p;
497{
498 return (0);
499}
500
501/*
502 * Flat namespace lookup.
503 * Currently unsupported.
504 */
505/* ARGSUSED */
506int
507isofs_vget(mp, ino, vpp)
508 struct mount *mp;
509 ino_t ino;
510 struct vnode **vpp;
511{
512
513 return (EOPNOTSUPP);
514}
515
516/*
517 * File handle to vnode
518 *
519 * Have to be really careful about stale file handles:
520 * - check that the inode number is in range
521 * - call iget() to get the locked inode
522 * - check for an unallocated inode (i_mode == 0)
523 * - check that the generation number matches
524 */
525
526struct ifid {
527 ushort ifid_len;
528 ushort ifid_pad;
529 int ifid_ino;
530 long ifid_start;
531};
532
533/* ARGSUSED */
534int
535isofs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
536 register struct mount *mp;
537 struct fid *fhp;
538 struct mbuf *nam;
539 struct vnode **vpp;
540 int *exflagsp;
541 struct ucred **credanonp;
542{
543 struct vnode tvp;
544 int error;
545 int lbn, off;
546 struct ifid *ifhp;
547 struct iso_mnt *imp;
548 struct buf *bp;
549 struct iso_directory_record *dirp;
550 struct iso_node tip, *ip, *nip;
551 struct netcred *np;
552
553 imp = VFSTOISOFS (mp);
554 ifhp = (struct ifid *)fhp;
555
556#ifdef ISOFS_DBG
557 printf("fhtovp: ino %d, start %ld\n",
558 ifhp->ifid_ino, ifhp->ifid_start);
559#endif
560
561 np = vfs_export_lookup(mp, &imp->im_export, nam);
562 if (np == NULL)
563 return (EACCES);
564
565 lbn = iso_lblkno(imp, ifhp->ifid_ino);
566 if (lbn >= imp->volume_space_size) {
567 printf("fhtovp: lbn exceed volume space %d\n", lbn);
568 return (ESTALE);
569 }
570
571 off = iso_blkoff(imp, ifhp->ifid_ino);
572 if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
573 printf("fhtovp: crosses block boundary %d\n",
574 off + ISO_DIRECTORY_RECORD_SIZE);
575 return (ESTALE);
576 }
577
578 error = bread(imp->im_devvp, btodb(lbn * imp->logical_block_size),
579 imp->logical_block_size, NOCRED, &bp);
580 if (error) {
581 printf("fhtovp: bread error %d\n",error);
582 brelse(bp);
583 return (error);
584 }
585
586 dirp = (struct iso_directory_record *)(bp->b_un.b_addr + off);
587 if (off + isonum_711(dirp->length) > imp->logical_block_size) {
588 brelse(bp);
589 printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
590 off+isonum_711(dirp->length), off,
591 isonum_711(dirp->length));
592 return (ESTALE);
593 }
594
595 if (isonum_733(dirp->extent) + isonum_711(dirp->ext_attr_length) !=
596 ifhp->ifid_start) {
597 brelse(bp);
598 printf("fhtovp: file start miss %d vs %d\n",
599 isonum_733(dirp->extent)+isonum_711(dirp->ext_attr_length),
600 ifhp->ifid_start);
601 return (ESTALE);
602 }
603 brelse(bp);
604
605 ip = &tip;
606 tvp.v_mount = mp;
607 tvp.v_data = ip;
608 ip->i_vnode = &tvp;
609 ip->i_dev = imp->im_dev;
610 if (error = iso_iget(ip, ifhp->ifid_ino, 0, &nip, dirp)) {
611 *vpp = NULLVP;
612 printf("fhtovp: failed to get inode\n");
613 return (error);
614 }
615 ip = nip;
616 /*
617 * XXX need generation number?
618 */
619 if (ip->inode.iso_mode == 0) {
620 iso_iput(ip);
621 *vpp = NULLVP;
622 printf("fhtovp: inode mode == 0\n");
623 return (ESTALE);
624 }
625 *vpp = ITOV(ip);
626 *exflagsp = np->netc_exflags;
627 *credanonp = &np->netc_anon;
628 return 0;
629}
630
631/*
632 * Vnode pointer to File handle
633 */
634/* ARGSUSED */
635isofs_vptofh(vp, fhp)
636 struct vnode *vp;
637 struct fid *fhp;
638{
639 register struct iso_node *ip = VTOI(vp);
640 register struct ifid *ifhp;
641 register struct iso_mnt *mp = ip->i_mnt;
642
643 ifhp = (struct ifid *)fhp;
644 ifhp->ifid_len = sizeof(struct ifid);
645
646 ifhp->ifid_ino = ip->i_number;
647 ifhp->ifid_start = ip->iso_start;
648
649#ifdef ISOFS_DBG
650 printf("vptofh: ino %d, start %ld\n",
651 ifhp->ifid_ino,ifhp->ifid_start);
652#endif
653 return 0;
654}