1 /*        $NetBSD: cd9660_vfsops.c,v 1.105 2025/02/16 18:38:58 joe Exp $        */
2 
3 /*-
4  * Copyright (c) 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley
8  * by Pace Willisson (pace@blitz.com).  The Rock Ridge Extension
9  * Support code is derived from software contributed to Berkeley
10  * by Atsushi Murai (amurai@spec.co.jp).
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *        @(#)cd9660_vfsops.c 8.18 (Berkeley) 5/22/95
37  */
38 
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: cd9660_vfsops.c,v 1.105 2025/02/16 18:38:58 joe Exp $");
41 
42 #if defined(_KERNEL_OPT)
43 #include "opt_compat_netbsd.h"
44 #endif
45 
46 #include <sys/param.h>
47 #include <sys/sysctl.h>
48 #include <sys/systm.h>
49 #include <sys/namei.h>
50 #include <sys/proc.h>
51 #include <sys/kernel.h>
52 #include <sys/vnode.h>
53 #include <miscfs/genfs/genfs.h>
54 #include <miscfs/specfs/specdev.h>
55 #include <sys/mount.h>
56 #include <sys/buf.h>
57 #include <sys/file.h>
58 #include <sys/disklabel.h>
59 #include <sys/device.h>
60 #include <sys/ioctl.h>
61 #include <sys/cdio.h>
62 #include <sys/errno.h>
63 #include <sys/malloc.h>
64 #include <sys/pool.h>
65 #include <sys/stat.h>
66 #include <sys/conf.h>
67 #include <sys/dirent.h>
68 #include <sys/kauth.h>
69 #include <sys/module.h>
70 
71 #include <fs/cd9660/iso.h>
72 #include <fs/cd9660/cd9660_extern.h>
73 #include <fs/cd9660/iso_rrip.h>
74 #include <fs/cd9660/cd9660_node.h>
75 #include <fs/cd9660/cd9660_mount.h>
76 
77 MODULE(MODULE_CLASS_VFS, cd9660, NULL);
78 
79 MALLOC_JUSTDEFINE(M_ISOFSMNT, "ISOFS mount", "ISOFS mount structure");
80 
81 extern const struct vnodeopv_desc cd9660_vnodeop_opv_desc;
82 extern const struct vnodeopv_desc cd9660_specop_opv_desc;
83 extern const struct vnodeopv_desc cd9660_fifoop_opv_desc;
84 
85 const struct vnodeopv_desc * const cd9660_vnodeopv_descs[] = {
86           &cd9660_vnodeop_opv_desc,
87           &cd9660_specop_opv_desc,
88           &cd9660_fifoop_opv_desc,
89           NULL,
90 };
91 
92 struct vfsops cd9660_vfsops = {
93           .vfs_name = MOUNT_CD9660,
94           .vfs_min_mount_data = sizeof (struct iso_args),
95           .vfs_mount = cd9660_mount,
96           .vfs_start = cd9660_start,
97           .vfs_unmount = cd9660_unmount,
98           .vfs_root = cd9660_root,
99           .vfs_quotactl = (void *)eopnotsupp,
100           .vfs_statvfs = cd9660_statvfs,
101           .vfs_sync = cd9660_sync,
102           .vfs_vget = cd9660_vget,
103           .vfs_loadvnode = cd9660_loadvnode,
104           .vfs_fhtovp = cd9660_fhtovp,
105           .vfs_vptofh = cd9660_vptofh,
106           .vfs_init = cd9660_init,
107           .vfs_reinit = cd9660_reinit,
108           .vfs_done = cd9660_done,
109           .vfs_mountroot = cd9660_mountroot,
110           .vfs_snapshot = (void *)eopnotsupp,
111           .vfs_extattrctl = vfs_stdextattrctl,
112           .vfs_suspendctl = genfs_suspendctl,
113           .vfs_renamelock_enter = genfs_renamelock_enter,
114           .vfs_renamelock_exit = genfs_renamelock_exit,
115           .vfs_fsync = (void *)eopnotsupp,
116           .vfs_opv_descs = cd9660_vnodeopv_descs
117 };
118 
119 static const struct genfs_ops cd9660_genfsops = {
120           .gop_size = genfs_size,
121 };
122 
123 /*
124  * Called by vfs_mountroot when iso is going to be mounted as root.
125  *
126  * Name is updated by mount(8) after booting.
127  */
128 static int iso_makemp(struct iso_mnt *isomp, struct buf *bp, int *ea_len);
129 static int iso_mountfs(struct vnode *devvp, struct mount *mp,
130                     struct lwp *l, struct iso_args *argp);
131 
132 SYSCTL_SETUP(cd9660_sysctl_setup, "cd9660 sysctl")
133 {
134 
135                     sysctl_createv(clog, 0, NULL, NULL,
136                                      CTLFLAG_PERMANENT, CTLTYPE_NODE, "cd9660",
137                                      SYSCTL_DESCR("ISO-9660 file system"),
138                                      NULL, 0, NULL, 0,
139                                      CTL_VFS, 14, CTL_EOL);
140                     sysctl_createv(clog, 0, NULL, NULL,
141                                      CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
142                                      CTLTYPE_INT, "utf8_joliet",
143                                      SYSCTL_DESCR("Encode Joliet filenames to UTF-8"),
144                                      NULL, 0, &cd9660_utf8_joliet, 0,
145                                      CTL_VFS, 14, CD9660_UTF8_JOLIET, CTL_EOL);
146                     /*
147                      * XXX the "14" above could be dynamic, thereby eliminating
148                      * one more instance of the "number to vfs" mapping problem,
149                      * but "14" is the order as taken from sys/mount.h
150                      */
151 }
152 
153 static int
cd9660_modcmd(modcmd_t cmd,void * arg)154 cd9660_modcmd(modcmd_t cmd, void *arg)
155 {
156           int error;
157 
158           switch (cmd) {
159           case MODULE_CMD_INIT:
160                     error = vfs_attach(&cd9660_vfsops);
161                     break;
162           case MODULE_CMD_FINI:
163                     error = vfs_detach(&cd9660_vfsops);
164                     break;
165           default:
166                     error = ENOTTY;
167                     break;
168           }
169 
170           return (error);
171 }
172 
173 /* Compat with pre uid/gid/fsize/dsize mount call */
174 #define OSIZE sizeof(struct { \
175           const char *fspec; \
176           struct export_args30 _pad1; \
177           int flags; \
178 })
179 
180 static int
iso_checkupdate(const struct vnode * devvp,const struct iso_mnt * imp,const struct iso_args * args)181 iso_checkupdate(const struct vnode *devvp, const struct iso_mnt *imp,
182     const struct iso_args *args)
183 {
184 
185           if (devvp != imp->im_devvp && devvp->v_rdev != imp->im_devvp->v_rdev)
186                     return EINVAL;
187 
188           if (((imp->im_flags & ISOFSMNT_UID) && args->uid != imp->im_uid) ||
189               ((imp->im_flags & ISOFSMNT_GID) && args->gid != imp->im_gid) ||
190               args->fmask != imp->im_fmask || args->dmask != imp->im_dmask)
191                     return EPERM;
192 
193           return 0;
194 }
195 
196 static void
iso_copyidmask(struct iso_args * args,const struct iso_mnt * imp)197 iso_copyidmask(struct iso_args *args, const struct iso_mnt *imp)
198 {
199 
200           if (imp == NULL) {
201                     args->uid = args->gid = 0;
202                     args->fmask = args->dmask = S_IRWXU|S_IRWXG|S_IRWXO;
203                     return;
204           }
205           args->uid = imp->im_uid;
206           args->gid = imp->im_gid;
207           args->fmask = imp->im_fmask;
208           args->dmask = imp->im_dmask;
209 }
210 
211 int
cd9660_mountroot(void)212 cd9660_mountroot(void)
213 {
214           struct mount *mp;
215           struct lwp *l = curlwp;
216           int error;
217           struct iso_args args;
218 
219           if (device_class(root_device) != DV_DISK)
220                     return (ENODEV);
221 
222           if ((error = vfs_rootmountalloc(MOUNT_CD9660, "root_device", &mp))
223                               != 0) {
224                     vrele(rootvp);
225                     return (error);
226           }
227 
228           args.flags = ISOFSMNT_ROOT;
229           iso_copyidmask(&args, NULL);
230           if ((error = iso_mountfs(rootvp, mp, l, &args)) != 0) {
231                     vfs_unbusy(mp);
232                     vfs_rele(mp);
233                     return (error);
234           }
235           mountlist_append(mp);
236           (void)cd9660_statvfs(mp, &mp->mnt_stat);
237           vfs_unbusy(mp);
238           return (0);
239 }
240 
241 
242 /*
243  * VFS Operations.
244  *
245  * mount system call
246  */
247 
248 int
cd9660_mount(struct mount * mp,const char * path,void * data,size_t * data_len)249 cd9660_mount(struct mount *mp, const char *path, void *data, size_t *data_len)
250 {
251           struct lwp *l = curlwp;
252           struct vnode *devvp;
253           struct iso_args aa, *args = data;
254           int error;
255           struct iso_mnt *imp = VFSTOISOFS(mp);
256 
257           if (args == NULL)
258                     return EINVAL;
259 
260           if (*data_len != OSIZE && *data_len < sizeof(*args))
261                     return EINVAL;
262 
263           if (mp->mnt_flag & MNT_GETARGS) {
264                     if (imp == NULL)
265                               return EIO;
266 
267                     args->fspec = NULL;
268                     args->flags = imp->im_flags;
269                     if (*data_len == OSIZE)
270                               return 0;
271 
272                     iso_copyidmask(args, imp);
273                     *data_len = sizeof(*args);
274                     return 0;
275           }
276 
277           if (*data_len == OSIZE) {
278                     memcpy(&aa, args, OSIZE);
279                     args = &aa;
280                     iso_copyidmask(args, (mp->mnt_flag & MNT_UPDATE) ? imp : NULL);
281           }
282 
283           if ((mp->mnt_flag & MNT_RDONLY) == 0)
284                     return EROFS;
285 
286           if ((mp->mnt_flag & MNT_UPDATE) && args->fspec == NULL)
287                     return EINVAL;
288 
289           /*
290            * Not an update, or updating the name: look up the name
291            * and verify that it refers to a sensible block device.
292            */
293           error = namei_simple_user(args->fspec,
294               NSM_FOLLOW_NOEMULROOT, &devvp);
295           if (error != 0)
296                     return error;
297 
298           if (devvp->v_type != VBLK) {
299                     vrele(devvp);
300                     return ENOTBLK;
301           }
302           if (bdevsw_lookup(devvp->v_rdev) == NULL) {
303                     vrele(devvp);
304                     return ENXIO;
305           }
306           /*
307            * If mount by non-root, then verify that user has necessary
308            * permissions on the device.
309            */
310           vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
311           error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_MOUNT,
312               KAUTH_REQ_SYSTEM_MOUNT_DEVICE, mp, devvp, KAUTH_ARG(VREAD));
313           if (error) {
314                     goto fail;
315           }
316           if ((mp->mnt_flag & MNT_UPDATE) == 0) {
317                     error = VOP_OPEN(devvp, FREAD, FSCRED);
318                     if (error)
319                               goto fail;
320                     VOP_UNLOCK(devvp);
321                     error = iso_mountfs(devvp, mp, l, args);
322                     vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
323                     if (error) {
324                               (void)VOP_CLOSE(devvp, FREAD, NOCRED);
325                               goto fail;
326                     }
327                     VOP_UNLOCK(devvp);
328                     /* reference to devvp is donated through iso_mountfs */
329           } else {
330                     if ((error = iso_checkupdate(devvp, imp, args)) != 0)
331                               goto fail;
332                     VOP_UNLOCK(devvp);
333                     vrele(devvp);
334           }
335           return set_statvfs_info(path, UIO_USERSPACE, args->fspec, UIO_USERSPACE,
336               mp->mnt_op->vfs_name, mp, l);
337 
338 fail:
339           VOP_UNLOCK(devvp);
340           vrele(devvp);
341           return error;
342 }
343 
344 /*
345  * Make a mount point from a volume descriptor
346  */
347 static int
iso_makemp(struct iso_mnt * isomp,struct buf * bp,int * ea_len)348 iso_makemp(struct iso_mnt *isomp, struct buf *bp, int *ea_len)
349 {
350           struct iso_primary_descriptor *pri;
351           int logical_block_size;
352           struct iso_directory_record *rootp;
353 
354           pri = (struct iso_primary_descriptor *)bp->b_data;
355 
356           logical_block_size = isonum_723 (pri->logical_block_size);
357 
358           if (logical_block_size < DEV_BSIZE || logical_block_size > MAXBSIZE
359               || (logical_block_size & (logical_block_size - 1)) != 0)
360                     return -1;
361 
362           rootp = (struct iso_directory_record *)pri->root_directory_record;
363 
364           isomp->logical_block_size = logical_block_size;
365           isomp->volume_space_size = isonum_733 (pri->volume_space_size);
366           memcpy(isomp->root, rootp, sizeof(isomp->root));
367           isomp->root_extent = isonum_733 (rootp->extent);
368           isomp->root_size = isonum_733 (rootp->size);
369           isomp->im_joliet_level = 0;
370 
371           isomp->im_bmask = logical_block_size - 1;
372           isomp->im_bshift = 0;
373           while ((1 << isomp->im_bshift) < isomp->logical_block_size)
374                     isomp->im_bshift++;
375 
376           if (ea_len != NULL)
377                     *ea_len = isonum_711(rootp->ext_attr_length);
378 
379           return 0;
380 }
381 
382 /*
383  * Common code for mount and mountroot
384  */
385 static int
iso_mountfs(struct vnode * devvp,struct mount * mp,struct lwp * l,struct iso_args * argp)386 iso_mountfs(struct vnode *devvp, struct mount *mp, struct lwp *l,
387           struct iso_args *argp)
388 {
389           struct iso_mnt *isomp = (struct iso_mnt *)0;
390           struct buf *bp = NULL, *pribp = NULL, *supbp = NULL;
391           dev_t dev = devvp->v_rdev;
392           int error = EINVAL;
393           int ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
394           int iso_bsize;
395           int iso_blknum;
396           int joliet_level;
397           struct iso_volume_descriptor *vdp;
398           struct iso_supplementary_descriptor *sup;
399           int sess = 0;
400           int ext_attr_length;
401           struct disklabel label;
402 
403           if (!ronly)
404                     return EROFS;
405 
406           /* Flush out any old buffers remaining from a previous use. */
407           vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
408           error = vinvalbuf(devvp, V_SAVE, l->l_cred, l, 0, 0);
409           VOP_UNLOCK(devvp);
410           if (error != 0)
411                     return (error);
412 
413           /* This is the "logical sector size".  The standard says this
414            * should be 2048 or the physical sector size on the device,
415            * whichever is greater.  For now, we'll just use a constant.
416            */
417           iso_bsize = ISO_DEFAULT_BLOCK_SIZE;
418 
419           error = VOP_IOCTL(devvp, DIOCGDINFO, &label, FREAD, FSCRED);
420           if (!error) {
421                     /* XXX more sanity checks? */
422                     sess = label.d_partitions[DISKPART(dev)].p_cdsession;
423           } else {
424                     /* fallback to old method */
425                     error = VOP_IOCTL(devvp, CDIOREADMSADDR, &sess, 0, FSCRED);
426                     if (error)
427                               sess = 0; /* never mind */
428           }
429 #ifdef ISO_DEBUG
430           printf("isofs: session offset (part %"PRId32") %d\n", DISKPART(dev), sess);
431 #endif
432 
433           for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
434                     if ((error = bread(devvp, (iso_blknum+sess) * btodb(iso_bsize),
435                                            iso_bsize, 0, &bp)) != 0)
436                               goto out;
437 
438                     vdp = (struct iso_volume_descriptor *)bp->b_data;
439                     if (memcmp(vdp->id, ISO_STANDARD_ID, sizeof(vdp->id)) != 0) {
440                               error = EINVAL;
441                               goto out;
442                     }
443 
444                     switch (isonum_711(vdp->type)) {
445                     case ISO_VD_PRIMARY:
446                               if (pribp == NULL) {
447                                         pribp = bp;
448                                         bp = NULL;
449                               }
450                               break;
451 
452                     case ISO_VD_SUPPLEMENTARY:
453                               if (supbp == NULL) {
454                                         supbp = bp;
455                                         bp = NULL;
456                               }
457                               break;
458 
459                     default:
460                               break;
461                     }
462 
463                     if (isonum_711 (vdp->type) == ISO_VD_END) {
464                               brelse(bp, 0);
465                               bp = NULL;
466                               break;
467                     }
468 
469                     if (bp != NULL) {
470                               brelse(bp, 0);
471                               bp = NULL;
472                     }
473           }
474 
475           if (pribp == NULL) {
476                     error = EINVAL;
477                     goto out;
478           }
479 
480           isomp = malloc(sizeof *isomp, M_ISOFSMNT, M_WAITOK);
481           memset(isomp, 0, sizeof *isomp);
482           if (iso_makemp(isomp, pribp, &ext_attr_length) == -1) {
483                     error = EINVAL;
484                     goto out;
485           }
486 
487           isomp->volume_space_size += sess;
488 
489           brelse(pribp, BC_AGE);
490           pribp = NULL;
491 
492           mp->mnt_data = isomp;
493           mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
494           mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_CD9660);
495           mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
496           mp->mnt_stat.f_namemax = ISO_MAXNAMLEN;
497           mp->mnt_flag |= MNT_LOCAL;
498           mp->mnt_iflag |= IMNT_MPSAFE | IMNT_SHRLOOKUP;
499           mp->mnt_dev_bshift = iso_bsize;
500           mp->mnt_fs_bshift = isomp->im_bshift;
501           isomp->im_mountp = mp;
502           isomp->im_dev = dev;
503           isomp->im_devvp = devvp;
504 
505           if (argp->flags & ISOFSMNT_UID)
506                     isomp->im_uid = argp->uid;
507           if (argp->flags & ISOFSMNT_GID)
508                     isomp->im_gid = argp->gid;
509           isomp->im_fmask = argp->fmask & ACCESSPERMS;
510           isomp->im_dmask = argp->dmask & ACCESSPERMS;
511 
512           /* Check the Rock Ridge Extension support */
513           if (!(argp->flags & ISOFSMNT_NORRIP)) {
514                     struct iso_directory_record *rootp;
515 
516                     if ((error = bread(isomp->im_devvp,
517                                            (isomp->root_extent + ext_attr_length) <<
518                                            (isomp->im_bshift - DEV_BSHIFT),
519                                            isomp->logical_block_size,
520                                            0, &bp)) != 0)
521                         goto out;
522 
523                     rootp = (struct iso_directory_record *)bp->b_data;
524 
525                     if ((isomp->rr_skip = cd9660_rrip_offset(rootp,isomp)) < 0) {
526                         argp->flags  |= ISOFSMNT_NORRIP;
527                     } else {
528                         argp->flags  &= ~ISOFSMNT_GENS;
529                     }
530 
531                     /*
532                      * The contents are valid,
533                      * but they will get reread as part of another vnode, so...
534                      */
535                     brelse(bp, BC_AGE);
536                     bp = NULL;
537           }
538           isomp->im_flags = argp->flags & (ISOFSMNT_NORRIP | ISOFSMNT_GENS |
539                      ISOFSMNT_EXTATT | ISOFSMNT_NOJOLIET | ISOFSMNT_RRCASEINS |
540                      ISOFSMNT_UID | ISOFSMNT_GID);
541 
542           if (isomp->im_flags & ISOFSMNT_GENS)
543                     isomp->iso_ftype = ISO_FTYPE_9660;
544           else if (isomp->im_flags & ISOFSMNT_NORRIP) {
545                     isomp->iso_ftype = ISO_FTYPE_DEFAULT;
546                     if (argp->flags & ISOFSMNT_NOCASETRANS)
547                               isomp->im_flags |= ISOFSMNT_NOCASETRANS;
548           } else
549                     isomp->iso_ftype = ISO_FTYPE_RRIP;
550 
551           /* Check the Joliet Extension support */
552           if ((argp->flags & ISOFSMNT_NORRIP) != 0 &&
553               (argp->flags & ISOFSMNT_NOJOLIET) == 0 &&
554               supbp != NULL) {
555                     joliet_level = 0;
556                     sup = (struct iso_supplementary_descriptor *)supbp->b_data;
557 
558                     if ((isonum_711(sup->flags) & 1) == 0) {
559                               if (memcmp(sup->escape, "%/@", 3) == 0)
560                                         joliet_level = 1;
561                               if (memcmp(sup->escape, "%/C", 3) == 0)
562                                         joliet_level = 2;
563                               if (memcmp(sup->escape, "%/E", 3) == 0)
564                                         joliet_level = 3;
565                     }
566                     if (joliet_level != 0) {
567                               if (iso_makemp(isomp, supbp, NULL) == -1) {
568                                         error = EINVAL;
569                                         goto out;
570                               }
571                               isomp->im_joliet_level = joliet_level;
572                     }
573           }
574 
575           if (supbp != NULL) {
576                     brelse(supbp, 0);
577                     supbp = NULL;
578           }
579 
580           spec_node_setmountedfs(devvp, mp);
581 
582           return 0;
583 out:
584           if (bp)
585                     brelse(bp, 0);
586           if (pribp)
587                     brelse(pribp, 0);
588           if (supbp)
589                     brelse(supbp, 0);
590           if (isomp) {
591                     free(isomp, M_ISOFSMNT);
592                     mp->mnt_data = NULL;
593           }
594           return error;
595 }
596 
597 /*
598  * Make a filesystem operational.
599  * Nothing to do at the moment.
600  */
601 /* ARGSUSED */
602 int
cd9660_start(struct mount * mp,int flags)603 cd9660_start(struct mount *mp, int flags)
604 {
605           return 0;
606 }
607 
608 /*
609  * unmount system call
610  */
611 int
cd9660_unmount(struct mount * mp,int mntflags)612 cd9660_unmount(struct mount *mp, int mntflags)
613 {
614           struct iso_mnt *isomp;
615           int error, flags = 0;
616 
617           if (mntflags & MNT_FORCE)
618                     flags |= FORCECLOSE;
619           if ((error = vflush(mp, NULLVP, flags)) != 0)
620                     return (error);
621 
622           isomp = VFSTOISOFS(mp);
623 
624           if (isomp->im_devvp->v_type != VBAD)
625                     spec_node_setmountedfs(isomp->im_devvp, NULL);
626 
627           vn_lock(isomp->im_devvp, LK_EXCLUSIVE | LK_RETRY);
628           error = VOP_CLOSE(isomp->im_devvp, FREAD, NOCRED);
629           vput(isomp->im_devvp);
630           free(isomp, M_ISOFSMNT);
631           mp->mnt_data = NULL;
632           mp->mnt_flag &= ~MNT_LOCAL;
633           return (error);
634 }
635 
636 /*
637  * Return root of a filesystem
638  */
639 int
cd9660_root(struct mount * mp,int lktype,struct vnode ** vpp)640 cd9660_root(struct mount *mp, int lktype, struct vnode **vpp)
641 {
642           struct iso_mnt *imp = VFSTOISOFS(mp);
643           struct iso_directory_record *dp =
644               (struct iso_directory_record *)imp->root;
645           ino_t ino = isodirino(dp, imp);
646 
647           return cd9660_vget(mp, ino, lktype, vpp);
648 }
649 
650 /*
651  * Get file system statistics.
652  */
653 int
cd9660_statvfs(struct mount * mp,struct statvfs * sbp)654 cd9660_statvfs(struct mount *mp, struct statvfs *sbp)
655 {
656           struct iso_mnt *isomp;
657 
658           isomp = VFSTOISOFS(mp);
659 
660           sbp->f_bsize = isomp->logical_block_size;
661           sbp->f_frsize = sbp->f_bsize;
662           sbp->f_iosize = sbp->f_bsize; /* XXX */
663           sbp->f_blocks = isomp->volume_space_size;
664           sbp->f_bfree = 0; /* total free blocks */
665           sbp->f_bavail = 0; /* blocks free for non superuser */
666           sbp->f_bresvd = 0; /* total reserved blocks */
667           sbp->f_files =  0; /* total files */
668           sbp->f_ffree = 0; /* free file nodes */
669           sbp->f_favail = 0; /* free file nodes for non superuser */
670           sbp->f_fresvd = 0; /* reserved file nodes */
671           copy_statvfs_info(sbp, mp);
672           /* Use the first spare for flags: */
673           sbp->f_spare[0] = isomp->im_flags;
674           return 0;
675 }
676 
677 /* ARGSUSED */
678 int
cd9660_sync(struct mount * mp,int waitfor,kauth_cred_t cred)679 cd9660_sync(struct mount *mp, int waitfor, kauth_cred_t cred)
680 {
681           return 0;
682 }
683 
684 /*
685  * File handle to vnode
686  *
687  * Have to be really careful about stale file handles:
688  * - check that the inode number is in range
689  * - call iget() to get the locked inode
690  * - check for an unallocated inode (i_mode == 0)
691  * - check that the generation number matches
692  */
693 
694 struct ifid {
695           ushort    ifid_len;
696           ushort    ifid_pad;
697           ino_t     ifid_ino;
698 #ifdef    ISOFS_DBG
699           u_long    ifid_start;
700 #endif
701 };
702 
703 /* ARGSUSED */
704 int
cd9660_fhtovp(struct mount * mp,struct fid * fhp,int lktype,struct vnode ** vpp)705 cd9660_fhtovp(struct mount *mp, struct fid *fhp, int lktype, struct vnode **vpp)
706 {
707           struct ifid ifh;
708           struct iso_node *ip;
709           struct vnode *nvp;
710           int error;
711 
712           if (fhp->fid_len != sizeof(ifh))
713                     return EINVAL;
714 
715           memcpy(&ifh, fhp, sizeof(ifh));
716 #ifdef    ISOFS_DBG
717           printf("fhtovp: ino %"PRIu64", start %lu\n",
718               ifh.ifid_ino, ifh.ifid_start);
719 #endif
720 
721           if ((error = VFS_VGET(mp, ifh.ifid_ino, lktype, &nvp)) != 0) {
722                     *vpp = NULLVP;
723                     return (error);
724           }
725           ip = VTOI(nvp);
726           if (ip->inode.iso_mode == 0) {
727                     vput(nvp);
728                     *vpp = NULLVP;
729                     return (ESTALE);
730           }
731           *vpp = nvp;
732           return (0);
733 }
734 
735 int
cd9660_vget(struct mount * mp,ino_t ino,int lktype,struct vnode ** vpp)736 cd9660_vget(struct mount *mp, ino_t ino, int lktype, struct vnode **vpp)
737 {
738           int error;
739 
740           error = vcache_get(mp, &ino, sizeof(ino), vpp);
741           if (error)
742                     return error;
743           error = vn_lock(*vpp, lktype);
744           if (error) {
745                     vrele(*vpp);
746                     *vpp = NULL;
747                     return error;
748           }
749           return 0;
750 }
751 
752 int
cd9660_loadvnode(struct mount * mp,struct vnode * vp,const void * key,size_t key_len,const void ** new_key)753 cd9660_loadvnode(struct mount *mp, struct vnode *vp,
754     const void *key, size_t key_len, const void **new_key)
755 {
756           struct iso_mnt *imp;
757           struct iso_node *ip;
758           struct iso_directory_record *isodir;
759           struct buf *bp;
760           dev_t dev;
761           ino_t ino;
762           int lbn, off;
763           int error;
764 
765           KASSERT(key_len == sizeof(ino));
766           memcpy(&ino, key, key_len);
767           imp = VFSTOISOFS(mp);
768           dev = imp->im_dev;
769 
770           ip = pool_get(&cd9660_node_pool, PR_WAITOK);
771 
772           memset(ip, 0, sizeof(struct iso_node));
773           ip->i_vnode = vp;
774           ip->i_dev = dev;
775           ip->i_number = ino;
776           ip->i_mnt = imp;
777           ip->i_devvp = imp->im_devvp;
778 
779           lbn = cd9660_lblkno(imp, ino);
780           if (lbn >= imp->volume_space_size) {
781                     pool_put(&cd9660_node_pool, ip);
782                     printf("fhtovp: lbn exceed volume space %d\n", lbn);
783                     return (ESTALE);
784           }
785 
786           off = cd9660_blkoff(imp, ino);
787           if (off + ISO_DIRECTORY_RECORD_SIZE > imp->logical_block_size) {
788                     pool_put(&cd9660_node_pool, ip);
789                     printf("fhtovp: crosses block boundary %d\n",
790                         off + ISO_DIRECTORY_RECORD_SIZE);
791                     return (ESTALE);
792           }
793 
794           error = bread(imp->im_devvp,
795                           lbn << (imp->im_bshift - DEV_BSHIFT),
796                           imp->logical_block_size, 0, &bp);
797           if (error) {
798                     pool_put(&cd9660_node_pool, ip);
799                     printf("fhtovp: bread error %d\n",error);
800                     return (error);
801           }
802           isodir = (struct iso_directory_record *)((char *)bp->b_data + off);
803 
804           if (off + isonum_711(isodir->length) > imp->logical_block_size) {
805                     pool_put(&cd9660_node_pool, ip);
806                     brelse(bp, 0);
807                     printf("fhtovp: directory crosses block boundary %d[off=%d/len=%d]\n",
808                         off +isonum_711(isodir->length), off,
809                         isonum_711(isodir->length));
810                     return (ESTALE);
811           }
812 
813 #if 0
814           if (isonum_733(isodir->extent) +
815               isonum_711(isodir->ext_attr_length) != ifhp->ifid_start) {
816                     pool_put(&cd9660_node_pool, ip);
817                     if (bp != 0)
818                               brelse(bp, 0);
819                     printf("fhtovp: file start miss %d vs %d\n",
820                         isonum_733(isodir->extent) + isonum_711(isodir->ext_attr_length),
821                         ifhp->ifid_start);
822                     return (ESTALE);
823           }
824 #endif
825 
826           ip->iso_extent = isonum_733(isodir->extent);
827           ip->i_size = isonum_733(isodir->size);
828           ip->iso_start = isonum_711(isodir->ext_attr_length) + ip->iso_extent;
829 
830           vp->v_tag = VT_ISOFS;
831           vp->v_op = cd9660_vnodeop_p;
832           vp->v_data = ip;
833           genfs_node_init(vp, &cd9660_genfsops);
834 
835           /*
836            * Setup time stamp, attribute
837            */
838           switch (imp->iso_ftype) {
839           default:  /* ISO_FTYPE_9660 */
840               {
841                     struct buf *bp2;
842                     if ((imp->im_flags & ISOFSMNT_EXTATT)
843                         && (off = isonum_711(isodir->ext_attr_length)))
844                               cd9660_blkatoff(vp, -((off_t) off << imp->im_bshift),
845                                   NULL, &bp2);
846                     else
847                               bp2 = NULL;
848                     cd9660_defattr(isodir, ip, bp2);
849                     cd9660_deftstamp(isodir, ip, bp2);
850                     if (bp2)
851                               brelse(bp2, 0);
852                     break;
853               }
854           case ISO_FTYPE_RRIP:
855                     cd9660_rrip_analyze(isodir, ip, imp);
856                     break;
857           }
858 
859           brelse(bp, 0);
860 
861           /*
862            * Initialize the associated vnode
863            */
864           switch (vp->v_type = IFTOVT(ip->inode.iso_mode)) {
865           case VFIFO:
866                     vp->v_op = cd9660_fifoop_p;
867                     break;
868           case VCHR:
869           case VBLK:
870                     /*
871                      * if device, look at device number table for translation
872                      */
873                     vp->v_op = cd9660_specop_p;
874                     spec_node_init(vp, ip->inode.iso_rdev);
875                     break;
876           case VLNK:
877           case VNON:
878           case VSOCK:
879           case VDIR:
880           case VBAD:
881                     break;
882           case VREG:
883                     uvm_vnp_setsize(vp, ip->i_size);
884                     break;
885           }
886 
887           if (vp->v_type != VREG)
888                     uvm_vnp_setsize(vp, 0);
889 
890           if (ip->iso_extent == imp->root_extent)
891                     vp->v_vflag |= VV_ROOT;
892 
893           /*
894            * XXX need generation number?
895            */
896 
897           *new_key = &ip->i_number;
898           return 0;
899 }
900 
901 /*
902  * Vnode pointer to File handle
903  */
904 /* ARGSUSED */
905 int
cd9660_vptofh(struct vnode * vp,struct fid * fhp,size_t * fh_size)906 cd9660_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
907 {
908           struct iso_node *ip = VTOI(vp);
909           struct ifid ifh;
910 
911           if (*fh_size < sizeof(struct ifid)) {
912                     *fh_size = sizeof(struct ifid);
913                     return E2BIG;
914           }
915           *fh_size = sizeof(struct ifid);
916 
917           memset(&ifh, 0, sizeof(ifh));
918           ifh.ifid_len = sizeof(struct ifid);
919           ifh.ifid_ino = ip->i_number;
920 #ifdef    ISOFS_DBG
921           ifh.ifid_start = ip->iso_start;
922 #endif
923           memcpy(fhp, &ifh, sizeof(ifh));
924 
925 #ifdef    ISOFS_DBG
926           printf("vptofh: ino %"PRIu64", start %lu\n",
927               ifh.ifid_ino,ifh.ifid_start);
928 #endif
929           return 0;
930 }
931