1 /* 2 * Copyright (C) 1996 Wolfgang Solfrank. 3 * Copyright (C) 1996 TooLs GmbH. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by TooLs GmbH. 17 * 4. The name of TooLs GmbH may not be used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* Originally derived from libsa/cd9660.c: */ 33 /* $NetBSD: cd9660.c,v 1.5 1997/06/26 19:11:33 drochner Exp $ */ 34 35 #include <sys/cdefs.h> 36 #include <sys/param.h> 37 #include <fs/cd9660/iso.h> 38 #include <fs/cd9660/cd9660_rrip.h> 39 40 static uint64_t cd9660_lookup(const char *); 41 static ssize_t cd9660_fsread(uint64_t, void *, size_t); 42 43 #define SUSP_CONTINUATION "CE" 44 #define SUSP_PRESENT "SP" 45 #define SUSP_STOP "ST" 46 #define SUSP_EXTREF "ER" 47 #define RRIP_NAME "NM" 48 49 typedef struct { 50 ISO_SUSP_HEADER h; 51 u_char signature [ISODCL ( 5, 6)]; 52 u_char len_skp [ISODCL ( 7, 7)]; /* 711 */ 53 } ISO_SUSP_PRESENT; 54 55 static int 56 read_iso_block(void *buffer, daddr_t blkno) 57 { 58 59 return (drvread(&dsk, buffer, blkno * 4, 4)); 60 } 61 62 static ISO_SUSP_HEADER * 63 susp_lookup_record(const char *identifier, struct iso_directory_record *dp, 64 int lenskip) 65 { 66 static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE]; 67 ISO_SUSP_HEADER *sh; 68 ISO_RRIP_CONT *shc; 69 char *p, *end; 70 int error; 71 72 p = dp->name + isonum_711(dp->name_len) + lenskip; 73 /* Names of even length have a padding byte after the name. */ 74 if ((isonum_711(dp->name_len) & 1) == 0) 75 p++; 76 end = (char *)dp + isonum_711(dp->length); 77 while (p + 3 < end) { 78 sh = (ISO_SUSP_HEADER *)p; 79 if (bcmp(sh->type, identifier, 2) == 0) 80 return (sh); 81 if (bcmp(sh->type, SUSP_STOP, 2) == 0) 82 return (NULL); 83 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) { 84 shc = (ISO_RRIP_CONT *)sh; 85 error = read_iso_block(susp_buffer, 86 isonum_733(shc->location)); 87 88 /* Bail if it fails. */ 89 if (error != 0) 90 return (NULL); 91 p = susp_buffer + isonum_733(shc->offset); 92 end = p + isonum_733(shc->length); 93 } else { 94 /* Ignore this record and skip to the next. */ 95 p += isonum_711(sh->length); 96 97 /* Avoid infinite loops with corrupted file systems */ 98 if (isonum_711(sh->length) == 0) 99 return (NULL); 100 } 101 } 102 return (NULL); 103 } 104 105 static const char * 106 rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len) 107 { 108 ISO_RRIP_ALTNAME *p; 109 110 if (len == NULL) 111 return (NULL); 112 113 p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip); 114 if (p == NULL) 115 return (NULL); 116 switch (*p->flags) { 117 case ISO_SUSP_CFLAG_CURRENT: 118 *len = 1; 119 return ("."); 120 case ISO_SUSP_CFLAG_PARENT: 121 *len = 2; 122 return (".."); 123 case 0: 124 *len = isonum_711(p->h.length) - 5; 125 return ((char *)p + 5); 126 default: 127 /* 128 * We don't handle hostnames or continued names as they are 129 * too hard, so just bail and use the default name. 130 */ 131 return (NULL); 132 } 133 } 134 135 static int 136 rrip_check(struct iso_directory_record *dp, int *lenskip) 137 { 138 ISO_SUSP_PRESENT *sp; 139 ISO_RRIP_EXTREF *er; 140 char *p; 141 142 /* First, see if we can find a SP field. */ 143 p = dp->name + isonum_711(dp->name_len); 144 if (p > (char *)dp + isonum_711(dp->length)) { 145 return (0); 146 } 147 sp = (ISO_SUSP_PRESENT *)p; 148 if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) { 149 return (0); 150 } 151 if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) { 152 return (0); 153 } 154 if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) { 155 return (0); 156 } 157 *lenskip = isonum_711(sp->len_skp); 158 159 /* 160 * Now look for an ER field. If RRIP is present, then there must 161 * be at least one of these. It would be more pedantic to walk 162 * through the list of fields looking for a Rock Ridge ER field. 163 */ 164 er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0); 165 if (er == NULL) { 166 return (0); 167 } 168 return (1); 169 } 170 171 static int 172 dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip, 173 int lenskip) 174 { 175 size_t len; 176 const char *cp = NULL; 177 int i, icase; 178 179 if (use_rrip) 180 cp = rrip_lookup_name(dp, lenskip, &len); 181 else 182 cp = NULL; 183 if (cp == NULL) { 184 len = isonum_711(dp->name_len); 185 cp = dp->name; 186 icase = 1; 187 } else 188 icase = 0; 189 for (i = len; --i >= 0; path++, cp++) { 190 if (!*path || *path == '/') 191 break; 192 if (*path == *cp) 193 continue; 194 if (!icase && toupper(*path) == *cp) 195 continue; 196 return 0; 197 } 198 if (*path && *path != '/') { 199 return 0; 200 } 201 /* 202 * Allow stripping of trailing dots and the version number. 203 * Note that this will find the first instead of the last version 204 * of a file. 205 */ 206 if (i >= 0 && (*cp == ';' || *cp == '.')) { 207 /* This is to prevent matching of numeric extensions */ 208 if (*cp == '.' && cp[1] != ';') { 209 return 0; 210 } 211 while (--i >= 0) 212 if (*++cp != ';' && (*cp < '0' || *cp > '9')) { 213 return 0; 214 } 215 } 216 return 1; 217 } 218 219 static uint64_t 220 cd9660_lookup(const char *path) 221 { 222 static char blkbuf[MAX(ISO_DEFAULT_BLOCK_SIZE, 223 sizeof(struct iso_primary_descriptor))]; 224 struct iso_primary_descriptor *vd; 225 struct iso_directory_record rec; 226 struct iso_directory_record *dp = NULL; 227 size_t dsize, off; 228 daddr_t bno, boff; 229 int rc, first, use_rrip, lenskip; 230 uint64_t cookie; 231 232 for (bno = 16;; bno++) { 233 rc = read_iso_block(blkbuf, bno); 234 vd = (struct iso_primary_descriptor *)blkbuf; 235 236 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 237 return (0); 238 if (isonum_711(vd->type) == ISO_VD_END) 239 return (0); 240 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 241 break; 242 } 243 244 bcopy(vd->root_directory_record, &rec, sizeof(rec)); 245 if (*path == '/') path++; /* eat leading '/' */ 246 247 first = 1; 248 use_rrip = 0; 249 lenskip = 0; 250 while (*path) { 251 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 252 dsize = isonum_733(rec.size); 253 off = 0; 254 boff = 0; 255 256 while (off < dsize) { 257 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 258 rc = read_iso_block(blkbuf, bno + boff); 259 if (rc) { 260 return (0); 261 } 262 boff++; 263 dp = (struct iso_directory_record *) blkbuf; 264 } 265 if (isonum_711(dp->length) == 0) { 266 /* skip to next block, if any */ 267 off = boff * ISO_DEFAULT_BLOCK_SIZE; 268 continue; 269 } 270 271 /* See if RRIP is in use. */ 272 if (first) 273 use_rrip = rrip_check(dp, &lenskip); 274 275 if (dirmatch(path, dp, use_rrip, 276 first ? 0 : lenskip)) { 277 first = 0; 278 break; 279 } else 280 first = 0; 281 282 dp = (struct iso_directory_record *) 283 ((char *) dp + isonum_711(dp->length)); 284 /* If the new block has zero length, it is padding. */ 285 if (isonum_711(dp->length) == 0) { 286 /* Skip to next block, if any. */ 287 off = boff * ISO_DEFAULT_BLOCK_SIZE; 288 continue; 289 } 290 off += isonum_711(dp->length); 291 } 292 if (off >= dsize) { 293 return (0); 294 } 295 296 rec = *dp; 297 while (*path && *path != '/') /* look for next component */ 298 path++; 299 if (*path) path++; /* skip '/' */ 300 } 301 302 if ((isonum_711(rec.flags) & 2) != 0) { 303 return (0); 304 } 305 306 cookie = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 307 cookie = (cookie << 32) | isonum_733(rec.size); 308 309 return (cookie); 310 } 311 312 static ssize_t 313 cd9660_fsread(uint64_t cookie, void *buf, size_t nbytes) 314 { 315 static char blkbuf[ISO_DEFAULT_BLOCK_SIZE]; 316 static daddr_t curstart = 0, curblk = 0; 317 daddr_t blk, blk_off; 318 off_t byte_off; 319 size_t size, remaining, n; 320 char *s; 321 322 size = cookie & 0xffffffff; 323 blk = (cookie >> 32) & 0xffffffff; 324 325 /* Make sure we're looking at the right file. */ 326 if (((blk << 32) | size) != cookie) { 327 return (-1); 328 } 329 330 if (blk != curstart) { 331 curstart = blk; 332 fs_off = 0; 333 } 334 335 size -= fs_off; 336 if (size < nbytes) { 337 nbytes = size; 338 } 339 remaining = nbytes; 340 s = buf; 341 342 while (remaining > 0) { 343 blk_off = fs_off >> ISO_DEFAULT_BLOCK_SHIFT; 344 byte_off = fs_off & (ISO_DEFAULT_BLOCK_SIZE - 1); 345 346 if (curblk != curstart + blk_off) { 347 curblk = curstart + blk_off; 348 read_iso_block(blkbuf, curblk); 349 } 350 351 if (remaining < ISO_DEFAULT_BLOCK_SIZE - byte_off) { 352 n = remaining; 353 } else { 354 n = ISO_DEFAULT_BLOCK_SIZE - byte_off; 355 } 356 memcpy(s, blkbuf + byte_off, n); 357 remaining -= n; 358 s += n; 359 360 fs_off += n; 361 } 362 363 return (nbytes); 364 } 365