Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/bin/parse_manifest.c
38767 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include <sys/types.h>26#include <sys/stat.h>27#include <fcntl.h>28#include <stdio.h>29#include <stdlib.h>30#include <string.h>31#include "jli_util.h"3233#include <zlib.h>34#include "manifest_info.h"3536static char *manifest;3738static const char *manifest_name = "META-INF/MANIFEST.MF";3940/*41* Inflate the manifest file (or any file for that matter).42*43* fd: File descriptor of the jar file.44* entry: Contains the information necessary to perform the inflation45* (the compressed and uncompressed sizes and the offset in46* the file where the compressed data is located).47* size_out: Returns the size of the inflated file.48*49* Upon success, it returns a pointer to a NUL-terminated malloc'd buffer50* containing the inflated manifest file. When the caller is done with it,51* this buffer should be released by a call to free(). Upon failure,52* returns NULL.53*/54static char *55inflate_file(int fd, zentry *entry, int *size_out)56{57char *in;58char *out;59z_stream zs;6061if (entry->csize == (size_t) -1 || entry->isize == (size_t) -1 )62return (NULL);63if (JLI_Lseek(fd, entry->offset, SEEK_SET) < (jlong)0)64return (NULL);65if ((in = malloc(entry->csize + 1)) == NULL)66return (NULL);67if ((size_t)(read(fd, in, (unsigned int)entry->csize)) != entry->csize) {68free(in);69return (NULL);70}71if (entry->how == STORED) {72*(char *)((size_t)in + entry->csize) = '\0';73if (size_out) {74*size_out = (int)entry->csize;75}76return (in);77} else if (entry->how == DEFLATED) {78zs.zalloc = (alloc_func)Z_NULL;79zs.zfree = (free_func)Z_NULL;80zs.opaque = (voidpf)Z_NULL;81zs.next_in = (Byte*)in;82zs.avail_in = (uInt)entry->csize;83if (inflateInit2(&zs, -MAX_WBITS) < 0) {84free(in);85return (NULL);86}87if ((out = malloc(entry->isize + 1)) == NULL) {88free(in);89return (NULL);90}91zs.next_out = (Byte*)out;92zs.avail_out = (uInt)entry->isize;93if (inflate(&zs, Z_PARTIAL_FLUSH) < 0) {94free(in);95free(out);96return (NULL);97}98*(char *)((size_t)out + entry->isize) = '\0';99free(in);100if (inflateEnd(&zs) < 0) {101free(out);102return (NULL);103}104if (size_out) {105*size_out = (int)entry->isize;106}107return (out);108}109free(in);110return (NULL);111}112113static jboolean zip64_present = JNI_FALSE;114115/*116* Checks to see if we have ZIP64 archive, and save117* the check for later use118*/119static int120haveZIP64(Byte *p) {121jlong cenlen, cenoff, centot;122cenlen = ENDSIZ(p);123cenoff = ENDOFF(p);124centot = ENDTOT(p);125zip64_present = (cenlen == ZIP64_MAGICVAL ||126cenoff == ZIP64_MAGICVAL ||127centot == ZIP64_MAGICCOUNT);128return zip64_present;129}130131static jlong132find_end64(int fd, Byte *ep, jlong pos)133{134jlong end64pos;135jlong bytes;136if ((end64pos = JLI_Lseek(fd, pos - ZIP64_LOCHDR, SEEK_SET)) < (jlong)0)137return -1;138if ((bytes = read(fd, ep, ZIP64_LOCHDR)) < 0)139return -1;140if (GETSIG(ep) == ZIP64_LOCSIG)141return end64pos;142return -1;143}144145/*146* A very little used routine to handle the case that zip file has147* a comment at the end. Believe it or not, the only way to find the148* END record is to walk backwards, byte by bloody byte looking for149* the END record signature.150*151* fd: File descriptor of the jar file.152* eb: Pointer to a buffer to receive a copy of the END header.153*154* Returns the offset of the END record in the file on success,155* -1 on failure.156*/157static jlong158find_end(int fd, Byte *eb)159{160jlong len;161jlong pos;162jlong flen;163int bytes;164Byte *cp;165Byte *endpos;166Byte *buffer;167168/*169* 99.44% (or more) of the time, there will be no comment at the170* end of the zip file. Try reading just enough to read the END171* record from the end of the file, at this time we should also172* check to see if we have a ZIP64 archive.173*/174if ((pos = JLI_Lseek(fd, -ENDHDR, SEEK_END)) < (jlong)0)175return (-1);176if ((bytes = read(fd, eb, ENDHDR)) < 0)177return (-1);178if (GETSIG(eb) == ENDSIG) {179return haveZIP64(eb) ? find_end64(fd, eb, pos) : pos;180}181182/*183* Shucky-Darn,... There is a comment at the end of the zip file.184*185* Allocate and fill a buffer with enough of the zip file186* to meet the specification for a maximal comment length.187*/188if ((flen = JLI_Lseek(fd, 0, SEEK_END)) < (jlong)0)189return (-1);190len = (flen < END_MAXLEN) ? flen : END_MAXLEN;191if (JLI_Lseek(fd, -len, SEEK_END) < (jlong)0)192return (-1);193if ((buffer = malloc(END_MAXLEN)) == NULL)194return (-1);195if ((bytes = read(fd, buffer, len)) < 0) {196free(buffer);197return (-1);198}199200/*201* Search backwards from the end of file stopping when the END header202* signature is found. (The first condition of the "if" is just a203* fast fail, because the GETSIG macro isn't always cheap. The204* final condition protects against false positives.)205*/206endpos = &buffer[bytes];207for (cp = &buffer[bytes - ENDHDR]; cp >= &buffer[0]; cp--)208if ((*cp == (ENDSIG & 0xFF)) && (GETSIG(cp) == ENDSIG) &&209(cp + ENDHDR + ENDCOM(cp) == endpos)) {210(void) memcpy(eb, cp, ENDHDR);211free(buffer);212pos = flen - (endpos - cp);213return haveZIP64(eb) ? find_end64(fd, eb, pos) : pos;214}215free(buffer);216return (-1);217}218219#define BUFSIZE (3 * 65536 + CENHDR + SIGSIZ)220#define MINREAD 1024221222/*223* Computes and positions at the start of the CEN header, ie. the central224* directory, this will also return the offset if there is a zip file comment225* at the end of the archive, for most cases this would be 0.226*/227static jlong228compute_cen(int fd, Byte *bp)229{230int bytes;231Byte *p;232jlong base_offset;233jlong offset;234char buffer[MINREAD];235p = buffer;236/*237* Read the END Header, which is the starting point for ZIP files.238* (Clearly designed to make writing a zip file easier than reading239* one. Now isn't that precious...)240*/241if ((base_offset = find_end(fd, bp)) == -1) {242return (-1);243}244p = bp;245/*246* There is a historical, but undocumented, ability to allow for247* additional "stuff" to be prepended to the zip/jar file. It seems248* that this has been used to prepend an actual java launcher249* executable to the jar on Windows. Although this is just another250* form of statically linking a small piece of the JVM to the251* application, we choose to continue to support it. Note that no252* guarantees have been made (or should be made) to the customer that253* this will continue to work.254*255* Therefore, calculate the base offset of the zip file (within the256* expanded file) by assuming that the central directory is followed257* immediately by the end record.258*/259if (zip64_present) {260if ((offset = ZIP64_LOCOFF(p)) < (jlong)0) {261return -1;262}263if (JLI_Lseek(fd, offset, SEEK_SET) < (jlong) 0) {264return (-1);265}266if ((bytes = read(fd, buffer, MINREAD)) < 0) {267return (-1);268}269if (GETSIG(buffer) != ZIP64_ENDSIG) {270return -1;271}272if ((offset = ZIP64_ENDOFF(buffer)) < (jlong)0) {273return -1;274}275if (JLI_Lseek(fd, offset, SEEK_SET) < (jlong)0) {276return (-1);277}278p = buffer;279base_offset = base_offset - ZIP64_ENDSIZ(p) - ZIP64_ENDOFF(p) - ZIP64_ENDHDR;280} else {281base_offset = base_offset - ENDSIZ(p) - ENDOFF(p);282/*283* The END Header indicates the start of the Central Directory284* Headers. Remember that the desired Central Directory Header (CEN)285* will almost always be the second one and the first one is a small286* directory entry ("META-INF/"). Keep the code optimized for287* that case.288*289* Seek to the beginning of the Central Directory.290*/291if (JLI_Lseek(fd, base_offset + ENDOFF(p), SEEK_SET) < (jlong) 0) {292return (-1);293}294}295return base_offset;296}297298/*299* Locate the manifest file with the zip/jar file.300*301* fd: File descriptor of the jar file.302* entry: To be populated with the information necessary to perform303* the inflation (the compressed and uncompressed sizes and304* the offset in the file where the compressed data is located).305*306* Returns zero upon success. Returns a negative value upon failure.307*308* The buffer for reading the Central Directory if the zip/jar file needs309* to be large enough to accommodate the largest possible single record310* and the signature of the next record which is:311*312* 3*2**16 + CENHDR + SIGSIZ313*314* Each of the three variable sized fields (name, comment and extension)315* has a maximum possible size of 64k.316*317* Typically, only a small bit of this buffer is used with bytes shuffled318* down to the beginning of the buffer. It is one thing to allocate such319* a large buffer and another thing to actually start faulting it in.320*321* In most cases, all that needs to be read are the first two entries in322* a typical jar file (META-INF and META-INF/MANIFEST.MF). Keep this factoid323* in mind when optimizing this code.324*/325static int326find_file(int fd, zentry *entry, const char *file_name)327{328int bytes;329int res;330int entry_size;331int read_size;332jlong base_offset;333Byte *p;334Byte *bp;335Byte *buffer;336Byte locbuf[LOCHDR];337338if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {339return(-1);340}341342bp = buffer;343base_offset = compute_cen(fd, bp);344if (base_offset == -1) {345free(buffer);346return -1;347}348349if ((bytes = read(fd, bp, MINREAD)) < 0) {350free(buffer);351return (-1);352}353p = bp;354/*355* Loop through the Central Directory Headers. Note that a valid zip/jar356* must have an ENDHDR (with ENDSIG) after the Central Directory.357*/358while (GETSIG(p) == CENSIG) {359360/*361* If a complete header isn't in the buffer, shift the contents362* of the buffer down and refill the buffer. Note that the check363* for "bytes < CENHDR" must be made before the test for the entire364* size of the header, because if bytes is less than CENHDR, the365* actual size of the header can't be determined. The addition of366* SIGSIZ guarantees that the next signature is also in the buffer367* for proper loop termination.368*/369if (bytes < CENHDR) {370p = memmove(bp, p, bytes);371if ((res = read(fd, bp + bytes, MINREAD)) <= 0) {372free(buffer);373return (-1);374}375bytes += res;376}377entry_size = CENHDR + CENNAM(p) + CENEXT(p) + CENCOM(p);378if (bytes < entry_size + SIGSIZ) {379if (p != bp)380p = memmove(bp, p, bytes);381read_size = entry_size - bytes + SIGSIZ;382read_size = (read_size < MINREAD) ? MINREAD : read_size;383if ((res = read(fd, bp + bytes, read_size)) <= 0) {384free(buffer);385return (-1);386}387bytes += res;388}389390/*391* Check if the name is the droid we are looking for; the jar file392* manifest. If so, build the entry record from the data found in393* the header located and return success.394*/395if ((size_t)CENNAM(p) == JLI_StrLen(file_name) &&396memcmp((p + CENHDR), file_name, JLI_StrLen(file_name)) == 0) {397if (JLI_Lseek(fd, base_offset + CENOFF(p), SEEK_SET) < (jlong)0) {398free(buffer);399return (-1);400}401if (read(fd, locbuf, LOCHDR) < 0) {402free(buffer);403return (-1);404}405if (GETSIG(locbuf) != LOCSIG) {406free(buffer);407return (-1);408}409entry->isize = CENLEN(p);410entry->csize = CENSIZ(p);411entry->offset = base_offset + CENOFF(p) + LOCHDR +412LOCNAM(locbuf) + LOCEXT(locbuf);413entry->how = CENHOW(p);414free(buffer);415return (0);416}417418/*419* Point to the next entry and decrement the count of valid remaining420* bytes.421*/422bytes -= entry_size;423p += entry_size;424}425free(buffer);426return (-1); /* Fell off the end the loop without a Manifest */427}428429/*430* Parse a Manifest file header entry into a distinct "name" and "value".431* Continuation lines are joined into a single "value". The documented432* syntax for a header entry is:433*434* header: name ":" value435*436* name: alphanum *headerchar437*438* value: SPACE *otherchar newline *continuation439*440* continuation: SPACE *otherchar newline441*442* newline: CR LF | LF | CR (not followed by LF)443*444* alphanum: {"A"-"Z"} | {"a"-"z"} | {"0"-"9"}445*446* headerchar: alphanum | "-" | "_"447*448* otherchar: any UTF-8 character except NUL, CR and LF449*450* Note that a manifest file may be composed of multiple sections,451* each of which may contain multiple headers.452*453* section: *header +newline454*455* nonempty-section: +header +newline456*457* (Note that the point of "nonempty-section" is unclear, because it isn't458* referenced elsewhere in the full specification for the Manifest file.)459*460* Arguments:461* lp pointer to a character pointer which points to the start462* of a valid header.463* name pointer to a character pointer which will be set to point464* to the name portion of the header (nul terminated).465* value pointer to a character pointer which will be set to point466* to the value portion of the header (nul terminated).467*468* Returns:469* 1 Successful parsing of an NV pair. lp is updated to point to the470* next character after the terminating newline in the string471* representing the Manifest file. name and value are updated to472* point to the strings parsed.473* 0 A valid end of section indicator was encountered. lp, name, and474* value are not modified.475* -1 lp does not point to a valid header. Upon return, the values of476* lp, name, and value are undefined.477*/478static int479parse_nv_pair(char **lp, char **name, char **value)480{481char *nl;482char *cp;483484/*485* End of the section - return 0. The end of section condition is486* indicated by either encountering a blank line or the end of the487* Manifest "string" (EOF).488*/489if (**lp == '\0' || **lp == '\n' || **lp == '\r')490return (0);491492/*493* Getting to here, indicates that *lp points to an "otherchar".494* Turn the "header" into a string on its own.495*/496nl = JLI_StrPBrk(*lp, "\n\r");497if (nl == NULL) {498nl = JLI_StrChr(*lp, (int)'\0');499} else {500cp = nl; /* For merging continuation lines */501if (*nl == '\r' && *(nl+1) == '\n')502*nl++ = '\0';503*nl++ = '\0';504505/*506* Process any "continuation" line(s), by making them part of the507* "header" line. Yes, I know that we are "undoing" the NULs we508* just placed here, but continuation lines are the fairly rare509* case, so we shouldn't unnecessarily complicate the code above.510*511* Note that an entire continuation line is processed each iteration512* through the outer while loop.513*/514while (*nl == ' ') {515nl++; /* First character to be moved */516while (*nl != '\n' && *nl != '\r' && *nl != '\0')517*cp++ = *nl++; /* Shift string */518if (*nl == '\0')519return (-1); /* Error: newline required */520*cp = '\0';521if (*nl == '\r' && *(nl+1) == '\n')522*nl++ = '\0';523*nl++ = '\0';524}525}526527/*528* Separate the name from the value;529*/530cp = JLI_StrChr(*lp, (int)':');531if (cp == NULL)532return (-1);533*cp++ = '\0'; /* The colon terminates the name */534if (*cp != ' ')535return (-1);536*cp++ = '\0'; /* Eat the required space */537*name = *lp;538*value = cp;539*lp = nl;540return (1);541}542543/*544* Read the manifest from the specified jar file and fill in the manifest_info545* structure with the information found within.546*547* Error returns are as follows:548* 0 Success549* -1 Unable to open jarfile550* -2 Error accessing the manifest from within the jarfile (most likely551* a manifest is not present, or this isn't a valid zip/jar file).552*/553int554JLI_ParseManifest(char *jarfile, manifest_info *info)555{556int fd;557zentry entry;558char *lp;559char *name;560char *value;561int rc;562char *splashscreen_name = NULL;563564if ((fd = open(jarfile, O_RDONLY565#ifdef O_LARGEFILE566| O_LARGEFILE /* large file mode */567#endif568#ifdef O_BINARY569| O_BINARY /* use binary mode on windows */570#endif571)) == -1) {572return (-1);573}574info->manifest_version = NULL;575info->main_class = NULL;576info->jre_version = NULL;577info->jre_restrict_search = 0;578info->splashscreen_image_file_name = NULL;579if (rc = find_file(fd, &entry, manifest_name) != 0) {580close(fd);581return (-2);582}583manifest = inflate_file(fd, &entry, NULL);584if (manifest == NULL) {585close(fd);586return (-2);587}588lp = manifest;589while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) {590if (JLI_StrCaseCmp(name, "Manifest-Version") == 0)591info->manifest_version = value;592else if (JLI_StrCaseCmp(name, "Main-Class") == 0)593info->main_class = value;594else if (JLI_StrCaseCmp(name, "JRE-Version") == 0)595info->jre_version = value;596else if (JLI_StrCaseCmp(name, "JRE-Restrict-Search") == 0) {597if (JLI_StrCaseCmp(value, "true") == 0)598info->jre_restrict_search = 1;599} else if (JLI_StrCaseCmp(name, "Splashscreen-Image") == 0) {600info->splashscreen_image_file_name = value;601}602}603close(fd);604if (rc == 0)605return (0);606else607return (-2);608}609610/*611* Opens the jar file and unpacks the specified file from its contents.612* Returns NULL on failure.613*/614void *615JLI_JarUnpackFile(const char *jarfile, const char *filename, int *size) {616int fd;617zentry entry;618void *data = NULL;619620if ((fd = open(jarfile, O_RDONLY621#ifdef O_LARGEFILE622| O_LARGEFILE /* large file mode */623#endif624#ifdef O_BINARY625| O_BINARY /* use binary mode on windows */626#endif627)) == -1) {628return NULL;629}630if (find_file(fd, &entry, filename) == 0) {631data = inflate_file(fd, &entry, size);632}633close(fd);634return (data);635}636637/*638* Specialized "free" function.639*/640void641JLI_FreeManifest()642{643if (manifest)644free(manifest);645}646647/*648* Iterate over the manifest of the specified jar file and invoke the provided649* closure function for each attribute encountered.650*651* Error returns are as follows:652* 0 Success653* -1 Unable to open jarfile654* -2 Error accessing the manifest from within the jarfile (most likely655* this means a manifest is not present, or it isn't a valid zip/jar file).656*/657int658JLI_ManifestIterate(const char *jarfile, attribute_closure ac, void *user_data)659{660int fd;661zentry entry;662char *mp; /* manifest pointer */663char *lp; /* pointer into manifest, updated during iteration */664char *name;665char *value;666int rc;667668if ((fd = open(jarfile, O_RDONLY669#ifdef O_LARGEFILE670| O_LARGEFILE /* large file mode */671#endif672#ifdef O_BINARY673| O_BINARY /* use binary mode on windows */674#endif675)) == -1) {676return (-1);677}678679if (rc = find_file(fd, &entry, manifest_name) != 0) {680close(fd);681return (-2);682}683684mp = inflate_file(fd, &entry, NULL);685if (mp == NULL) {686close(fd);687return (-2);688}689690lp = mp;691while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) {692(*ac)(name, value, user_data);693}694free(mp);695close(fd);696return (rc == 0) ? 0 : -2;697}698699700