| /* | |
| * Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "jni.h" | |
| #include "jli_util.h" | |
| #include <zlib.h> | |
| #include "manifest_info.h" | |
| static char *manifest; | |
| static const char *manifest_name = "META-INF/MANIFEST.MF"; | |
| /* | |
| * Inflate the manifest file (or any file for that matter). | |
| * | |
| * fd: File descriptor of the jar file. | |
| * entry: Contains the information necessary to perform the inflation | |
| * (the compressed and uncompressed sizes and the offset in | |
| * the file where the compressed data is located). | |
| * size_out: Returns the size of the inflated file. | |
| * | |
| * Upon success, it returns a pointer to a NUL-terminated malloc'd buffer | |
| * containing the inflated manifest file. When the caller is done with it, | |
| * this buffer should be released by a call to free(). Upon failure, | |
| * returns NULL. | |
| */ | |
| static char * | |
| inflate_file(int fd, zentry *entry, int *size_out) | |
| { | |
| char *in; | |
| char *out; | |
| z_stream zs; | |
| if (entry->csize == (size_t) -1 || entry->isize == (size_t) -1 ) | |
| return (NULL); | |
| if (JLI_Lseek(fd, entry->offset, SEEK_SET) < (jlong)0) | |
| return (NULL); | |
| if ((in = malloc(entry->csize + 1)) == NULL) | |
| return (NULL); | |
| if ((size_t)(read(fd, in, (unsigned int)entry->csize)) != entry->csize) { | |
| free(in); | |
| return (NULL); | |
| } | |
| if (entry->how == STORED) { | |
| *(char *)((size_t)in + entry->csize) = '\0'; | |
| if (size_out) { | |
| *size_out = (int)entry->csize; | |
| } | |
| return (in); | |
| } else if (entry->how == DEFLATED) { | |
| zs.zalloc = (alloc_func)Z_NULL; | |
| zs.zfree = (free_func)Z_NULL; | |
| zs.opaque = (voidpf)Z_NULL; | |
| zs.next_in = (Byte*)in; | |
| zs.avail_in = (uInt)entry->csize; | |
| if (inflateInit2(&zs, -MAX_WBITS) < 0) { | |
| free(in); | |
| return (NULL); | |
| } | |
| if ((out = malloc(entry->isize + 1)) == NULL) { | |
| free(in); | |
| return (NULL); | |
| } | |
| zs.next_out = (Byte*)out; | |
| zs.avail_out = (uInt)entry->isize; | |
| if (inflate(&zs, Z_PARTIAL_FLUSH) < 0) { | |
| free(in); | |
| free(out); | |
| return (NULL); | |
| } | |
| *(char *)((size_t)out + entry->isize) = '\0'; | |
| free(in); | |
| if (inflateEnd(&zs) < 0) { | |
| free(out); | |
| return (NULL); | |
| } | |
| if (size_out) { | |
| *size_out = (int)entry->isize; | |
| } | |
| return (out); | |
| } | |
| free(in); | |
| return (NULL); | |
| } | |
| /* | |
| * Implementation notes: | |
| * | |
| * This is a zip format reader for seekable files, that tolerates | |
| * leading and trailing garbage, and tolerates having had internal | |
| * offsets adjusted for leading garbage (as with Info-Zip's zip -A). | |
| * | |
| * We find the end header by scanning backwards from the end of the | |
| * file for the end signature. This may fail in the presence of | |
| * trailing garbage or a ZIP file comment that contains binary data. | |
| * Similarly, the ZIP64 end header may need to be located by scanning | |
| * backwards from the end header. It may be misidentified, but this | |
| * is very unlikely to happen in practice without adversarial input. | |
| * | |
| * The zip file format is documented at: | |
| * https://www.pkware.com/documents/casestudies/APPNOTE.TXT | |
| * | |
| * TODO: more informative error messages | |
| */ | |
| /** Reads count bytes from fd at position pos into given buffer. */ | |
| static jboolean | |
| readAt(int fd, jlong pos, unsigned int count, void *buf) { | |
| return (pos >= 0 | |
| && JLI_Lseek(fd, pos, SEEK_SET) == pos | |
| && read(fd, buf, count) == (jlong) count); | |
| } | |
| /* | |
| * Tells whether given header values (obtained from either ZIP64 or | |
| * non-ZIP64 header) appear to be correct, by checking the first LOC | |
| * and CEN headers. | |
| */ | |
| static jboolean | |
| is_valid_end_header(int fd, jlong endpos, | |
| jlong censiz, jlong cenoff, jlong entries) { | |
| Byte cenhdr[CENHDR]; | |
| Byte lochdr[LOCHDR]; | |
| // Expected offset of the first central directory header | |
| jlong censtart = endpos - censiz; | |
| // Expected position within the file that offsets are relative to | |
| jlong base_offset = endpos - (censiz + cenoff); | |
| return censtart >= 0 && cenoff >= 0 && | |
| (censiz == 0 || | |
| // Validate first CEN and LOC header signatures. | |
| // Central directory must come directly before the end header. | |
| (readAt(fd, censtart, CENHDR, cenhdr) | |
| && CENSIG_AT(cenhdr) | |
| && readAt(fd, base_offset + CENOFF(cenhdr), LOCHDR, lochdr) | |
| && LOCSIG_AT(lochdr) | |
| && CENNAM(cenhdr) == LOCNAM(lochdr))); | |
| } | |
| /* | |
| * Tells whether p appears to be pointing at a valid ZIP64 end header. | |
| * Values censiz, cenoff, and entries are the corresponding values | |
| * from the non-ZIP64 end header. We perform extra checks to avoid | |
| * misidentifying data from the last entry as a ZIP64 end header. | |
| */ | |
| static jboolean | |
| is_zip64_endhdr(int fd, const Byte *p, jlong end64pos, | |
| jlong censiz, jlong cenoff, jlong entries) { | |
| if (ZIP64_ENDSIG_AT(p)) { | |
| jlong censiz64 = ZIP64_ENDSIZ(p); | |
| jlong cenoff64 = ZIP64_ENDOFF(p); | |
| jlong entries64 = ZIP64_ENDTOT(p); | |
| return (censiz64 == censiz || censiz == ZIP64_MAGICVAL) | |
| && (cenoff64 == cenoff || cenoff == ZIP64_MAGICVAL) | |
| && (entries64 == entries || entries == ZIP64_MAGICCOUNT) | |
| && is_valid_end_header(fd, end64pos, censiz64, cenoff64, entries64); | |
| } | |
| return JNI_FALSE; | |
| } | |
| /* | |
| * Given a non-ZIP64 end header located at endhdr and endpos, look for | |
| * an adjacent ZIP64 end header, finding the base offset and censtart | |
| * from the ZIP64 header if available, else from the non-ZIP64 header. | |
| * @return 0 if successful, -1 in case of failure | |
| */ | |
| static int | |
| find_positions64(int fd, const Byte * const endhdr, const jlong endpos, | |
| jlong* base_offset, jlong* censtart) | |
| { | |
| jlong censiz = ENDSIZ(endhdr); | |
| jlong cenoff = ENDOFF(endhdr); | |
| jlong entries = ENDTOT(endhdr); | |
| jlong end64pos; | |
| Byte buf[ZIP64_ENDHDR + ZIP64_LOCHDR]; | |
| if (censiz + cenoff != endpos | |
| && (end64pos = endpos - sizeof(buf)) >= (jlong)0 | |
| && readAt(fd, end64pos, sizeof(buf), buf) | |
| && ZIP64_LOCSIG_AT(buf + ZIP64_ENDHDR) | |
| && (jlong) ZIP64_LOCDSK(buf + ZIP64_ENDHDR) == ENDDSK(endhdr) | |
| && (is_zip64_endhdr(fd, buf, end64pos, censiz, cenoff, entries) | |
| || // A variable sized "zip64 extensible data sector" ? | |
| ((end64pos = ZIP64_LOCOFF(buf + ZIP64_ENDHDR)) >= (jlong)0 | |
| && readAt(fd, end64pos, ZIP64_ENDHDR, buf) | |
| && is_zip64_endhdr(fd, buf, end64pos, censiz, cenoff, entries))) | |
| ) { | |
| *censtart = end64pos - ZIP64_ENDSIZ(buf); | |
| *base_offset = *censtart - ZIP64_ENDOFF(buf); | |
| } else { | |
| if (!is_valid_end_header(fd, endpos, censiz, cenoff, entries)) | |
| return -1; | |
| *censtart = endpos - censiz; | |
| *base_offset = *censtart - cenoff; | |
| } | |
| return 0; | |
| } | |
| /* | |
| * Finds the base offset and censtart of the zip file. | |
| * | |
| * @param fd file descriptor of the jar file | |
| * @param eb scratch buffer | |
| * @return 0 if successful, -1 in case of failure | |
| */ | |
| static int | |
| find_positions(int fd, Byte *eb, jlong* base_offset, jlong* censtart) | |
| { | |
| jlong len; | |
| jlong pos; | |
| jlong flen; | |
| int bytes; | |
| Byte *cp; | |
| Byte *endpos; | |
| Byte *buffer; | |
| /* | |
| * 99.44% (or more) of the time, there will be no comment at the | |
| * end of the zip file. Try reading just enough to read the END | |
| * record from the end of the file, at this time we should also | |
| * check to see if we have a ZIP64 archive. | |
| */ | |
| if ((pos = JLI_Lseek(fd, -ENDHDR, SEEK_END)) < (jlong)0) | |
| return (-1); | |
| if (read(fd, eb, ENDHDR) < 0) | |
| return (-1); | |
| if (ENDSIG_AT(eb)) { | |
| return find_positions64(fd, eb, pos, base_offset, censtart); | |
| } | |
| /* | |
| * Shucky-Darn,... There is a comment at the end of the zip file. | |
| * | |
| * Allocate and fill a buffer with enough of the zip file | |
| * to meet the specification for a maximal comment length. | |
| */ | |
| if ((flen = JLI_Lseek(fd, 0, SEEK_END)) < (jlong)0) | |
| return (-1); | |
| len = (flen < END_MAXLEN) ? flen : END_MAXLEN; | |
| if (JLI_Lseek(fd, -len, SEEK_END) < (jlong)0) | |
| return (-1); | |
| if ((buffer = malloc(END_MAXLEN)) == NULL) | |
| return (-1); | |
| /* | |
| * read() on windows takes an unsigned int for count. Casting len | |
| * to an unsigned int here is safe since it is guaranteed to be | |
| * less than END_MAXLEN. | |
| */ | |
| if ((bytes = read(fd, buffer, (unsigned int)len)) < 0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| /* | |
| * Search backwards from the end of file stopping when the END header | |
| * signature is found. | |
| */ | |
| endpos = &buffer[bytes]; | |
| for (cp = &buffer[bytes - ENDHDR]; cp >= &buffer[0]; cp--) | |
| if (ENDSIG_AT(cp) && (cp + ENDHDR + ENDCOM(cp) == endpos)) { | |
| (void) memcpy(eb, cp, ENDHDR); | |
| pos = flen - (endpos - cp); | |
| free(buffer); | |
| return find_positions64(fd, eb, pos, base_offset, censtart); | |
| } | |
| free(buffer); | |
| return (-1); | |
| } | |
| #define BUFSIZE (3 * 65536 + CENHDR + SIGSIZ) | |
| #define MINREAD 1024 | |
| /* | |
| * Locate the manifest file with the zip/jar file. | |
| * | |
| * fd: File descriptor of the jar file. | |
| * entry: To be populated with the information necessary to perform | |
| * the inflation (the compressed and uncompressed sizes and | |
| * the offset in the file where the compressed data is located). | |
| * | |
| * Returns zero upon success. Returns a negative value upon failure. | |
| * | |
| * The buffer for reading the Central Directory if the zip/jar file needs | |
| * to be large enough to accommodate the largest possible single record | |
| * and the signature of the next record which is: | |
| * | |
| * 3*2**16 + CENHDR + SIGSIZ | |
| * | |
| * Each of the three variable sized fields (name, comment and extension) | |
| * has a maximum possible size of 64k. | |
| * | |
| * Typically, only a small bit of this buffer is used with bytes shuffled | |
| * down to the beginning of the buffer. It is one thing to allocate such | |
| * a large buffer and another thing to actually start faulting it in. | |
| * | |
| * In most cases, all that needs to be read are the first two entries in | |
| * a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid | |
| * in mind when optimizing this code. | |
| */ | |
| static int | |
| find_file(int fd, zentry *entry, const char *file_name) | |
| { | |
| int bytes; | |
| int res; | |
| int entry_size; | |
| int read_size; | |
| /* | |
| * The (imaginary) position within the file relative to which | |
| * offsets within the zip file refer. This is usually the | |
| * location of the first local header (the start of the zip data) | |
| * (which in turn is usually 0), but if the zip file has content | |
| * prepended, then it will be either 0 or the length of the | |
| * prepended content, depending on whether or not internal offsets | |
| * have been adjusted (via e.g. zip -A). May be negative if | |
| * content is prepended, zip -A is run, then the prefix is | |
| * detached! | |
| */ | |
| jlong base_offset; | |
| /** The position within the file of the start of the central directory. */ | |
| jlong censtart; | |
| Byte *p; | |
| Byte *bp; | |
| Byte *buffer; | |
| Byte locbuf[LOCHDR]; | |
| if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) { | |
| return(-1); | |
| } | |
| bp = buffer; | |
| if (find_positions(fd, bp, &base_offset, &censtart) == -1) { | |
| free(buffer); | |
| return -1; | |
| } | |
| if (JLI_Lseek(fd, censtart, SEEK_SET) < (jlong) 0) { | |
| free(buffer); | |
| return -1; | |
| } | |
| if ((bytes = read(fd, bp, MINREAD)) < 0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| p = bp; | |
| /* | |
| * Loop through the Central Directory Headers. Note that a valid zip/jar | |
| * must have an ENDHDR (with ENDSIG) after the Central Directory. | |
| */ | |
| while (CENSIG_AT(p)) { | |
| /* | |
| * If a complete header isn't in the buffer, shift the contents | |
| * of the buffer down and refill the buffer. Note that the check | |
| * for "bytes < CENHDR" must be made before the test for the entire | |
| * size of the header, because if bytes is less than CENHDR, the | |
| * actual size of the header can't be determined. The addition of | |
| * SIGSIZ guarantees that the next signature is also in the buffer | |
| * for proper loop termination. | |
| */ | |
| if (bytes < CENHDR) { | |
| p = memmove(bp, p, bytes); | |
| if ((res = read(fd, bp + bytes, MINREAD)) <= 0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| bytes += res; | |
| } | |
| entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p); | |
| if (bytes < entry_size + SIGSIZ) { | |
| if (p != bp) | |
| p = memmove(bp, p, bytes); | |
| read_size = entry_size - bytes + SIGSIZ; | |
| read_size = (read_size < MINREAD) ? MINREAD : read_size; | |
| if ((res = read(fd, bp + bytes, read_size)) <= 0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| bytes += res; | |
| } | |
| /* | |
| * Check if the name is the droid we are looking for; the jar file | |
| * manifest. If so, build the entry record from the data found in | |
| * the header located and return success. | |
| */ | |
| if ((size_t)CENNAM(p) == JLI_StrLen(file_name) && | |
| memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) { | |
| if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| if (read(fd, locbuf, LOCHDR) < 0) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| if (!LOCSIG_AT(locbuf)) { | |
| free(buffer); | |
| return (-1); | |
| } | |
| entry->isize = CENLEN(p); | |
| entry->csize = CENSIZ(p); | |
| entry->offset = base_offset + CENOFF(p) + LOCHDR + | |
| LOCNAM(locbuf) + LOCEXT(locbuf); | |
| entry->how = CENHOW(p); | |
| free(buffer); | |
| return (0); | |
| } | |
| /* | |
| * Point to the next entry and decrement the count of valid remaining | |
| * bytes. | |
| */ | |
| bytes -= entry_size; | |
| p += entry_size; | |
| } | |
| free(buffer); | |
| return (-1); /* Fell off the end the loop without a Manifest */ | |
| } | |
| /* | |
| * Parse a Manifest file header entry into a distinct "name" and "value". | |
| * Continuation lines are joined into a single "value". The documented | |
| * syntax for a header entry is: | |
| * | |
| * header: name ":" value | |
| * | |
| * name: alphanum *headerchar | |
| * | |
| * value: SPACE *otherchar newline *continuation | |
| * | |
| * continuation: SPACE *otherchar newline | |
| * | |
| * newline: CR LF | LF | CR (not followed by LF) | |
| * | |
| * alphanum: {"A"-"Z"} | {"a"-"z"} | {"0"-"9"} | |
| * | |
| * headerchar: alphanum | "-" | "_" | |
| * | |
| * otherchar: any UTF-8 character except NUL, CR and LF | |
| * | |
| * Note that a manifest file may be composed of multiple sections, | |
| * each of which may contain multiple headers. | |
| * | |
| * section: *header +newline | |
| * | |
| * nonempty-section: +header +newline | |
| * | |
| * (Note that the point of "nonempty-section" is unclear, because it isn't | |
| * referenced elsewhere in the full specification for the Manifest file.) | |
| * | |
| * Arguments: | |
| * lp pointer to a character pointer which points to the start | |
| * of a valid header. | |
| * name pointer to a character pointer which will be set to point | |
| * to the name portion of the header (nul terminated). | |
| * value pointer to a character pointer which will be set to point | |
| * to the value portion of the header (nul terminated). | |
| * | |
| * Returns: | |
| * 1 Successful parsing of an NV pair. lp is updated to point to the | |
| * next character after the terminating newline in the string | |
| * representing the Manifest file. name and value are updated to | |
| * point to the strings parsed. | |
| * 0 A valid end of section indicator was encountered. lp, name, and | |
| * value are not modified. | |
| * -1 lp does not point to a valid header. Upon return, the values of | |
| * lp, name, and value are undefined. | |
| */ | |
| static int | |
| parse_nv_pair(char **lp, char **name, char **value) | |
| { | |
| char *nl; | |
| char *cp; | |
| /* | |
| * End of the section - return 0. The end of section condition is | |
| * indicated by either encountering a blank line or the end of the | |
| * Manifest "string" (EOF). | |
| */ | |
| if (**lp == '\0' || **lp == '\n' || **lp == '\r') | |
| return (0); | |
| /* | |
| * Getting to here, indicates that *lp points to an "otherchar". | |
| * Turn the "header" into a string on its own. | |
| */ | |
| nl = JLI_StrPBrk(*lp, "\n\r"); | |
| if (nl == NULL) { | |
| nl = JLI_StrChr(*lp, (int)'\0'); | |
| } else { | |
| cp = nl; /* For merging continuation lines */ | |
| if (*nl == '\r' && *(nl+1) == '\n') | |
| *nl++ = '\0'; | |
| *nl++ = '\0'; | |
| /* | |
| * Process any "continuation" line(s), by making them part of the | |
| * "header" line. Yes, I know that we are "undoing" the NULs we | |
| * just placed here, but continuation lines are the fairly rare | |
| * case, so we shouldn't unnecessarily complicate the code above. | |
| * | |
| * Note that an entire continuation line is processed each iteration | |
| * through the outer while loop. | |
| */ | |
| while (*nl == ' ') { | |
| nl++; /* First character to be moved */ | |
| while (*nl != '\n' && *nl != '\r' && *nl != '\0') | |
| *cp++ = *nl++; /* Shift string */ | |
| if (*nl == '\0') | |
| return (-1); /* Error: newline required */ | |
| *cp = '\0'; | |
| if (*nl == '\r' && *(nl+1) == '\n') | |
| *nl++ = '\0'; | |
| *nl++ = '\0'; | |
| } | |
| } | |
| /* | |
| * Separate the name from the value; | |
| */ | |
| cp = JLI_StrChr(*lp, (int)':'); | |
| if (cp == NULL) | |
| return (-1); | |
| *cp++ = '\0'; /* The colon terminates the name */ | |
| if (*cp != ' ') | |
| return (-1); | |
| *cp++ = '\0'; /* Eat the required space */ | |
| *name = *lp; | |
| *value = cp; | |
| *lp = nl; | |
| return (1); | |
| } | |
| /* | |
| * Read the manifest from the specified jar file and fill in the manifest_info | |
| * structure with the information found within. | |
| * | |
| * Error returns are as follows: | |
| * 0 Success | |
| * -1 Unable to open jarfile | |
| * -2 Error accessing the manifest from within the jarfile (most likely | |
| * a manifest is not present, or this isn't a valid zip/jar file). | |
| */ | |
| int | |
| JLI_ParseManifest(char *jarfile, manifest_info *info) | |
| { | |
| int fd; | |
| zentry entry; | |
| char *lp; | |
| char *name; | |
| char *value; | |
| int rc; | |
| if ((fd = JLI_Open(jarfile, O_RDONLY | |
| #ifdef O_LARGEFILE | |
| | O_LARGEFILE /* large file mode */ | |
| #endif | |
| #ifdef O_BINARY | |
| | O_BINARY /* use binary mode on windows */ | |
| #endif | |
| )) == -1) { | |
| return (-1); | |
| } | |
| info->splashscreen_image_file_name = NULL; | |
| if ((rc = find_file(fd, &entry, manifest_name)) != 0) { | |
| close(fd); | |
| return (-2); | |
| } | |
| manifest = inflate_file(fd, &entry, NULL); | |
| if (manifest == NULL) { | |
| close(fd); | |
| return (-2); | |
| } | |
| lp = manifest; | |
| while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { | |
| if (JLI_StrCaseCmp(name, "Splashscreen-Image") == 0) { | |
| info->splashscreen_image_file_name = value; | |
| } | |
| } | |
| close(fd); | |
| if (rc == 0) | |
| return (0); | |
| else | |
| return (-2); | |
| } | |
| /* | |
| * Opens the jar file and unpacks the specified file from its contents. | |
| * Returns NULL on failure. | |
| */ | |
| void * | |
| JLI_JarUnpackFile(const char *jarfile, const char *filename, int *size) { | |
| int fd; | |
| zentry entry; | |
| void *data = NULL; | |
| if ((fd = JLI_Open(jarfile, O_RDONLY | |
| #ifdef O_LARGEFILE | |
| | O_LARGEFILE /* large file mode */ | |
| #endif | |
| #ifdef O_BINARY | |
| | O_BINARY /* use binary mode on windows */ | |
| #endif | |
| )) == -1) { | |
| return NULL; | |
| } | |
| if (find_file(fd, &entry, filename) == 0) { | |
| data = inflate_file(fd, &entry, size); | |
| } | |
| close(fd); | |
| return (data); | |
| } | |
| /* | |
| * Specialized "free" function. | |
| */ | |
| void | |
| JLI_FreeManifest() | |
| { | |
| if (manifest) | |
| free(manifest); | |
| } | |
| /* | |
| * Iterate over the manifest of the specified jar file and invoke the provided | |
| * closure function for each attribute encountered. | |
| * | |
| * Error returns are as follows: | |
| * 0 Success | |
| * -1 Unable to open jarfile | |
| * -2 Error accessing the manifest from within the jarfile (most likely | |
| * this means a manifest is not present, or it isn't a valid zip/jar file). | |
| */ | |
| JNIEXPORT int JNICALL | |
| JLI_ManifestIterate(const char *jarfile, attribute_closure ac, void *user_data) | |
| { | |
| int fd; | |
| zentry entry; | |
| char *mp; /* manifest pointer */ | |
| char *lp; /* pointer into manifest, updated during iteration */ | |
| char *name; | |
| char *value; | |
| int rc; | |
| if ((fd = JLI_Open(jarfile, O_RDONLY | |
| #ifdef O_LARGEFILE | |
| | O_LARGEFILE /* large file mode */ | |
| #endif | |
| #ifdef O_BINARY | |
| | O_BINARY /* use binary mode on windows */ | |
| #endif | |
| )) == -1) { | |
| return (-1); | |
| } | |
| if ((rc = find_file(fd, &entry, manifest_name)) != 0) { | |
| close(fd); | |
| return (-2); | |
| } | |
| mp = inflate_file(fd, &entry, NULL); | |
| if (mp == NULL) { | |
| close(fd); | |
| return (-2); | |
| } | |
| lp = mp; | |
| while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) { | |
| (*ac)(name, value, user_data); | |
| } | |
| free(mp); | |
| close(fd); | |
| return (rc == 0) ? 0 : -2; | |
| } |