]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/libsa/cd9660.c
MFV: less: Do not trust st_size if it equals zero
[FreeBSD/FreeBSD.git] / stand / libsa / cd9660.c
1 /*      $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $      */
2
3 /*
4  * Copyright (C) 1996 Wolfgang Solfrank.
5  * Copyright (C) 1996 TooLs GmbH.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by TooLs GmbH.
19  * 4. The name of TooLs GmbH may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 /*
35  * Stand-alone ISO9660 file reading package.
36  *
37  * Note: This doesn't support Rock Ridge extensions, extended attributes,
38  * blocksizes other than 2048 bytes, multi-extent files, etc.
39  */
40 #include <sys/param.h>
41 #include <string.h>
42 #include <stdbool.h>
43 #include <sys/dirent.h>
44 #include <fs/cd9660/iso.h>
45 #include <fs/cd9660/cd9660_rrip.h>
46
47 #include "stand.h"
48
49 #define SUSP_CONTINUATION       "CE"
50 #define SUSP_PRESENT            "SP"
51 #define SUSP_STOP               "ST"
52 #define SUSP_EXTREF             "ER"
53 #define RRIP_NAME               "NM"
54
55 typedef struct {
56         ISO_SUSP_HEADER         h;
57         u_char signature        [ISODCL (  5,    6)];
58         u_char len_skp          [ISODCL (  7,    7)]; /* 711 */
59 } ISO_SUSP_PRESENT;
60         
61 static int      buf_read_file(struct open_file *f, char **buf_p,
62                     size_t *size_p);
63 static int      cd9660_open(const char *path, struct open_file *f);
64 static int      cd9660_close(struct open_file *f);
65 static int      cd9660_read(struct open_file *f, void *buf, size_t size,
66                     size_t *resid);
67 static off_t    cd9660_seek(struct open_file *f, off_t offset, int where);
68 static int      cd9660_stat(struct open_file *f, struct stat *sb);
69 static int      cd9660_readdir(struct open_file *f, struct dirent *d);
70 static int      cd9660_mount(const char *, const char *, void **);
71 static int      cd9660_unmount(const char *, void *);
72 static int      dirmatch(struct open_file *f, const char *path,
73                     struct iso_directory_record *dp, int use_rrip, int lenskip);
74 static int      rrip_check(struct open_file *f, struct iso_directory_record *dp,
75                     int *lenskip);
76 static char     *rrip_lookup_name(struct open_file *f,
77                     struct iso_directory_record *dp, int lenskip, size_t *len);
78 static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f,
79                     const char *identifier, struct iso_directory_record *dp,
80                     int lenskip);
81
82 struct fs_ops cd9660_fsops = {
83         .fs_name = "cd9660",
84         .fo_open = cd9660_open,
85         .fo_close = cd9660_close,
86         .fo_read = cd9660_read,
87         .fo_write = null_write,
88         .fo_seek = cd9660_seek,
89         .fo_stat = cd9660_stat,
90         .fo_readdir = cd9660_readdir,
91         .fo_mount = cd9660_mount,
92         .fo_unmount = cd9660_unmount
93 };
94
95 typedef struct cd9660_mnt {
96         struct devdesc                  *cd_dev;
97         int                             cd_fd;
98         struct iso_directory_record     cd_rec;
99         STAILQ_ENTRY(cd9660_mnt)        cd_link;
100 } cd9660_mnt_t;
101
102 typedef STAILQ_HEAD(cd9660_mnt_list, cd9660_mnt) cd9660_mnt_list_t;
103 static cd9660_mnt_list_t mnt_list = STAILQ_HEAD_INITIALIZER(mnt_list);
104
105 #define F_ISDIR         0x0001          /* Directory */
106 #define F_ROOTDIR       0x0002          /* Root directory */
107 #define F_RR            0x0004          /* Rock Ridge on this volume */
108
109 struct file {
110         int             f_flags;        /* file flags */
111         off_t           f_off;          /* Current offset within file */
112         daddr_t         f_bno;          /* Starting block number */
113         off_t           f_size;         /* Size of file */
114         daddr_t         f_buf_blkno;    /* block number of data block */        
115         char            *f_buf;         /* buffer for data block */
116         int             f_susp_skip;    /* len_skip for SUSP records */
117 };
118
119 struct ptable_ent {
120         char namlen     [ISODCL( 1, 1)];        /* 711 */
121         char extlen     [ISODCL( 2, 2)];        /* 711 */
122         char block      [ISODCL( 3, 6)];        /* 732 */
123         char parent     [ISODCL( 7, 8)];        /* 722 */
124         char name       [1];
125 };
126 #define PTFIXSZ         8
127 #define PTSIZE(pp)      roundup(PTFIXSZ + isonum_711((pp)->namlen), 2)
128
129 #define cdb2devb(bno)   ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE)
130
131 static ISO_SUSP_HEADER *
132 susp_lookup_record(struct open_file *f, const char *identifier,
133     struct iso_directory_record *dp, int lenskip)
134 {
135         static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE];
136         ISO_SUSP_HEADER *sh;
137         ISO_RRIP_CONT *shc;
138         char *p, *end;
139         int error;
140         size_t read;
141
142         p = dp->name + isonum_711(dp->name_len) + lenskip;
143         /* Names of even length have a padding byte after the name. */
144         if ((isonum_711(dp->name_len) & 1) == 0)
145                 p++;
146         end = (char *)dp + isonum_711(dp->length);
147         while (p + 3 < end) {
148                 sh = (ISO_SUSP_HEADER *)p;
149                 if (bcmp(sh->type, identifier, 2) == 0)
150                         return (sh);
151                 if (bcmp(sh->type, SUSP_STOP, 2) == 0)
152                         return (NULL);
153                 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) {
154                         shc = (ISO_RRIP_CONT *)sh;
155                         error = f->f_dev->dv_strategy(f->f_devdata, F_READ,
156                             cdb2devb(isonum_733(shc->location)),
157                             ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read);
158
159                         /* Bail if it fails. */
160                         if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE)
161                                 return (NULL);
162                         p = susp_buffer + isonum_733(shc->offset);
163                         end = p + isonum_733(shc->length);
164                 } else {
165                         /* Ignore this record and skip to the next. */
166                         p += isonum_711(sh->length);
167
168                         /* Avoid infinite loops with corrupted file systems */
169                         if (isonum_711(sh->length) == 0)
170                                 return (NULL);
171                 }
172         }
173         return (NULL);
174 }
175
176 static char *
177 rrip_lookup_name(struct open_file *f, struct iso_directory_record *dp,
178     int lenskip, size_t *len)
179 {
180         ISO_RRIP_ALTNAME *p;
181
182         if (len == NULL)
183                 return (NULL);
184
185         p = (ISO_RRIP_ALTNAME *)susp_lookup_record(f, RRIP_NAME, dp, lenskip);
186         if (p == NULL)
187                 return (NULL);
188         switch (*p->flags) {
189         case ISO_SUSP_CFLAG_CURRENT:
190                 *len = 1;
191                 return (".");
192         case ISO_SUSP_CFLAG_PARENT:
193                 *len = 2;
194                 return ("..");
195         case 0:
196                 *len = isonum_711(p->h.length) - 5;
197                 return ((char *)p + 5);
198         default:
199                 /*
200                  * We don't handle hostnames or continued names as they are
201                  * too hard, so just bail and use the default name.
202                  */
203                 return (NULL);
204         }
205 }
206
207 static int
208 rrip_check(struct open_file *f, struct iso_directory_record *dp, int *lenskip)
209 {
210         ISO_SUSP_PRESENT *sp;
211         ISO_RRIP_EXTREF *er;
212         char *p;
213
214         /* First, see if we can find a SP field. */
215         p = dp->name + isonum_711(dp->name_len);
216         if (p > (char *)dp + isonum_711(dp->length))
217                 return (0);
218         sp = (ISO_SUSP_PRESENT *)p;
219         if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0)
220                 return (0);
221         if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT))
222                 return (0);
223         if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef)
224                 return (0);
225         *lenskip = isonum_711(sp->len_skp);
226
227         /*
228          * Now look for an ER field.  If RRIP is present, then there must
229          * be at least one of these.  It would be more pedantic to walk
230          * through the list of fields looking for a Rock Ridge ER field.
231          */
232         er = (ISO_RRIP_EXTREF *)susp_lookup_record(f, SUSP_EXTREF, dp, 0);
233         if (er == NULL)
234                 return (0);
235         return (1);
236 }
237
238 static int
239 dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp,
240     int use_rrip, int lenskip)
241 {
242         size_t len, plen;
243         char *cp, *sep;
244         int i, icase;
245
246         if (use_rrip)
247                 cp = rrip_lookup_name(f, dp, lenskip, &len);
248         else
249                 cp = NULL;
250         if (cp == NULL) {
251                 len = isonum_711(dp->name_len);
252                 cp = dp->name;
253                 icase = 1;
254         } else
255                 icase = 0;
256
257         sep = strchr(path, '/');
258         if (sep != NULL) {
259                 plen = sep - path;
260         } else {
261                 plen = strlen(path);
262         }
263
264         if (plen != len)
265                 return (0);
266
267         for (i = len; --i >= 0; path++, cp++) {
268                 if (!*path || *path == '/')
269                         break;
270                 if (*path == *cp)
271                         continue;
272                 if (!icase && toupper(*path) == *cp)
273                         continue;
274                 return 0;
275         }
276         if (*path && *path != '/')
277                 return 0;
278         /*
279          * Allow stripping of trailing dots and the version number.
280          * Note that this will find the first instead of the last version
281          * of a file.
282          */
283         if (i >= 0 && (*cp == ';' || *cp == '.')) {
284                 /* This is to prevent matching of numeric extensions */
285                 if (*cp == '.' && cp[1] != ';')
286                         return 0;
287                 while (--i >= 0)
288                         if (*++cp != ';' && (*cp < '0' || *cp > '9'))
289                                 return 0;
290         }
291         return 1;
292 }
293
294 static int
295 cd9660_read_dr(struct open_file *f, struct iso_directory_record *rec)
296 {
297         struct iso_primary_descriptor *vd;
298         size_t read;
299         daddr_t bno;
300         int rc;
301
302         errno = 0;
303         vd = malloc(MAX(ISO_DEFAULT_BLOCK_SIZE,
304             sizeof(struct iso_primary_descriptor)));
305         if (vd == NULL)
306                 return (errno);
307
308         for (bno = 16;; bno++) {
309                 twiddle(1);
310                 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
311                     ISO_DEFAULT_BLOCK_SIZE, (char *)vd, &read);
312                 if (rc)
313                         goto out;
314                 if (read != ISO_DEFAULT_BLOCK_SIZE) {
315                         rc = EIO;
316                         goto out;
317                 }
318                 rc = EINVAL;
319                 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof(vd->id)) != 0)
320                         goto out;
321                 if (isonum_711(vd->type) == ISO_VD_END)
322                         goto out;
323                 if (isonum_711(vd->type) == ISO_VD_PRIMARY)
324                         break;
325         }
326         if (isonum_723(vd->logical_block_size) == ISO_DEFAULT_BLOCK_SIZE) {
327                 bcopy(vd->root_directory_record, rec, sizeof(*rec));
328                 rc = 0;
329         }
330 out:
331         free(vd);
332         return (rc);
333 }
334
335 static int
336 cd9660_open(const char *path, struct open_file *f)
337 {
338         struct file *fp = NULL;
339         void *buf;
340         size_t read, dsize, off;
341         daddr_t bno, boff;
342         struct iso_directory_record rec;
343         struct iso_directory_record *dp = NULL;
344         int rc, first, use_rrip, lenskip;
345         bool isdir = false;
346         struct devdesc *dev;
347         cd9660_mnt_t *mnt;
348
349         /* First find the volume descriptor */
350         errno = 0;
351         buf = malloc(MAX(ISO_DEFAULT_BLOCK_SIZE,
352             sizeof(struct iso_primary_descriptor)));
353         if (buf == NULL)
354                 return (errno);
355
356         dev = f->f_devdata;
357         STAILQ_FOREACH(mnt, &mnt_list, cd_link) {
358                 if (dev->d_dev->dv_type == mnt->cd_dev->d_dev->dv_type &&
359                     dev->d_unit == mnt->cd_dev->d_unit)
360                         break;
361         }
362
363         rc = 0;
364         if (mnt == NULL)
365                 rc = cd9660_read_dr(f, &rec);
366         else
367                 rec = mnt->cd_rec;
368
369         if (rc != 0)
370                 goto out;
371
372         if (*path == '/')
373                 path++; /* eat leading '/' */
374
375         first = 1;
376         use_rrip = 0;
377         lenskip = 0;
378         while (*path) {
379                 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
380                 dsize = isonum_733(rec.size);
381                 off = 0;
382                 boff = 0;
383
384                 while (off < dsize) {
385                         if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) {
386                                 twiddle(1);
387                                 rc = f->f_dev->dv_strategy
388                                         (f->f_devdata, F_READ,
389                                          cdb2devb(bno + boff),
390                                          ISO_DEFAULT_BLOCK_SIZE,
391                                          buf, &read);
392                                 if (rc)
393                                         goto out;
394                                 if (read != ISO_DEFAULT_BLOCK_SIZE) {
395                                         rc = EIO;
396                                         goto out;
397                                 }
398                                 boff++;
399                                 dp = (struct iso_directory_record *) buf;
400                         }
401                         if (isonum_711(dp->length) == 0) {
402                             /* skip to next block, if any */
403                             off = boff * ISO_DEFAULT_BLOCK_SIZE;
404                             continue;
405                         }
406
407                         /* See if RRIP is in use. */
408                         if (first)
409                                 use_rrip = rrip_check(f, dp, &lenskip);
410
411                         if (dirmatch(f, path, dp, use_rrip,
412                             first ? 0 : lenskip)) {
413                                 first = 0;
414                                 break;
415                         } else
416                                 first = 0;
417
418                         dp = (struct iso_directory_record *)
419                                 ((char *) dp + isonum_711(dp->length));
420                         /* If the new block has zero length, it is padding. */
421                         if (isonum_711(dp->length) == 0) {
422                                 /* Skip to next block, if any. */
423                                 off = boff * ISO_DEFAULT_BLOCK_SIZE;
424                                 continue;
425                         }
426                         off += isonum_711(dp->length);
427                 }
428                 if (off >= dsize) {
429                         rc = ENOENT;
430                         goto out;
431                 }
432
433                 rec = *dp;
434                 while (*path && *path != '/') /* look for next component */
435                         path++;
436
437                 if (*path)      /* this component was directory */
438                         isdir = true;
439
440                 while (*path == '/')
441                         path++; /* skip '/' */
442
443                 if (*path)      /* We do have next component. */
444                         isdir = false;
445         }
446
447         /*
448          * if the path had trailing / but the path does point to file,
449          * report the error ENOTDIR.
450          */
451         if (isdir == true && (isonum_711(rec.flags) & 2) == 0) {
452                 rc = ENOTDIR;
453                 goto out;
454         }
455
456         /* allocate file system specific data structure */
457         fp = malloc(sizeof(struct file));
458         bzero(fp, sizeof(struct file));
459         f->f_fsdata = (void *)fp;
460
461         if ((isonum_711(rec.flags) & 2) != 0) {
462                 fp->f_flags = F_ISDIR;
463         }
464         if (first) {
465                 fp->f_flags |= F_ROOTDIR;
466
467                 /* Check for Rock Ridge since we didn't in the loop above. */
468                 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
469                 twiddle(1);
470                 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno),
471                     ISO_DEFAULT_BLOCK_SIZE, buf, &read);
472                 if (rc)
473                         goto out;
474                 if (read != ISO_DEFAULT_BLOCK_SIZE) {
475                         rc = EIO;
476                         goto out;
477                 }
478                 dp = (struct iso_directory_record *)buf;
479                 use_rrip = rrip_check(f, dp, &lenskip);
480         }
481         if (use_rrip) {
482                 fp->f_flags |= F_RR;
483                 fp->f_susp_skip = lenskip;
484         }
485         fp->f_off = 0;
486         fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length);
487         fp->f_size = isonum_733(rec.size);
488         free(buf);
489
490         return 0;
491
492 out:
493         free(fp);
494         free(buf);
495
496         return rc;
497 }
498
499 static int
500 cd9660_close(struct open_file *f)
501 {
502         struct file *fp = (struct file *)f->f_fsdata;
503
504         f->f_fsdata = NULL;
505         free(fp);
506
507         return 0;
508 }
509
510 static int
511 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
512 {
513         struct file *fp = (struct file *)f->f_fsdata;
514         daddr_t blkno, blkoff;
515         int rc = 0;
516         size_t read;
517
518         blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno;
519         blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE;
520
521         if (blkno != fp->f_buf_blkno) {
522                 if (fp->f_buf == (char *)0)
523                         fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE);
524
525                 twiddle(16);
526                 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ,
527                     cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE,
528                     fp->f_buf, &read);
529                 if (rc)
530                         return (rc);
531                 if (read != ISO_DEFAULT_BLOCK_SIZE)
532                         return (EIO);
533
534                 fp->f_buf_blkno = blkno;
535         }
536
537         *buf_p = fp->f_buf + blkoff;
538         *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff;
539
540         if (*size_p > fp->f_size - fp->f_off)
541                 *size_p = fp->f_size - fp->f_off;
542         return (rc);
543 }
544
545 static int
546 cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid)
547 {
548         struct file *fp = (struct file *)f->f_fsdata;
549         char *buf, *addr;
550         size_t buf_size, csize;
551         int rc = 0;
552
553         addr = start;
554         while (size) {
555                 if (fp->f_off < 0 || fp->f_off >= fp->f_size)
556                         break;
557
558                 rc = buf_read_file(f, &buf, &buf_size);
559                 if (rc)
560                         break;
561
562                 csize = size > buf_size ? buf_size : size;
563                 bcopy(buf, addr, csize);
564
565                 fp->f_off += csize;
566                 addr += csize;
567                 size -= csize;
568         }
569         if (resid)
570                 *resid = size;
571         return (rc);
572 }
573
574 static int
575 cd9660_readdir(struct open_file *f, struct dirent *d)
576 {
577         struct file *fp = (struct file *)f->f_fsdata;
578         struct iso_directory_record *ep;
579         size_t buf_size, reclen, namelen;
580         int error = 0;
581         int lenskip;
582         char *buf, *name;
583
584 again:
585         if (fp->f_off >= fp->f_size)
586                 return (ENOENT);
587         error = buf_read_file(f, &buf, &buf_size);
588         if (error)
589                 return (error);
590         ep = (struct iso_directory_record *)buf;
591
592         if (isonum_711(ep->length) == 0) {
593                 daddr_t blkno;
594                 
595                 /* skip to next block, if any */
596                 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE;
597                 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE;
598                 goto again;
599         }
600
601         if (fp->f_flags & F_RR) {
602                 if (fp->f_flags & F_ROOTDIR && fp->f_off == 0)
603                         lenskip = 0;
604                 else
605                         lenskip = fp->f_susp_skip;
606                 name = rrip_lookup_name(f, ep, lenskip, &namelen);
607         } else
608                 name = NULL;
609         if (name == NULL) {
610                 namelen = isonum_711(ep->name_len);
611                 name = ep->name;
612                 if (namelen == 1) {
613                         if (ep->name[0] == 0)
614                                 name = ".";
615                         else if (ep->name[0] == 1) {
616                                 namelen = 2;
617                                 name = "..";
618                         }
619                 }
620         }
621         reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1;
622         reclen = (reclen + 3) & ~3;
623
624         d->d_fileno = isonum_733(ep->extent);
625         d->d_reclen = reclen;
626         if (isonum_711(ep->flags) & 2)
627                 d->d_type = DT_DIR;
628         else
629                 d->d_type = DT_REG;
630         d->d_namlen = namelen;
631
632         bcopy(name, d->d_name, d->d_namlen);
633         d->d_name[d->d_namlen] = 0;
634
635         fp->f_off += isonum_711(ep->length);
636         return (0);
637 }
638
639 static off_t
640 cd9660_seek(struct open_file *f, off_t offset, int where)
641 {
642         struct file *fp = (struct file *)f->f_fsdata;
643
644         switch (where) {
645         case SEEK_SET:
646                 fp->f_off = offset;
647                 break;
648         case SEEK_CUR:
649                 fp->f_off += offset;
650                 break;
651         case SEEK_END:
652                 fp->f_off = fp->f_size - offset;
653                 break;
654         default:
655                 return -1;
656         }
657         return fp->f_off;
658 }
659
660 static int
661 cd9660_stat(struct open_file *f, struct stat *sb)
662 {
663         struct file *fp = (struct file *)f->f_fsdata;
664
665         /* only important stuff */
666         sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
667         if (fp->f_flags & F_ISDIR)
668                 sb->st_mode |= S_IFDIR;
669         else
670                 sb->st_mode |= S_IFREG;
671         sb->st_uid = sb->st_gid = 0;
672         sb->st_size = fp->f_size;
673         return 0;
674 }
675
676 static int
677 cd9660_mount(const char *dev, const char *path, void **data)
678 {
679         cd9660_mnt_t *mnt;
680         struct open_file *f;
681         char *fs;
682
683         errno = 0;
684         mnt = calloc(1, sizeof(*mnt));
685         if (mnt == NULL)
686                 return (errno);
687         mnt->cd_fd = -1;
688
689         if (asprintf(&fs, "%s%s", dev, path) < 0)
690                 goto done;
691
692         mnt->cd_fd = open(fs, O_RDONLY);
693         free(fs);
694         if (mnt->cd_fd == -1)
695                 goto done;
696
697         f = fd2open_file(mnt->cd_fd);
698         /* Is it cd9660 file system? */
699         if (strcmp(f->f_ops->fs_name, "cd9660") == 0) {
700                 mnt->cd_dev = f->f_devdata;
701                 errno = cd9660_read_dr(f, &mnt->cd_rec);
702                 STAILQ_INSERT_TAIL(&mnt_list, mnt, cd_link);
703         } else {
704                 errno = ENXIO;
705         }
706
707 done:
708         if (errno != 0) {
709                 free(mnt->cd_dev);
710                 if (mnt->cd_fd >= 0)
711                         close(mnt->cd_fd);
712                 free(mnt);
713         } else {
714                 *data = mnt;
715         }
716         return (errno);
717 }
718
719 static int
720 cd9660_unmount(const char *dev __unused, void *data)
721 {
722         cd9660_mnt_t *mnt = data;
723
724         STAILQ_REMOVE(&mnt_list, mnt, cd9660_mnt, cd_link);
725         close(mnt->cd_fd);
726         free(mnt);
727         return (0);
728 }