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