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