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