cd9660.c revision 56222
12258Scsgr/* $FreeBSD: head/lib/libstand/cd9660.c 56222 2000-01-18 07:37:10Z obrien $ */ 22258Scsgr/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */ 32258Scsgr 42258Scsgr/* 52258Scsgr * Copyright (C) 1996 Wolfgang Solfrank. 62258Scsgr * Copyright (C) 1996 TooLs GmbH. 72258Scsgr * All rights reserved. 82258Scsgr * 92258Scsgr * Redistribution and use in source and binary forms, with or without 102258Scsgr * modification, are permitted provided that the following conditions 112258Scsgr * are met: 122258Scsgr * 1. Redistributions of source code must retain the above copyright 132258Scsgr * notice, this list of conditions and the following disclaimer. 142258Scsgr * 2. Redistributions in binary form must reproduce the above copyright 152258Scsgr * notice, this list of conditions and the following disclaimer in the 162258Scsgr * documentation and/or other materials provided with the distribution. 172258Scsgr * 3. All advertising materials mentioning features or use of this software 182258Scsgr * must display the following acknowledgement: 192258Scsgr * This product includes software developed by TooLs GmbH. 202258Scsgr * 4. The name of TooLs GmbH may not be used to endorse or promote products 212258Scsgr * derived from this software without specific prior written permission. 222258Scsgr * 232258Scsgr * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 242258Scsgr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 252258Scsgr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 262258Scsgr * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 272258Scsgr * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 282258Scsgr * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 292258Scsgr * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 302258Scsgr * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 312258Scsgr * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 322258Scsgr * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 332258Scsgr */ 342258Scsgr 352258Scsgr/* 362258Scsgr * Stand-alone ISO9660 file reading package. 372258Scsgr * 382258Scsgr * Note: This doesn't support Rock Ridge extensions, extended attributes, 392258Scsgr * blocksizes other than 2048 bytes, multi-extent files, etc. 402258Scsgr */ 412258Scsgr#include <sys/param.h> 422258Scsgr#include <string.h> 432258Scsgr#include <sys/dirent.h> 442258Scsgr#include <isofs/cd9660/iso.h> 452258Scsgr 462258Scsgr#include "stand.h" 472258Scsgr 482258Scsgrstatic int cd9660_open(const char *path, struct open_file *f); 492258Scsgrstatic int cd9660_close(struct open_file *f); 502258Scsgrstatic int cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid); 512258Scsgrstatic int cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid); 522258Scsgrstatic off_t cd9660_seek(struct open_file *f, off_t offset, int where); 532258Scsgrstatic int cd9660_stat(struct open_file *f, struct stat *sb); 542258Scsgr 552258Scsgrstruct fs_ops cd9660_fsops = { 562258Scsgr "cd9660", cd9660_open, cd9660_close, cd9660_read, cd9660_write, cd9660_seek, cd9660_stat 572258Scsgr}; 582258Scsgr 592258Scsgrstruct file { 602258Scsgr int isdir; /* nonzero if file is directory */ 612258Scsgr off_t off; /* Current offset within file */ 622258Scsgr daddr_t bno; /* Starting block number */ 632258Scsgr off_t size; /* Size of file */ 642258Scsgr}; 652258Scsgr 662258Scsgrstruct ptable_ent { 672258Scsgr char namlen [ISODCL( 1, 1)]; /* 711 */ 682258Scsgr char extlen [ISODCL( 2, 2)]; /* 711 */ 692258Scsgr char block [ISODCL( 3, 6)]; /* 732 */ 702258Scsgr char parent [ISODCL( 7, 8)]; /* 722 */ 712258Scsgr char name [1]; 722258Scsgr}; 732258Scsgr#define PTFIXSZ 8 742258Scsgr#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 752258Scsgr 762258Scsgr#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 772258Scsgr 782258Scsgr/* XXX these should be in the system headers */ 792258Scsgrstatic __inline int 802258Scsgrisonum_722(p) 812258Scsgr u_char *p; 822258Scsgr{ 832258Scsgr return (*p << 8)|p[1]; 842258Scsgr} 852258Scsgr 862258Scsgrstatic __inline int 872258Scsgrisonum_732(p) 882258Scsgr u_char *p; 892258Scsgr{ 902258Scsgr return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3]; 912258Scsgr} 922258Scsgr 932258Scsgr 942258Scsgr 952258Scsgrstatic int 962258Scsgrdirmatch(path, dp) 972258Scsgr const char *path; 982258Scsgr struct iso_directory_record *dp; 992258Scsgr{ 1002258Scsgr char *cp; 1012258Scsgr int i; 1022258Scsgr 1032258Scsgr cp = dp->name; 1042258Scsgr for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 1052258Scsgr if (!*path || *path == '/') 1062258Scsgr break; 1072258Scsgr if (toupper(*path) == *cp) 1082258Scsgr continue; 1092258Scsgr return 0; 1102258Scsgr } 1112258Scsgr if (*path && *path != '/') 1122258Scsgr return 0; 1132258Scsgr /* 1142258Scsgr * Allow stripping of trailing dots and the version number. 1152258Scsgr * Note that this will find the first instead of the last version 1162258Scsgr * of a file. 1172258Scsgr */ 1182258Scsgr if (i >= 0 && (*cp == ';' || *cp == '.')) { 1192258Scsgr /* This is to prevent matching of numeric extensions */ 1202258Scsgr if (*cp == '.' && cp[1] != ';') 1212258Scsgr return 0; 1222258Scsgr while (--i >= 0) 1232258Scsgr if (*++cp != ';' && (*cp < '0' || *cp > '9')) 1242258Scsgr return 0; 1252258Scsgr } 1262258Scsgr return 1; 1272258Scsgr} 1282258Scsgr 1292258Scsgrstatic int 1302258Scsgrcd9660_open(path, f) 1312258Scsgr const char *path; 1322258Scsgr struct open_file *f; 1332258Scsgr{ 1342258Scsgr struct file *fp = 0; 1352258Scsgr void *buf; 1362258Scsgr struct iso_primary_descriptor *vd; 1372258Scsgr size_t buf_size, read, dsize, off; 1382258Scsgr daddr_t bno, boff; 1392258Scsgr struct iso_directory_record rec; 1402258Scsgr struct iso_directory_record *dp = 0; 1412258Scsgr int rc; 1422258Scsgr 1432258Scsgr /* First find the volume descriptor */ 1442258Scsgr buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 1452258Scsgr vd = buf; 1462258Scsgr for (bno = 16;; bno++) { 1472258Scsgr twiddle(); 1482258Scsgr rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 1492258Scsgr ISO_DEFAULT_BLOCK_SIZE, buf, &read); 1502258Scsgr if (rc) 1512258Scsgr goto out; 1522258Scsgr if (read != ISO_DEFAULT_BLOCK_SIZE) { 1532258Scsgr rc = EIO; 1542258Scsgr goto out; 1552258Scsgr } 1562258Scsgr rc = EINVAL; 1572258Scsgr if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 1582258Scsgr goto out; 1592258Scsgr if (isonum_711(vd->type) == ISO_VD_END) 1602258Scsgr goto out; 1612258Scsgr if (isonum_711(vd->type) == ISO_VD_PRIMARY) 1622258Scsgr break; 1632258Scsgr } 1642258Scsgr if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 1652258Scsgr goto out; 1662258Scsgr 1672258Scsgr rec = *(struct iso_directory_record *) vd->root_directory_record; 1682258Scsgr if (*path == '/') path++; /* eat leading '/' */ 1692258Scsgr 1702258Scsgr while (*path) { 1712258Scsgr bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 1722258Scsgr dsize = isonum_733(rec.size); 1732258Scsgr off = 0; 1742258Scsgr boff = 0; 1752258Scsgr 1762258Scsgr while (off < dsize) { 1772258Scsgr if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 1782258Scsgr twiddle(); 1792258Scsgr rc = f->f_dev->dv_strategy 1802258Scsgr (f->f_devdata, F_READ, 1812258Scsgr cdb2devb(bno + boff), 1822258Scsgr ISO_DEFAULT_BLOCK_SIZE, 1832258Scsgr buf, &read); 1842258Scsgr if (rc) 1852258Scsgr goto out; 1862258Scsgr if (read != ISO_DEFAULT_BLOCK_SIZE) { 1872258Scsgr rc = EIO; 1882258Scsgr goto out; 1892258Scsgr } 1902258Scsgr boff++; 1912258Scsgr dp = (struct iso_directory_record *) buf; 1922258Scsgr } 1932258Scsgr if (isonum_711(dp->length) == 0) { 1942258Scsgr /* skip to next block, if any */ 1952258Scsgr off = boff * ISO_DEFAULT_BLOCK_SIZE; 1962258Scsgr continue; 1972258Scsgr } 1982258Scsgr 1992258Scsgr if (dirmatch(path, dp)) 2002258Scsgr break; 2012258Scsgr 2022258Scsgr dp = (struct iso_directory_record *) 2032258Scsgr ((char *) dp + isonum_711(dp->length)); 2042258Scsgr off += isonum_711(dp->length); 2052258Scsgr } 2062258Scsgr if (off == dsize) { 2072258Scsgr rc = ENOENT; 2082258Scsgr goto out; 2092258Scsgr } 2102258Scsgr 2112258Scsgr rec = *dp; 2122258Scsgr while (*path && *path != '/') /* look for next component */ 2132258Scsgr path++; 2142258Scsgr if (*path) path++; /* skip '/' */ 2152258Scsgr } 2162258Scsgr 2172258Scsgr /* allocate file system specific data structure */ 2182258Scsgr fp = malloc(sizeof(struct file)); 2192258Scsgr bzero(fp, sizeof(struct file)); 2202258Scsgr f->f_fsdata = (void *)fp; 2212258Scsgr 2222258Scsgr fp->isdir = (isonum_711(rec.flags) & 2) != 0; 2232258Scsgr fp->off = 0; 2242258Scsgr fp->bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 2252258Scsgr fp->size = isonum_733(rec.size); 226 free(buf); 227 228 return 0; 229 230out: 231 if (fp) 232 free(fp); 233 free(buf); 234 235 return rc; 236} 237 238static int 239cd9660_close(f) 240 struct open_file *f; 241{ 242 struct file *fp = (struct file *)f->f_fsdata; 243 244 f->f_fsdata = 0; 245 free(fp); 246 247 return 0; 248} 249 250static int 251cd9660_readfile(f, start, size, resid) 252 struct open_file *f; 253 void *start; 254 size_t size; 255 size_t *resid; 256{ 257 struct file *fp = (struct file *)f->f_fsdata; 258 int rc = 0; 259 daddr_t bno; 260 char buf[ISO_DEFAULT_BLOCK_SIZE]; 261 char *dp; 262 size_t read, off; 263 264 while (size) { 265 if (fp->off < 0 || fp->off >= fp->size) 266 break; 267 bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno; 268 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1) 269 || size < ISO_DEFAULT_BLOCK_SIZE) 270 dp = buf; 271 else 272 dp = start; 273 twiddle(); 274 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 275 ISO_DEFAULT_BLOCK_SIZE, dp, &read); 276 if (rc) 277 return rc; 278 if (read != ISO_DEFAULT_BLOCK_SIZE) 279 return EIO; 280 if (dp == buf) { 281 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1); 282 if (read > off + size) 283 read = off + size; 284 read -= off; 285 bcopy(buf + off, start, read); 286 start += read; 287 fp->off += read; 288 size -= read; 289 } else { 290 start += ISO_DEFAULT_BLOCK_SIZE; 291 fp->off += ISO_DEFAULT_BLOCK_SIZE; 292 size -= ISO_DEFAULT_BLOCK_SIZE; 293 } 294 } 295 if (resid) 296 *resid = size; 297 return rc; 298} 299 300static int 301cd9660_readdir(f, start, size, resid) 302 struct open_file *f; 303 void *start; 304 size_t size; 305 size_t *resid; 306{ 307 struct file *fp = (struct file *)f->f_fsdata; 308 int rc = 0; 309 daddr_t bno, boff; 310 char buf[ISO_DEFAULT_BLOCK_SIZE]; 311 struct dirent *dp; 312 struct dirent *lastdp; 313 struct iso_directory_record *ep = 0; 314 size_t read, off, reclen, namelen; 315 316 if (fp->off < 0 || fp->off >= fp->size) 317 return 0; 318 boff = fp->off / ISO_DEFAULT_BLOCK_SIZE; 319 bno = fp->bno; 320 off = fp->off; 321 322 if (off % ISO_DEFAULT_BLOCK_SIZE) { 323 twiddle(); 324 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 325 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 326 if (rc) 327 return rc; 328 if (read != ISO_DEFAULT_BLOCK_SIZE) 329 return EIO; 330 boff++; 331 ep = (struct iso_directory_record *) 332 (buf + (off % ISO_DEFAULT_BLOCK_SIZE)); 333 } 334 335 lastdp = dp = (struct dirent *) start; 336 dp->d_fileno = 0; 337 dp->d_type = DT_UNKNOWN; 338 dp->d_namlen = 0; 339 while (size && off < fp->size) { 340 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 341 twiddle(); 342 rc = f->f_dev->dv_strategy 343 (f->f_devdata, F_READ, 344 cdb2devb(bno + boff), 345 ISO_DEFAULT_BLOCK_SIZE, 346 buf, &read); 347 if (rc) 348 break; 349 if (read != ISO_DEFAULT_BLOCK_SIZE) { 350 rc = EIO; 351 break; 352 } 353 boff++; 354 ep = (struct iso_directory_record *) buf; 355 } 356 357 if (isonum_711(ep->length) == 0) { 358 /* skip to next block, if any */ 359 off = boff * ISO_DEFAULT_BLOCK_SIZE; 360 continue; 361 } 362 363 namelen = isonum_711(ep->name_len); 364 if (namelen == 1 && ep->name[0] == 1) 365 namelen = 2; 366 reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1; 367 reclen = (reclen + 3) & ~3; 368 if (reclen > size) 369 break; 370 371 dp->d_fileno = isonum_733(ep->extent); 372 dp->d_reclen = reclen; 373 if (isonum_711(ep->flags) & 2) 374 dp->d_type = DT_DIR; 375 else 376 dp->d_type = DT_REG; 377 dp->d_namlen = namelen; 378 if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0) 379 strcpy(dp->d_name, "."); 380 else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1) 381 strcpy(dp->d_name, ".."); 382 else 383 bcopy(ep->name, dp->d_name, dp->d_namlen); 384 dp->d_name[dp->d_namlen] = 0; 385 386 lastdp = dp; 387 dp = (struct dirent *) ((char *) dp + dp->d_reclen); 388 size -= reclen; 389 ep = (struct iso_directory_record *) 390 ((char *) ep + isonum_711(ep->length)); 391 off += isonum_711(ep->length); 392 } 393 394 fp->off = off; 395 lastdp->d_reclen += size; 396 if (resid) 397 *resid = 0; 398 return rc; 399} 400 401static int 402cd9660_read(f, start, size, resid) 403 struct open_file *f; 404 void *start; 405 size_t size; 406 size_t *resid; 407{ 408 struct file *fp = (struct file *)f->f_fsdata; 409 410 if (fp->isdir) 411 return cd9660_readdir(f, start, size, resid); 412 else 413 return cd9660_readfile(f, start, size, resid); 414} 415 416static int 417cd9660_write(f, start, size, resid) 418 struct open_file *f; 419 void *start; 420 size_t size; 421 size_t *resid; 422{ 423 return EROFS; 424} 425 426static off_t 427cd9660_seek(f, offset, where) 428 struct open_file *f; 429 off_t offset; 430 int where; 431{ 432 struct file *fp = (struct file *)f->f_fsdata; 433 434 switch (where) { 435 case SEEK_SET: 436 fp->off = offset; 437 break; 438 case SEEK_CUR: 439 fp->off += offset; 440 break; 441 case SEEK_END: 442 fp->off = fp->size - offset; 443 break; 444 default: 445 return -1; 446 } 447 return fp->off; 448} 449 450static int 451cd9660_stat(f, sb) 452 struct open_file *f; 453 struct stat *sb; 454{ 455 struct file *fp = (struct file *)f->f_fsdata; 456 457 /* only important stuff */ 458 sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH; 459 if (fp->isdir) 460 sb->st_mode |= S_IFDIR; 461 else 462 sb->st_mode |= S_IFREG; 463 sb->st_uid = sb->st_gid = 0; 464 sb->st_size = fp->size; 465 return 0; 466} 467