cd9660.c revision 86158
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: head/lib/libstand/cd9660.c 86158 2001-11-06 22:31:10Z jhb $"); 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 <sys/dirent.h> 46#include <isofs/cd9660/iso.h> 47#include <isofs/cd9660/cd9660_rrip.h> 48 49#include "stand.h" 50 51#define SUSP_CONTINUATION "CE" 52#define SUSP_PRESENT "SP" 53#define SUSP_STOP "ST" 54#define SUSP_EXTREF "ER" 55#define RRIP_NAME "NM" 56 57typedef struct { 58 ISO_SUSP_HEADER h; 59 u_char signature [ISODCL ( 5, 6)]; 60 u_char len_skp [ISODCL ( 7, 7)]; /* 711 */ 61} ISO_SUSP_PRESENT; 62 63static int buf_read_file(struct open_file *f, char **buf_p, 64 size_t *size_p); 65static int cd9660_open(const char *path, struct open_file *f); 66static int cd9660_close(struct open_file *f); 67static int cd9660_read(struct open_file *f, void *buf, size_t size, 68 size_t *resid); 69static int cd9660_write(struct open_file *f, void *buf, size_t size, 70 size_t *resid); 71static off_t cd9660_seek(struct open_file *f, off_t offset, int where); 72static int cd9660_stat(struct open_file *f, struct stat *sb); 73static int cd9660_readdir(struct open_file *f, struct dirent *d); 74static int dirmatch(struct open_file *f, const char *path, 75 struct iso_directory_record *dp, int use_rrip, int lenskip); 76static int rrip_check(struct open_file *f, struct iso_directory_record *dp, 77 int *lenskip); 78static char *rrip_lookup_name(struct open_file *f, 79 struct iso_directory_record *dp, int lenskip, size_t *len); 80static ISO_SUSP_HEADER *susp_lookup_record(struct open_file *f, 81 const char *identifier, struct iso_directory_record *dp, 82 int lenskip); 83 84struct fs_ops cd9660_fsops = { 85 "cd9660", 86 cd9660_open, 87 cd9660_close, 88 cd9660_read, 89 cd9660_write, 90 cd9660_seek, 91 cd9660_stat, 92 cd9660_readdir 93}; 94 95#define F_ISDIR 0x0001 /* Directory */ 96#define F_ROOTDIR 0x0002 /* Root directory */ 97#define F_RR 0x0004 /* Rock Ridge on this volume */ 98 99struct file { 100 int f_flags; /* file flags */ 101 off_t f_off; /* Current offset within file */ 102 daddr_t f_bno; /* Starting block number */ 103 off_t f_size; /* Size of file */ 104 daddr_t f_buf_blkno; /* block number of data block */ 105 char *f_buf; /* buffer for data block */ 106 int f_susp_skip; /* len_skip for SUSP records */ 107}; 108 109struct ptable_ent { 110 char namlen [ISODCL( 1, 1)]; /* 711 */ 111 char extlen [ISODCL( 2, 2)]; /* 711 */ 112 char block [ISODCL( 3, 6)]; /* 732 */ 113 char parent [ISODCL( 7, 8)]; /* 722 */ 114 char name [1]; 115}; 116#define PTFIXSZ 8 117#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 118 119#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 120 121/* XXX these should be in the system headers */ 122static __inline int 123isonum_722(p) 124 u_char *p; 125{ 126 return (*p << 8)|p[1]; 127} 128 129static __inline int 130isonum_732(p) 131 u_char *p; 132{ 133 return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3]; 134} 135 136static ISO_SUSP_HEADER * 137susp_lookup_record(struct open_file *f, const char *identifier, 138 struct iso_directory_record *dp, int lenskip) 139{ 140 static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE]; 141 ISO_SUSP_HEADER *sh; 142 ISO_RRIP_CONT *shc; 143 char *p, *end; 144 int error; 145 size_t read; 146 147 p = dp->name + isonum_711(dp->name_len) + lenskip; 148 /* Names of even length have a padding byte after the name. */ 149 if ((isonum_711(dp->name_len) & 1) == 0) 150 p++; 151 end = (char *)dp + isonum_711(dp->length); 152 while (p + 3 < end) { 153 sh = (ISO_SUSP_HEADER *)p; 154 if (bcmp(sh->type, identifier, 2) == 0) 155 return (sh); 156 if (bcmp(sh->type, SUSP_STOP, 2) == 0) 157 return (NULL); 158 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) { 159 shc = (ISO_RRIP_CONT *)sh; 160 error = f->f_dev->dv_strategy(f->f_devdata, F_READ, 161 cdb2devb(isonum_733(shc->location)), 162 ISO_DEFAULT_BLOCK_SIZE, susp_buffer, &read); 163 164 /* Bail if it fails. */ 165 if (error != 0 || read != ISO_DEFAULT_BLOCK_SIZE) 166 return (NULL); 167 p = susp_buffer + isonum_733(shc->offset); 168 end = p + isonum_733(shc->length); 169 } else 170 /* Ignore this record and skip to the next. */ 171 p += isonum_711(sh->length); 172 } 173 return (NULL); 174} 175 176static char * 177rrip_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 207static int 208rrip_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 238static int 239dirmatch(struct open_file *f, const char *path, struct iso_directory_record *dp, 240 int use_rrip, int lenskip) 241{ 242 size_t len; 243 char *cp; 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 for (i = len; --i >= 0; path++, cp++) { 257 if (!*path || *path == '/') 258 break; 259 if (*path == *cp) 260 continue; 261 if (!icase && toupper(*path) == *cp) 262 continue; 263 return 0; 264 } 265 if (*path && *path != '/') 266 return 0; 267 /* 268 * Allow stripping of trailing dots and the version number. 269 * Note that this will find the first instead of the last version 270 * of a file. 271 */ 272 if (i >= 0 && (*cp == ';' || *cp == '.')) { 273 /* This is to prevent matching of numeric extensions */ 274 if (*cp == '.' && cp[1] != ';') 275 return 0; 276 while (--i >= 0) 277 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 278 return 0; 279 } 280 return 1; 281} 282 283static int 284cd9660_open(const char *path, struct open_file *f) 285{ 286 struct file *fp = 0; 287 void *buf; 288 struct iso_primary_descriptor *vd; 289 size_t buf_size, read, dsize, off; 290 daddr_t bno, boff; 291 struct iso_directory_record rec; 292 struct iso_directory_record *dp = 0; 293 int rc, first, use_rrip, lenskip; 294 295 /* First find the volume descriptor */ 296 buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 297 vd = buf; 298 for (bno = 16;; bno++) { 299 twiddle(); 300 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 301 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 302 if (rc) 303 goto out; 304 if (read != ISO_DEFAULT_BLOCK_SIZE) { 305 rc = EIO; 306 goto out; 307 } 308 rc = EINVAL; 309 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 310 goto out; 311 if (isonum_711(vd->type) == ISO_VD_END) 312 goto out; 313 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 314 break; 315 } 316 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 317 goto out; 318 319 rec = *(struct iso_directory_record *) vd->root_directory_record; 320 if (*path == '/') path++; /* eat leading '/' */ 321 322 first = 1; 323 use_rrip = 0; 324 while (*path) { 325 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 326 dsize = isonum_733(rec.size); 327 off = 0; 328 boff = 0; 329 330 while (off < dsize) { 331 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 332 twiddle(); 333 rc = f->f_dev->dv_strategy 334 (f->f_devdata, F_READ, 335 cdb2devb(bno + boff), 336 ISO_DEFAULT_BLOCK_SIZE, 337 buf, &read); 338 if (rc) 339 goto out; 340 if (read != ISO_DEFAULT_BLOCK_SIZE) { 341 rc = EIO; 342 goto out; 343 } 344 boff++; 345 dp = (struct iso_directory_record *) buf; 346 } 347 if (isonum_711(dp->length) == 0) { 348 /* skip to next block, if any */ 349 off = boff * ISO_DEFAULT_BLOCK_SIZE; 350 continue; 351 } 352 353 /* See if RRIP is in use. */ 354 if (first) 355 use_rrip = rrip_check(f, dp, &lenskip); 356 357 if (dirmatch(f, path, dp, use_rrip, 358 first ? 0 : lenskip)) { 359 first = 0; 360 break; 361 } else 362 first = 0; 363 364 dp = (struct iso_directory_record *) 365 ((char *) dp + isonum_711(dp->length)); 366 off += isonum_711(dp->length); 367 } 368 if (off >= dsize) { 369 rc = ENOENT; 370 goto out; 371 } 372 373 rec = *dp; 374 while (*path && *path != '/') /* look for next component */ 375 path++; 376 if (*path) path++; /* skip '/' */ 377 } 378 379 /* allocate file system specific data structure */ 380 fp = malloc(sizeof(struct file)); 381 bzero(fp, sizeof(struct file)); 382 f->f_fsdata = (void *)fp; 383 384 if ((isonum_711(rec.flags) & 2) != 0) { 385 fp->f_flags = F_ISDIR; 386 } 387 if (first) { 388 fp->f_flags |= F_ROOTDIR; 389 390 /* Check for Rock Ridge since we didn't in the loop above. */ 391 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 392 twiddle(); 393 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 394 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 395 if (rc) 396 goto out; 397 if (read != ISO_DEFAULT_BLOCK_SIZE) { 398 rc = EIO; 399 goto out; 400 } 401 dp = (struct iso_directory_record *)buf; 402 use_rrip = rrip_check(f, dp, &lenskip); 403 } 404 if (use_rrip) { 405 fp->f_flags |= F_RR; 406 fp->f_susp_skip = lenskip; 407 } 408 fp->f_off = 0; 409 fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 410 fp->f_size = isonum_733(rec.size); 411 free(buf); 412 413 return 0; 414 415out: 416 if (fp) 417 free(fp); 418 free(buf); 419 420 return rc; 421} 422 423static int 424cd9660_close(struct open_file *f) 425{ 426 struct file *fp = (struct file *)f->f_fsdata; 427 428 f->f_fsdata = 0; 429 free(fp); 430 431 return 0; 432} 433 434static int 435buf_read_file(struct open_file *f, char **buf_p, size_t *size_p) 436{ 437 struct file *fp = (struct file *)f->f_fsdata; 438 daddr_t blkno, blkoff; 439 int rc = 0; 440 size_t read; 441 442 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno; 443 blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE; 444 445 if (blkno != fp->f_buf_blkno) { 446 if (fp->f_buf == (char *)0) 447 fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE); 448 449 twiddle(); 450 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, 451 cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read); 452 if (rc) 453 return (rc); 454 if (read != ISO_DEFAULT_BLOCK_SIZE) 455 return (EIO); 456 457 fp->f_buf_blkno = blkno; 458 } 459 460 *buf_p = fp->f_buf + blkoff; 461 *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff; 462 463 if (*size_p > fp->f_size - fp->f_off) 464 *size_p = fp->f_size - fp->f_off; 465 return (rc); 466} 467 468static int 469cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid) 470{ 471 struct file *fp = (struct file *)f->f_fsdata; 472 char *buf, *addr; 473 size_t buf_size, csize; 474 int rc = 0; 475 476 addr = start; 477 while (size) { 478 if (fp->f_off < 0 || fp->f_off >= fp->f_size) 479 break; 480 481 rc = buf_read_file(f, &buf, &buf_size); 482 if (rc) 483 break; 484 485 csize = size > buf_size ? buf_size : size; 486 bcopy(buf, addr, csize); 487 488 fp->f_off += csize; 489 addr += csize; 490 size -= csize; 491 } 492 if (resid) 493 *resid = size; 494 return (rc); 495} 496 497static int 498cd9660_readdir(struct open_file *f, struct dirent *d) 499{ 500 struct file *fp = (struct file *)f->f_fsdata; 501 struct iso_directory_record *ep; 502 size_t buf_size, reclen, namelen; 503 int error = 0; 504 int lenskip; 505 char *buf, *name; 506 507again: 508 if (fp->f_off >= fp->f_size) 509 return (ENOENT); 510 error = buf_read_file(f, &buf, &buf_size); 511 if (error) 512 return (error); 513 ep = (struct iso_directory_record *)buf; 514 515 if (isonum_711(ep->length) == 0) { 516 daddr_t blkno; 517 518 /* skip to next block, if any */ 519 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE; 520 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE; 521 goto again; 522 } 523 524 if (fp->f_flags & F_RR) { 525 if (fp->f_flags & F_ROOTDIR && fp->f_off == 0) 526 lenskip = 0; 527 else 528 lenskip = fp->f_susp_skip; 529 name = rrip_lookup_name(f, ep, lenskip, &namelen); 530 } else 531 name = NULL; 532 if (name == NULL) { 533 namelen = isonum_711(ep->name_len); 534 name = ep->name; 535 if (namelen == 1) { 536 if (ep->name[0] == 0) 537 name = "."; 538 else if (ep->name[0] == 1) { 539 namelen = 2; 540 name = ".."; 541 } 542 } 543 } 544 reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1; 545 reclen = (reclen + 3) & ~3; 546 547 d->d_fileno = isonum_733(ep->extent); 548 d->d_reclen = reclen; 549 if (isonum_711(ep->flags) & 2) 550 d->d_type = DT_DIR; 551 else 552 d->d_type = DT_REG; 553 d->d_namlen = namelen; 554 555 bcopy(name, d->d_name, d->d_namlen); 556 d->d_name[d->d_namlen] = 0; 557 558 fp->f_off += isonum_711(ep->length); 559 return (0); 560} 561 562static int 563cd9660_write(struct open_file *f, void *start, size_t size, size_t *resid) 564{ 565 return EROFS; 566} 567 568static off_t 569cd9660_seek(struct open_file *f, off_t offset, int where) 570{ 571 struct file *fp = (struct file *)f->f_fsdata; 572 573 switch (where) { 574 case SEEK_SET: 575 fp->f_off = offset; 576 break; 577 case SEEK_CUR: 578 fp->f_off += offset; 579 break; 580 case SEEK_END: 581 fp->f_off = fp->f_size - offset; 582 break; 583 default: 584 return -1; 585 } 586 return fp->f_off; 587} 588 589static int 590cd9660_stat(struct open_file *f, struct stat *sb) 591{ 592 struct file *fp = (struct file *)f->f_fsdata; 593 594 /* only important stuff */ 595 sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH; 596 if (fp->f_flags & F_ISDIR) 597 sb->st_mode |= S_IFDIR; 598 else 599 sb->st_mode |= S_IFREG; 600 sb->st_uid = sb->st_gid = 0; 601 sb->st_size = fp->f_size; 602 return 0; 603} 604