diff --git a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java index 195b8a8..da93b18 100644 --- a/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java +++ b/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java @@ -179,7 +178,7 @@ public abstract class AbstractArchiveResourceSet extends AbstractResourceSet { * @return The archives entries mapped to their names or null if * {@link #getArchiveEntry(String)} should be used. */ - protected abstract HashMap getArchiveEntries(boolean single); + protected abstract Map getArchiveEntries(boolean single); /** diff --git a/java/org/apache/catalina/webresources/AbstractSingleArchiveResourceSet.java b/java/org/apache/catalina/webresources/AbstractSingleArchiveResourceSet.java index 2257803..0d86ff9 100644 --- a/java/org/apache/catalina/webresources/AbstractSingleArchiveResourceSet.java +++ b/java/org/apache/catalina/webresources/AbstractSingleArchiveResourceSet.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.MalformedURLException; import java.util.Enumeration; import java.util.HashMap; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -59,7 +60,7 @@ public abstract class AbstractSingleArchiveResourceSet extends AbstractArchiveRe @Override - protected HashMap getArchiveEntries(boolean single) { + protected Map getArchiveEntries(boolean single) { synchronized (archiveLock) { if (archiveEntries == null && !single) { JarFile jarFile = null; diff --git a/java/org/apache/catalina/webresources/JarWarResource.java b/java/org/apache/catalina/webresources/JarWarResource.java index 90321ee..729197f 100644 --- a/java/org/apache/catalina/webresources/JarWarResource.java +++ b/java/org/apache/catalina/webresources/JarWarResource.java @@ -21,6 +21,8 @@ import java.io.InputStream; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipEntry; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -47,26 +49,23 @@ public class JarWarResource extends AbstractArchiveResource { @Override protected JarInputStreamWrapper getJarInputStreamWrapper() { - JarFile warFile = null; - JarInputStream jarIs = null; - JarEntry entry = null; try { - warFile = getArchiveResourceSet().openJarFile(); - JarEntry jarFileInWar = warFile.getJarEntry(archivePath); - InputStream isInWar = warFile.getInputStream(jarFileInWar); - - jarIs = new JarInputStream(isInWar); - entry = jarIs.getNextJarEntry(); - while (entry != null && - !entry.getName().equals(getResource().getName())) { - entry = jarIs.getNextJarEntry(); - } - + JarEntry entry = getArchiveResourceSet().getArchiveEntries(false).get(getResource().getName()); if (entry == null) { return null; } - return new JarInputStreamWrapper(entry, jarIs); + JarFile warFile = getArchiveResourceSet().openJarFile(); + JarEntry jarFileInWar = warFile.getJarEntry(archivePath); + + Long skipPos = ((JarWarResourceSet)getArchiveResourceSet()).getArchiveEntryPositions().get(getResource().getName()); + InputStream isInWar = warFile.getInputStream(jarFileInWar); + isInWar.skip(skipPos); + + /* entry exists and was alread verified */ + ZipInputStream zipIs = new ZipInputStream(isInWar); + ZipEntry ze = zipIs.getNextEntry(); + return new JarInputStreamWrapper(entry, zipIs); } catch (IOException e) { if (log.isDebugEnabled()) { log.debug(sm.getString("jarResource.getInputStreamFail", @@ -74,18 +73,6 @@ public class JarWarResource extends AbstractArchiveResource { } return null; } finally { - if (entry == null) { - if (jarIs != null) { - try { - jarIs.close(); - } catch (IOException ioe) { - // Ignore - } - } - if (warFile != null) { - getArchiveResourceSet().closeJarFile(); - } - } } } diff --git a/java/org/apache/catalina/webresources/JarWarResourceSet.java b/java/org/apache/catalina/webresources/JarWarResourceSet.java index 7f16b63..544ece0 100644 --- a/java/org/apache/catalina/webresources/JarWarResourceSet.java +++ b/java/org/apache/catalina/webresources/JarWarResourceSet.java @@ -19,12 +19,18 @@ package org.apache.catalina.webresources; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.FilterInputStream; +import java.io.PushbackInputStream; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipEntry; import java.net.MalformedURLException; import java.util.HashMap; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.Manifest; +import java.util.Collections; import org.apache.catalina.LifecycleException; import org.apache.catalina.WebResource; @@ -39,6 +45,7 @@ import org.apache.tomcat.util.buf.UriUtil; public class JarWarResourceSet extends AbstractArchiveResourceSet { private final String archivePath; + private Map archiveEntryPositions; /** * Creates a new {@link org.apache.catalina.WebResourceSet} based on a JAR @@ -94,18 +101,20 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { * returned. */ @Override - protected HashMap getArchiveEntries(boolean single) { + protected Map getArchiveEntries(boolean single) { synchronized (archiveLock) { if (archiveEntries == null) { JarFile warFile = null; - InputStream jarFileIs = null; archiveEntries = new HashMap<>(); + archiveEntryPositions = new HashMap<>(); + try { warFile = openJarFile(); JarEntry jarFileInWar = warFile.getJarEntry(archivePath); - jarFileIs = warFile.getInputStream(jarFileInWar); - try (JarInputStream jarIs = new JarInputStream(jarFileIs)) { + /* process and validate the JAR file */ + try (InputStream jarFileIs = warFile.getInputStream(jarFileInWar); + JarInputStream jarIs = new JarInputStream(jarFileIs)) { JarEntry entry = jarIs.getNextJarEntry(); while (entry != null) { archiveEntries.put(entry.getName(), entry); @@ -113,6 +122,22 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { } setManifest(jarIs.getManifest()); } + + /* process again, this time with positions of PK entries */ + try (InputStream jarFileIs = warFile.getInputStream(jarFileInWar); + ZipInputStreamWithPosition zipIs = new ZipInputStreamWithPosition(jarFileIs)) { + long posBefore = zipIs.getBytesRead(); + ZipEntry entry = zipIs.getNextEntry(); + while (entry != null) { + if(archiveEntries.containsKey(entry.getName())) { + archiveEntryPositions.put(entry.getName(), posBefore); + } + zipIs.closeEntry(); + + posBefore = zipIs.getBytesRead(); + entry = zipIs.getNextEntry(); + } + } } catch (IOException ioe) { // Should never happen archiveEntries = null; @@ -121,16 +146,9 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { if (warFile != null) { closeJarFile(); } - if (jarFileIs != null) { - try { - jarFileIs.close(); - } catch (IOException e) { - // Ignore - } - } } } - return archiveEntries; + return Collections.unmodifiableMap(archiveEntries); } } @@ -146,6 +164,9 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { throw new IllegalStateException("Coding error"); } + public Map getArchiveEntryPositions() { + return Collections.unmodifiableMap(archiveEntryPositions); + } //-------------------------------------------------------- Lifecycle methods @Override @@ -169,3 +190,84 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet { } } } + +class ZipInputStreamWithPosition extends ZipInputStream { + + public ZipInputStreamWithPosition(final InputStream in) throws IOException { + super(in); + this.in = new PushbackCountingInputStream(in, 512); + } + + /** + * Returns the current number of bytes read from this stream. + * @return the number of read bytes + */ + public long getBytesRead() { + return ((PushbackCountingInputStream)in).getBytesRead(); + } +} + +/** + * Stream that tracks the number of bytes read. + * @NotThreadSafe + */ +class PushbackCountingInputStream extends PushbackInputStream { + private long bytesRead; + + public PushbackCountingInputStream(final InputStream in, int size) { + super(in, size); + } + + @Override + public int read() throws IOException { + final int r = super.read(); + if (r >= 0) { + count(1); + } + return r; + } + @Override + public int read(final byte[] b) throws IOException { + return read(b, 0, b.length); + } + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + final int r = super.read(b, off, len); + if (r >= 0) { + count(r); + } + return r; + } + /** + * Increments the counter of already read bytes. + * Doesn't increment if the EOF has been hit (read == -1) + * + * @param read the number of bytes read + */ + protected final void count(final long read) { + if (read != -1) { + bytesRead += read; + } + } + + /** + * Returns the current number of bytes read from this stream. + * @return the number of read bytes + */ + public long getBytesRead() { + return bytesRead; + } + + @Override + public void unread(int b) throws IOException { + super.unread(b); + bytesRead--; + } + + @Override + public void unread(byte[] b, int off, int len) throws IOException { + super.unread(b,off,len); + bytesRead -= len; + } +}