0N/A/*
3261N/A * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * Support for reading ZIP/JAR files.
0N/A */
0N/A
0N/A#include <stdio.h>
0N/A#include <stdlib.h>
0N/A#include <stddef.h>
0N/A#include <string.h>
0N/A#include <fcntl.h>
0N/A#include <limits.h>
0N/A#include <time.h>
0N/A#include <ctype.h>
0N/A#include <assert.h>
0N/A
0N/A#include "jni.h"
0N/A#include "jni_util.h"
0N/A#include "jlong.h"
0N/A#include "jvm.h"
0N/A#include "io_util.h"
0N/A#include "io_util_md.h"
0N/A#include "zip_util.h"
5353N/A#include <zlib.h>
0N/A
4632N/A#ifdef _ALLBSD_SOURCE
4632N/A#define off64_t off_t
4632N/A#define mmap64 mmap
4632N/A#endif
4632N/A
0N/A/* USE_MMAP means mmap the CEN & ENDHDR part of the zip file. */
0N/A#ifdef USE_MMAP
0N/A#include <sys/mman.h>
0N/A#endif
0N/A
0N/A#define MAXREFS 0xFFFF /* max number of open zip file references */
0N/A
0N/A#define MCREATE() JVM_RawMonitorCreate()
0N/A#define MLOCK(lock) JVM_RawMonitorEnter(lock)
0N/A#define MUNLOCK(lock) JVM_RawMonitorExit(lock)
0N/A#define MDESTROY(lock) JVM_RawMonitorDestroy(lock)
0N/A
0N/A#define CENSIZE(cen) (CENHDR + CENNAM(cen) + CENEXT(cen) + CENCOM(cen))
0N/A
0N/Astatic jzfile *zfiles = 0; /* currently open zip files */
0N/Astatic void *zfiles_lock = 0;
0N/A
0N/Astatic void freeCEN(jzfile *);
0N/A
0N/A#ifndef PATH_MAX
0N/A#define PATH_MAX 1024
0N/A#endif
0N/A
0N/Astatic jint INITIAL_META_COUNT = 2; /* initial number of entries in meta name array */
0N/A
0N/A/*
0N/A * The ZFILE_* functions exist to provide some platform-independence with
0N/A * respect to file access needs.
0N/A */
0N/A
0N/A/*
0N/A * Opens the named file for reading, returning a ZFILE.
0N/A *
0N/A * Compare this with winFileHandleOpen in windows/native/java/io/io_util_md.c.
0N/A * This function does not take JNIEnv* and uses CreateFile (instead of
0N/A * CreateFileW). The expectation is that this function will be called only
0N/A * from ZIP_Open_Generic, which in turn is used by the JVM, where we do not
0N/A * need to concern ourselves with wide chars.
0N/A */
0N/Astatic ZFILE
0N/AZFILE_Open(const char *fname, int flags) {
0N/A#ifdef WIN32
0N/A const DWORD access =
0N/A (flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) :
0N/A (flags & O_WRONLY) ? GENERIC_WRITE :
0N/A GENERIC_READ;
0N/A const DWORD sharing =
0N/A FILE_SHARE_READ | FILE_SHARE_WRITE;
0N/A const DWORD disposition =
0N/A /* Note: O_TRUNC overrides O_CREAT */
0N/A (flags & O_TRUNC) ? CREATE_ALWAYS :
0N/A (flags & O_CREAT) ? OPEN_ALWAYS :
0N/A OPEN_EXISTING;
0N/A const DWORD maybeWriteThrough =
0N/A (flags & (O_SYNC | O_DSYNC)) ?
0N/A FILE_FLAG_WRITE_THROUGH :
0N/A FILE_ATTRIBUTE_NORMAL;
0N/A const DWORD maybeDeleteOnClose =
0N/A (flags & O_TEMPORARY) ?
0N/A FILE_FLAG_DELETE_ON_CLOSE :
0N/A FILE_ATTRIBUTE_NORMAL;
0N/A const DWORD flagsAndAttributes = maybeWriteThrough | maybeDeleteOnClose;
0N/A
0N/A return (jlong) CreateFile(
0N/A fname, /* Wide char path name */
0N/A access, /* Read and/or write permission */
0N/A sharing, /* File sharing flags */
0N/A NULL, /* Security attributes */
0N/A disposition, /* creation disposition */
0N/A flagsAndAttributes, /* flags and attributes */
0N/A NULL);
0N/A#else
0N/A return JVM_Open(fname, flags, 0);
0N/A#endif
0N/A}
0N/A
0N/A/*
0N/A * The io_util_md.h files do not provide IO_CLOSE, hence we use platform
0N/A * specifics.
0N/A */
0N/Astatic void
0N/AZFILE_Close(ZFILE zfd) {
0N/A#ifdef WIN32
0N/A CloseHandle((HANDLE) zfd);
0N/A#else
0N/A JVM_Close(zfd);
0N/A#endif
0N/A}
0N/A
0N/Astatic int
0N/AZFILE_read(ZFILE zfd, char *buf, jint nbytes) {
0N/A#ifdef WIN32
0N/A return (int) IO_Read(zfd, buf, nbytes);
0N/A#else
0N/A /*
0N/A * Calling JVM_Read will return JVM_IO_INTR when Thread.interrupt is called
0N/A * only on Solaris. Continue reading jar file in this case is the best
0N/A * thing to do since zip file reading is relatively fast and it is very onerous
0N/A * for a interrupted thread to deal with this kind of hidden I/O. However, handling
0N/A * JVM_IO_INTR is tricky and could cause undesired side effect. So we decided
0N/A * to simply call "read" on Solaris/Linux. See details in bug 6304463.
0N/A */
0N/A return read(zfd, buf, nbytes);
0N/A#endif
0N/A}
0N/A
0N/A/*
0N/A * Initialize zip file support. Return 0 if successful otherwise -1
0N/A * if could not be initialized.
0N/A */
0N/Astatic jint
0N/AInitializeZip()
0N/A{
0N/A static jboolean inited = JNI_FALSE;
0N/A
0N/A // Initialize errno to 0. It may be set later (e.g. during memory
0N/A // allocation) but we can disregard previous values.
0N/A errno = 0;
0N/A
0N/A if (inited)
0N/A return 0;
0N/A zfiles_lock = MCREATE();
0N/A if (zfiles_lock == 0) {
0N/A return -1;
0N/A }
0N/A inited = JNI_TRUE;
0N/A
0N/A return 0;
0N/A}
0N/A
0N/A/*
0N/A * Reads len bytes of data into buf.
0N/A * Returns 0 if all bytes could be read, otherwise returns -1.
0N/A */
0N/Astatic int
0N/AreadFully(ZFILE zfd, void *buf, jlong len) {
0N/A char *bp = (char *) buf;
0N/A
0N/A while (len > 0) {
0N/A jlong limit = ((((jlong) 1) << 31) - 1);
0N/A jint count = (len < limit) ?
0N/A (jint) len :
0N/A (jint) limit;
0N/A jint n = ZFILE_read(zfd, bp, count);
0N/A if (n > 0) {
0N/A bp += n;
0N/A len -= n;
0N/A } else if (n == JVM_IO_ERR && errno == EINTR) {
0N/A /* Retry after EINTR (interrupted by signal).
0N/A We depend on the fact that JVM_IO_ERR == -1. */
0N/A continue;
0N/A } else { /* EOF or IO error */
0N/A return -1;
0N/A }
0N/A }
0N/A return 0;
0N/A}
0N/A
0N/A/*
0N/A * Reads len bytes of data from the specified offset into buf.
0N/A * Returns 0 if all bytes could be read, otherwise returns -1.
0N/A */
0N/Astatic int
0N/AreadFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset)
0N/A{
910N/A if (IO_Lseek(zfd, offset, SEEK_SET) == -1) {
0N/A return -1; /* lseek failure. */
0N/A }
0N/A
0N/A return readFully(zfd, buf, len);
0N/A}
0N/A
0N/A/*
0N/A * Allocates a new zip file object for the specified file name.
0N/A * Returns the zip file object or NULL if not enough memory.
0N/A */
0N/Astatic jzfile *
0N/AallocZip(const char *name)
0N/A{
0N/A jzfile *zip;
0N/A if (((zip = calloc(1, sizeof(jzfile))) != NULL) &&
0N/A ((zip->name = strdup(name)) != NULL) &&
0N/A ((zip->lock = MCREATE()) != NULL)) {
0N/A zip->zfd = -1;
0N/A return zip;
0N/A }
0N/A
0N/A if (zip != NULL) {
0N/A free(zip->name);
0N/A free(zip);
0N/A }
0N/A return NULL;
0N/A}
0N/A
0N/A/*
0N/A * Frees all native resources owned by the specified zip file object.
0N/A */
0N/Astatic void
0N/AfreeZip(jzfile *zip)
0N/A{
0N/A /* First free any cached jzentry */
0N/A ZIP_FreeEntry(zip,0);
0N/A if (zip->lock != NULL) MDESTROY(zip->lock);
0N/A free(zip->name);
0N/A freeCEN(zip);
2227N/A
0N/A#ifdef USE_MMAP
2227N/A if (zip->usemmap) {
2227N/A if (zip->maddr != NULL)
2227N/A munmap((char *)zip->maddr, zip->mlen);
2227N/A } else
0N/A#endif
2227N/A {
2227N/A free(zip->cencache.data);
2227N/A }
1332N/A if (zip->comment != NULL)
1332N/A free(zip->comment);
0N/A if (zip->zfd != -1) ZFILE_Close(zip->zfd);
0N/A free(zip);
0N/A}
0N/A
0N/A/* The END header is followed by a variable length comment of size < 64k. */
0N/Astatic const jlong END_MAXLEN = 0xFFFF + ENDHDR;
0N/A
0N/A#define READBLOCKSZ 128
0N/A
1332N/Astatic jboolean verifyEND(jzfile *zip, jlong endpos, char *endbuf) {
1332N/A /* ENDSIG matched, however the size of file comment in it does not
1332N/A match the real size. One "common" cause for this problem is some
1332N/A "extra" bytes are padded at the end of the zipfile.
1332N/A Let's do some extra verification, we don't care about the performance
1332N/A in this situation.
1332N/A */
1332N/A jlong cenpos = endpos - ENDSIZ(endbuf);
1332N/A jlong locpos = cenpos - ENDOFF(endbuf);
1332N/A char buf[4];
1332N/A return (cenpos >= 0 &&
1332N/A locpos >= 0 &&
1332N/A readFullyAt(zip->zfd, buf, sizeof(buf), cenpos) != -1 &&
1332N/A GETSIG(buf) == CENSIG &&
1332N/A readFullyAt(zip->zfd, buf, sizeof(buf), locpos) != -1 &&
1332N/A GETSIG(buf) == LOCSIG);
1332N/A}
1332N/A
0N/A/*
0N/A * Searches for end of central directory (END) header. The contents of
0N/A * the END header will be read and placed in endbuf. Returns the file
701N/A * position of the END header, otherwise returns -1 if the END header
701N/A * was not found or an error occurred.
0N/A */
0N/Astatic jlong
0N/AfindEND(jzfile *zip, void *endbuf)
0N/A{
0N/A char buf[READBLOCKSZ];
0N/A jlong pos;
0N/A const jlong len = zip->len;
0N/A const ZFILE zfd = zip->zfd;
0N/A const jlong minHDR = len - END_MAXLEN > 0 ? len - END_MAXLEN : 0;
0N/A const jlong minPos = minHDR - (sizeof(buf)-ENDHDR);
1332N/A jint clen;
0N/A
0N/A for (pos = len - sizeof(buf); pos >= minPos; pos -= (sizeof(buf)-ENDHDR)) {
0N/A
0N/A int i;
0N/A jlong off = 0;
0N/A if (pos < 0) {
0N/A /* Pretend there are some NUL bytes before start of file */
0N/A off = -pos;
3115N/A memset(buf, '\0', (size_t)off);
0N/A }
0N/A
0N/A if (readFullyAt(zfd, buf + off, sizeof(buf) - off,
0N/A pos + off) == -1) {
0N/A return -1; /* System error */
0N/A }
0N/A
0N/A /* Now scan the block backwards for END header signature */
0N/A for (i = sizeof(buf) - ENDHDR; i >= 0; i--) {
0N/A if (buf[i+0] == 'P' &&
0N/A buf[i+1] == 'K' &&
0N/A buf[i+2] == '\005' &&
0N/A buf[i+3] == '\006' &&
1332N/A ((pos + i + ENDHDR + ENDCOM(buf + i) == len)
1332N/A || verifyEND(zip, pos + i, buf + i))) {
1332N/A /* Found END header */
1332N/A memcpy(endbuf, buf + i, ENDHDR);
1332N/A
1332N/A clen = ENDCOM(endbuf);
1332N/A if (clen != 0) {
1332N/A zip->comment = malloc(clen + 1);
1332N/A if (zip->comment == NULL) {
1332N/A return -1;
1332N/A }
1332N/A if (readFullyAt(zfd, zip->comment, clen, pos + i + ENDHDR)
1332N/A == -1) {
1332N/A free(zip->comment);
1332N/A zip->comment = NULL;
1332N/A return -1;
1332N/A }
1332N/A zip->comment[clen] = '\0';
1332N/A zip->clen = clen;
1332N/A }
1332N/A return pos + i;
0N/A }
0N/A }
0N/A }
1332N/A
701N/A return -1; /* END header not found */
0N/A}
0N/A
0N/A/*
1032N/A * Searches for the ZIP64 end of central directory (END) header. The
1032N/A * contents of the ZIP64 END header will be read and placed in end64buf.
1032N/A * Returns the file position of the ZIP64 END header, otherwise returns
1032N/A * -1 if the END header was not found or an error occurred.
1032N/A *
1032N/A * The ZIP format specifies the "position" of each related record as
1032N/A * ...
1032N/A * [central directory]
1032N/A * [zip64 end of central directory record]
1032N/A * [zip64 end of central directory locator]
1032N/A * [end of central directory record]
1032N/A *
1032N/A * The offset of zip64 end locator can be calculated from endpos as
1032N/A * "endpos - ZIP64_LOCHDR".
1032N/A * The "offset" of zip64 end record is stored in zip64 end locator.
1032N/A */
1032N/Astatic jlong
1032N/AfindEND64(jzfile *zip, void *end64buf, jlong endpos)
1032N/A{
1032N/A char loc64[ZIP64_LOCHDR];
1032N/A jlong end64pos;
1032N/A if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) {
1032N/A return -1; // end64 locator not found
1032N/A }
1032N/A end64pos = ZIP64_LOCOFF(loc64);
1032N/A if (readFullyAt(zip->zfd, end64buf, ZIP64_ENDHDR, end64pos) == -1) {
1032N/A return -1; // end64 record not found
1032N/A }
1032N/A return end64pos;
1032N/A}
1032N/A
1032N/A/*
0N/A * Returns a hash code value for a C-style NUL-terminated string.
0N/A */
0N/Astatic unsigned int
0N/Ahash(const char *s)
0N/A{
0N/A int h = 0;
0N/A while (*s != '\0')
0N/A h = 31*h + *s++;
0N/A return h;
0N/A}
0N/A
0N/A/*
0N/A * Returns a hash code value for a string of a specified length.
0N/A */
0N/Astatic unsigned int
0N/AhashN(const char *s, int length)
0N/A{
0N/A int h = 0;
0N/A while (length-- > 0)
0N/A h = 31*h + *s++;
0N/A return h;
0N/A}
0N/A
0N/Astatic unsigned int
0N/Ahash_append(unsigned int hash, char c)
0N/A{
0N/A return ((int)hash)*31 + c;
0N/A}
0N/A
0N/A/*
0N/A * Returns true if the specified entry's name begins with the string
0N/A * "META-INF/" irrespective of case.
0N/A */
0N/Astatic int
0N/AisMetaName(const char *name, int length)
0N/A{
0N/A const char *s;
3115N/A if (length < (int)sizeof("META-INF/") - 1)
0N/A return 0;
0N/A for (s = "META-INF/"; *s != '\0'; s++) {
0N/A char c = *name++;
0N/A // Avoid toupper; it's locale-dependent
0N/A if (c >= 'a' && c <= 'z') c += 'A' - 'a';
0N/A if (*s != c)
0N/A return 0;
0N/A }
0N/A return 1;
0N/A}
0N/A
0N/A/*
0N/A * Increases the capacity of zip->metanames.
0N/A * Returns non-zero in case of allocation error.
0N/A */
0N/Astatic int
0N/AgrowMetaNames(jzfile *zip)
0N/A{
0N/A jint i;
0N/A /* double the meta names array */
0N/A const jint new_metacount = zip->metacount << 1;
0N/A zip->metanames =
0N/A realloc(zip->metanames, new_metacount * sizeof(zip->metanames[0]));
0N/A if (zip->metanames == NULL) return -1;
0N/A for (i = zip->metacount; i < new_metacount; i++)
0N/A zip->metanames[i] = NULL;
0N/A zip->metacurrent = zip->metacount;
0N/A zip->metacount = new_metacount;
0N/A return 0;
0N/A}
0N/A
0N/A/*
0N/A * Adds name to zip->metanames.
0N/A * Returns non-zero in case of allocation error.
0N/A */
0N/Astatic int
0N/AaddMetaName(jzfile *zip, const char *name, int length)
0N/A{
0N/A jint i;
0N/A if (zip->metanames == NULL) {
0N/A zip->metacount = INITIAL_META_COUNT;
0N/A zip->metanames = calloc(zip->metacount, sizeof(zip->metanames[0]));
0N/A if (zip->metanames == NULL) return -1;
0N/A zip->metacurrent = 0;
0N/A }
0N/A
0N/A i = zip->metacurrent;
0N/A
0N/A /* current meta name array isn't full yet. */
0N/A if (i < zip->metacount) {
0N/A zip->metanames[i] = (char *) malloc(length+1);
0N/A if (zip->metanames[i] == NULL) return -1;
0N/A memcpy(zip->metanames[i], name, length);
0N/A zip->metanames[i][length] = '\0';
0N/A zip->metacurrent++;
0N/A return 0;
0N/A }
0N/A
0N/A /* No free entries in zip->metanames? */
0N/A if (growMetaNames(zip) != 0) return -1;
0N/A return addMetaName(zip, name, length);
0N/A}
0N/A
0N/Astatic void
0N/AfreeMetaNames(jzfile *zip)
0N/A{
0N/A if (zip->metanames) {
0N/A jint i;
0N/A for (i = 0; i < zip->metacount; i++)
0N/A free(zip->metanames[i]);
0N/A free(zip->metanames);
0N/A zip->metanames = NULL;
0N/A }
0N/A}
0N/A
0N/A/* Free Zip data allocated by readCEN() */
0N/Astatic void
0N/AfreeCEN(jzfile *zip)
0N/A{
0N/A free(zip->entries); zip->entries = NULL;
0N/A free(zip->table); zip->table = NULL;
0N/A freeMetaNames(zip);
0N/A}
0N/A
0N/A/*
0N/A * Counts the number of CEN headers in a central directory extending
0N/A * from BEG to END. Might return a bogus answer if the zip file is
0N/A * corrupt, but will not crash.
0N/A */
0N/Astatic jint
0N/AcountCENHeaders(unsigned char *beg, unsigned char *end)
0N/A{
0N/A jint count = 0;
0N/A ptrdiff_t i;
4752N/A for (i = 0; i + CENHDR <= end - beg; i += CENSIZE(beg + i))
0N/A count++;
0N/A return count;
0N/A}
0N/A
0N/A#define ZIP_FORMAT_ERROR(message) \
0N/Aif (1) { zip->msg = message; goto Catch; } else ((void)0)
0N/A
0N/A/*
0N/A * Reads zip file central directory. Returns the file position of first
701N/A * CEN header, otherwise returns -1 if an error occured. If zip->msg != NULL
701N/A * then the error was a zip format error and zip->msg has the error text.
0N/A * Always pass in -1 for knownTotal; it's used for a recursive call.
0N/A */
0N/Astatic jlong
0N/AreadCEN(jzfile *zip, jint knownTotal)
0N/A{
0N/A /* Following are unsigned 32-bit */
1032N/A jlong endpos, end64pos, cenpos, cenlen, cenoff;
0N/A /* Following are unsigned 16-bit */
0N/A jint total, tablelen, i, j;
0N/A unsigned char *cenbuf = NULL;
0N/A unsigned char *cenend;
0N/A unsigned char *cp;
0N/A#ifdef USE_MMAP
0N/A static jlong pagesize;
910N/A jlong offset;
0N/A#endif
0N/A unsigned char endbuf[ENDHDR];
1032N/A jint endhdrlen = ENDHDR;
0N/A jzcell *entries;
0N/A jint *table;
0N/A
0N/A /* Clear previous zip error */
0N/A zip->msg = NULL;
0N/A /* Get position of END header */
0N/A if ((endpos = findEND(zip, endbuf)) == -1)
701N/A return -1; /* no END header or system error */
0N/A
701N/A if (endpos == 0) return 0; /* only END header present */
0N/A
0N/A freeCEN(zip);
0N/A /* Get position and length of central directory */
0N/A cenlen = ENDSIZ(endbuf);
1032N/A cenoff = ENDOFF(endbuf);
1032N/A total = ENDTOT(endbuf);
1032N/A if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL ||
1032N/A total == ZIP64_MAGICCOUNT) {
1032N/A unsigned char end64buf[ZIP64_ENDHDR];
1032N/A if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) {
1032N/A cenlen = ZIP64_ENDSIZ(end64buf);
1032N/A cenoff = ZIP64_ENDOFF(end64buf);
1032N/A total = (jint)ZIP64_ENDTOT(end64buf);
1032N/A endpos = end64pos;
1032N/A endhdrlen = ZIP64_ENDHDR;
1032N/A }
1032N/A }
1032N/A
0N/A if (cenlen > endpos)
0N/A ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
0N/A cenpos = endpos - cenlen;
0N/A
0N/A /* Get position of first local file (LOC) header, taking into
0N/A * account that there may be a stub prefixed to the zip file. */
1032N/A zip->locpos = cenpos - cenoff;
0N/A if (zip->locpos < 0)
0N/A ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
0N/A
0N/A#ifdef USE_MMAP
2227N/A if (zip->usemmap) {
2227N/A /* On Solaris & Linux prior to JDK 6, we used to mmap the whole jar file to
2227N/A * read the jar file contents. However, this greatly increased the perceived
2227N/A * footprint numbers because the mmap'ed pages were adding into the totals shown
2227N/A * by 'ps' and 'top'. We switched to mmaping only the central directory of jar
2227N/A * file while calling 'read' to read the rest of jar file. Here are a list of
2227N/A * reasons apart from above of why we are doing so:
2227N/A * 1. Greatly reduces mmap overhead after startup complete;
2227N/A * 2. Avoids dual path code maintainance;
2227N/A * 3. Greatly reduces risk of address space (not virtual memory) exhaustion.
2227N/A */
2227N/A if (pagesize == 0) {
2227N/A pagesize = (jlong)sysconf(_SC_PAGESIZE);
2227N/A if (pagesize == 0) goto Catch;
2227N/A }
2227N/A if (cenpos > pagesize) {
2227N/A offset = cenpos & ~(pagesize - 1);
2227N/A } else {
2227N/A offset = 0;
2227N/A }
2227N/A /* When we are not calling recursively, knownTotal is -1. */
2227N/A if (knownTotal == -1) {
2227N/A void* mappedAddr;
2227N/A /* Mmap the CEN and END part only. We have to figure
2227N/A out the page size in order to make offset to be multiples of
2227N/A page size.
2227N/A */
2227N/A zip->mlen = cenpos - offset + cenlen + endhdrlen;
2227N/A zip->offset = offset;
2227N/A mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
2227N/A zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
2227N/A (unsigned char*)mappedAddr;
2227N/A
2227N/A if (zip->maddr == NULL) {
2227N/A jio_fprintf(stderr, "mmap failed for CEN and END part of zip file\n");
2227N/A goto Catch;
2227N/A }
2227N/A }
2227N/A cenbuf = zip->maddr + cenpos - offset;
2227N/A } else
2227N/A#endif
2227N/A {
2227N/A if ((cenbuf = malloc((size_t) cenlen)) == NULL ||
2227N/A (readFullyAt(zip->zfd, cenbuf, cenlen, cenpos) == -1))
2227N/A goto Catch;
0N/A }
0N/A
0N/A cenend = cenbuf + cenlen;
0N/A
0N/A /* Initialize zip file data structures based on the total number
0N/A * of central directory entries as stored in ENDTOT. Since this
0N/A * is a 2-byte field, but we (and other zip implementations)
0N/A * support approx. 2**31 entries, we do not trust ENDTOT, but
0N/A * treat it only as a strong hint. When we call ourselves
1032N/A * recursively, knownTotal will have the "true" value.
1032N/A *
1032N/A * Keep this path alive even with the Zip64 END support added, just
1032N/A * for zip files that have more than 0xffff entries but don't have
1032N/A * the Zip64 enabled.
1032N/A */
1032N/A total = (knownTotal != -1) ? knownTotal : total;
0N/A entries = zip->entries = calloc(total, sizeof(entries[0]));
0N/A tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
0N/A table = zip->table = malloc(tablelen * sizeof(table[0]));
0N/A if (entries == NULL || table == NULL) goto Catch;
0N/A for (j = 0; j < tablelen; j++)
0N/A table[j] = ZIP_ENDCHAIN;
0N/A
0N/A /* Iterate through the entries in the central directory */
0N/A for (i = 0, cp = cenbuf; cp <= cenend - CENHDR; i++, cp += CENSIZE(cp)) {
0N/A /* Following are unsigned 16-bit */
0N/A jint method, nlen;
0N/A unsigned int hsh;
0N/A
0N/A if (i >= total) {
0N/A /* This will only happen if the zip file has an incorrect
0N/A * ENDTOT field, which usually means it contains more than
0N/A * 65535 entries. */
0N/A cenpos = readCEN(zip, countCENHeaders(cenbuf, cenend));
0N/A goto Finally;
0N/A }
0N/A
0N/A method = CENHOW(cp);
0N/A nlen = CENNAM(cp);
0N/A
0N/A if (GETSIG(cp) != CENSIG)
0N/A ZIP_FORMAT_ERROR("invalid CEN header (bad signature)");
0N/A if (CENFLG(cp) & 1)
0N/A ZIP_FORMAT_ERROR("invalid CEN header (encrypted entry)");
0N/A if (method != STORED && method != DEFLATED)
0N/A ZIP_FORMAT_ERROR("invalid CEN header (bad compression method)");
0N/A if (cp + CENHDR + nlen > cenend)
0N/A ZIP_FORMAT_ERROR("invalid CEN header (bad header size)");
0N/A
0N/A /* if the entry is metadata add it to our metadata names */
0N/A if (isMetaName((char *)cp+CENHDR, nlen))
0N/A if (addMetaName(zip, (char *)cp+CENHDR, nlen) != 0)
0N/A goto Catch;
0N/A
0N/A /* Record the CEN offset and the name hash in our hash cell. */
0N/A entries[i].cenpos = cenpos + (cp - cenbuf);
0N/A entries[i].hash = hashN((char *)cp+CENHDR, nlen);
0N/A
0N/A /* Add the entry to the hash table */
0N/A hsh = entries[i].hash % tablelen;
0N/A entries[i].next = table[hsh];
0N/A table[hsh] = i;
0N/A }
0N/A if (cp != cenend)
0N/A ZIP_FORMAT_ERROR("invalid CEN header (bad header size)");
0N/A
0N/A zip->total = i;
0N/A goto Finally;
0N/A
0N/A Catch:
0N/A freeCEN(zip);
0N/A cenpos = -1;
0N/A
0N/A Finally:
2227N/A#ifdef USE_MMAP
2227N/A if (!zip->usemmap)
0N/A#endif
2227N/A free(cenbuf);
2227N/A
0N/A return cenpos;
0N/A}
0N/A
0N/A/*
0N/A * Opens a zip file with the specified mode. Returns the jzfile object
0N/A * or NULL if an error occurred. If a zip error occurred then *pmsg will
0N/A * be set to the error message text if pmsg != 0. Otherwise, *pmsg will be
5345N/A * set to NULL. Caller is responsible to free the error message.
0N/A */
0N/Ajzfile *
0N/AZIP_Open_Generic(const char *name, char **pmsg, int mode, jlong lastModified)
0N/A{
0N/A jzfile *zip = NULL;
0N/A
0N/A /* Clear zip error message */
0N/A if (pmsg != 0) {
0N/A *pmsg = NULL;
0N/A }
0N/A
0N/A zip = ZIP_Get_From_Cache(name, pmsg, lastModified);
0N/A
0N/A if (zip == NULL && *pmsg == NULL) {
0N/A ZFILE zfd = ZFILE_Open(name, mode);
0N/A zip = ZIP_Put_In_Cache(name, zfd, pmsg, lastModified);
0N/A }
0N/A return zip;
0N/A}
0N/A
0N/A/*
0N/A * Returns the jzfile corresponding to the given file name from the cache of
0N/A * zip files, or NULL if the file is not in the cache. If the name is longer
0N/A * than PATH_MAX or a zip error occurred then *pmsg will be set to the error
5345N/A * message text if pmsg != 0. Otherwise, *pmsg will be set to NULL. Caller
5345N/A * is responsible to free the error message.
0N/A */
0N/Ajzfile *
0N/AZIP_Get_From_Cache(const char *name, char **pmsg, jlong lastModified)
0N/A{
0N/A char buf[PATH_MAX];
0N/A jzfile *zip;
0N/A
0N/A if (InitializeZip()) {
0N/A return NULL;
0N/A }
0N/A
0N/A /* Clear zip error message */
0N/A if (pmsg != 0) {
0N/A *pmsg = NULL;
0N/A }
0N/A
0N/A if (strlen(name) >= PATH_MAX) {
0N/A if (pmsg) {
5345N/A *pmsg = strdup("zip file name too long");
0N/A }
0N/A return NULL;
0N/A }
0N/A strcpy(buf, name);
0N/A JVM_NativePath(buf);
0N/A name = buf;
0N/A
0N/A MLOCK(zfiles_lock);
0N/A for (zip = zfiles; zip != NULL; zip = zip->next) {
0N/A if (strcmp(name, zip->name) == 0
0N/A && (zip->lastModified == lastModified || zip->lastModified == 0)
0N/A && zip->refs < MAXREFS) {
0N/A zip->refs++;
0N/A break;
0N/A }
0N/A }
0N/A MUNLOCK(zfiles_lock);
0N/A return zip;
0N/A}
0N/A
0N/A/*
0N/A * Reads data from the given file descriptor to create a jzfile, puts the
0N/A * jzfile in a cache, and returns that jzfile. Returns NULL in case of error.
0N/A * If a zip error occurs, then *pmsg will be set to the error message text if
5345N/A * pmsg != 0. Otherwise, *pmsg will be set to NULL. Caller is responsible to
5345N/A * free the error message.
0N/A */
2227N/A
0N/Ajzfile *
0N/AZIP_Put_In_Cache(const char *name, ZFILE zfd, char **pmsg, jlong lastModified)
0N/A{
2227N/A return ZIP_Put_In_Cache0(name, zfd, pmsg, lastModified, JNI_TRUE);
2227N/A}
2227N/A
2227N/Ajzfile *
2227N/AZIP_Put_In_Cache0(const char *name, ZFILE zfd, char **pmsg, jlong lastModified,
2227N/A jboolean usemmap)
2227N/A{
5345N/A char errbuf[256];
0N/A jlong len;
0N/A jzfile *zip;
0N/A
0N/A if ((zip = allocZip(name)) == NULL) {
0N/A return NULL;
0N/A }
0N/A
2227N/A#ifdef USE_MMAP
2227N/A zip->usemmap = usemmap;
2227N/A#endif
0N/A zip->refs = 1;
0N/A zip->lastModified = lastModified;
0N/A
0N/A if (zfd == -1) {
0N/A if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0)
5345N/A *pmsg = strdup(errbuf);
0N/A freeZip(zip);
0N/A return NULL;
0N/A }
0N/A
6329N/A // Assumption, zfd refers to start of file. Trivially, reuse errbuf.
6329N/A if (readFully(zfd, errbuf, 4) != -1) { // errors will be handled later
6329N/A if (GETSIG(errbuf) == LOCSIG)
6329N/A zip->locsig = JNI_TRUE;
6329N/A else
6329N/A zip->locsig = JNI_FALSE;
6329N/A }
6329N/A
910N/A len = zip->len = IO_Lseek(zfd, 0, SEEK_END);
575N/A if (len <= 0) {
575N/A if (len == 0) { /* zip file is empty */
575N/A if (pmsg) {
5345N/A *pmsg = strdup("zip file is empty");
575N/A }
575N/A } else { /* error */
575N/A if (pmsg && JVM_GetLastErrorString(errbuf, sizeof(errbuf)) > 0)
5345N/A *pmsg = strdup(errbuf);
575N/A }
0N/A ZFILE_Close(zfd);
0N/A freeZip(zip);
0N/A return NULL;
0N/A }
0N/A
0N/A zip->zfd = zfd;
575N/A if (readCEN(zip, -1) < 0) {
0N/A /* An error occurred while trying to read the zip file */
0N/A if (pmsg != 0) {
0N/A /* Set the zip error message */
5345N/A if (zip->msg != NULL)
5345N/A *pmsg = strdup(zip->msg);
0N/A }
0N/A freeZip(zip);
0N/A return NULL;
0N/A }
0N/A MLOCK(zfiles_lock);
0N/A zip->next = zfiles;
0N/A zfiles = zip;
0N/A MUNLOCK(zfiles_lock);
0N/A
0N/A return zip;
0N/A}
0N/A
0N/A/*
0N/A * Opens a zip file for reading. Returns the jzfile object or NULL
0N/A * if an error occurred. If a zip error occurred then *msg will be
0N/A * set to the error message text if msg != 0. Otherwise, *msg will be
5345N/A * set to NULL. Caller doesn't need to free the error message.
0N/A */
0N/Ajzfile * JNICALL
0N/AZIP_Open(const char *name, char **pmsg)
0N/A{
5345N/A jzfile *file = ZIP_Open_Generic(name, pmsg, O_RDONLY, 0);
5345N/A if (file == NULL && pmsg != NULL && *pmsg != NULL) {
5345N/A free(*pmsg);
5345N/A *pmsg = "Zip file open error";
5345N/A }
5345N/A return file;
0N/A}
0N/A
0N/A/*
0N/A * Closes the specified zip file object.
0N/A */
0N/Avoid JNICALL
0N/AZIP_Close(jzfile *zip)
0N/A{
0N/A MLOCK(zfiles_lock);
0N/A if (--zip->refs > 0) {
0N/A /* Still more references so just return */
0N/A MUNLOCK(zfiles_lock);
0N/A return;
0N/A }
0N/A /* No other references so close the file and remove from list */
0N/A if (zfiles == zip) {
0N/A zfiles = zfiles->next;
0N/A } else {
0N/A jzfile *zp;
0N/A for (zp = zfiles; zp->next != 0; zp = zp->next) {
0N/A if (zp->next == zip) {
0N/A zp->next = zip->next;
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A MUNLOCK(zfiles_lock);
0N/A freeZip(zip);
0N/A return;
0N/A}
0N/A
0N/A/* Empirically, most CEN headers are smaller than this. */
0N/A#define AMPLE_CEN_HEADER_SIZE 160
0N/A
0N/A/* A good buffer size when we want to read CEN headers sequentially. */
0N/A#define CENCACHE_PAGESIZE 8192
0N/A
0N/Astatic char *
0N/AreadCENHeader(jzfile *zip, jlong cenpos, jint bufsize)
0N/A{
0N/A jint censize;
0N/A ZFILE zfd = zip->zfd;
0N/A char *cen;
0N/A if (bufsize > zip->len - cenpos)
3115N/A bufsize = (jint)(zip->len - cenpos);
0N/A if ((cen = malloc(bufsize)) == NULL) goto Catch;
0N/A if (readFullyAt(zfd, cen, bufsize, cenpos) == -1) goto Catch;
0N/A censize = CENSIZE(cen);
0N/A if (censize <= bufsize) return cen;
0N/A if ((cen = realloc(cen, censize)) == NULL) goto Catch;
0N/A if (readFully(zfd, cen+bufsize, censize-bufsize) == -1) goto Catch;
0N/A return cen;
0N/A
0N/A Catch:
0N/A free(cen);
0N/A return NULL;
0N/A}
0N/A
0N/Astatic char *
0N/AsequentialAccessReadCENHeader(jzfile *zip, jlong cenpos)
0N/A{
0N/A cencache *cache = &zip->cencache;
0N/A char *cen;
0N/A if (cache->data != NULL
0N/A && (cenpos >= cache->pos)
0N/A && (cenpos + CENHDR <= cache->pos + CENCACHE_PAGESIZE))
0N/A {
0N/A cen = cache->data + cenpos - cache->pos;
0N/A if (cenpos + CENSIZE(cen) <= cache->pos + CENCACHE_PAGESIZE)
0N/A /* A cache hit */
0N/A return cen;
0N/A }
0N/A
0N/A if ((cen = readCENHeader(zip, cenpos, CENCACHE_PAGESIZE)) == NULL)
0N/A return NULL;
0N/A free(cache->data);
0N/A cache->data = cen;
0N/A cache->pos = cenpos;
0N/A return cen;
0N/A}
0N/A
0N/Atypedef enum { ACCESS_RANDOM, ACCESS_SEQUENTIAL } AccessHint;
0N/A
0N/A/*
0N/A * Return a new initialized jzentry corresponding to a given hash cell.
0N/A * In case of error, returns NULL.
0N/A * We already sanity-checked all the CEN headers for ZIP format errors
0N/A * in readCEN(), so we don't check them again here.
0N/A * The ZIP lock should be held here.
0N/A */
0N/Astatic jzentry *
0N/AnewEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
0N/A{
1032N/A jlong locoff;
0N/A jint nlen, elen, clen;
0N/A jzentry *ze;
0N/A char *cen;
0N/A
0N/A if ((ze = (jzentry *) malloc(sizeof(jzentry))) == NULL) return NULL;
0N/A ze->name = NULL;
0N/A ze->extra = NULL;
0N/A ze->comment = NULL;
0N/A
0N/A#ifdef USE_MMAP
2227N/A if (zip->usemmap) {
2227N/A cen = (char*) zip->maddr + zc->cenpos - zip->offset;
2227N/A } else
0N/A#endif
2227N/A {
2227N/A if (accessHint == ACCESS_RANDOM)
2227N/A cen = readCENHeader(zip, zc->cenpos, AMPLE_CEN_HEADER_SIZE);
2227N/A else
2227N/A cen = sequentialAccessReadCENHeader(zip, zc->cenpos);
2227N/A if (cen == NULL) goto Catch;
2227N/A }
0N/A
0N/A nlen = CENNAM(cen);
0N/A elen = CENEXT(cen);
0N/A clen = CENCOM(cen);
0N/A ze->time = CENTIM(cen);
0N/A ze->size = CENLEN(cen);
0N/A ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen);
0N/A ze->crc = CENCRC(cen);
1032N/A locoff = CENOFF(cen);
1032N/A ze->pos = -(zip->locpos + locoff);
1107N/A ze->flag = CENFLG(cen);
0N/A
0N/A if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
0N/A memcpy(ze->name, cen + CENHDR, nlen);
0N/A ze->name[nlen] = '\0';
0N/A if (elen > 0) {
1032N/A char *extra = cen + CENHDR + nlen;
1032N/A
0N/A /* This entry has "extra" data */
0N/A if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch;
0N/A ze->extra[0] = (unsigned char) elen;
0N/A ze->extra[1] = (unsigned char) (elen >> 8);
1032N/A memcpy(ze->extra+2, extra, elen);
1032N/A if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL ||
1032N/A locoff == ZIP64_MAGICVAL) {
1032N/A jint off = 0;
1032N/A while ((off + 4) < elen) { // spec: HeaderID+DataSize+Data
1032N/A jint sz = SH(extra, off + 2);
1032N/A if (SH(extra, off) == ZIP64_EXTID) {
1032N/A off += 4;
1032N/A if (ze->size == ZIP64_MAGICVAL) {
1032N/A // if invalid zip64 extra fields, just skip
1032N/A if (sz < 8 || (off + 8) > elen)
1032N/A break;
1032N/A ze->size = LL(extra, off);
1032N/A sz -= 8;
1032N/A off += 8;
1032N/A }
1032N/A if (ze->csize == ZIP64_MAGICVAL) {
1032N/A if (sz < 8 || (off + 8) > elen)
1032N/A break;
1032N/A ze->csize = LL(extra, off);
1032N/A sz -= 8;
1032N/A off += 8;
1032N/A }
1032N/A if (locoff == ZIP64_MAGICVAL) {
1032N/A if (sz < 8 || (off + 8) > elen)
1032N/A break;
1032N/A ze->pos = -(zip->locpos + LL(extra, off));
1032N/A sz -= 8;
1032N/A off += 8;
1032N/A }
1032N/A break;
1032N/A }
1032N/A off += (sz + 4);
1032N/A }
1032N/A }
0N/A }
0N/A
0N/A if (clen > 0) {
0N/A /* This entry has a comment */
0N/A if ((ze->comment = malloc(clen + 1)) == NULL) goto Catch;
0N/A memcpy(ze->comment, cen + CENHDR + nlen + elen, clen);
0N/A ze->comment[clen] = '\0';
0N/A }
0N/A goto Finally;
0N/A
0N/A Catch:
0N/A free(ze->name);
0N/A free(ze->extra);
0N/A free(ze->comment);
0N/A free(ze);
0N/A ze = NULL;
0N/A
0N/A Finally:
2227N/A#ifdef USE_MMAP
2227N/A if (!zip->usemmap)
0N/A#endif
2227N/A if (cen != NULL && accessHint == ACCESS_RANDOM) free(cen);
0N/A return ze;
0N/A}
0N/A
0N/A/*
0N/A * Free the given jzentry.
0N/A * In fact we maintain a one-entry cache of the most recently used
0N/A * jzentry for each zip. This optimizes a common access pattern.
0N/A */
0N/A
0N/Avoid
0N/AZIP_FreeEntry(jzfile *jz, jzentry *ze)
0N/A{
0N/A jzentry *last;
0N/A ZIP_Lock(jz);
0N/A last = jz->cache;
0N/A jz->cache = ze;
0N/A ZIP_Unlock(jz);
0N/A if (last != NULL) {
0N/A /* Free the previously cached jzentry */
0N/A free(last->name);
0N/A if (last->extra) free(last->extra);
0N/A if (last->comment) free(last->comment);
0N/A free(last);
0N/A }
0N/A}
0N/A
0N/A/*
0N/A * Returns the zip entry corresponding to the specified name, or
0N/A * NULL if not found.
0N/A */
0N/Ajzentry *
0N/AZIP_GetEntry(jzfile *zip, char *name, jint ulen)
0N/A{
0N/A unsigned int hsh = hash(name);
575N/A jint idx;
575N/A jzentry *ze = 0;
0N/A
0N/A ZIP_Lock(zip);
575N/A if (zip->total == 0) {
575N/A goto Finally;
575N/A }
575N/A
575N/A idx = zip->table[hsh % zip->tablelen];
0N/A
0N/A /*
0N/A * This while loop is an optimization where a double lookup
0N/A * for name and name+/ is being performed. The name char
0N/A * array has enough room at the end to try again with a
0N/A * slash appended if the first table lookup does not succeed.
0N/A */
0N/A while(1) {
0N/A
0N/A /* Check the cached entry first */
0N/A ze = zip->cache;
0N/A if (ze && strcmp(ze->name,name) == 0) {
0N/A /* Cache hit! Remove and return the cached entry. */
0N/A zip->cache = 0;
0N/A ZIP_Unlock(zip);
0N/A return ze;
0N/A }
0N/A ze = 0;
0N/A
0N/A /*
0N/A * Search down the target hash chain for a cell whose
0N/A * 32 bit hash matches the hashed name.
0N/A */
0N/A while (idx != ZIP_ENDCHAIN) {
0N/A jzcell *zc = &zip->entries[idx];
0N/A
0N/A if (zc->hash == hsh) {
0N/A /*
0N/A * OK, we've found a ZIP entry whose 32 bit hashcode
0N/A * matches the name we're looking for. Try to read
0N/A * its entry information from the CEN. If the CEN
0N/A * name matches the name we're looking for, we're
0N/A * done.
0N/A * If the names don't match (which should be very rare)
0N/A * we keep searching.
0N/A */
0N/A ze = newEntry(zip, zc, ACCESS_RANDOM);
0N/A if (ze && strcmp(ze->name, name)==0) {
0N/A break;
0N/A }
0N/A if (ze != 0) {
0N/A /* We need to release the lock across the free call */
0N/A ZIP_Unlock(zip);
0N/A ZIP_FreeEntry(zip, ze);
0N/A ZIP_Lock(zip);
0N/A }
0N/A ze = 0;
0N/A }
0N/A idx = zc->next;
0N/A }
0N/A
0N/A /* Entry found, return it */
0N/A if (ze != 0) {
0N/A break;
0N/A }
0N/A
0N/A /* If no real length was passed in, we are done */
0N/A if (ulen == 0) {
0N/A break;
0N/A }
0N/A
0N/A /* Slash is already there? */
0N/A if (name[ulen-1] == '/') {
0N/A break;
0N/A }
0N/A
0N/A /* Add slash and try once more */
0N/A name[ulen] = '/';
0N/A name[ulen+1] = '\0';
0N/A hsh = hash_append(hsh, '/');
0N/A idx = zip->table[hsh % zip->tablelen];
0N/A ulen = 0;
0N/A }
0N/A
575N/AFinally:
0N/A ZIP_Unlock(zip);
0N/A return ze;
0N/A}
0N/A
0N/A/*
0N/A * Returns the n'th (starting at zero) zip file entry, or NULL if the
0N/A * specified index was out of range.
0N/A */
0N/Ajzentry * JNICALL
0N/AZIP_GetNextEntry(jzfile *zip, jint n)
0N/A{
0N/A jzentry *result;
0N/A if (n < 0 || n >= zip->total) {
0N/A return 0;
0N/A }
0N/A ZIP_Lock(zip);
0N/A result = newEntry(zip, &zip->entries[n], ACCESS_SEQUENTIAL);
0N/A ZIP_Unlock(zip);
0N/A return result;
0N/A}
0N/A
0N/A/*
0N/A * Locks the specified zip file for reading.
0N/A */
0N/Avoid
0N/AZIP_Lock(jzfile *zip)
0N/A{
0N/A MLOCK(zip->lock);
0N/A}
0N/A
0N/A/*
0N/A * Unlocks the specified zip file.
0N/A */
0N/Avoid
0N/AZIP_Unlock(jzfile *zip)
0N/A{
0N/A MUNLOCK(zip->lock);
0N/A}
0N/A
0N/A/*
0N/A * Returns the offset of the entry data within the zip file.
0N/A * Returns -1 if an error occurred, in which case zip->msg will
0N/A * contain the error text.
0N/A */
0N/Ajlong
0N/AZIP_GetEntryDataOffset(jzfile *zip, jzentry *entry)
0N/A{
0N/A /* The Zip file spec explicitly allows the LOC extra data size to
0N/A * be different from the CEN extra data size, although the JDK
0N/A * never creates such zip files. Since we cannot trust the CEN
0N/A * extra data size, we need to read the LOC to determine the entry
0N/A * data offset. We do this lazily to avoid touching the virtual
0N/A * memory page containing the LOC when initializing jzentry
0N/A * objects. (This speeds up javac by a factor of 10 when the JDK
0N/A * is installed on a very slow filesystem.)
0N/A */
0N/A if (entry->pos <= 0) {
0N/A unsigned char loc[LOCHDR];
0N/A if (readFullyAt(zip->zfd, loc, LOCHDR, -(entry->pos)) == -1) {
0N/A zip->msg = "error reading zip file";
0N/A return -1;
0N/A }
0N/A if (GETSIG(loc) != LOCSIG) {
0N/A zip->msg = "invalid LOC header (bad signature)";
0N/A return -1;
0N/A }
0N/A entry->pos = (- entry->pos) + LOCHDR + LOCNAM(loc) + LOCEXT(loc);
0N/A }
0N/A return entry->pos;
0N/A}
0N/A
0N/A/*
0N/A * Reads bytes from the specified zip entry. Assumes that the zip
0N/A * file had been previously locked with ZIP_Lock(). Returns the
0N/A * number of bytes read, or -1 if an error occurred. If zip->msg != 0
0N/A * then a zip error occurred and zip->msg contains the error text.
3115N/A *
3115N/A * The current implementation does not support reading an entry that
3115N/A * has the size bigger than 2**32 bytes in ONE invocation.
0N/A */
0N/Ajint
0N/AZIP_Read(jzfile *zip, jzentry *entry, jlong pos, void *buf, jint len)
0N/A{
0N/A jlong entry_size = (entry->csize != 0) ? entry->csize : entry->size;
0N/A jlong start;
0N/A
0N/A /* Clear previous zip error */
0N/A zip->msg = NULL;
0N/A
0N/A /* Check specified position */
0N/A if (pos < 0 || pos > entry_size - 1) {
0N/A zip->msg = "ZIP_Read: specified offset out of range";
0N/A return -1;
0N/A }
0N/A
0N/A /* Check specified length */
0N/A if (len <= 0)
0N/A return 0;
0N/A if (len > entry_size - pos)
3115N/A len = (jint)(entry_size - pos);
0N/A
0N/A /* Get file offset to start reading data */
0N/A start = ZIP_GetEntryDataOffset(zip, entry);
0N/A if (start < 0)
0N/A return -1;
0N/A start += pos;
0N/A
0N/A if (start + len > zip->len) {
0N/A zip->msg = "ZIP_Read: corrupt zip file: invalid entry size";
0N/A return -1;
0N/A }
0N/A
0N/A if (readFullyAt(zip->zfd, buf, len, start) == -1) {
0N/A zip->msg = "ZIP_Read: error reading zip file";
0N/A return -1;
0N/A }
0N/A return len;
0N/A}
0N/A
0N/A
0N/A/* The maximum size of a stack-allocated buffer.
0N/A */
0N/A#define BUF_SIZE 4096
0N/A
0N/A/*
0N/A * This function is used by the runtime system to load compressed entries
0N/A * from ZIP/JAR files specified in the class path. It is defined here
0N/A * so that it can be dynamically loaded by the runtime if the zip library
0N/A * is found.
3115N/A *
3115N/A * The current implementation does not support reading an entry that
3115N/A * has the size bigger than 2**32 bytes in ONE invocation.
0N/A */
0N/Ajboolean
0N/AInflateFully(jzfile *zip, jzentry *entry, void *buf, char **msg)
0N/A{
0N/A z_stream strm;
0N/A char tmp[BUF_SIZE];
0N/A jlong pos = 0;
0N/A jlong count = entry->csize;
0N/A
0N/A *msg = 0; /* Reset error message */
0N/A
0N/A if (count == 0) {
0N/A *msg = "inflateFully: entry not compressed";
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A memset(&strm, 0, sizeof(z_stream));
0N/A if (inflateInit2(&strm, -MAX_WBITS) != Z_OK) {
0N/A *msg = strm.msg;
0N/A return JNI_FALSE;
0N/A }
0N/A
0N/A strm.next_out = buf;
3115N/A strm.avail_out = (uInt)entry->size;
0N/A
0N/A while (count > 0) {
3115N/A jint n = count > (jlong)sizeof(tmp) ? (jint)sizeof(tmp) : (jint)count;
0N/A ZIP_Lock(zip);
0N/A n = ZIP_Read(zip, entry, pos, tmp, n);
0N/A ZIP_Unlock(zip);
0N/A if (n <= 0) {
0N/A if (n == 0) {
0N/A *msg = "inflateFully: Unexpected end of file";
0N/A }
0N/A inflateEnd(&strm);
0N/A return JNI_FALSE;
0N/A }
0N/A pos += n;
0N/A count -= n;
0N/A strm.next_in = (Bytef *)tmp;
0N/A strm.avail_in = n;
0N/A do {
0N/A switch (inflate(&strm, Z_PARTIAL_FLUSH)) {
0N/A case Z_OK:
0N/A break;
0N/A case Z_STREAM_END:
0N/A if (count != 0 || strm.total_out != entry->size) {
0N/A *msg = "inflateFully: Unexpected end of stream";
0N/A inflateEnd(&strm);
0N/A return JNI_FALSE;
0N/A }
0N/A break;
0N/A default:
0N/A break;
0N/A }
0N/A } while (strm.avail_in > 0);
0N/A }
0N/A inflateEnd(&strm);
0N/A return JNI_TRUE;
0N/A}
0N/A
3115N/A/*
3115N/A * The current implementation does not support reading an entry that
3115N/A * has the size bigger than 2**32 bytes in ONE invocation.
3115N/A */
0N/Ajzentry * JNICALL
0N/AZIP_FindEntry(jzfile *zip, char *name, jint *sizeP, jint *nameLenP)
0N/A{
0N/A jzentry *entry = ZIP_GetEntry(zip, name, 0);
0N/A if (entry) {
3115N/A *sizeP = (jint)entry->size;
0N/A *nameLenP = strlen(entry->name);
0N/A }
0N/A return entry;
0N/A}
0N/A
0N/A/*
0N/A * Reads a zip file entry into the specified byte array
0N/A * When the method completes, it releases the jzentry.
0N/A * Note: this is called from the separately delivered VM (hotspot/classic)
0N/A * so we have to be careful to maintain the expected behaviour.
0N/A */
0N/Ajboolean JNICALL
0N/AZIP_ReadEntry(jzfile *zip, jzentry *entry, unsigned char *buf, char *entryname)
0N/A{
0N/A char *msg;
0N/A
0N/A strcpy(entryname, entry->name);
0N/A if (entry->csize == 0) {
0N/A /* Entry is stored */
0N/A jlong pos = 0;
0N/A jlong size = entry->size;
0N/A while (pos < size) {
0N/A jint n;
0N/A jlong limit = ((((jlong) 1) << 31) - 1);
0N/A jint count = (size - pos < limit) ?
0N/A /* These casts suppress a VC++ Internal Compiler Error */
0N/A (jint) (size - pos) :
0N/A (jint) limit;
0N/A ZIP_Lock(zip);
0N/A n = ZIP_Read(zip, entry, pos, buf, count);
0N/A msg = zip->msg;
0N/A ZIP_Unlock(zip);
0N/A if (n == -1) {
0N/A jio_fprintf(stderr, "%s: %s\n", zip->name,
0N/A msg != 0 ? msg : strerror(errno));
0N/A return JNI_FALSE;
0N/A }
0N/A buf += n;
0N/A pos += n;
0N/A }
0N/A } else {
0N/A /* Entry is compressed */
0N/A int ok = InflateFully(zip, entry, buf, &msg);
0N/A if (!ok) {
0N/A if ((msg == NULL) || (*msg == 0)) {
0N/A msg = zip->msg;
0N/A }
0N/A jio_fprintf(stderr, "%s: %s\n", zip->name,
0N/A msg != 0 ? msg : strerror(errno));
0N/A return JNI_FALSE;
0N/A }
0N/A }
0N/A
0N/A ZIP_FreeEntry(zip, entry);
0N/A
0N/A return JNI_TRUE;
0N/A}