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