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