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