cd9660.c revision 59766
1/* $FreeBSD: head/lib/libstand/cd9660.c 59766 2000-04-29 20:47:10Z jlemon $ */ 2/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */ 3 4/* 5 * Copyright (C) 1996 Wolfgang Solfrank. 6 * Copyright (C) 1996 TooLs GmbH. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35/* 36 * Stand-alone ISO9660 file reading package. 37 * 38 * Note: This doesn't support Rock Ridge extensions, extended attributes, 39 * blocksizes other than 2048 bytes, multi-extent files, etc. 40 */ 41#include <sys/param.h> 42#include <string.h> 43#include <sys/dirent.h> 44#include <isofs/cd9660/iso.h> 45 46#include "stand.h" 47 48static int cd9660_open(const char *path, struct open_file *f); 49static int cd9660_close(struct open_file *f); 50static int cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid); 51static int cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid); 52static off_t cd9660_seek(struct open_file *f, off_t offset, int where); 53static int cd9660_stat(struct open_file *f, struct stat *sb); 54static int cd9660_readdir(struct open_file *f, struct dirent *d); 55 56struct fs_ops cd9660_fsops = { 57 "cd9660", 58 cd9660_open, 59 cd9660_close, 60 cd9660_read, 61 cd9660_write, 62 cd9660_seek, 63 cd9660_stat, 64 cd9660_readdir 65}; 66 67struct file { 68 int f_isdir; /* nonzero if file is directory */ 69 off_t f_off; /* Current offset within file */ 70 daddr_t f_bno; /* Starting block number */ 71 off_t f_size; /* Size of file */ 72 daddr_t f_buf_blkno; /* block number of data block */ 73 char *f_buf; /* buffer for data block */ 74}; 75 76struct ptable_ent { 77 char namlen [ISODCL( 1, 1)]; /* 711 */ 78 char extlen [ISODCL( 2, 2)]; /* 711 */ 79 char block [ISODCL( 3, 6)]; /* 732 */ 80 char parent [ISODCL( 7, 8)]; /* 722 */ 81 char name [1]; 82}; 83#define PTFIXSZ 8 84#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 85 86#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 87 88/* XXX these should be in the system headers */ 89static __inline int 90isonum_722(p) 91 u_char *p; 92{ 93 return (*p << 8)|p[1]; 94} 95 96static __inline int 97isonum_732(p) 98 u_char *p; 99{ 100 return (*p << 24)|(p[1] << 16)|(p[2] << 8)|p[3]; 101} 102 103static int 104dirmatch(path, dp) 105 const char *path; 106 struct iso_directory_record *dp; 107{ 108 char *cp; 109 int i; 110 111 cp = dp->name; 112 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 113 if (!*path || *path == '/') 114 break; 115 if (toupper(*path) == *cp) 116 continue; 117 return 0; 118 } 119 if (*path && *path != '/') 120 return 0; 121 /* 122 * Allow stripping of trailing dots and the version number. 123 * Note that this will find the first instead of the last version 124 * of a file. 125 */ 126 if (i >= 0 && (*cp == ';' || *cp == '.')) { 127 /* This is to prevent matching of numeric extensions */ 128 if (*cp == '.' && cp[1] != ';') 129 return 0; 130 while (--i >= 0) 131 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 132 return 0; 133 } 134 return 1; 135} 136 137static int 138cd9660_open(path, f) 139 const char *path; 140 struct open_file *f; 141{ 142 struct file *fp = 0; 143 void *buf; 144 struct iso_primary_descriptor *vd; 145 size_t buf_size, read, dsize, off; 146 daddr_t bno, boff; 147 struct iso_directory_record rec; 148 struct iso_directory_record *dp = 0; 149 int rc; 150 151 /* First find the volume descriptor */ 152 buf = malloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 153 vd = buf; 154 for (bno = 16;; bno++) { 155 twiddle(); 156 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 157 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 158 if (rc) 159 goto out; 160 if (read != ISO_DEFAULT_BLOCK_SIZE) { 161 rc = EIO; 162 goto out; 163 } 164 rc = EINVAL; 165 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 166 goto out; 167 if (isonum_711(vd->type) == ISO_VD_END) 168 goto out; 169 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 170 break; 171 } 172 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 173 goto out; 174 175 rec = *(struct iso_directory_record *) vd->root_directory_record; 176 if (*path == '/') path++; /* eat leading '/' */ 177 178 while (*path) { 179 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 180 dsize = isonum_733(rec.size); 181 off = 0; 182 boff = 0; 183 184 while (off < dsize) { 185 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 186 twiddle(); 187 rc = f->f_dev->dv_strategy 188 (f->f_devdata, F_READ, 189 cdb2devb(bno + boff), 190 ISO_DEFAULT_BLOCK_SIZE, 191 buf, &read); 192 if (rc) 193 goto out; 194 if (read != ISO_DEFAULT_BLOCK_SIZE) { 195 rc = EIO; 196 goto out; 197 } 198 boff++; 199 dp = (struct iso_directory_record *) buf; 200 } 201 if (isonum_711(dp->length) == 0) { 202 /* skip to next block, if any */ 203 off = boff * ISO_DEFAULT_BLOCK_SIZE; 204 continue; 205 } 206 207 if (dirmatch(path, dp)) 208 break; 209 210 dp = (struct iso_directory_record *) 211 ((char *) dp + isonum_711(dp->length)); 212 off += isonum_711(dp->length); 213 } 214 if (off == dsize) { 215 rc = ENOENT; 216 goto out; 217 } 218 219 rec = *dp; 220 while (*path && *path != '/') /* look for next component */ 221 path++; 222 if (*path) path++; /* skip '/' */ 223 } 224 225 /* allocate file system specific data structure */ 226 fp = malloc(sizeof(struct file)); 227 bzero(fp, sizeof(struct file)); 228 f->f_fsdata = (void *)fp; 229 230 fp->f_isdir = (isonum_711(rec.flags) & 2) != 0; 231 fp->f_off = 0; 232 fp->f_bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 233 fp->f_size = isonum_733(rec.size); 234 free(buf); 235 236 return 0; 237 238out: 239 if (fp) 240 free(fp); 241 free(buf); 242 243 return rc; 244} 245 246static int 247cd9660_close(f) 248 struct open_file *f; 249{ 250 struct file *fp = (struct file *)f->f_fsdata; 251 252 f->f_fsdata = 0; 253 free(fp); 254 255 return 0; 256} 257 258static int 259buf_read_file(f, buf_p, size_p) 260 struct open_file *f; 261 char **buf_p; 262 size_t *size_p; 263{ 264 struct file *fp = (struct file *)f->f_fsdata; 265 daddr_t blkno; 266 int rc = 0; 267 size_t read; 268 269 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno; 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 + fp->f_off; 287 *size_p = ISO_DEFAULT_BLOCK_SIZE - fp->f_off; 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(f, start, size, resid) 296 struct open_file *f; 297 void *start; 298 size_t size; 299 size_t *resid; 300{ 301 struct file *fp = (struct file *)f->f_fsdata; 302 char *buf, *addr; 303 size_t buf_size, csize; 304 int rc = 0; 305 306 addr = start; 307 while (size) { 308 if (fp->f_off < 0 || fp->f_off >= fp->f_size) 309 break; 310 311 rc = buf_read_file(f, &buf, &buf_size); 312 if (rc) 313 break; 314 315 csize = size > buf_size ? buf_size : size; 316 bcopy(buf, addr, csize); 317 318 fp->f_off += csize; 319 addr += csize; 320 size -= csize; 321 } 322 if (resid) 323 *resid = size; 324 return (rc); 325} 326 327static int 328cd9660_readdir(struct open_file *f, struct dirent *d) 329{ 330 struct file *fp = (struct file *)f->f_fsdata; 331 struct iso_directory_record *ep; 332 size_t buf_size, reclen, namelen; 333 int error = 0; 334 char *buf; 335 336again: 337 if (fp->f_off >= fp->f_size) 338 return (ENOENT); 339 error = buf_read_file(f, &buf, &buf_size); 340 if (error) 341 return (error); 342 ep = (struct iso_directory_record *)buf; 343 344 if (isonum_711(ep->length) == 0) { 345 daddr_t blkno; 346 347 /* skip to next block, if any */ 348 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE; 349 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE; 350 goto again; 351 } 352 353 namelen = isonum_711(ep->name_len); 354 if (namelen == 1 && ep->name[0] == 1) 355 namelen = 2; 356 reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1; 357 reclen = (reclen + 3) & ~3; 358 359 d->d_fileno = isonum_733(ep->extent); 360 d->d_reclen = reclen; 361 if (isonum_711(ep->flags) & 2) 362 d->d_type = DT_DIR; 363 else 364 d->d_type = DT_REG; 365 d->d_namlen = namelen; 366 367 if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0) 368 strcpy(d->d_name, "."); 369 else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1) 370 strcpy(d->d_name, ".."); 371 else 372 bcopy(ep->name, d->d_name, d->d_namlen); 373 d->d_name[d->d_namlen] = 0; 374 375 fp->f_off += isonum_711(ep->length); 376 return (0); 377} 378 379static int 380cd9660_write(f, start, size, resid) 381 struct open_file *f; 382 void *start; 383 size_t size; 384 size_t *resid; 385{ 386 return EROFS; 387} 388 389static off_t 390cd9660_seek(f, offset, where) 391 struct open_file *f; 392 off_t offset; 393 int where; 394{ 395 struct file *fp = (struct file *)f->f_fsdata; 396 397 switch (where) { 398 case SEEK_SET: 399 fp->f_off = offset; 400 break; 401 case SEEK_CUR: 402 fp->f_off += offset; 403 break; 404 case SEEK_END: 405 fp->f_off = fp->f_size - offset; 406 break; 407 default: 408 return -1; 409 } 410 return fp->f_off; 411} 412 413static int 414cd9660_stat(f, sb) 415 struct open_file *f; 416 struct stat *sb; 417{ 418 struct file *fp = (struct file *)f->f_fsdata; 419 420 /* only important stuff */ 421 sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH; 422 if (fp->f_isdir) 423 sb->st_mode |= S_IFDIR; 424 else 425 sb->st_mode |= S_IFREG; 426 sb->st_uid = sb->st_gid = 0; 427 sb->st_size = fp->f_size; 428 return 0; 429} 430