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