diff options
Diffstat (limited to 'luni/src/main/java/java/util/zip/ZipFile.java')
-rw-r--r-- | luni/src/main/java/java/util/zip/ZipFile.java | 95 |
1 files changed, 71 insertions, 24 deletions
diff --git a/luni/src/main/java/java/util/zip/ZipFile.java b/luni/src/main/java/java/util/zip/ZipFile.java index b44156e..307e7fe 100644 --- a/luni/src/main/java/java/util/zip/ZipFile.java +++ b/luni/src/main/java/java/util/zip/ZipFile.java @@ -107,6 +107,18 @@ public class ZipFile implements Closeable, ZipConstants { private final CloseGuard guard = CloseGuard.get(); + static class EocdRecord { + final long numEntries; + final long centralDirOffset; + final int commentLength; + + EocdRecord(long numEntries, long centralDirOffset, int commentLength) { + this.numEntries = numEntries; + this.centralDirOffset = centralDirOffset; + this.commentLength = commentLength; + } + } + /** * Constructs a new {@code ZipFile} allowing read access to the contents of the given file. * @@ -390,9 +402,11 @@ public class ZipFile implements Closeable, ZipConstants { stopOffset = 0; } + long eocdOffset; while (true) { raf.seek(scanOffset); if (Integer.reverseBytes(raf.readInt()) == ENDSIG) { + eocdOffset = scanOffset; break; } @@ -402,41 +416,35 @@ public class ZipFile implements Closeable, ZipConstants { } } - // Read the End Of Central Directory. ENDHDR includes the signature bytes, - // which we've already read. - byte[] eocd = new byte[ENDHDR - 4]; - raf.readFully(eocd); + final long zip64EocdRecordOffset = Zip64.parseZip64EocdRecordLocator(raf, eocdOffset); - // Pull out the information we need. - BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN); - int diskNumber = it.readShort() & 0xffff; - int diskWithCentralDir = it.readShort() & 0xffff; - int numEntries = it.readShort() & 0xffff; - int totalNumEntries = it.readShort() & 0xffff; - it.skip(4); // Ignore centralDirSize. - long centralDirOffset = ((long) it.readInt()) & 0xffffffffL; - int commentLength = it.readShort() & 0xffff; - - if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { - throw new ZipException("Spanned archives not supported"); - } - - if (commentLength > 0) { - byte[] commentBytes = new byte[commentLength]; + // Seek back past the eocd signature so that we can continue with our search. + // Note that we add 4 bytes to the offset to skip past the signature. + EocdRecord record = parseEocdRecord(raf, eocdOffset + 4, (zip64EocdRecordOffset != -1) /* isZip64 */); + // Read the comment now to avoid an additional seek. We also know the commentLength + // won't change because that information isn't present in the zip64 eocd record. + if (record.commentLength > 0) { + byte[] commentBytes = new byte[record.commentLength]; raf.readFully(commentBytes); comment = new String(commentBytes, 0, commentBytes.length, StandardCharsets.UTF_8); } + // We have a zip64 eocd record : use that for getting the information we need. + if (zip64EocdRecordOffset != -1) { + record = Zip64.parseZip64EocdRecord(raf, zip64EocdRecordOffset, record.commentLength); + } + // Seek to the first CDE and read all entries. // We have to do this now (from the constructor) rather than lazily because the // public API doesn't allow us to throw IOException except from the constructor // or from getInputStream. - RAFStream rafStream = new RAFStream(raf, centralDirOffset); + RAFStream rafStream = new RAFStream(raf, record.centralDirOffset); BufferedInputStream bufferedStream = new BufferedInputStream(rafStream, 4096); byte[] hdrBuf = new byte[CENHDR]; // Reuse the same buffer for each entry. - for (int i = 0; i < numEntries; ++i) { - ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8); - if (newEntry.localHeaderRelOffset >= centralDirOffset) { + for (long i = 0; i < record.numEntries; ++i) { + ZipEntry newEntry = new ZipEntry(hdrBuf, bufferedStream, StandardCharsets.UTF_8, + (zip64EocdRecordOffset != -1) /* isZip64 */); + if (newEntry.localHeaderRelOffset >= record.centralDirOffset) { throw new ZipException("Local file header offset is after central directory"); } String entryName = newEntry.getName(); @@ -446,6 +454,45 @@ public class ZipFile implements Closeable, ZipConstants { } } + private static EocdRecord parseEocdRecord(RandomAccessFile raf, long offset, boolean isZip64) throws IOException { + raf.seek(offset); + + // Read the End Of Central Directory. ENDHDR includes the signature bytes, + // which we've already read. + byte[] eocd = new byte[ENDHDR - 4]; + raf.readFully(eocd); + + BufferIterator it = HeapBufferIterator.iterator(eocd, 0, eocd.length, ByteOrder.LITTLE_ENDIAN); + final long numEntries; + final long centralDirOffset; + if (isZip64) { + numEntries = -1; + centralDirOffset = -1; + + // If we have a zip64 end of central directory record, we skip through the regular + // end of central directory record and use the information from the zip64 eocd record. + // We're still forced to read the comment length (below) since it isn't present in the + // zip64 eocd record. + it.skip(16); + } else { + // If we don't have a zip64 eocd record, we read values from the "regular" + // eocd record. + int diskNumber = it.readShort() & 0xffff; + int diskWithCentralDir = it.readShort() & 0xffff; + numEntries = it.readShort() & 0xffff; + int totalNumEntries = it.readShort() & 0xffff; + it.skip(4); // Ignore centralDirSize. + + centralDirOffset = ((long) it.readInt()) & 0xffffffffL; + if (numEntries != totalNumEntries || diskNumber != 0 || diskWithCentralDir != 0) { + throw new ZipException("Spanned archives not supported"); + } + } + + final int commentLength = it.readShort() & 0xffff; + return new EocdRecord(numEntries, centralDirOffset, commentLength); + } + static void throwZipException(String msg, int magic) throws ZipException { final String hexString = IntegralToString.intToHexString(magic, true, 8); throw new ZipException(msg + " signature not found; was " + hexString); |