1 /* $NetBSD: cd9660.c,v 1.11 1999/11/23 12:20:53 simonb 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 /* 35 * Stand-alone ISO9660 file reading package. 36 * 37 * Note: This doesn't support Rock Ridge extensions, extended attributes, 38 * blocksizes other than 2048 bytes, multi-extent files, etc. 39 */ 40 #include <sys/param.h> 41 #ifdef _STANDALONE 42 #include <lib/libkern/libkern.h> 43 #else 44 #include <string.h> 45 #endif 46 #include <isofs/cd9660/iso.h> 47 48 #include "stand.h" 49 #include "cd9660.h" 50 51 /* 52 * XXX Does not currently implement: 53 * XXX 54 * XXX LIBSA_NO_FS_SYMLINK (does this even make sense?) 55 * XXX LIBSA_FS_SINGLECOMPONENT 56 */ 57 58 struct file { 59 off_t off; /* Current offset within file */ 60 daddr_t bno; /* Starting block number */ 61 off_t size; /* Size of file */ 62 }; 63 64 struct ptable_ent { 65 char namlen [ISODCL( 1, 1)]; /* 711 */ 66 char extlen [ISODCL( 2, 2)]; /* 711 */ 67 char block [ISODCL( 3, 6)]; /* 732 */ 68 char parent [ISODCL( 7, 8)]; /* 722 */ 69 char name [1]; 70 }; 71 #define PTFIXSZ 8 72 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 73 74 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 75 76 static int toupper __P((int)); 77 static int pnmatch __P((char *, struct ptable_ent *)); 78 static int dirmatch __P((char *, struct iso_directory_record *)); 79 80 static int 81 toupper(c) 82 int c; 83 { 84 return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; 85 } 86 87 static int 88 pnmatch(path, pp) 89 char *path; 90 struct ptable_ent *pp; 91 { 92 char *cp; 93 int i; 94 95 cp = pp->name; 96 for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) { 97 if (toupper(*path) == *cp) 98 continue; 99 return 0; 100 } 101 if (*path != '/') 102 return 0; 103 return 1; 104 } 105 106 static int 107 dirmatch(path, dp) 108 char *path; 109 struct iso_directory_record *dp; 110 { 111 char *cp; 112 int i; 113 114 /* This needs to be a regular file */ 115 if (dp->flags[0] & 6) 116 return 0; 117 118 cp = dp->name; 119 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 120 if (!*path) 121 break; 122 if (toupper(*path) == *cp) 123 continue; 124 return 0; 125 } 126 if (*path) 127 return 0; 128 /* 129 * Allow stripping of trailing dots and the version number. 130 * Note that this will find the first instead of the last version 131 * of a file. 132 */ 133 if (i >= 0 && (*cp == ';' || *cp == '.')) { 134 /* This is to prevent matching of numeric extensions */ 135 if (*cp == '.' && cp[1] != ';') 136 return 0; 137 while (--i >= 0) 138 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 139 return 0; 140 } 141 return 1; 142 } 143 144 int 145 cd9660_open(path, f) 146 char *path; 147 struct open_file *f; 148 { 149 struct file *fp = 0; 150 void *buf; 151 struct iso_primary_descriptor *vd; 152 size_t buf_size, read, psize, dsize; 153 daddr_t bno; 154 int parent, ent; 155 struct ptable_ent *pp; 156 struct iso_directory_record *dp = 0; 157 int rc; 158 159 /* First find the volume descriptor */ 160 buf = alloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 161 vd = buf; 162 for (bno = 16;; bno++) { 163 #if !defined(LIBSA_NO_TWIDDLE) 164 twiddle(); 165 #endif 166 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 167 ISO_DEFAULT_BLOCK_SIZE, buf, &read); 168 if (rc) 169 goto out; 170 if (read != ISO_DEFAULT_BLOCK_SIZE) { 171 rc = EIO; 172 goto out; 173 } 174 rc = EINVAL; 175 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 176 goto out; 177 if (isonum_711(vd->type) == ISO_VD_END) 178 goto out; 179 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 180 break; 181 } 182 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 183 goto out; 184 185 /* Now get the path table and lookup the directory of the file */ 186 bno = isonum_732(vd->type_m_path_table); 187 psize = isonum_733(vd->path_table_size); 188 189 if (psize > ISO_DEFAULT_BLOCK_SIZE) { 190 free(buf, ISO_DEFAULT_BLOCK_SIZE); 191 buf = alloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE)); 192 } 193 194 #if !defined(LIBSA_NO_TWIDDLE) 195 twiddle(); 196 #endif 197 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 198 buf_size, buf, &read); 199 if (rc) 200 goto out; 201 if (read != buf_size) { 202 rc = EIO; 203 goto out; 204 } 205 206 parent = 1; 207 pp = (struct ptable_ent *)buf; 208 ent = 1; 209 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 210 211 rc = ENOENT; 212 /* 213 * Remove extra separators 214 */ 215 while (*path == '/') 216 path++; 217 218 while (*path) { 219 if ((caddr_t)pp >= (caddr_t)buf + psize) 220 break; 221 if (isonum_722(pp->parent) != parent) 222 break; 223 if (!pnmatch(path, pp)) { 224 pp = (struct ptable_ent *)((caddr_t)pp + PTSIZE(pp)); 225 ent++; 226 continue; 227 } 228 path += isonum_711(pp->namlen) + 1; 229 parent = ent; 230 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 231 while ((caddr_t)pp < (caddr_t)buf + psize) { 232 if (isonum_722(pp->parent) == parent) 233 break; 234 pp = (struct ptable_ent *)((caddr_t)pp + PTSIZE(pp)); 235 ent++; 236 } 237 } 238 239 /* Now bno has the start of the directory that supposedly contains the file */ 240 bno--; 241 dsize = 1; /* Something stupid, but > 0 XXX */ 242 for (psize = 0; psize < dsize;) { 243 if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) { 244 bno++; 245 #if !defined(LIBSA_NO_TWIDDLE) 246 twiddle(); 247 #endif 248 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 249 cdb2devb(bno), 250 ISO_DEFAULT_BLOCK_SIZE, 251 buf, &read); 252 if (rc) 253 goto out; 254 if (read != ISO_DEFAULT_BLOCK_SIZE) { 255 rc = EIO; 256 goto out; 257 } 258 dp = (struct iso_directory_record *)buf; 259 } 260 if (!isonum_711(dp->length)) { 261 if ((void *)dp == buf) 262 psize += ISO_DEFAULT_BLOCK_SIZE; 263 else 264 psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE); 265 continue; 266 } 267 if (dsize == 1) 268 dsize = isonum_733(dp->size); 269 if (dirmatch(path, dp)) 270 break; 271 psize += isonum_711(dp->length); 272 dp = (struct iso_directory_record *)((caddr_t)dp + isonum_711(dp->length)); 273 } 274 275 if (psize >= dsize) { 276 rc = ENOENT; 277 goto out; 278 } 279 280 /* allocate file system specific data structure */ 281 fp = alloc(sizeof(struct file)); 282 bzero(fp, sizeof(struct file)); 283 f->f_fsdata = (void *)fp; 284 285 fp->off = 0; 286 fp->bno = isonum_733(dp->extent); 287 fp->size = isonum_733(dp->size); 288 free(buf, buf_size); 289 290 return 0; 291 292 out: 293 if (fp) 294 free(fp, sizeof(struct file)); 295 free(buf, buf_size); 296 297 return rc; 298 } 299 300 #if !defined(LIBSA_NO_FS_CLOSE) 301 int 302 cd9660_close(f) 303 struct open_file *f; 304 { 305 struct file *fp = (struct file *)f->f_fsdata; 306 307 f->f_fsdata = 0; 308 free(fp, sizeof *fp); 309 310 return 0; 311 } 312 #endif /* !defined(LIBSA_NO_FS_CLOSE) */ 313 314 int 315 cd9660_read(f, start, size, resid) 316 struct open_file *f; 317 void *start; 318 size_t size; 319 size_t *resid; 320 { 321 struct file *fp = (struct file *)f->f_fsdata; 322 int rc = 0; 323 daddr_t bno; 324 char buf[ISO_DEFAULT_BLOCK_SIZE]; 325 char *dp; 326 size_t read, off; 327 328 while (size) { 329 if (fp->off < 0 || fp->off >= fp->size) 330 break; 331 bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno; 332 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1) 333 || size < ISO_DEFAULT_BLOCK_SIZE) 334 dp = buf; 335 else 336 dp = start; 337 #if !defined(LIBSA_NO_TWIDDLE) 338 twiddle(); 339 #endif 340 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 341 ISO_DEFAULT_BLOCK_SIZE, dp, &read); 342 if (rc) 343 return rc; 344 if (read != ISO_DEFAULT_BLOCK_SIZE) 345 return EIO; 346 if (dp == buf) { 347 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1); 348 if (read > off + size) 349 read = off + size; 350 read -= off; 351 bcopy(buf + off, start, read); 352 start = (caddr_t)start + read; 353 fp->off += read; 354 size -= read; 355 } else { 356 start = (caddr_t)start + ISO_DEFAULT_BLOCK_SIZE; 357 fp->off += ISO_DEFAULT_BLOCK_SIZE; 358 size -= ISO_DEFAULT_BLOCK_SIZE; 359 } 360 } 361 if (resid) 362 *resid = size; 363 return rc; 364 } 365 366 #if !defined(LIBSA_NO_FS_WRITE) 367 int 368 cd9660_write(f, start, size, resid) 369 struct open_file *f; 370 void *start; 371 size_t size; 372 size_t *resid; 373 { 374 return EROFS; 375 } 376 #endif /* !defined(LIBSA_NO_FS_WRITE) */ 377 378 #if !defined(LIBSA_NO_FS_SEEK) 379 off_t 380 cd9660_seek(f, offset, where) 381 struct open_file *f; 382 off_t offset; 383 int where; 384 { 385 struct file *fp = (struct file *)f->f_fsdata; 386 387 switch (where) { 388 case SEEK_SET: 389 fp->off = offset; 390 break; 391 case SEEK_CUR: 392 fp->off += offset; 393 break; 394 case SEEK_END: 395 fp->off = fp->size - offset; 396 break; 397 default: 398 return -1; 399 } 400 return fp->off; 401 } 402 #endif /* !defined(LIBSA_NO_FS_SEEK) */ 403 404 int 405 cd9660_stat(f, sb) 406 struct open_file *f; 407 struct stat *sb; 408 { 409 struct file *fp = (struct file *)f->f_fsdata; 410 411 /* only importatn stuff */ 412 sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 413 sb->st_uid = sb->st_gid = 0; 414 sb->st_size = fp->size; 415 return 0; 416 } 417