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