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