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 } 729