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/param.h> 36 #include <fs/cd9660/iso.h> 37 #include <fs/cd9660/cd9660_rrip.h> 38 39 static uint64_t cd9660_lookup(const char *); 40 static ssize_t cd9660_fsread(uint64_t, void *, size_t); 41 42 #define SUSP_CONTINUATION "CE" 43 #define SUSP_PRESENT "SP" 44 #define SUSP_STOP "ST" 45 #define SUSP_EXTREF "ER" 46 #define RRIP_NAME "NM" 47 48 typedef struct { 49 ISO_SUSP_HEADER h; 50 u_char signature [ISODCL ( 5, 6)]; 51 u_char len_skp [ISODCL ( 7, 7)]; /* 711 */ 52 } ISO_SUSP_PRESENT; 53 54 static int 55 read_iso_block(void *buffer, daddr_t blkno) 56 { 57 58 return (drvread(&dsk, buffer, blkno * 4, 4)); 59 } 60 61 static ISO_SUSP_HEADER * 62 susp_lookup_record(const char *identifier, struct iso_directory_record *dp, 63 int lenskip) 64 { 65 static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE]; 66 ISO_SUSP_HEADER *sh; 67 ISO_RRIP_CONT *shc; 68 char *p, *end; 69 int error; 70 71 p = dp->name + isonum_711(dp->name_len) + lenskip; 72 /* Names of even length have a padding byte after the name. */ 73 if ((isonum_711(dp->name_len) & 1) == 0) 74 p++; 75 end = (char *)dp + isonum_711(dp->length); 76 while (p + 3 < end) { 77 sh = (ISO_SUSP_HEADER *)p; 78 if (bcmp(sh->type, identifier, 2) == 0) 79 return (sh); 80 if (bcmp(sh->type, SUSP_STOP, 2) == 0) 81 return (NULL); 82 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) { 83 shc = (ISO_RRIP_CONT *)sh; 84 error = read_iso_block(susp_buffer, 85 isonum_733(shc->location)); 86 87 /* Bail if it fails. */ 88 if (error != 0) 89 return (NULL); 90 p = susp_buffer + isonum_733(shc->offset); 91 end = p + isonum_733(shc->length); 92 } else { 93 /* Ignore this record and skip to the next. */ 94 p += isonum_711(sh->length); 95 96 /* Avoid infinite loops with corrupted file systems */ 97 if (isonum_711(sh->length) == 0) 98 return (NULL); 99 } 100 } 101 return (NULL); 102 } 103 104 static const char * 105 rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len) 106 { 107 ISO_RRIP_ALTNAME *p; 108 109 if (len == NULL) 110 return (NULL); 111 112 p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip); 113 if (p == NULL) 114 return (NULL); 115 switch (*p->flags) { 116 case ISO_SUSP_CFLAG_CURRENT: 117 *len = 1; 118 return ("."); 119 case ISO_SUSP_CFLAG_PARENT: 120 *len = 2; 121 return (".."); 122 case 0: 123 *len = isonum_711(p->h.length) - 5; 124 return ((char *)p + 5); 125 default: 126 /* 127 * We don't handle hostnames or continued names as they are 128 * too hard, so just bail and use the default name. 129 */ 130 return (NULL); 131 } 132 } 133 134 static int 135 rrip_check(struct iso_directory_record *dp, int *lenskip) 136 { 137 ISO_SUSP_PRESENT *sp; 138 ISO_RRIP_EXTREF *er; 139 char *p; 140 141 /* First, see if we can find a SP field. */ 142 p = dp->name + isonum_711(dp->name_len); 143 if (p > (char *)dp + isonum_711(dp->length)) { 144 return (0); 145 } 146 sp = (ISO_SUSP_PRESENT *)p; 147 if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) { 148 return (0); 149 } 150 if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) { 151 return (0); 152 } 153 if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) { 154 return (0); 155 } 156 *lenskip = isonum_711(sp->len_skp); 157 158 /* 159 * Now look for an ER field. If RRIP is present, then there must 160 * be at least one of these. It would be more pedantic to walk 161 * through the list of fields looking for a Rock Ridge ER field. 162 */ 163 er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0); 164 if (er == NULL) { 165 return (0); 166 } 167 return (1); 168 } 169 170 static int 171 dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip, 172 int lenskip) 173 { 174 size_t len; 175 const char *cp = NULL; 176 int i, icase; 177 178 if (use_rrip) 179 cp = rrip_lookup_name(dp, lenskip, &len); 180 else 181 cp = NULL; 182 if (cp == NULL) { 183 len = isonum_711(dp->name_len); 184 cp = dp->name; 185 icase = 1; 186 } else 187 icase = 0; 188 for (i = len; --i >= 0; path++, cp++) { 189 if (!*path || *path == '/') 190 break; 191 if (*path == *cp) 192 continue; 193 if (!icase && toupper(*path) == *cp) 194 continue; 195 return 0; 196 } 197 if (*path && *path != '/') { 198 return 0; 199 } 200 /* 201 * Allow stripping of trailing dots and the version number. 202 * Note that this will find the first instead of the last version 203 * of a file. 204 */ 205 if (i >= 0 && (*cp == ';' || *cp == '.')) { 206 /* This is to prevent matching of numeric extensions */ 207 if (*cp == '.' && cp[1] != ';') { 208 return 0; 209 } 210 while (--i >= 0) 211 if (*++cp != ';' && (*cp < '0' || *cp > '9')) { 212 return 0; 213 } 214 } 215 return 1; 216 } 217 218 static uint64_t 219 cd9660_lookup(const char *path) 220 { 221 static char blkbuf[MAX(ISO_DEFAULT_BLOCK_SIZE, 222 sizeof(struct iso_primary_descriptor))]; 223 struct iso_primary_descriptor *vd; 224 struct iso_directory_record rec; 225 struct iso_directory_record *dp = NULL; 226 size_t dsize, off; 227 daddr_t bno, boff; 228 int rc, first, use_rrip, lenskip; 229 uint64_t cookie; 230 231 for (bno = 16;; bno++) { 232 rc = read_iso_block(blkbuf, bno); 233 vd = (struct iso_primary_descriptor *)blkbuf; 234 235 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 236 return (0); 237 if (isonum_711(vd->type) == ISO_VD_END) 238 return (0); 239 if (isonum_711(vd->type) == ISO_VD_PRIMARY) 240 break; 241 } 242 243 bcopy(vd->root_directory_record, &rec, sizeof(rec)); 244 if (*path == '/') path++; /* eat leading '/' */ 245 246 first = 1; 247 use_rrip = 0; 248 lenskip = 0; 249 while (*path) { 250 bno = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 251 dsize = isonum_733(rec.size); 252 off = 0; 253 boff = 0; 254 255 while (off < dsize) { 256 if ((off % ISO_DEFAULT_BLOCK_SIZE) == 0) { 257 rc = read_iso_block(blkbuf, bno + boff); 258 if (rc) { 259 return (0); 260 } 261 boff++; 262 dp = (struct iso_directory_record *) blkbuf; 263 } 264 if (isonum_711(dp->length) == 0) { 265 /* skip to next block, if any */ 266 off = boff * ISO_DEFAULT_BLOCK_SIZE; 267 continue; 268 } 269 270 /* See if RRIP is in use. */ 271 if (first) 272 use_rrip = rrip_check(dp, &lenskip); 273 274 if (dirmatch(path, dp, use_rrip, 275 first ? 0 : lenskip)) { 276 first = 0; 277 break; 278 } else 279 first = 0; 280 281 dp = (struct iso_directory_record *) 282 ((char *) dp + isonum_711(dp->length)); 283 /* If the new block has zero length, it is padding. */ 284 if (isonum_711(dp->length) == 0) { 285 /* Skip to next block, if any. */ 286 off = boff * ISO_DEFAULT_BLOCK_SIZE; 287 continue; 288 } 289 off += isonum_711(dp->length); 290 } 291 if (off >= dsize) { 292 return (0); 293 } 294 295 rec = *dp; 296 while (*path && *path != '/') /* look for next component */ 297 path++; 298 if (*path) path++; /* skip '/' */ 299 } 300 301 if ((isonum_711(rec.flags) & 2) != 0) { 302 return (0); 303 } 304 305 cookie = isonum_733(rec.extent) + isonum_711(rec.ext_attr_length); 306 cookie = (cookie << 32) | isonum_733(rec.size); 307 308 return (cookie); 309 } 310 311 static ssize_t 312 cd9660_fsread(uint64_t cookie, void *buf, size_t nbytes) 313 { 314 static char blkbuf[ISO_DEFAULT_BLOCK_SIZE]; 315 static daddr_t curstart = 0, curblk = 0; 316 daddr_t blk, blk_off; 317 off_t byte_off; 318 size_t size, remaining, n; 319 char *s; 320 321 size = cookie & 0xffffffff; 322 blk = (cookie >> 32) & 0xffffffff; 323 324 /* Make sure we're looking at the right file. */ 325 if (((blk << 32) | size) != cookie) { 326 return (-1); 327 } 328 329 if (blk != curstart) { 330 curstart = blk; 331 fs_off = 0; 332 } 333 334 size -= fs_off; 335 if (size < nbytes) { 336 nbytes = size; 337 } 338 remaining = nbytes; 339 s = buf; 340 341 while (remaining > 0) { 342 blk_off = fs_off >> ISO_DEFAULT_BLOCK_SHIFT; 343 byte_off = fs_off & (ISO_DEFAULT_BLOCK_SIZE - 1); 344 345 if (curblk != curstart + blk_off) { 346 curblk = curstart + blk_off; 347 read_iso_block(blkbuf, curblk); 348 } 349 350 if (remaining < ISO_DEFAULT_BLOCK_SIZE - byte_off) { 351 n = remaining; 352 } else { 353 n = ISO_DEFAULT_BLOCK_SIZE - byte_off; 354 } 355 memcpy(s, blkbuf + byte_off, n); 356 remaining -= n; 357 s += n; 358 359 fs_off += n; 360 } 361 362 return (nbytes); 363 } 364