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 }