1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include "jni.h" 33 #include "jli_util.h" 34 35 #include <zlib.h> 36 #include "manifest_info.h" 37 38 static char *manifest; 39 40 static const char *manifest_name = "META-INF/MANIFEST.MF"; 41 42 /* 43 * Inflate the manifest file (or any file for that matter). 44 * 45 * fd: File descriptor of the jar file. 46 * entry: Contains the information necessary to perform the inflation 47 * (the compressed and uncompressed sizes and the offset in 48 * the file where the compressed data is located). 49 * size_out: Returns the size of the inflated file. 50 * 51 * Upon success, it returns a pointer to a NUL-terminated malloc'd buffer 52 * containing the inflated manifest file. When the caller is done with it, 53 * this buffer should be released by a call to free(). Upon failure, 54 * returns NULL. 55 */ 56 static char * 57 inflate_file(int fd, zentry *entry, int *size_out) 58 { 59 char *in; 60 char *out; 61 z_stream zs; 62 63 if (entry->csize == (size_t) -1 || entry->isize == (size_t) -1 ) 64 return (NULL); 65 if (JLI_Lseek(fd, entry->offset, SEEK_SET) < (jlong)0) 66 return (NULL); 67 if ((in = malloc(entry->csize + 1)) == NULL) 68 return (NULL); 69 if ((size_t)(read(fd, in, (unsigned int)entry->csize)) != entry->csize) { 70 free(in); 71 return (NULL); 72 } 73 if (entry->how == STORED) { 74 *(char *)((size_t)in + entry->csize) = '\0'; 75 if (size_out) { 76 *size_out = (int)entry->csize; 77 } 78 return (in); 79 } else if (entry->how == DEFLATED) { 80 zs.zalloc = (alloc_func)Z_NULL; 81 zs.zfree = (free_func)Z_NULL; 82 zs.opaque = (voidpf)Z_NULL; 83 zs.next_in = (Byte*)in; 84 zs.avail_in = (uInt)entry->csize; 85 if (inflateInit2(&zs, -MAX_WBITS) < 0) { 86 free(in); 87 return (NULL); 88 } 89 if ((out = malloc(entry->isize + 1)) == NULL) { 90 free(in); 91 return (NULL); 92 } 93 zs.next_out = (Byte*)out; 94 zs.avail_out = (uInt)entry->isize; 95 if (inflate(&zs, Z_PARTIAL_FLUSH) < 0) { 96 free(in); 97 free(out); 98 return (NULL); 99 } 100 *(char *)((size_t)out + entry->isize) = '\0'; 101 free(in); 102 if (inflateEnd(&zs) < 0) { 103 free(out); 104 return (NULL); 105 } 106 if (size_out) { 107 *size_out = (int)entry->isize; 108 } 109 return (out); 110 } 111 free(in); 112 return (NULL); 113 } 114 115 /* 116 * Implementation notes: 117 * 118 * This is a zip format reader for seekable files, that tolerates 119 * leading and trailing garbage, and tolerates having had internal 120 * offsets adjusted for leading garbage (as with Info-Zip's zip -A). 121 * 122 * We find the end header by scanning backwards from the end of the 123 * file for the end signature. This may fail in the presence of 124 * trailing garbage or a ZIP file comment that contains binary data. 125 * Similarly, the ZIP64 end header may need to be located by scanning 126 * backwards from the end header. It may be misidentified, but this 127 * is very unlikely to happen in practice without adversarial input. 128 * 129 * The zip file format is documented at: 130 * https://www.pkware.com/documents/casestudies/APPNOTE.TXT 131 * 132 * TODO: more informative error messages 133 */ 134 135 /** Reads count bytes from fd at position pos into given buffer. */ 136 static jboolean 137 readAt(int fd, jlong pos, unsigned int count, void *buf) { 138 return (pos >= 0 139 && JLI_Lseek(fd, pos, SEEK_SET) == pos 140 && read(fd, buf, count) == (jlong) count); 141 } 142 143 144 /* 145 * Tells whether given header values (obtained from either ZIP64 or 146 * non-ZIP64 header) appear to be correct, by checking the first LOC 147 * and CEN headers. 148 */ 149 static jboolean 150 is_valid_end_header(int fd, jlong endpos, 151 jlong censiz, jlong cenoff, jlong entries) { 152 Byte cenhdr[CENHDR]; 153 Byte lochdr[LOCHDR]; 154 // Expected offset of the first central directory header 155 jlong censtart = endpos - censiz; 156 // Expected position within the file that offsets are relative to 157 jlong base_offset = endpos - (censiz + cenoff); 158 return censtart >= 0 && cenoff >= 0 && 159 (censiz == 0 || 160 // Validate first CEN and LOC header signatures. 161 // Central directory must come directly before the end header. 162 (readAt(fd, censtart, CENHDR, cenhdr) 163 && CENSIG_AT(cenhdr) 164 && readAt(fd, base_offset + CENOFF(cenhdr), LOCHDR, lochdr) 165 && LOCSIG_AT(lochdr) 166 && CENNAM(cenhdr) == LOCNAM(lochdr))); 167 } 168 169 /* 170 * Tells whether p appears to be pointing at a valid ZIP64 end header. 171 * Values censiz, cenoff, and entries are the corresponding values 172 * from the non-ZIP64 end header. We perform extra checks to avoid 173 * misidentifying data from the last entry as a ZIP64 end header. 174 */ 175 static jboolean 176 is_zip64_endhdr(int fd, const Byte *p, jlong end64pos, 177 jlong censiz, jlong cenoff, jlong entries) { 178 if (ZIP64_ENDSIG_AT(p)) { 179 jlong censiz64 = ZIP64_ENDSIZ(p); 180 jlong cenoff64 = ZIP64_ENDOFF(p); 181 jlong entries64 = ZIP64_ENDTOT(p); 182 return (censiz64 == censiz || censiz == ZIP64_MAGICVAL) 183 && (cenoff64 == cenoff || cenoff == ZIP64_MAGICVAL) 184 && (entries64 == entries || entries == ZIP64_MAGICCOUNT) 185 && is_valid_end_header(fd, end64pos, censiz64, cenoff64, entries64); 186 } 187 return JNI_FALSE; 188 } 189 190 /* 191 * Given a non-ZIP64 end header located at endhdr and endpos, look for 192 * an adjacent ZIP64 end header, finding the base offset and censtart 193 * from the ZIP64 header if available, else from the non-ZIP64 header. 194 * @return 0 if successful, -1 in case of failure 195 */ 196 static int 197 find_positions64(int fd, const Byte * const endhdr, const jlong endpos, 198 jlong* base_offset, jlong* censtart) 199 { 200 jlong censiz = ENDSIZ(endhdr); 201 jlong cenoff = ENDOFF(endhdr); 202 jlong entries = ENDTOT(endhdr); 203 jlong end64pos; 204 Byte buf[ZIP64_ENDHDR + ZIP64_LOCHDR]; 205 if (censiz + cenoff != endpos 206 && (end64pos = endpos - sizeof(buf)) >= (jlong)0 207 && readAt(fd, end64pos, sizeof(buf), buf) 208 && ZIP64_LOCSIG_AT(buf + ZIP64_ENDHDR) 209 && (jlong) ZIP64_LOCDSK(buf + ZIP64_ENDHDR) == ENDDSK(endhdr) 210 && (is_zip64_endhdr(fd, buf, end64pos, censiz, cenoff, entries) 211 || // A variable sized "zip64 extensible data sector" ? 212 ((end64pos = ZIP64_LOCOFF(buf + ZIP64_ENDHDR)) >= (jlong)0 213 && readAt(fd, end64pos, ZIP64_ENDHDR, buf) 214 && is_zip64_endhdr(fd, buf, end64pos, censiz, cenoff, entries))) 215 ) { 216 *censtart = end64pos - ZIP64_ENDSIZ(buf); 217 *base_offset = *censtart - ZIP64_ENDOFF(buf); 218 } else { 219 if (!is_valid_end_header(fd, endpos, censiz, cenoff, entries)) 220 return -1; 221 *censtart = endpos - censiz; 222 *base_offset = *censtart - cenoff; 223 } 224 return 0; 225 } 226 227 /* 228 * Finds the base offset and censtart of the zip file. 229 * 230 * @param fd file descriptor of the jar file 231 * @param eb scratch buffer 232 * @return 0 if successful, -1 in case of failure 233 */ 234 static int 235 find_positions(int fd, Byte *eb, jlong* base_offset, jlong* censtart) 236 { 237 jlong len; 238 jlong pos; 239 jlong flen; 240 int bytes; 241 Byte *cp; 242 Byte *endpos; 243 Byte *buffer; 244 245 /* 246 * 99.44% (or more) of the time, there will be no comment at the 247 * end of the zip file. Try reading just enough to read the END 248 * record from the end of the file, at this time we should also 249 * check to see if we have a ZIP64 archive. 250 */ 251 if ((pos = JLI_Lseek(fd, -ENDHDR, SEEK_END)) < (jlong)0) 252 return (-1); 253 if (read(fd, eb, ENDHDR) < 0) 254 return (-1); 255 if (ENDSIG_AT(eb)) { 256 return find_positions64(fd, eb, pos, base_offset, censtart); 257 } 258 259 /* 260 * Shucky-Darn,... There is a comment at the end of the zip file. 261 * 262 * Allocate and fill a buffer with enough of the zip file 263 * to meet the specification for a maximal comment length. 264 */ 265 if ((flen = JLI_Lseek(fd, 0, SEEK_END)) < (jlong)0) 266 return (-1); 267 len = (flen < END_MAXLEN) ? flen : END_MAXLEN; 268 if (JLI_Lseek(fd, -len, SEEK_END) < (jlong)0) 269 return (-1); 270 if ((buffer = malloc(END_MAXLEN)) == NULL) 271 return (-1); 272 273 /* 274 * read() on windows takes an unsigned int for count. Casting len 275 * to an unsigned int here is safe since it is guaranteed to be 276 * less than END_MAXLEN. 277 */ 278 if ((bytes = read(fd, buffer, (unsigned int)len)) < 0) { 279 free(buffer); 280 return (-1); 281 } 282 283 /* 284 * Search backwards from the end of file stopping when the END header 285 * signature is found. 286 */ 287 endpos = &buffer[bytes]; 288 for (cp = &buffer[bytes - ENDHDR]; cp >= &buffer[0]; cp--) 289 if (ENDSIG_AT(cp) && (cp + ENDHDR + ENDCOM(cp) == endpos)) { 290 (void) memcpy(eb, cp, ENDHDR); 291 free(buffer); 292 pos = flen - (endpos - cp); 293 return find_positions64(fd, eb, pos, base_offset, censtart); 294 } 295 free(buffer); 296 return (-1); 297 } 298 299 #define BUFSIZE (3 * 65536 + CENHDR + SIGSIZ) 300 #define MINREAD 1024 301 302 /* 303 * Locate the manifest file with the zip/jar file. 304 * 305 * fd: File descriptor of the jar file. 306 * entry: To be populated with the information necessary to perform 307 * the inflation (the compressed and uncompressed sizes and 308 * the offset in the file where the compressed data is located). 309 * 310 * Returns zero upon success. Returns a negative value upon failure. 311 * 312 * The buffer for reading the Central Directory if the zip/jar file needs 313 * to be large enough to accommodate the largest possible single record 314 * and the signature of the next record which is: 315 * 316 * 3*2**16 + CENHDR + SIGSIZ 317 * 318 * Each of the three variable sized fields (name, comment and extension) 319 * has a maximum possible size of 64k. 320 * 321 * Typically, only a small bit of this buffer is used with bytes shuffled 322 * down to the beginning of the buffer. It is one thing to allocate such 323 * a large buffer and another thing to actually start faulting it in. 324 * 325 * In most cases, all that needs to be read are the first two entries in 326 * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid 327 * in mind when optimizing this code. 328 */ 329 static int 330 find_file(int fd, zentry *entry, const char *file_name) 331 { 332 int bytes; 333 int res; 334 int entry_size; 335 int read_size; 336 337 /* 338 * The (imaginary) position within the file relative to which 339 * offsets within the zip file refer. This is usually the 340 * location of the first local header (the start of the zip data) 341 * (which in turn is usually 0), but if the zip file has content 342 * prepended, then it will be either 0 or the length of the 343 * prepended content, depending on whether or not internal offsets 344 * have been adjusted (via e.g. zip -A). May be negative if 345 * content is prepended, zip -A is run, then the prefix is 346 * detached! 347 */ 348 jlong base_offset; 349 350 /** The position within the file of the start of the central directory. */ 351 jlong censtart; 352 353 Byte *p; 354 Byte *bp; 355 Byte *buffer; 356 Byte locbuf[LOCHDR]; 357 358 if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) { 359 return(-1); 360 } 361 362 bp = buffer; 363 364 if (find_positions(fd, bp, &base_offset, &censtart) == -1) { 365 return -1; 366 } 367 if (JLI_Lseek(fd, censtart, SEEK_SET) < (jlong) 0) { 368 return -1; 369 } 370 371 if ((bytes = read(fd, bp, MINREAD)) < 0) { 372 free(buffer); 373 return (-1); 374 } 375 p = bp; 376 /* 377 * Loop through the Central Directory Headers. Note that a valid zip/jar 378 * must have an ENDHDR (with ENDSIG) after the Central Directory. 379 */ 380 while (CENSIG_AT(p)) { 381 382 /* 383 * If a complete header isn't in the buffer, shift the contents 384 * of the buffer down and refill the buffer. Note that the check 385 * for "bytes < CENHDR" must be made before the test for the entire 386 * size of the header, because if bytes is less than CENHDR, the 387 * actual size of the header can't be determined. The addition of 388 * SIGSIZ guarantees that the next signature is also in the buffer 389 * for proper loop termination. 390 */ 391 if (bytes < CENHDR) { 392 p = memmove(bp, p, bytes); 393 if ((res = read(fd, bp + bytes, MINREAD)) <= 0) { 394 free(buffer); 395 return (-1); 396 } 397 bytes += res; 398 } 399 entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p); 400 if (bytes < entry_size + SIGSIZ) { 401 if (p != bp) 402 p = memmove(bp, p, bytes); 403 read_size = entry_size - bytes + SIGSIZ; 404 read_size = (read_size < MINREAD) ? MINREAD : read_size; 405 if ((res = read(fd, bp + bytes, read_size)) <= 0) { 406 free(buffer); 407 return (-1); 408 } 409 bytes += res; 410 } 411 412 /* 413 * Check if the name is the droid we are looking for; the jar file 414 * manifest. If so, build the entry record from the data found in 415 * the header located and return success. 416 */ 417 if ((size_t)CENNAM(p) == JLI_StrLen(file_name) && 418 memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) { 419 if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) { 420 free(buffer); 421 return (-1); 422 } 423 if (read(fd, locbuf, LOCHDR) < 0) { 424 free(buffer); 425 return (-1); 426 } 427 if (!LOCSIG_AT(locbuf)) { 428 free(buffer); 429 return (-1); 430 } 431 entry->isize = CENLEN(p); 432 entry->csize = CENSIZ(p); 433 entry->offset = base_offset + CENOFF(p) + LOCHDR + 434 LOCNAM(locbuf) + LOCEXT(locbuf); 435 entry->how = CENHOW(p); 436 free(buffer); 437 return (0); 438 } 439 440 /* 441 * Point to the next entry and decrement the count of valid remaining 442 * bytes. 443 */ 444 bytes -= entry_size; 445 p += entry_size; 446 } 447 free(buffer); 448 return (-1); /* Fell off the end the loop without a Manifest */ 449 } 450 451 /* 452 * Parse a Manifest file header entry into a distinct "name" and "value". 453 * Continuation lines are joined into a single "value". The documented 454 * syntax for a header entry is: 455 * 456 * header: name ":" value 457 * 458 * name: alphanum *headerchar 459 * 460 * value: SPACE *otherchar newline *continuation 461 * 462 * continuation: SPACE *otherchar newline 463 * 464 * newline: CR LF | LF | CR (not followed by LF) 465 * 466 * alphanum: {"A"-"Z"} | {"a"-"z"} | {"0"-"9"} 467 * 468 * headerchar: alphanum | "-" | "_" 469 * 470 * otherchar: any UTF-8 character except NUL, CR and LF 471 * 472 * Note that a manifest file may be composed of multiple sections, 473 * each of which may contain multiple headers. 474 * 475 * section: *header +newline 476 * 477 * nonempty-section: +header +newline 478 * 479 * (Note that the point of "nonempty-section" is unclear, because it isn't 480 * referenced elsewhere in the full specification for the Manifest file.) 481 * 482 * Arguments: 483 * lp pointer to a character pointer which points to the start 484 * of a valid header. 485 * name pointer to a character pointer which will be set to point 486 * to the name portion of the header (nul terminated). 487 * value pointer to a character pointer which will be set to point 488 * to the value portion of the header (nul terminated). 489 * 490 * Returns: 491 * 1 Successful parsing of an NV pair. lp is updated to point to the 492 * next character after the terminating newline in the string 493 * representing the Manifest file. name and value are updated to 494 * point to the strings parsed. 495 * 0 A valid end of section indicator was encountered. lp, name, and 496 * value are not modified. 497 * -1 lp does not point to a valid header. Upon return, the values of 498 * lp, name, and value are undefined. 499 */ 500 static int 501 parse_nv_pair(char **lp, char **name, char **value) 502 { 503 char *nl; 504 char *cp; 505 506 /* 507 * End of the section - return 0. The end of section condition is 508 * indicated by either encountering a blank line or the end of the 509 * Manifest "string" (EOF). 510 */ 511 if (**lp == '\0' || **lp == '\n' || **lp == '\r') 512 return (0); 513 514 /* 515 * Getting to here, indicates that *lp points to an "otherchar". 516 * Turn the "header" into a string on its own. 517 */ 518 nl = JLI_StrPBrk(*lp, "\n\r"); 519 if (nl == NULL) { 520 nl = JLI_StrChr(*lp, (int)'\0'); 521 } else { 522 cp = nl; /* For merging continuation lines */ 523 if (*nl == '\r' && *(nl+1) == '\n') 524 *nl++ = '\0'; 525 *nl++ = '\0'; 526 527 /* 528 * Process any "continuation" line(s), by making them part of the 529 * "header" line. Yes, I know that we are "undoing" the NULs we 530 * just placed here, but continuation lines are the fairly rare 531 * case, so we shouldn't unnecessarily complicate the code above. 532 * 533 * Note that an entire continuation line is processed each iteration 534 * through the outer while loop. 535 */ 536 while (*nl == ' ') { 537 nl++; /* First character to be moved */ 538 while (*nl != '\n' && *nl != '\r' && *nl != '\0') 539 *cp++ = *nl++; /* Shift string */ 540 if (*nl == '\0') 541 return (-1); /* Error: newline required */ 542 *cp = '\0'; 543 if (*nl == '\r' && *(nl+1) == '\n') 544 *nl++ = '\0'; 545 *nl++ = '\0'; 546 } 547 } 548 549 /* 550 * Separate the name from the value; 551 */ 552 cp = JLI_StrChr(*lp, (int)':'); 553 if (cp == NULL) 554 return (-1); 555 *cp++ = '\0'; /* The colon terminates the name */ 556 if (*cp != ' ') 557 return (-1); 558 *cp++ = '\0'; /* Eat the required space */ 559 *name = *lp; 560 *value = cp; 561 *lp = nl; 562 return (1); 563 } 564 565 /* 566 * Read the manifest from the specified jar file and fill in the manifest_info 567 * structure with the information found within. 568 * 569 * Error returns are as follows: 570 * 0 Success 571 * -1 Unable to open jarfile 572 * -2 Error accessing the manifest from within the jarfile (most likely 573 * a manifest is not present, or this isn't a valid zip/jar file). 574 */ 575 int 576 JLI_ParseManifest(char *jarfile, manifest_info *info) 577 { 578 int fd; 579 zentry entry; 580 char *lp; 581 char *name; 582 char *value; 583 int rc; 584 char *splashscreen_name = NULL; 585 586 if ((fd = open(jarfile, O_RDONLY 587 #ifdef O_LARGEFILE 588 | O_LARGEFILE /* large file mode */ 589 #endif 590 #ifdef O_BINARY 591 | O_BINARY /* use binary mode on windows */ 592 #endif 593 )) == -1) { 594 return (-1); 595 } 596 info->manifest_version = NULL; 597 info->main_class = NULL; 598 info->jre_version = NULL; 599 info->jre_restrict_search = 0; 600 info->splashscreen_image_file_name = NULL; 601 if ((rc = find_file(fd, &entry, manifest_name)) != 0) { 602 close(fd); 603 return (-2); 604 } 605 manifest = inflate_file(fd, &entry, NULL); 606 if (manifest == NULL) { 607 close(fd); 608 return (-2); 609 } 610 lp = manifest; 611 while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { 612 if (JLI_StrCaseCmp(name, "Manifest-Version") == 0) { 613 info->manifest_version = value; 614 } else if (JLI_StrCaseCmp(name, "Main-Class") == 0) { 615 info->main_class = value; 616 } else if (JLI_StrCaseCmp(name, "JRE-Version") == 0) { 617 /* 618 * Manifest specification overridden by command line option 619 * so we will silently override there with no specification. 620 */ 621 info->jre_version = 0; 622 } else if (JLI_StrCaseCmp(name, "Splashscreen-Image") == 0) { 623 info->splashscreen_image_file_name = value; 624 } 625 } 626 close(fd); 627 if (rc == 0) 628 return (0); 629 else 630 return (-2); 631 } 632 633 /* 634 * Opens the jar file and unpacks the specified file from its contents. 635 * Returns NULL on failure. 636 */ 637 void * 638 JLI_JarUnpackFile(const char *jarfile, const char *filename, int *size) { 639 int fd; 640 zentry entry; 641 void *data = NULL; 642 643 if ((fd = open(jarfile, O_RDONLY 644 #ifdef O_LARGEFILE 645 | O_LARGEFILE /* large file mode */ 646 #endif 647 #ifdef O_BINARY 648 | O_BINARY /* use binary mode on windows */ 649 #endif 650 )) == -1) { 651 return NULL; 652 } 653 if (find_file(fd, &entry, filename) == 0) { 654 data = inflate_file(fd, &entry, size); 655 } 656 close(fd); 657 return (data); 658 } 659 660 /* 661 * Specialized "free" function. 662 */ 663 void 664 JLI_FreeManifest() 665 { 666 if (manifest) 667 free(manifest); 668 } 669 670 /* 671 * Iterate over the manifest of the specified jar file and invoke the provided 672 * closure function for each attribute encountered. 673 * 674 * Error returns are as follows: 675 * 0 Success 676 * -1 Unable to open jarfile 677 * -2 Error accessing the manifest from within the jarfile (most likely 678 * this means a manifest is not present, or it isn't a valid zip/jar file). 679 */ 680 JNIEXPORT int JNICALL 681 JLI_ManifestIterate(const char *jarfile, attribute_closure ac, void *user_data) 682 { 683 int fd; 684 zentry entry; 685 char *mp; /* manifest pointer */ 686 char *lp; /* pointer into manifest, updated during iteration */ 687 char *name; 688 char *value; 689 int rc; 690 691 if ((fd = open(jarfile, O_RDONLY 692 #ifdef O_LARGEFILE 693 | O_LARGEFILE /* large file mode */ 694 #endif 695 #ifdef O_BINARY 696 | O_BINARY /* use binary mode on windows */ 697 #endif 698 )) == -1) { 699 return (-1); 700 } 701 702 if ((rc = find_file(fd, &entry, manifest_name)) != 0) { 703 close(fd); 704 return (-2); 705 } 706 707 mp = inflate_file(fd, &entry, NULL); 708 if (mp == NULL) { 709 close(fd); 710 return (-2); 711 } 712 713 lp = mp; 714 while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { 715 (*ac)(name, value, user_data); 716 } 717 free(mp); 718 close(fd); 719 return (rc == 0) ? 0 : -2; 720 }