1 /* $NetBSD: cd9660.c,v 1.12 2001/04/05 04:39:02 thorpej 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 pnmatch __P((char *, struct ptable_ent *)); 77 static int dirmatch __P((char *, struct iso_directory_record *)); 78 79 static int 80 pnmatch(path, pp) 81 char *path; 82 struct ptable_ent *pp; 83 { 84 char *cp; 85 int i; 86 87 cp = pp->name; 88 for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) { 89 if (toupper(*path) == *cp) 90 continue; 91 return 0; 92 } 93 if (*path != '/') 94 return 0; 95 return 1; 96 } 97 98 static int 99 dirmatch(path, dp) 100 char *path; 101 struct iso_directory_record *dp; 102 { 103 char *cp; 104 int i; 105 106 /* This needs to be a regular file */ 107 if (dp->flags[0] & 6) 108 return 0; 109 110 cp = dp->name; 111 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 112 if (!*path) 113 break; 114 if (toupper(*path) == *cp) 115 continue; 116 return 0; 117 } 118 if (*path) 119 return 0; 120 /* 121 * Allow stripping of trailing dots and the version number. 122 * Note that this will find the first instead of the last version 123 * of a file. 124 */ 125 if (i >= 0 && (*cp == ';' || *cp == '.')) { 126 /* This is to prevent matching of numeric extensions */ 127 if (*cp == '.' && cp[1] != ';') 128 return 0; 129 while (--i >= 0) 130 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 131 return 0; 132 } 133 return 1; 134 } 135 136 int 137 cd9660_open(path, f) 138 char *path; 139 struct open_file *f; 140 { 141 struct file *fp = 0; 142 void *buf; 143 struct iso_primary_descriptor *vd; 144 size_t buf_size, read, psize, dsize; 145 daddr_t bno; 146 int parent, ent; 147 struct ptable_ent *pp; 148 struct iso_directory_record *dp = 0; 149 int rc; 150 151 /* First find the volume descriptor */ 152 buf = alloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 153 vd = buf; 154 for (bno = 16;; bno++) { 155 #if !defined(LIBSA_NO_TWIDDLE) 156 twiddle(); 157 #endif 158 rc = DEV_STRATEGY(f->f_dev)(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 /* Now get the path table and lookup the directory of the file */ 178 bno = isonum_732(vd->type_m_path_table); 179 psize = isonum_733(vd->path_table_size); 180 181 if (psize > ISO_DEFAULT_BLOCK_SIZE) { 182 free(buf, ISO_DEFAULT_BLOCK_SIZE); 183 buf = alloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE)); 184 } 185 186 #if !defined(LIBSA_NO_TWIDDLE) 187 twiddle(); 188 #endif 189 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 190 buf_size, buf, &read); 191 if (rc) 192 goto out; 193 if (read != buf_size) { 194 rc = EIO; 195 goto out; 196 } 197 198 parent = 1; 199 pp = (struct ptable_ent *)buf; 200 ent = 1; 201 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 202 203 rc = ENOENT; 204 /* 205 * Remove extra separators 206 */ 207 while (*path == '/') 208 path++; 209 210 while (*path) { 211 if ((caddr_t)pp >= (caddr_t)buf + psize) 212 break; 213 if (isonum_722(pp->parent) != parent) 214 break; 215 if (!pnmatch(path, pp)) { 216 pp = (struct ptable_ent *)((caddr_t)pp + PTSIZE(pp)); 217 ent++; 218 continue; 219 } 220 path += isonum_711(pp->namlen) + 1; 221 parent = ent; 222 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 223 while ((caddr_t)pp < (caddr_t)buf + psize) { 224 if (isonum_722(pp->parent) == parent) 225 break; 226 pp = (struct ptable_ent *)((caddr_t)pp + PTSIZE(pp)); 227 ent++; 228 } 229 } 230 231 /* Now bno has the start of the directory that supposedly contains the file */ 232 bno--; 233 dsize = 1; /* Something stupid, but > 0 XXX */ 234 for (psize = 0; psize < dsize;) { 235 if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) { 236 bno++; 237 #if !defined(LIBSA_NO_TWIDDLE) 238 twiddle(); 239 #endif 240 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 241 cdb2devb(bno), 242 ISO_DEFAULT_BLOCK_SIZE, 243 buf, &read); 244 if (rc) 245 goto out; 246 if (read != ISO_DEFAULT_BLOCK_SIZE) { 247 rc = EIO; 248 goto out; 249 } 250 dp = (struct iso_directory_record *)buf; 251 } 252 if (!isonum_711(dp->length)) { 253 if ((void *)dp == buf) 254 psize += ISO_DEFAULT_BLOCK_SIZE; 255 else 256 psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE); 257 continue; 258 } 259 if (dsize == 1) 260 dsize = isonum_733(dp->size); 261 if (dirmatch(path, dp)) 262 break; 263 psize += isonum_711(dp->length); 264 dp = (struct iso_directory_record *)((caddr_t)dp + isonum_711(dp->length)); 265 } 266 267 if (psize >= dsize) { 268 rc = ENOENT; 269 goto out; 270 } 271 272 /* allocate file system specific data structure */ 273 fp = alloc(sizeof(struct file)); 274 bzero(fp, sizeof(struct file)); 275 f->f_fsdata = (void *)fp; 276 277 fp->off = 0; 278 fp->bno = isonum_733(dp->extent); 279 fp->size = isonum_733(dp->size); 280 free(buf, buf_size); 281 282 return 0; 283 284 out: 285 if (fp) 286 free(fp, sizeof(struct file)); 287 free(buf, buf_size); 288 289 return rc; 290 } 291 292 #if !defined(LIBSA_NO_FS_CLOSE) 293 int 294 cd9660_close(f) 295 struct open_file *f; 296 { 297 struct file *fp = (struct file *)f->f_fsdata; 298 299 f->f_fsdata = 0; 300 free(fp, sizeof *fp); 301 302 return 0; 303 } 304 #endif /* !defined(LIBSA_NO_FS_CLOSE) */ 305 306 int 307 cd9660_read(f, start, size, resid) 308 struct open_file *f; 309 void *start; 310 size_t size; 311 size_t *resid; 312 { 313 struct file *fp = (struct file *)f->f_fsdata; 314 int rc = 0; 315 daddr_t bno; 316 char buf[ISO_DEFAULT_BLOCK_SIZE]; 317 char *dp; 318 size_t read, off; 319 320 while (size) { 321 if (fp->off < 0 || fp->off >= fp->size) 322 break; 323 bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno; 324 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1) 325 || size < ISO_DEFAULT_BLOCK_SIZE) 326 dp = buf; 327 else 328 dp = start; 329 #if !defined(LIBSA_NO_TWIDDLE) 330 twiddle(); 331 #endif 332 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 333 ISO_DEFAULT_BLOCK_SIZE, dp, &read); 334 if (rc) 335 return rc; 336 if (read != ISO_DEFAULT_BLOCK_SIZE) 337 return EIO; 338 if (dp == buf) { 339 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1); 340 if (read > off + size) 341 read = off + size; 342 read -= off; 343 bcopy(buf + off, start, read); 344 start = (caddr_t)start + read; 345 fp->off += read; 346 size -= read; 347 } else { 348 start = (caddr_t)start + ISO_DEFAULT_BLOCK_SIZE; 349 fp->off += ISO_DEFAULT_BLOCK_SIZE; 350 size -= ISO_DEFAULT_BLOCK_SIZE; 351 } 352 } 353 if (resid) 354 *resid = size; 355 return rc; 356 } 357 358 #if !defined(LIBSA_NO_FS_WRITE) 359 int 360 cd9660_write(f, start, size, resid) 361 struct open_file *f; 362 void *start; 363 size_t size; 364 size_t *resid; 365 { 366 return EROFS; 367 } 368 #endif /* !defined(LIBSA_NO_FS_WRITE) */ 369 370 #if !defined(LIBSA_NO_FS_SEEK) 371 off_t 372 cd9660_seek(f, offset, where) 373 struct open_file *f; 374 off_t offset; 375 int where; 376 { 377 struct file *fp = (struct file *)f->f_fsdata; 378 379 switch (where) { 380 case SEEK_SET: 381 fp->off = offset; 382 break; 383 case SEEK_CUR: 384 fp->off += offset; 385 break; 386 case SEEK_END: 387 fp->off = fp->size - offset; 388 break; 389 default: 390 return -1; 391 } 392 return fp->off; 393 } 394 #endif /* !defined(LIBSA_NO_FS_SEEK) */ 395 396 int 397 cd9660_stat(f, sb) 398 struct open_file *f; 399 struct stat *sb; 400 { 401 struct file *fp = (struct file *)f->f_fsdata; 402 403 /* only importatn stuff */ 404 sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 405 sb->st_uid = sb->st_gid = 0; 406 sb->st_size = fp->size; 407 return 0; 408 } 409