cd9660.c revision 84221
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 84221 2001-09-30 22:28:01Z dillon $"); 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 48#include "stand.h" 49 50static int cd9660_open(const char *path, struct open_file *f); 51static int cd9660_close(struct open_file *f); 52static int cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid); 53static int cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid); 54static off_t cd9660_seek(struct open_file *f, off_t offset, int where); 55static int cd9660_stat(struct open_file *f, struct stat *sb); 56static int cd9660_readdir(struct open_file *f, struct dirent *d); 57 58struct fs_ops cd9660_fsops = { 59 "cd9660", 60 cd9660_open, 61 cd9660_close, 62 cd9660_read, 63 cd9660_write, 64 cd9660_seek, 65 cd9660_stat, 66 cd9660_readdir 67}; 68 69struct file { 70 int f_isdir; /* nonzero if file is directory */ 71 off_t f_off; /* Current offset within file */ 72 daddr_t f_bno; /* Starting block number */ 73 off_t f_size; /* Size of file */ 74 daddr_t f_buf_blkno; /* block number of data block */ 75 char *f_buf; /* buffer for data block */ 76}; 77 78struct ptable_ent { 79 char namlen [ISODCL( 1, 1)]; /* 711 */ 80 char extlen [ISODCL( 2, 2)]; /* 711 */ 81 char block [ISODCL( 3, 6)]; /* 732 */ 82 char parent [ISODCL( 7, 8)]; /* 722 */ 83 char name [1]; 84}; 85#define PTFIXSZ 8 86#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 87 88#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 89 90/* XXX these should be in the system headers */ 91static __inline int 92isonum_722(p) 93 u_char *p; 94{ 95 return (*p << 8)|p[1]; 96} 97 98static __inline int 99isonum_732(p) 100 u_char *p; 101{ 102 return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3]; 103} 104 105static int 106dirmatch(path, dp) 107 const char *path; 108 struct iso_directory_record *dp; 109{ 110 char *cp; 111 int i; 112 113 cp = dp->name; 114 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 115 if (!*path || *path == '/') 116 break; 117 if (toupper(*path) == *cp) 118 continue; 119 return 0; 120 } 121 if (*path && *path != '/') 122 return 0; 123 /* 124 * Allow stripping of trailing dots and the version number. 125 * Note that this will find the first instead of the last version 126 * of a file. 127 */ 128 if (i >= 0 && (*cp == ';' || *cp == '.')) { 129 /* This is to prevent matching of numeric extensions */ 130 if (*cp == '.' && cp[1] != ';') 131 return 0; 132 while (--i >= 0) 133 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 134 return 0; 135 } 136 return 1; 137} 138 139static int 140cd9660_open(path, f) 141 const char *path; 142 struct open_file *f; 143{ 144 struct file *fp = 0; 145 void *buf; 146 struct iso_primary_descriptor *vd; 147 size_t buf_size, read, dsize, off; 148 daddr_t bno, boff; 149 struct iso_directory_record rec; 150 struct iso_directory_record *dp = 0; 151 int rc; 152 153 /* First find the volume descriptor */ 154 buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 155 vd = buf; 156 for (bno = 16;; bno++) { 157 twiddle(); 158 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 159 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 160 if (rc) 161 goto out; 162 if (read != ISO_DEFAULT_BLOCK_SIZE) { 163 rc = EIO; 164 goto out; 165 } 166 rc = EINVAL; 167 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 168 goto out; 169 if (isonum_711(vd->type) == ISO_VD_END) 170 goto out; 171 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 172 break; 173 } 174 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 175 goto out; 176 177 rec = *(struct iso_directory_record *) vd->root_directory_record; 178 if (*path == '/') path++; /* eat leading '/' */ 179 180 while (*path) { 181 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 182 dsize = isonum_733(rec.size); 183 off = 0; 184 boff = 0; 185 186 while (off < dsize) { 187 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 188 twiddle(); 189 rc = f->f_dev->dv_strategy 190 (f->f_devdata, F_READ, 191 cdb2devb(bno + boff), 192 ISO_DEFAULT_BLOCK_SIZE, 193 buf, &read); 194 if (rc) 195 goto out; 196 if (read != ISO_DEFAULT_BLOCK_SIZE) { 197 rc = EIO; 198 goto out; 199 } 200 boff++; 201 dp = (struct iso_directory_record *) buf; 202 } 203 if (isonum_711(dp->length) == 0) { 204 /* skip to next block, if any */ 205 off = boff * ISO_DEFAULT_BLOCK_SIZE; 206 continue; 207 } 208 209 if (dirmatch(path, dp)) 210 break; 211 212 dp = (struct iso_directory_record *) 213 ((char *) dp + isonum_711(dp->length)); 214 off += isonum_711(dp->length); 215 } 216 if (off >= dsize) { 217 rc = ENOENT; 218 goto out; 219 } 220 221 rec = *dp; 222 while (*path && *path != '/') /* look for next component */ 223 path++; 224 if (*path) path++; /* skip '/' */ 225 } 226 227 /* allocate file system specific data structure */ 228 fp = malloc(sizeof(struct file)); 229 bzero(fp, sizeof(struct file)); 230 f->f_fsdata = (void *)fp; 231 232 fp->f_isdir = (isonum_711(rec.flags) & 2) != 0; 233 fp->f_off = 0; 234 fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 235 fp->f_size = isonum_733(rec.size); 236 free(buf); 237 238 return 0; 239 240out: 241 if (fp) 242 free(fp); 243 free(buf); 244 245 return rc; 246} 247 248static int 249cd9660_close(f) 250 struct open_file *f; 251{ 252 struct file *fp = (struct file *)f->f_fsdata; 253 254 f->f_fsdata = 0; 255 free(fp); 256 257 return 0; 258} 259 260static int 261buf_read_file(f, buf_p, size_p) 262 struct open_file *f; 263 char **buf_p; 264 size_t *size_p; 265{ 266 struct file *fp = (struct file *)f->f_fsdata; 267 daddr_t blkno, blkoff; 268 int rc = 0; 269 size_t read; 270 271 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno; 272 blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE; 273 274 if (blkno != fp->f_buf_blkno) { 275 if (fp->f_buf == (char *)0) 276 fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE); 277 278 twiddle(); 279 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, 280 cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read); 281 if (rc) 282 return (rc); 283 if (read != ISO_DEFAULT_BLOCK_SIZE) 284 return (EIO); 285 286 fp->f_buf_blkno = blkno; 287 } 288 289 *buf_p = fp->f_buf + blkoff; 290 *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff; 291 292 if (*size_p > fp->f_size - fp->f_off) 293 *size_p = fp->f_size - fp->f_off; 294 return (rc); 295} 296 297static int 298cd9660_read(f, start, size, resid) 299 struct open_file *f; 300 void *start; 301 size_t size; 302 size_t *resid; 303{ 304 struct file *fp = (struct file *)f->f_fsdata; 305 char *buf, *addr; 306 size_t buf_size, csize; 307 int rc = 0; 308 309 addr = start; 310 while (size) { 311 if (fp->f_off < 0 || fp->f_off >= fp->f_size) 312 break; 313 314 rc = buf_read_file(f, &buf, &buf_size); 315 if (rc) 316 break; 317 318 csize = size > buf_size ? buf_size : size; 319 bcopy(buf, addr, csize); 320 321 fp->f_off += csize; 322 addr += csize; 323 size -= csize; 324 } 325 if (resid) 326 *resid = size; 327 return (rc); 328} 329 330static int 331cd9660_readdir(struct open_file *f, struct dirent *d) 332{ 333 struct file *fp = (struct file *)f->f_fsdata; 334 struct iso_directory_record *ep; 335 size_t buf_size, reclen, namelen; 336 int error = 0; 337 char *buf; 338 339again: 340 if (fp->f_off >= fp->f_size) 341 return (ENOENT); 342 error = buf_read_file(f, &buf, &buf_size); 343 if (error) 344 return (error); 345 ep = (struct iso_directory_record *)buf; 346 347 if (isonum_711(ep->length) == 0) { 348 daddr_t blkno; 349 350 /* skip to next block, if any */ 351 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE; 352 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE; 353 goto again; 354 } 355 356 namelen = isonum_711(ep->name_len); 357 if (namelen == 1 && ep->name[0] == 1) 358 namelen = 2; 359 reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1; 360 reclen = (reclen + 3) & ~3; 361 362 d->d_fileno = isonum_733(ep->extent); 363 d->d_reclen = reclen; 364 if (isonum_711(ep->flags) & 2) 365 d->d_type = DT_DIR; 366 else 367 d->d_type = DT_REG; 368 d->d_namlen = namelen; 369 370 if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0) 371 strcpy(d->d_name, "."); 372 else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1) 373 strcpy(d->d_name, ".."); 374 else 375 bcopy(ep->name, d->d_name, d->d_namlen); 376 d->d_name[d->d_namlen] = 0; 377 378 fp->f_off += isonum_711(ep->length); 379 return (0); 380} 381 382static int 383cd9660_write(f, start, size, resid) 384 struct open_file *f; 385 void *start; 386 size_t size; 387 size_t *resid; 388{ 389 return EROFS; 390} 391 392static off_t 393cd9660_seek(f, offset, where) 394 struct open_file *f; 395 off_t offset; 396 int where; 397{ 398 struct file *fp = (struct file *)f->f_fsdata; 399 400 switch (where) { 401 case SEEK_SET: 402 fp->f_off = offset; 403 break; 404 case SEEK_CUR: 405 fp->f_off += offset; 406 break; 407 case SEEK_END: 408 fp->f_off = fp->f_size - offset; 409 break; 410 default: 411 return -1; 412 } 413 return fp->f_off; 414} 415 416static int 417cd9660_stat(f, sb) 418 struct open_file *f; 419 struct stat *sb; 420{ 421 struct file *fp = (struct file *)f->f_fsdata; 422 423 /* only important stuff */ 424 sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH; 425 if (fp->f_isdir) 426 sb->st_mode |= S_IFDIR; 427 else 428 sb->st_mode |= S_IFREG; 429 sb->st_uid = sb->st_gid = 0; 430 sb->st_size = fp->f_size; 431 return 0; 432} 433