--- old/src/java.base/share/classes/java/util/jar/JarFile.java 2015-05-22 11:32:38.079612786 -0700 +++ new/src/java.base/share/classes/java/util/jar/JarFile.java 2015-05-22 11:32:37.951612790 -0700 @@ -34,10 +34,7 @@ import java.util.zip.*; import java.security.CodeSigner; import java.security.cert.Certificate; -import java.security.AccessController; import java.security.CodeSource; -import sun.misc.IOUtils; -import sun.security.action.GetPropertyAction; import sun.security.util.ManifestEntryVerifier; import sun.misc.SharedSecrets; import sun.security.util.SignatureFileVerifier; @@ -85,6 +82,9 @@ SharedSecrets.setJavaUtilJarAccess(new JavaUtilJarAccessImpl()); } + private static final sun.misc.JavaUtilZipFileAccess zipAccess + = sun.misc.SharedSecrets.getJavaUtilZipFileAccess(); + /** * The JAR manifest file name. */ @@ -185,18 +185,14 @@ if (man == null) { - JarEntry manEntry = getManEntry(); + JarEntry entry = getManEntry(); // If found then load the manifest - if (manEntry != null) { - if (verify) { - byte[] b = getBytes(manEntry); - man = new Manifest(new ByteArrayInputStream(b)); - if (!jvInitialized) { - jv = new JarVerifier(b); - } - } else { - man = new Manifest(super.getInputStream(manEntry)); + if (entry != null) { + byte[] b = zipAccess.getBytes(this, entry); + man = new Manifest(new ByteArrayInputStream(b)); + if (verify && !jvInitialized) { + jv = new JarVerifier(b); } manRef = new SoftReference<>(man); } @@ -376,7 +372,7 @@ mev = new ManifestEntryVerifier (getManifestFromReference()); } - byte[] b = getBytes(e); + byte[] b = zipAccess.getBytes(this, e); if (b != null && b.length > 0) { jv.beginEntry(e, mev); jv.update(b.length, b, 0, b.length, mev); @@ -416,16 +412,6 @@ } } - /* - * Reads all the bytes for a given entry. Used to process the - * META-INF files. - */ - private byte[] getBytes(ZipEntry ze) throws IOException { - try (InputStream is = super.getInputStream(ze)) { - return IOUtils.readFully(is, (int)ze.getSize(), true); - } - } - /** * Returns an input stream for reading the contents of the specified * zip file entry. @@ -548,9 +534,9 @@ */ private void checkForSpecialAttributes() throws IOException { if (hasCheckedSpecialAttributes) return; - JarEntry manEntry = getManEntry(); - if (manEntry != null) { - byte[] b = getBytes(manEntry); + JarEntry entry = getManEntry(); + if (entry != null) { + byte[] b = zipAccess.getBytes(this, entry); if (match(CLASSPATH_CHARS, b, CLASSPATH_LASTOCC, CLASSPATH_OPTOSFT)) hasClassPathAttribute = true; } --- old/src/java.base/share/classes/java/util/zip/ZipFile.java 2015-05-22 11:32:38.471612772 -0700 +++ new/src/java.base/share/classes/java/util/zip/ZipFile.java 2015-05-22 11:32:38.347612776 -0700 @@ -25,6 +25,7 @@ package java.util.zip; +import java.io.ByteArrayInputStream; import java.io.Closeable; import java.io.InputStream; import java.io.IOException; @@ -67,6 +68,9 @@ private static final int STORED = ZipEntry.STORED; private static final int DEFLATED = ZipEntry.DEFLATED; + // Max buffer size when returning bytebuffers directly + private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; + /** * Mode flag to open a zip file for reading. */ @@ -345,8 +349,9 @@ if (entry == null) { throw new NullPointerException("entry"); } - long jzentry = 0; - ZipFileInputStream in = null; + + long jzentry, csize, size; + int cmethod; synchronized (this) { ensureOpen(); if (!zc.isUTF8() && (entry.flag & EFS) != 0) { @@ -357,30 +362,46 @@ if (jzentry == 0) { return null; } - in = new ZipFileInputStream(jzentry); + size = getEntrySize(jzentry); + csize = getEntryCSize(jzentry); + cmethod = getEntryMethod(jzentry); + } - switch (getEntryMethod(jzentry)) { - case STORED: - synchronized (streams) { - streams.put(in, null); - } - return in; - case DEFLATED: - // MORE: Compute good size for inflater stream: - long size = getEntrySize(jzentry) + 2; // Inflater likes a bit of slack - if (size > 65536) size = 8192; - if (size <= 0) size = 4096; - Inflater inf = getInflater(); - InputStream is = - new ZipFileInflaterInputStream(in, inf, (int)size); - synchronized (streams) { - streams.put(is, inf); + if (csize >= 0 && size > 0 && size < 128 * 1024) { + try { + return new ByteArrayInputStream(getBytes(jzentry, csize, size, cmethod)); + } finally { + synchronized (this) { + if (jzfile != 0) { + freeEntry(jzfile, jzentry); + } } - return is; - default: - throw new ZipException("invalid compression method"); } } + + ZipFileInputStream in = new ZipFileInputStream(jzentry, csize, size); + + switch (cmethod) { + case STORED: + synchronized (streams) { + streams.put(in, null); + } + return in; + case DEFLATED: + // MORE: Compute good size for inflater stream: + size += 2; // Inflater likes a bit of slack + if (size > 65536) size = 8192; + if (size <= 0) size = 4096; + Inflater inf = getInflater(); + InputStream is = + new ZipFileInflaterInputStream(in, inf, (int)size); + synchronized (streams) { + streams.put(is, inf); + } + return is; + default: + throw new ZipException("invalid compression method"); + } } private class ZipFileInflaterInputStream extends InflaterInputStream { @@ -469,6 +490,103 @@ // List of available Inflater objects for decompression private Deque inflaterCache = new ArrayDeque<>(); + /* + * Uncompress the zip entry into a new byte[]. + * + * This method can only read entries smaller than 2GB, for larger entries + * use getInputStream. + * + * @param entry the zip file entry + * + * @return the byte[] with the deflated contents of the specified zip file entry. + * + * @throws ZipException if a ZIP format error has occurred + * @throws IOException if an I/O error has occurred + * @throws IllegalStateException if the zip file has been closed + * @throws OutOfMemory if the zip entry is larger than 2GB + */ + private byte[] getBytes(ZipEntry entry) throws IOException { + if (entry == null) { + throw new NullPointerException("entry"); + } + + long jzentry, csize, size; + int cmethod; + synchronized (this) { + ensureOpen(); + if (!zc.isUTF8() && (entry.flag & EFS) != 0) { + jzentry = getEntry(jzfile, zc.getBytesUTF8(entry.name), false); + } else { + jzentry = getEntry(jzfile, zc.getBytes(entry.name), false); + } + if (jzentry == 0) { + return null; + } + + csize = getEntryCSize(jzentry); + size = getEntrySize(jzentry); + cmethod = getEntryMethod(jzentry); + } + + try { + return getBytes(jzentry, csize, size, cmethod); + } finally { + synchronized (this) { + if (jzfile != 0) { + freeEntry(jzfile, jzentry); + } + } + } + } + + private byte[] getBytes(long jzentry, long csize, long size, int cmethod) + throws IOException { + + if (csize < 0 || size < 0) { + throw new ZipException("Unknown size of ZipEntry"); + } + if (csize > MAX_BUFFER_SIZE || size > MAX_BUFFER_SIZE) { + throw new OutOfMemoryError("ZipEntry too large"); + } + + byte[] cbytes = new byte[(int) csize]; + readCompressedBytes(jzentry, (int) csize, cbytes); + switch (cmethod) { + case STORED: + return cbytes; + case DEFLATED: + byte[] bytes = new byte[(int) size]; + Inflater inf = getInflater(); + try { + inf.setInput(cbytes); + inf.inflate(bytes); + } catch (DataFormatException e) { + String s = e.getMessage(); + throw new ZipException(s != null ? s : "Invalid ZLIB data format"); + } finally { + releaseInflater(inf); + } + return bytes; + default: + throw new ZipException("invalid compression method"); + } + } + + private int readCompressedBytes(long jzentry, int csize, byte[] buf) + throws IOException { + assert csize == buf.length; + synchronized (this) { + ensureOpenOrZipException(); + int n = 0; + int nread = 0; + while (nread < csize + && (n = read(jzfile, jzentry, nread, buf, nread, csize - nread)) > 0) { + nread += n; + } + return nread; + } + } + /** * Returns the path name of the ZIP file. * @return the path name of the ZIP file @@ -691,10 +809,10 @@ protected long rem; // number of remaining bytes within entry protected long size; // uncompressed size of this entry - ZipFileInputStream(long jzentry) { + ZipFileInputStream(long jzentry, long csize, long size) { pos = 0; - rem = getEntryCSize(jzentry); - size = getEntrySize(jzentry); + rem = csize; + this.size = size; this.jzentry = jzentry; } @@ -779,9 +897,16 @@ static { sun.misc.SharedSecrets.setJavaUtilZipFileAccess( new sun.misc.JavaUtilZipFileAccess() { + @Override public boolean startsWithLocHeader(ZipFile zip) { return zip.startsWithLocHeader(); } + + @Override + public byte[] getBytes(ZipFile zip, ZipEntry entry) + throws IOException { + return zip.getBytes(entry); + } } ); } --- old/src/java.base/share/classes/sun/misc/JavaUtilZipFileAccess.java 2015-05-22 11:32:38.871612758 -0700 +++ new/src/java.base/share/classes/sun/misc/JavaUtilZipFileAccess.java 2015-05-22 11:32:38.747612762 -0700 @@ -25,9 +25,12 @@ package sun.misc; +import java.io.IOException; +import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public interface JavaUtilZipFileAccess { public boolean startsWithLocHeader(ZipFile zip); + public byte[] getBytes(ZipFile zip, ZipEntry entry) throws IOException; } --- old/src/java.base/share/classes/sun/misc/URLClassPath.java 2015-05-22 11:32:39.255612745 -0700 +++ new/src/java.base/share/classes/sun/misc/URLClassPath.java 2015-05-22 11:32:39.131612749 -0700 @@ -39,6 +39,7 @@ import java.net.URLConnection; import java.net.URLStreamHandler; import java.net.URLStreamHandlerFactory; +import java.nio.ByteBuffer; import java.security.AccessControlException; import java.security.AccessController; import java.security.CodeSigner; @@ -799,6 +800,13 @@ public URL getCodeSourceURL() { return csu; } public InputStream getInputStream() throws IOException { return jar.getInputStream(entry); } + @Override + public ByteBuffer getByteBuffer() throws IOException + { return ByteBuffer.wrap(getBytes()); } + @Override + public byte[] getBytes() throws IOException { + return zipAccess.getBytes(jar, entry); + } public int getContentLength() { return (int)entry.getSize(); } public Manifest getManifest() throws IOException