1 /* $NetBSD: cd9660.c,v 1.31 2018/03/08 23:02:50 nonaka 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 #ifndef cdb2devb 75 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / DEV_BSIZE) 76 #endif 77 78 static int pnmatch(const char *, struct ptable_ent *); 79 static int dirmatch(const char *, struct iso_directory_record *); 80 81 static int 82 pnmatch(const char *path, struct ptable_ent *pp) 83 { 84 char *cp; 85 int i; 86 87 cp = pp->name; 88 for (i = isonum_711(pp->namlen); --i >= 0; path++, cp++) { 89 if (toupper(*path) == *cp) 90 continue; 91 return 0; 92 } 93 if (*path != '/') 94 return 0; 95 return 1; 96 } 97 98 static int 99 dirmatch(const char *path, struct iso_directory_record *dp) 100 { 101 char *cp; 102 int i; 103 104 /* This needs to be a regular file */ 105 if (dp->flags[0] & 6) 106 return 0; 107 108 cp = dp->name; 109 for (i = isonum_711(dp->name_len); --i >= 0; path++, cp++) { 110 if (!*path) 111 break; 112 if (toupper(*path) == *cp) 113 continue; 114 return 0; 115 } 116 if (*path) 117 return 0; 118 /* 119 * Allow stripping of trailing dots and the version number. 120 * Note that this will find the first instead of the last version 121 * of a file. 122 */ 123 if (i >= 0 && (*cp == ';' || *cp == '.')) { 124 /* This is to prevent matching of numeric extensions */ 125 if (*cp == '.' && cp[1] != ';') 126 return 0; 127 while (--i >= 0) 128 if (*++cp != ';' && (*cp < '0' || *cp > '9')) 129 return 0; 130 } 131 return 1; 132 } 133 134 __compactcall int 135 cd9660_open(const char *path, struct open_file *f) 136 { 137 struct file *fp = 0; 138 void *buf; 139 struct iso_primary_descriptor *vd; 140 size_t buf_size, nread, psize, dsize; 141 daddr_t bno; 142 int parent, ent; 143 struct ptable_ent *pp; 144 struct iso_directory_record *dp = 0; 145 int rc; 146 147 /* First find the volume descriptor */ 148 buf_size = ISO_DEFAULT_BLOCK_SIZE; 149 buf = alloc(buf_size); 150 vd = buf; 151 for (bno = 16;; bno++) { 152 #if !defined(LIBSA_NO_TWIDDLE) 153 twiddle(); 154 #endif 155 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 156 ISO_DEFAULT_BLOCK_SIZE, buf, &nread); 157 if (rc) 158 goto out; 159 if (nread != ISO_DEFAULT_BLOCK_SIZE) { 160 rc = EIO; 161 goto out; 162 } 163 rc = EINVAL; 164 if (memcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 165 goto out; 166 if (isonum_711(vd->type) == ISO_VD_END) 167 goto out; 168 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 169 break; 170 } 171 if (isonum_723(vd->logical_block_size) != ISO_DEFAULT_BLOCK_SIZE) 172 goto out; 173 174 /* Now get the path table and lookup the directory of the file */ 175 bno = isonum_732(vd->type_m_path_table); 176 psize = isonum_733(vd->path_table_size); 177 178 if (psize > ISO_DEFAULT_BLOCK_SIZE) { 179 dealloc(buf, ISO_DEFAULT_BLOCK_SIZE); 180 buf = alloc(buf_size = roundup(psize, ISO_DEFAULT_BLOCK_SIZE)); 181 } 182 183 #if !defined(LIBSA_NO_TWIDDLE) 184 twiddle(); 185 #endif 186 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 187 buf_size, buf, &nread); 188 if (rc) 189 goto out; 190 if (nread != buf_size) { 191 rc = EIO; 192 goto out; 193 } 194 195 parent = 1; 196 pp = (struct ptable_ent *)buf; 197 ent = 1; 198 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 199 200 rc = ENOENT; 201 202 while (*path) { 203 /* 204 * Remove extra separators 205 */ 206 while (*path == '/') 207 path++; 208 209 if ((char *)pp >= (char *)buf + psize) 210 break; 211 if (isonum_722(pp->parent) != parent) 212 break; 213 if (!pnmatch(path, pp)) { 214 pp = (struct ptable_ent *)((char *)pp + PTSIZE(pp)); 215 ent++; 216 continue; 217 } 218 path += isonum_711(pp->namlen) + 1; 219 parent = ent; 220 bno = isonum_732(pp->block) + isonum_711(pp->extlen); 221 while ((char *)pp < (char *)buf + psize) { 222 if (isonum_722(pp->parent) == parent) 223 break; 224 pp = (struct ptable_ent *)((char *)pp + PTSIZE(pp)); 225 ent++; 226 } 227 } 228 229 /* 230 * Now bno has the start of the directory that supposedly 231 * contains the file 232 */ 233 bno--; 234 dsize = 1; /* Something stupid, but > 0 XXX */ 235 for (psize = 0; psize < dsize;) { 236 if (!(psize % ISO_DEFAULT_BLOCK_SIZE)) { 237 bno++; 238 #if !defined(LIBSA_NO_TWIDDLE) 239 twiddle(); 240 #endif 241 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 242 cdb2devb(bno), 243 ISO_DEFAULT_BLOCK_SIZE, 244 buf, &nread); 245 if (rc) 246 goto out; 247 if (nread != 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 *) 266 ((char *)dp + isonum_711(dp->length)); 267 } 268 269 if (psize >= dsize) { 270 rc = ENOENT; 271 goto out; 272 } 273 274 /* allocate file system specific data structure */ 275 fp = alloc(sizeof(struct file)); 276 memset(fp, 0, sizeof(struct file)); 277 f->f_fsdata = (void *)fp; 278 279 fp->off = 0; 280 fp->bno = isonum_733(dp->extent); 281 fp->size = isonum_733(dp->size); 282 dealloc(buf, buf_size); 283 fsmod = "cd9660"; 284 285 return 0; 286 287 out: 288 if (fp) 289 dealloc(fp, sizeof(struct file)); 290 dealloc(buf, buf_size); 291 292 return rc; 293 } 294 295 #if !defined(LIBSA_NO_FS_CLOSE) 296 __compactcall int 297 cd9660_close(struct open_file *f) 298 { 299 struct file *fp = (struct file *)f->f_fsdata; 300 301 f->f_fsdata = 0; 302 dealloc(fp, sizeof *fp); 303 304 return 0; 305 } 306 #endif /* !defined(LIBSA_NO_FS_CLOSE) */ 307 308 __compactcall int 309 cd9660_read(struct open_file *f, void *start, size_t size, size_t *resid) 310 { 311 struct file *fp = (struct file *)f->f_fsdata; 312 int rc = 0; 313 daddr_t bno; 314 char buf[ISO_DEFAULT_BLOCK_SIZE]; 315 char *dp; 316 size_t nread, off; 317 318 while (size) { 319 if (fp->off < 0 || fp->off >= fp->size) 320 break; 321 bno = fp->off / ISO_DEFAULT_BLOCK_SIZE + fp->bno; 322 if (fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1) 323 || (fp->off + ISO_DEFAULT_BLOCK_SIZE) > fp->size 324 || size < ISO_DEFAULT_BLOCK_SIZE) 325 dp = buf; 326 else 327 dp = start; 328 #if !defined(LIBSA_NO_TWIDDLE) 329 twiddle(); 330 #endif 331 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, cdb2devb(bno), 332 ISO_DEFAULT_BLOCK_SIZE, dp, &nread); 333 if (rc) 334 return rc; 335 if (nread != ISO_DEFAULT_BLOCK_SIZE) 336 return EIO; 337 if (dp == buf) { 338 off = fp->off & (ISO_DEFAULT_BLOCK_SIZE - 1); 339 if (nread > off + size) 340 nread = off + size; 341 nread -= off; 342 if (nread > fp->size - fp->off) 343 nread = fp->size - fp->off; 344 memcpy(start, buf + off, nread); 345 start = (char *)start + nread; 346 fp->off += nread; 347 size -= nread; 348 } else { 349 start = (char *)start + ISO_DEFAULT_BLOCK_SIZE; 350 fp->off += ISO_DEFAULT_BLOCK_SIZE; 351 size -= ISO_DEFAULT_BLOCK_SIZE; 352 } 353 } 354 if(fp->off > fp->size) 355 size += fp->off - fp->size; 356 if (resid) 357 *resid = size; 358 return rc; 359 } 360 361 #if !defined(LIBSA_NO_FS_WRITE) 362 __compactcall int 363 cd9660_write(struct open_file *f, void *start, size_t size, size_t *resid) 364 { 365 366 return EROFS; 367 } 368 #endif /* !defined(LIBSA_NO_FS_WRITE) */ 369 370 #if !defined(LIBSA_NO_FS_SEEK) 371 __compactcall off_t 372 cd9660_seek(struct open_file *f, off_t offset, int where) 373 { 374 struct file *fp = (struct file *)f->f_fsdata; 375 376 switch (where) { 377 case SEEK_SET: 378 fp->off = offset; 379 break; 380 case SEEK_CUR: 381 fp->off += offset; 382 break; 383 case SEEK_END: 384 fp->off = fp->size - offset; 385 break; 386 default: 387 return -1; 388 } 389 return fp->off; 390 } 391 #endif /* !defined(LIBSA_NO_FS_SEEK) */ 392 393 __compactcall int 394 cd9660_stat(struct open_file *f, struct stat *sb) 395 { 396 struct file *fp = (struct file *)f->f_fsdata; 397 398 /* only importatn stuff */ 399 sb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; 400 sb->st_uid = sb->st_gid = 0; 401 sb->st_size = fp->size; 402 return 0; 403 } 404 405 #if defined(LIBSA_ENABLE_LS_OP) 406 #include "ls.h" 407 __compactcall void 408 cd9660_ls(struct open_file *f, const char *pattern) 409 { 410 lsunsup("cd9660"); 411 } 412 #endif 413