src/share/bin/parse_manifest.c::JLI_ParseManifest
/*
* 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;
char *splashscreen_name = NULL;
//打开jarfile文件
if ((fd = 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->manifest_version = NULL;
info->main_class = NULL;
info->jre_version = NULL;
info->jre_restrict_search = 0;
info->splashscreen_image_file_name = NULL;
//搜索jar压缩文件中是否包含META-INF/MANIFEST.MF
if (rc = find_file(fd, &entry, manifest_name) != 0) {
close(fd);
return (-2);
}
//读取META-INF/MANIFEST.MF填充manifset
manifest = inflate_file(fd, &entry, NULL);
if (manifest == NULL) {
close(fd);
return (-2);
}
lp = manifest;
//读取maifset键值对设置manifest_info属性
while ((rc = parse_nv_pair(&lp, &name, &value)) > 0) {
if (JLI_StrCaseCmp(name, "Manifest-Version") == 0)
info->manifest_version = value;
else if (JLI_StrCaseCmp(name, "Main-Class") == 0)
info->main_class = value;
else if (JLI_StrCaseCmp(name, "JRE-Version") == 0)
info->jre_version = value;
else if (JLI_StrCaseCmp(name, "JRE-Restrict-Search") == 0) {
if (JLI_StrCaseCmp(value, "true") == 0)
info->jre_restrict_search = 1;
} else if (JLI_StrCaseCmp(name, "Splashscreen-Image") == 0) {
info->splashscreen_image_file_name = value;
}
}
//关闭文件
close(fd);
if (rc == 0)
return (0);
else
return (-2);
}
src/share/bin/parse_manifest.c
static const char *manifest_name = "META-INF/MANIFEST.MF";
src/share/bin/parse_manifest.c::find_file
/*
* 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;
jlong base_offset;
Byte *p;
Byte *bp;
Byte *buffer;
Byte locbuf[LOCHDR];
if ((buffer = (Byte*)malloc(BUFSIZE)) == NULL) {
return(-1);
}
bp = buffer;
base_offset = compute_cen(fd, bp);
if (base_offset == -1) {
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 (GETSIG(p) == CENSIG) {
/*
* 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 (GETSIG(locbuf) != LOCSIG) {
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 */
}
zip文件格式说明添加链接描述
zip文件格式说明添加链接描述
src/share/bin/manifest_info.h::zentry
/*
* Supported compression methods.
*/
#define STORED 0 /* 表示仅打包归档存储 */
#define DEFLATED 8 /* 表示进行压缩存储 */
/*
* Information from the CEN entry to inflate a file.
*/
typedef struct zentry { /* Zip file entry */
size_t isize; /* size of inflated data */
size_t csize; /* size of compressed data (zero if uncompressed) */
jlong offset; /* position of compressed data */
int how; /* compression method (if any) */
} zentry;
src/share/bin/parse_manifest.c::inflate_file
/*
* 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);
}
src/share/bin/parse_manifest.c::parse_nv_pair
/*
* 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);
}