cd9660.c revision 82208
172909Sjulian/* $FreeBSD: head/lib/libstand/cd9660.c 82208 2001-08-23 17:08:26Z gallatin $ */ 272909Sjulian/* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */ 379727Sschweikh 472909Sjulian/* 572909Sjulian * Copyright (C) 1996 Wolfgang Solfrank. 672909Sjulian * Copyright (C) 1996 TooLs GmbH. 772909Sjulian * All rights reserved. 872909Sjulian * 972909Sjulian * Redistribution and use in source and binary forms, with or without 1072909Sjulian * modification, are permitted provided that the following conditions 1172909Sjulian * are met: 1272909Sjulian * 1. Redistributions of source code must retain the above copyright 1372909Sjulian * notice, this list of conditions and the following disclaimer. 1479727Sschweikh * 2. Redistributions in binary form must reproduce the above copyright 1572909Sjulian * notice, this list of conditions and the following disclaimer in the 1672909Sjulian * documentation and/or other materials provided with the distribution. 1772909Sjulian * 3. All advertising materials mentioning features or use of this software 1872909Sjulian * must display the following acknowledgement: 1972909Sjulian * This product includes software developed by TooLs GmbH. 2072909Sjulian * 4. The name of TooLs GmbH may not be used to endorse or promote products 2172909Sjulian * derived from this software without specific prior written permission. 2272909Sjulian * 2372909Sjulian * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 2472909Sjulian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2572909Sjulian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2672909Sjulian * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2772909Sjulian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 2872909Sjulian * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 2972909Sjulian * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 3072909Sjulian * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 3172909Sjulian * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 3279727Sschweikh * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3372909Sjulian */ 3472909Sjulian 3572909Sjulian/* 3672909Sjulian * Stand-alone ISO9660 file reading package. 3772909Sjulian * 3872909Sjulian * Note: This doesn't support Rock Ridge extensions, extended attributes, 3979538Sru * blocksizes other than 2048 bytes, multi-extent files, etc. 4072909Sjulian */ 4172909Sjulian#include <sys/param.h> 4273753Sru#include <string.h> 4372909Sjulian#include <sys/dirent.h> 4484306Sru#include <isofs/cd9660/iso.h> 4572909Sjulian 4672909Sjulian#include "stand.h" 4772909Sjulian 4879727Sschweikhstatic int cd9660_open(const char *path, struct open_file *f); 4972909Sjulianstatic int cd9660_close(struct open_file *f); 5072909Sjulianstatic int cd9660_read(struct open_file *f, void *buf, size_t size, size_t *resid); 5172909Sjulianstatic int cd9660_write(struct open_file *f, void *buf, size_t size, size_t *resid); 52242997Sjoelstatic off_t cd9660_seek(struct open_file *f, off_t offset, int where); 53242997Sjoelstatic int cd9660_stat(struct open_file *f, struct stat *sb); 5472909Sjulianstatic int cd9660_readdir(struct open_file *f, struct dirent *d); 5572909Sjulian 5679727Sschweikhstruct fs_ops cd9660_fsops = { 5772909Sjulian "cd9660", 58242997Sjoel cd9660_open, 5979727Sschweikh cd9660_close, 6072909Sjulian cd9660_read, 6172909Sjulian cd9660_write, 62242997Sjoel cd9660_seek, 6372909Sjulian cd9660_stat, 6472909Sjulian cd9660_readdir 6572909Sjulian}; 6672909Sjulian 6772909Sjulianstruct file { 6872909Sjulian int f_isdir; /* nonzero if file is directory */ 6972909Sjulian off_t f_off; /* Current offset within file */ 7072909Sjulian daddr_t f_bno; /* Starting block number */ 7172909Sjulian off_t f_size; /* Size of file */ 7272909Sjulian daddr_t f_buf_blkno; /* block number of data block */ 7372909Sjulian char *f_buf; /* buffer for data block */ 7472909Sjulian}; 7572909Sjulian 7672909Sjulianstruct ptable_ent { 7772909Sjulian char namlen [ISODCL( 1, 1)]; /* 711 */ 7872909Sjulian char extlen [ISODCL( 2, 2)]; /* 711 */ 7972909Sjulian char block [ISODCL( 3, 6)]; /* 732 */ 8072909Sjulian char parent [ISODCL( 7, 8)]; /* 722 */ 8179727Sschweikh char name [1]; 8289610Smpp}; 8373753Sru#define PTFIXSZ 8 8479727Sschweikh#define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 8572909Sjulian 8672909Sjulian#define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 8772909Sjulian 8872909Sjulian/* 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, blkoff; 266 int rc = 0; 267 size_t read; 268 269 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE + fp->f_bno; 270 blkoff = fp->f_off % ISO_DEFAULT_BLOCK_SIZE; 271 272 if (blkno != fp->f_buf_blkno) { 273 if (fp->f_buf == (char *)0) 274 fp->f_buf = malloc(ISO_DEFAULT_BLOCK_SIZE); 275 276 twiddle(); 277 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, 278 cdb2devb(blkno), ISO_DEFAULT_BLOCK_SIZE, fp->f_buf, &read); 279 if (rc) 280 return (rc); 281 if (read != ISO_DEFAULT_BLOCK_SIZE) 282 return (EIO); 283 284 fp->f_buf_blkno = blkno; 285 } 286 287 *buf_p = fp->f_buf + blkoff; 288 *size_p = ISO_DEFAULT_BLOCK_SIZE - blkoff; 289 290 if (*size_p > fp->f_size - fp->f_off) 291 *size_p = fp->f_size - fp->f_off; 292 return (rc); 293} 294 295static int 296cd9660_read(f, start, size, resid) 297 struct open_file *f; 298 void *start; 299 size_t size; 300 size_t *resid; 301{ 302 struct file *fp = (struct file *)f->f_fsdata; 303 char *buf, *addr; 304 size_t buf_size, csize; 305 int rc = 0; 306 307 addr = start; 308 while (size) { 309 if (fp->f_off < 0 || fp->f_off >= fp->f_size) 310 break; 311 312 rc = buf_read_file(f, &buf, &buf_size); 313 if (rc) 314 break; 315 316 csize = size > buf_size ? buf_size : size; 317 bcopy(buf, addr, csize); 318 319 fp->f_off += csize; 320 addr += csize; 321 size -= csize; 322 } 323 if (resid) 324 *resid = size; 325 return (rc); 326} 327 328static int 329cd9660_readdir(struct open_file *f, struct dirent *d) 330{ 331 struct file *fp = (struct file *)f->f_fsdata; 332 struct iso_directory_record *ep; 333 size_t buf_size, reclen, namelen; 334 int error = 0; 335 char *buf; 336 337again: 338 if (fp->f_off >= fp->f_size) 339 return (ENOENT); 340 error = buf_read_file(f, &buf, &buf_size); 341 if (error) 342 return (error); 343 ep = (struct iso_directory_record *)buf; 344 345 if (isonum_711(ep->length) == 0) { 346 daddr_t blkno; 347 348 /* skip to next block, if any */ 349 blkno = fp->f_off / ISO_DEFAULT_BLOCK_SIZE; 350 fp->f_off = (blkno + 1) * ISO_DEFAULT_BLOCK_SIZE; 351 goto again; 352 } 353 354 namelen = isonum_711(ep->name_len); 355 if (namelen == 1 && ep->name[0] == 1) 356 namelen = 2; 357 reclen = sizeof(struct dirent) - (MAXNAMLEN+1) + namelen + 1; 358 reclen = (reclen + 3) & ~3; 359 360 d->d_fileno = isonum_733(ep->extent); 361 d->d_reclen = reclen; 362 if (isonum_711(ep->flags) & 2) 363 d->d_type = DT_DIR; 364 else 365 d->d_type = DT_REG; 366 d->d_namlen = namelen; 367 368 if (isonum_711(ep->name_len) == 1 && ep->name[0] == 0) 369 strcpy(d->d_name, "."); 370 else if (isonum_711(ep->name_len) == 1 && ep->name[0] == 1) 371 strcpy(d->d_name, ".."); 372 else 373 bcopy(ep->name, d->d_name, d->d_namlen); 374 d->d_name[d->d_namlen] = 0; 375 376 fp->f_off += isonum_711(ep->length); 377 return (0); 378} 379 380static int 381cd9660_write(f, start, size, resid) 382 struct open_file *f; 383 void *start; 384 size_t size; 385 size_t *resid; 386{ 387 return EROFS; 388} 389 390static off_t 391cd9660_seek(f, offset, where) 392 struct open_file *f; 393 off_t offset; 394 int where; 395{ 396 struct file *fp = (struct file *)f->f_fsdata; 397 398 switch (where) { 399 case SEEK_SET: 400 fp->f_off = offset; 401 break; 402 case SEEK_CUR: 403 fp->f_off += offset; 404 break; 405 case SEEK_END: 406 fp->f_off = fp->f_size - offset; 407 break; 408 default: 409 return -1; 410 } 411 return fp->f_off; 412} 413 414static int 415cd9660_stat(f, sb) 416 struct open_file *f; 417 struct stat *sb; 418{ 419 struct file *fp = (struct file *)f->f_fsdata; 420 421 /* only important stuff */ 422 sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH; 423 if (fp->f_isdir) 424 sb->st_mode |= S_IFDIR; 425 else 426 sb->st_mode |= S_IFREG; 427 sb->st_uid = sb->st_gid = 0; 428 sb->st_size = fp->f_size; 429 return 0; 430} 431