1 /* $OpenBSD: cd9660.c,v 1.12 2004/07/09 19:20:17 drahn Exp $ */ 2 /* $NetBSD: cd9660.c,v 1.1 1996/09/30 16:01:19 ws Exp $ */ 3 4 /* 5 * Copyright (C) 1996 Wolfgang Solfrank. 6 * Copyright (C) 1996 TooLs GmbH. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35 /* 36 * Stand-alone ISO9660 file reading package. 37 * 38 * Note: This doesn't support Rock Ridge extensions, extended attributes, 39 * blocksizes other than 2048 bytes, multi-extent files, etc. 40 */ 41 #include <sys/param.h> 42 #include <sys/stat.h> 43 44 #include <lib/libkern/libkern.h> 45 46 /* THIS IS AN UGLY HACK!!! XXX */ 47 struct fid; 48 struct mbuf; 49 struct nameidata; 50 struct netexport { int x; }; 51 struct proc; 52 struct statfs; 53 struct ucred; 54 #include <isofs/cd9660/iso.h> 55 56 #include "stand.h" 57 #include "cd9660.h" 58 59 struct file { 60 off_t off; /* Current offset within file */ 61 daddr_t bno; /* Starting block number */ 62 off_t size; /* Size of file */ 63 }; 64 65 struct ptable_ent { 66 char namlen [ISODCL( 1, 1)]; /* 711 */ 67 char extlen [ISODCL( 2, 2)]; /* 711 */ 68 char block [ISODCL( 3, 6)]; /* 732 */ 69 char parent [ISODCL( 7, 8)]; /* 722 */ 70 char name [1]; 71 }; 72 #define PTFIXSZ 8 73 #define PTSIZE(pp) roundup(PTFIXSZ + isonum_711((pp)->namlen), 2) 74 75 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 76 77 static int 78 pnmatch(char *path, struct ptable_ent *pp) 79 { 80 char *cp; 81 int i; 82 83 cp = pp->name; 84 for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) { 85 if (toupper(*path) == *cp) 86 continue; 87 return 0; 88 } 89 if (*path != '/') 90 return 0; 91 return 1; 92 } 93 94 static int 95 dirmatch(char *path, struct iso_directory_record *dp) 96 { 97 char *cp; 98 int i; 99 100 /* This needs to be a regular file */ 101 if (dp->flags[0] & 6) 102 return 0; 103 104 cp = dp->name; 105 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 106 if (!*path) 107 break; 108 if (toupper(*path) == *cp) 109 continue; 110 return 0; 111 } 112 if (*path) 113 return 0; 114 /* 115 * Allow stripping of trailing dots and the version number. 116 * Note that this will find the first instead of the last version 117 * of a file. 118 */ 119 if (i >= 0 && (*cp == ';' || *cp == '.')) { 120 /* This is to prevent matching of numeric extensions */ 121 if (*cp == '.' && cp[1] != ';') 122 return 0; 123 while (--i >= 0) 124 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 125 return 0; 126 } 127 return 1; 128 } 129 130 int 131 cd9660_open(char *path, struct open_file *f) 132 { 133 struct file *fp = 0; 134 void *buf; 135 struct iso_primary_descriptor *vd; 136 size_t buf_size, nread, psize, dsize; 137 daddr_t bno; 138 int parent, ent; 139 struct ptable_ent *pp; 140 struct iso_directory_record *dp; 141 int rc; 142 143 /* First find the volume descriptor */ 144 buf = alloc(buf_size = ISO_DEFAULT_BLOCK_SIZE); 145 dp = (struct iso_directory_record *)buf; 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, &nread); 151 if (rc) 152 goto out; 153 if (nread != 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, &nread); 180 if (rc) 181 goto out; 182 if (nread != 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 /* 194 * Remove extra separators 195 */ 196 while (*path == '/') 197 path++; 198 199 while (*path) { 200 if ((void *)pp >= buf + psize) 201 break; 202 if (isonum_722(pp->parent) != parent) 203 break; 204 if (!pnmatch(path, pp)) { 205 pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp)); 206 ent++; 207 continue; 208 } 209 path += isonum_711(pp->namlen) + 1; 210 parent = ent; 211 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 212 while ((void *)pp < buf + psize) { 213 if (isonum_722(pp->parent) == parent) 214 break; 215 pp = (struct ptable_ent *)((void *)pp + PTSIZE(pp)); 216 ent++; 217 } 218 } 219 220 /* Now bno has the start of the directory that supposedly contains the file */ 221 bno--; 222 dsize = 1; /* Something stupid, but > 0 XXX */ 223 for (psize = 0; psize < dsize;) { 224 if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) { 225 bno++; 226 twiddle(); 227 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, 228 cdb2devb(bno), 229 ISO_DEFAULT_BLOCK_SIZE, 230 buf, &nread); 231 if (rc) 232 goto out; 233 if (nread != ISO_DEFAULT_BLOCK_SIZE) { 234 rc = EIO; 235 goto out; 236 } 237 dp = (struct iso_directory_record *)buf; 238 } 239 if (!isonum_711(dp->length)) { 240 if ((void *)dp == buf) 241 psize += ISO_DEFAULT_BLOCK_SIZE; 242 else 243 psize = roundup(psize, ISO_DEFAULT_BLOCK_SIZE); 244 continue; 245 } 246 if (dsize == 1) 247 dsize = isonum_733(dp->size); 248 if (dirmatch(path, dp)) 249 break; 250 psize += isonum_711(dp->length); 251 dp = (struct iso_directory_record *)((void *)dp + 252 isonum_711(dp->length)); 253 } 254 255 if (psize >= dsize) { 256 rc = ENOENT; 257 goto out; 258 } 259 260 /* allocate file system specific data structure */ 261 fp = alloc(sizeof(struct file)); 262 bzero(fp, sizeof(struct file)); 263 f->f_fsdata = (void *)fp; 264 265 fp->off = 0; 266 fp->bno = isonum_733(dp->extent); 267 fp->size = isonum_733(dp->size); 268 free(buf, buf_size); 269 270 return 0; 271 272 out: 273 if (fp) 274 free(fp, sizeof(struct file)); 275 free(buf, buf_size); 276 277 return rc; 278 } 279 280 int 281 cd9660_close(struct open_file *f) 282 { 283 struct file *fp = (struct file *)f->f_fsdata; 284 285 f->f_fsdata = 0; 286 free(fp, sizeof *fp); 287 288 return 0; 289 } 290 291 int 292 cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid) 293 { 294 struct file *fp = (struct file *)f->f_fsdata; 295 int rc = 0; 296 daddr_t bno; 297 char buf[ISO_DEFAULT_BLOCK_SIZE]; 298 char *dp; 299 size_t nread, off; 300 301 while (size) { 302 if (fp->off < 0 || fp->off >= fp->size) 303 break; 304 bno = (fp->off >> ISO_DEFAULT_BLOCK_SHIFT) + fp->bno; 305 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1) 306 || size < ISO_DEFAULT_BLOCK_SIZE) 307 dp = buf; 308 else 309 dp = start; 310 twiddle(); 311 rc = f->f_dev->dv_strategy(f->f_devdata, F_READ, cdb2devb(bno), 312 ISO_DEFAULT_BLOCK_SIZE, dp, &nread); 313 if (rc) 314 return rc; 315 if (nread != ISO_DEFAULT_BLOCK_SIZE) 316 return EIO; 317 318 /* 319 * off is either 0 in the dp == start case or 320 * the offset to the interesting data into the buffer of 'buf' 321 */ 322 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1); 323 nread -= off; 324 if (nread > size) 325 nread = size; 326 327 if (nread > (fp->size - fp->off)) 328 nread = (fp->size - fp->off); 329 330 if (dp == buf) 331 bcopy(buf + off, start, nread); 332 333 start += nread; 334 fp->off += nread; 335 size -= nread; 336 } 337 if (resid) 338 *resid = size; 339 return rc; 340 } 341 342 int 343 cd9660_write(struct open_file *f, void *start, size_t size, size_t *resid) 344 { 345 return EROFS; 346 } 347 348 off_t 349 cd9660_seek(struct open_file *f, off_t offset, 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(struct open_file *f, struct stat *sb) 371 { 372 struct file *fp = (struct file *)f->f_fsdata; 373 374 /* only important stuff */ 375 sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 376 sb->st_uid = sb->st_gid = 0; 377 sb->st_size = fp->size; 378 return 0; 379 } 380 381 /* 382 * Not implemented. 383 */ 384 #ifndef NO_READDIR 385 int 386 cd9660_readdir(struct open_file *f, char *name) 387 { 388 return (EROFS); 389 } 390 #endif 391