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