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