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