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 <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 u_char signature [ISODCL ( 5, 6)]; 53 u_char len_skp [ISODCL ( 7, 7)]; /* 711 */ 54 } ISO_SUSP_PRESENT; 55 56 static int 57 read_iso_block(void *buffer, daddr_t blkno) 58 { 59 60 return (drvread(&dsk, buffer, blkno * 4, 4)); 61 } 62 63 static ISO_SUSP_HEADER * 64 susp_lookup_record(const char *identifier, struct iso_directory_record *dp, 65 int lenskip) 66 { 67 static char susp_buffer[ISO_DEFAULT_BLOCK_SIZE]; 68 ISO_SUSP_HEADER *sh; 69 ISO_RRIP_CONT *shc; 70 char *p, *end; 71 int error; 72 73 p = dp->name + isonum_711(dp->name_len) + lenskip; 74 /* Names of even length have a padding byte after the name. */ 75 if ((isonum_711(dp->name_len) & 1) == 0) 76 p++; 77 end = (char *)dp + isonum_711(dp->length); 78 while (p + 3 < end) { 79 sh = (ISO_SUSP_HEADER *)p; 80 if (bcmp(sh->type, identifier, 2) == 0) 81 return (sh); 82 if (bcmp(sh->type, SUSP_STOP, 2) == 0) 83 return (NULL); 84 if (bcmp(sh->type, SUSP_CONTINUATION, 2) == 0) { 85 shc = (ISO_RRIP_CONT *)sh; 86 error = read_iso_block(susp_buffer, 87 isonum_733(shc->location)); 88 89 /* Bail if it fails. */ 90 if (error != 0) 91 return (NULL); 92 p = susp_buffer + isonum_733(shc->offset); 93 end = p + isonum_733(shc->length); 94 } else { 95 /* Ignore this record and skip to the next. */ 96 p += isonum_711(sh->length); 97 98 /* Avoid infinite loops with corrupted file systems */ 99 if (isonum_711(sh->length) == 0) 100 return (NULL); 101 } 102 } 103 return (NULL); 104 } 105 106 static const char * 107 rrip_lookup_name(struct iso_directory_record *dp, int lenskip, size_t *len) 108 { 109 ISO_RRIP_ALTNAME *p; 110 111 if (len == NULL) 112 return (NULL); 113 114 p = (ISO_RRIP_ALTNAME *)susp_lookup_record(RRIP_NAME, dp, lenskip); 115 if (p == NULL) 116 return (NULL); 117 switch (*p->flags) { 118 case ISO_SUSP_CFLAG_CURRENT: 119 *len = 1; 120 return ("."); 121 case ISO_SUSP_CFLAG_PARENT: 122 *len = 2; 123 return (".."); 124 case 0: 125 *len = isonum_711(p->h.length) - 5; 126 return ((char *)p + 5); 127 default: 128 /* 129 * We don't handle hostnames or continued names as they are 130 * too hard, so just bail and use the default name. 131 */ 132 return (NULL); 133 } 134 } 135 136 static int 137 rrip_check(struct iso_directory_record *dp, int *lenskip) 138 { 139 ISO_SUSP_PRESENT *sp; 140 ISO_RRIP_EXTREF *er; 141 char *p; 142 143 /* First, see if we can find a SP field. */ 144 p = dp->name + isonum_711(dp->name_len); 145 if (p > (char *)dp + isonum_711(dp->length)) { 146 return (0); 147 } 148 sp = (ISO_SUSP_PRESENT *)p; 149 if (bcmp(sp->h.type, SUSP_PRESENT, 2) != 0) { 150 return (0); 151 } 152 if (isonum_711(sp->h.length) != sizeof(ISO_SUSP_PRESENT)) { 153 return (0); 154 } 155 if (sp->signature[0] != 0xbe || sp->signature[1] != 0xef) { 156 return (0); 157 } 158 *lenskip = isonum_711(sp->len_skp); 159 160 /* 161 * Now look for an ER field. If RRIP is present, then there must 162 * be at least one of these. It would be more pedantic to walk 163 * through the list of fields looking for a Rock Ridge ER field. 164 */ 165 er = (ISO_RRIP_EXTREF *)susp_lookup_record(SUSP_EXTREF, dp, 0); 166 if (er == NULL) { 167 return (0); 168 } 169 return (1); 170 } 171 172 static int 173 dirmatch(const char *path, struct iso_directory_record *dp, int use_rrip, 174 int lenskip) 175 { 176 size_t len; 177 const char *cp = NULL; 178 int i, icase; 179 180 if (use_rrip) 181 cp = rrip_lookup_name(dp, lenskip, &len); 182 else 183 cp = NULL; 184 if (cp == NULL) { 185 len = isonum_711(dp->name_len); 186 cp = dp->name; 187 icase = 1; 188 } else 189 icase = 0; 190 for (i = len; --i >= 0; path++, cp++) { 191 if (!*path || *path == '/') 192 break; 193 if (*path == *cp) 194 continue; 195 if (!icase && toupper(*path) == *cp) 196 continue; 197 return 0; 198 } 199 if (*path && *path != '/') { 200 return 0; 201 } 202 /* 203 * Allow stripping of trailing dots and the version number. 204 * Note that this will find the first instead of the last version 205 * of a file. 206 */ 207 if (i >= 0 && (*cp == ';' || *cp == '.')) { 208 /* This is to prevent matching of numeric extensions */ 209 if (*cp == '.' && cp[1] != ';') { 210 return 0; 211 } 212 while (--i >= 0) 213 if (*++cp != ';' && (*cp < '0' || *cp > '9')) { 214 return 0; 215 } 216 } 217 return 1; 218 } 219 220 static uint64_t 221 cd9660_lookup(const char *path) 222 { 223 static char blkbuf[ISO_DEFAULT_BLOCK_SIZE]; 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 rec = *(struct iso_directory_record *) vd->root_directory_record; 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