Author: bodewig
Date: Sun Jul 31 20:30:32 2011
New Revision: 1152630
URL: http://svn.apache.org/viewvc?rev=1152630&view=rev
Log:
ensure ZIP64 features are kept to a minimum for entries that don't need them.
COMPRESS-150
Modified:
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipConstants.java
commons/proper/compress/branches/zip64/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
Modified:
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java?rev=1152630&r1=1152629&r2=1152630&view=diff
==============================================================================
---
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
(original)
+++
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
Sun Jul 31 20:30:32 2011
@@ -35,8 +35,9 @@ import org.apache.commons.compress.archi
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import static org.apache.commons.compress.archivers.zip.ZipConstants.BYTE_MASK;
-import static
org.apache.commons.compress.archivers.zip.ZipConstants.DEFLATE_MIN_VERSION;
+import static
org.apache.commons.compress.archivers.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION;
import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
+import static
org.apache.commons.compress.archivers.zip.ZipConstants.INITIAL_VERSION;
import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
import static
org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
@@ -254,6 +255,13 @@ public class ZipArchiveOutputStream exte
private boolean hasUsedZip64 = false;
/**
+ * Whether current entry was the first one using ZIP64 features.
+ *
+ * @since Apache Commons Compress 1.3
+ */
+ private boolean entryCausedUseOfZip64 = false;
+
+ /**
* Creates a new ZIP OutputStream filtering the underlying stream.
* @param out the outputstream to zip
*/
@@ -441,12 +449,12 @@ public class ZipArchiveOutputStream exte
// the correct CRC and compressed/uncompressed sizes
if (raf != null) {
long save = raf.getFilePointer();
+ boolean actuallyNeedsZip64 = entry.getSize() >= ZIP64_MAGIC
+ || entry.getCompressedSize() >= ZIP64_MAGIC;
raf.seek(localDataStart);
writeOut(ZipLong.getBytes(entry.getCrc()));
- if (!hasZip64Extra(entry)
- || (entry.getSize() < ZIP64_MAGIC
- && entry.getCompressedSize() < ZIP64_MAGIC)) {
+ if (!hasZip64Extra(entry) || !actuallyNeedsZip64) {
writeOut(ZipLong.getBytes(entry.getCompressedSize()));
writeOut(ZipLong.getBytes(entry.getSize()));
} else {
@@ -462,6 +470,23 @@ public class ZipArchiveOutputStream exte
// first, unlike the LFH, CD or data descriptor
writeOut(ZipEightByteInteger.getBytes(entry.getSize()));
writeOut(ZipEightByteInteger.getBytes(entry.getCompressedSize()));
+
+ if (!actuallyNeedsZip64) {
+ // do some cleanup:
+ // * rewrite version needed to extract
+ raf.seek(localDataStart - 5 * SHORT);
+ writeOut(ZipShort.getBytes(INITIAL_VERSION));
+
+ // * remove ZIP64 extra so it doesn't get written
+ // to the central directory
+ entry.removeExtraField(Zip64ExtendedInformationExtraField
+ .HEADER_ID);
+ entry.setExtra();
+
+ // * reset hasUsedZip64 if it has been set because
+ // of this entry
+ hasUsedZip64 &= !entryCausedUseOfZip64;
+ }
}
raf.seek(save);
}
@@ -469,6 +494,7 @@ public class ZipArchiveOutputStream exte
writeDataDescriptor(entry);
entry = null;
bytesRead = 0;
+ entryCausedUseOfZip64 = false;
}
/**
@@ -856,7 +882,7 @@ public class ZipArchiveOutputStream exte
// version made by
// CheckStyle:MagicNumber OFF
writeOut(ZipShort.getBytes((ze.getPlatform() << 8) |
- (!hasUsedZip64 ? DEFLATE_MIN_VERSION
+ (!hasUsedZip64 ? DATA_DESCRIPTOR_MIN_VERSION
: ZIP64_MIN_VERSION)));
written += SHORT;
@@ -1076,13 +1102,13 @@ public class ZipArchiveOutputStream exte
throws IOException {
// CheckStyle:MagicNumber OFF
- int versionNeededToExtract = 10;
+ int versionNeededToExtract = INITIAL_VERSION;
GeneralPurposeBit b = new GeneralPurposeBit();
b.useUTF8ForNames(useUTF8Flag || utfFallback);
if (zipMethod == DEFLATED && raf == null) {
// requires version 2 as we are going to store length info
// in the data descriptor
- versionNeededToExtract = DEFLATE_MIN_VERSION;
+ versionNeededToExtract = DATA_DESCRIPTOR_MIN_VERSION;
b.useDataDescriptor(true);
}
if (zip64) {
@@ -1153,6 +1179,7 @@ public class ZipArchiveOutputStream exte
*/
private Zip64ExtendedInformationExtraField
getZip64Extra(ZipArchiveEntry ze) {
+ entryCausedUseOfZip64 = !hasUsedZip64;
hasUsedZip64 = true;
Zip64ExtendedInformationExtraField z64 =
(Zip64ExtendedInformationExtraField)
Modified:
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipConstants.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipConstants.java?rev=1152630&r1=1152629&r2=1152630&view=diff
==============================================================================
---
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipConstants.java
(original)
+++
commons/proper/compress/branches/zip64/src/main/java/org/apache/commons/compress/archivers/zip/ZipConstants.java
Sun Jul 31 20:30:32 2011
@@ -37,8 +37,11 @@ final class ZipConstants {
/** length of a ZipEightByteInteger in bytes */
static final int DWORD = 8;
- /** ZIP specification version that introduced DEFLATE method */
- static final int DEFLATE_MIN_VERSION = 20;
+ /** Initial ZIP specification version */
+ static final int INITIAL_VERSION = 10;
+
+ /** ZIP specification version that introduced data descriptor method */
+ static final int DATA_DESCRIPTOR_MIN_VERSION = 20;
/** ZIP specification version that introduced ZIP64 */
static final int ZIP64_MIN_VERSION = 45;
Modified:
commons/proper/compress/branches/zip64/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/branches/zip64/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java?rev=1152630&r1=1152629&r2=1152630&view=diff
==============================================================================
---
commons/proper/compress/branches/zip64/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
(original)
+++
commons/proper/compress/branches/zip64/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
Sun Jul 31 20:30:32 2011
@@ -39,6 +39,7 @@ import static org.junit.Assume.assumeTru
public class Zip64SupportTest {
private static final long FIVE_BILLION = 5000000000l;
+ private static final int ONE_MILLION = 1000000;
private static final int ONE_HUNDRED_THOUSAND = 100000;
@Test public void read5GBOfZerosUsingInputStream() throws Throwable {
@@ -188,7 +189,7 @@ public class Zip64SupportTest {
new ZipOutputTest() {
public void test(File f, ZipArchiveOutputStream zos)
throws IOException {
- byte[] buf = new byte[1000 * 1000];
+ byte[] buf = new byte[ONE_MILLION];
ZipArchiveEntry zae = null;
for (int i = 0; i < 2; i++) {
zae = new ZipArchiveEntry(String.valueOf(i));
@@ -312,7 +313,7 @@ public class Zip64SupportTest {
return new ZipOutputTest() {
public void test(File f, ZipArchiveOutputStream zos)
throws IOException {
- byte[] buf = new byte[1000 * 1000];
+ byte[] buf = new byte[ONE_MILLION];
ZipArchiveEntry zae = new ZipArchiveEntry("0");
if (knownSize) {
zae.setSize(FIVE_BILLION);
@@ -461,6 +462,7 @@ public class Zip64SupportTest {
true);
}
+ @Ignore
@Test public void writeBigStoredEntryUnnownSizeToFile() throws Throwable {
withTemporaryArchive("writeBigStoredEntryUnknownSizeToFile",
writeBigStoredEntry(false),
@@ -484,7 +486,7 @@ public class Zip64SupportTest {
public void test(File f,
ZipArchiveOutputStream zos)
throws IOException {
- byte[] buf = new byte[1000 * 1000];
+ byte[] buf = new byte[ONE_MILLION];
ZipArchiveEntry zae =
new ZipArchiveEntry("0");
zae.setSize(FIVE_BILLION);
@@ -655,7 +657,7 @@ public class Zip64SupportTest {
return new ZipOutputTest() {
public void test(File f, ZipArchiveOutputStream zos)
throws IOException {
- byte[] buf = new byte[1000 * 1000];
+ byte[] buf = new byte[ONE_MILLION];
ZipArchiveEntry zae = new ZipArchiveEntry("0");
if (knownSize) {
zae.setSize(FIVE_BILLION);
@@ -799,6 +801,7 @@ public class Zip64SupportTest {
true);
}
+ @Ignore
@Test public void writeBigDeflatedEntryUnknownSizeToFile()
throws Throwable {
withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFile",
@@ -806,6 +809,452 @@ public class Zip64SupportTest {
true);
}
+ /*
+ * One entry of length 1 million bytes, written without compression.
+ *
+ * No Compression => sizes are stored directly inside the LFH. No
+ * Data Descriptor at all. Shouldn't contain any ZIP64 extra
+ * field if size was known.
+ *
+ * Creates a temporary archive of approx 1MB in size
+ */
+ private static ZipOutputTest writeSmallStoredEntry(final boolean
knownSize) {
+ return new ZipOutputTest() {
+ public void test(File f, ZipArchiveOutputStream zos)
+ throws IOException {
+ byte[] buf = new byte[ONE_MILLION];
+ ZipArchiveEntry zae = new ZipArchiveEntry("0");
+ if (knownSize) {
+ zae.setSize(ONE_MILLION);
+ zae.setCrc(0x1279CB9EL);
+ }
+ zae.setMethod(ZipArchiveEntry.STORED);
+ zos.putArchiveEntry(zae);
+ zos.write(buf);
+ zos.closeArchiveEntry();
+ zos.close();
+
+ RandomAccessFile a = new RandomAccessFile(f, "r");
+ try {
+ final long end = getLengthAndPositionAtCentralDirectory(a);
+
+ // grab first CF entry, verify sizes are 1e6 and it
+ // has no ZIP64 extended information extra field
+ // at all
+ byte[] header = new byte[12];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 1, 2,
+ // version made by
+ 20, 0,
+ // version needed to extract
+ 10, 0,
+ // GPB (EFS bit)
+ 0, 8,
+ // method
+ 0, 0
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ byte[] rest = new byte[31];
+ a.readFully(rest);
+ // 1e6 == 0xF4240
+ assertArrayEquals(new byte[] {
+ // CRC
+ (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
+ // Compressed Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // Original Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ 0, 0,
+ // comment length
+ 0, 0,
+ // disk number
+ 0, 0,
+ // attributes
+ 0, 0,
+ 0, 0, 0, 0,
+ // offset
+ 0, 0, 0, 0,
+ // file name
+ (byte) '0'
+ }, rest);
+
+ // and now validate local file header: this one
+ // has a ZIP64 extra field if and only if size was
+ // unknown
+ a.seek(0);
+ header = new byte[10];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 3, 4,
+ // version needed to extract
+ 10, 0,
+ // GPB (EFS bit)
+ 0, 8,
+ // method
+ 0, 0
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ rest = new byte[17];
+ a.readFully(rest);
+ // 1e6 == 0xF4240
+ assertArrayEquals(new byte[] {
+ // CRC
+ (byte) 0x9E, (byte) 0xCB, (byte) 0x79, (byte) 0x12,
+ // Compressed Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // Original Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ (byte) (knownSize ? 0 : 20), 0,
+ // file name
+ (byte) '0'
+ }, rest);
+ if (!knownSize) {
+ byte[] extra = new byte[20];
+ a.readFully(extra);
+ assertArrayEquals(new byte[] {
+ // Header-ID
+ 1, 0,
+ // size of extra
+ 16, 0,
+ // original size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ 0, 0, 0, 0,
+ // compressed size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ 0, 0, 0, 0,
+ }, extra);
+ }
+ } finally {
+ a.close();
+ }
+ }
+ };
+ }
+
+ @Test public void writeSmallStoredEntryToStream() throws Throwable {
+ withTemporaryArchive("writeSmallStoredEntryToStream",
+ writeSmallStoredEntry(true),
+ false);
+ }
+
+ @Test public void writeSmallStoredEntryKnownSizeToFile() throws Throwable {
+ withTemporaryArchive("writeSmallStoredEntryKnownSizeToFile",
+ writeSmallStoredEntry(true),
+ true);
+ }
+
+ @Test public void writeSmallStoredEntryUnnownSizeToFile() throws Throwable
{
+ withTemporaryArchive("writeSmallStoredEntryUnknownSizeToFile",
+ writeSmallStoredEntry(false),
+ true);
+ }
+
+ /*
+ * One entry of length 1 million bytes, written with compression
+ * to a stream.
+ *
+ * Compression + Stream => sizes are set to 0 in LFH, real values
+ * are inside the data descriptor. No ZIP64 extra field at all.
+ */
+ @Test public void writeSmallDeflatedEntryKnownSizeToStream()
+ throws Throwable {
+ withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToStream",
+ new ZipOutputTest() {
+ public void test(File f,
+ ZipArchiveOutputStream zos)
+ throws IOException {
+ byte[] buf = new byte[ONE_MILLION];
+ ZipArchiveEntry zae =
+ new ZipArchiveEntry("0");
+ zae.setSize(ONE_MILLION);
+ zae.setMethod(ZipArchiveEntry.DEFLATED);
+ zos.putArchiveEntry(zae);
+ zos.write(buf);
+ zos.closeArchiveEntry();
+ zos.close();
+
+ RandomAccessFile a =
+ new RandomAccessFile(f, "r");
+ try {
+ final long end =
+
getLengthAndPositionAtCentralDirectory(a);
+
+ long cfhPos = a.getFilePointer();
+ // grab first entry, verify
+ // sizes are not 0xFFFFFFF
+ // and it has no ZIP64
+ // extended information extra
+ // field
+ byte[] header = new byte[12];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 1,
2,
+ // version made by
+ 20, 0,
+ // version needed to extract
+ 20, 0,
+ // GPB (EFS + Data Descriptor)
+ 8, 8,
+ // method
+ 8, 0,
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ byte[] crc = new byte[4];
+ a.readFully(crc);
+ assertArrayEquals(new byte[] {
+ (byte) 0x9E, (byte) 0xCB,
+ (byte) 0x79, (byte) 0x12,
+ }, crc);
+ // skip compressed size
+ a.skipBytes(4);
+ byte[] rest = new byte[23];
+ a.readFully(rest);
+ assertArrayEquals(new byte[] {
+ // Original Size
+ (byte) 0x40, (byte) 0x42,
+ (byte) 0x0F, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ 0, 0,
+ // comment length
+ 0, 0,
+ // disk number
+ 0, 0,
+ // attributes
+ 0, 0,
+ 0, 0, 0, 0,
+ // offset
+ 0, 0, 0, 0,
+ // file name
+ (byte) '0'
+ }, rest);
+
+ // validate data descriptor
+ a.seek(cfhPos - 16);
+ byte[] dd = new byte[8];
+ a.readFully(dd);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 7,
8,
+ // CRC
+ (byte) 0x9E, (byte) 0xCB,
+ (byte) 0x79, (byte) 0x12,
+ }, dd);
+ // skip uncompressed size
+ a.skipBytes(4);
+ dd = new byte[4];
+ a.readFully(dd);
+ assertArrayEquals(new byte[] {
+ // original size
+ (byte) 0x40, (byte) 0x42,
+ (byte) 0x0F, 0,
+ }, dd);
+
+ // and now validate local file header
+ a.seek(0);
+ header = new byte[10];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 3,
4,
+ // version needed to extract
+ 20, 0,
+ // GPB (EFS + Data Descriptor)
+ 8, 8,
+ // method
+ 8, 0,
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ rest = new byte[17];
+ a.readFully(rest);
+ assertArrayEquals(new byte[] {
+ // CRC
+ 0, 0, 0, 0,
+ // Compressed Size
+ 0, 0, 0, 0,
+ // Original Size
+ 0, 0, 0, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ 0, 0,
+ // file name
+ (byte) '0'
+ }, rest);
+ } finally {
+ a.close();
+ }
+ }
+ },
+ false);
+ }
+
+ /*
+ * One entry of length 1 million bytes, written with compression
+ * to a file.
+ *
+ * Writing to a file => sizes are stored directly inside the LFH.
+ * No Data Descriptor at all. Shouldn't contain any ZIP64 extra
+ * field if size was known.
+ */
+ private static ZipOutputTest writeSmallDeflatedEntryToFile(final boolean
knownSize) {
+ return new ZipOutputTest() {
+ public void test(File f, ZipArchiveOutputStream zos)
+ throws IOException {
+ byte[] buf = new byte[ONE_MILLION];
+ ZipArchiveEntry zae = new ZipArchiveEntry("0");
+ if (knownSize) {
+ zae.setSize(ONE_MILLION);
+ }
+ zae.setMethod(ZipArchiveEntry.DEFLATED);
+ zos.putArchiveEntry(zae);
+ zos.write(buf);
+ zos.closeArchiveEntry();
+ zos.close();
+
+ RandomAccessFile a = new RandomAccessFile(f, "r");
+ try {
+ final long end = getLengthAndPositionAtCentralDirectory(a);
+
+ long cfhPos = a.getFilePointer();
+ // grab first CD entry, verify sizes are not
+ // 0xFFFFFFFF and it has a no ZIP64 extended
+ // information extra field
+ byte[] header = new byte[12];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 1, 2,
+ // version made by
+ 20, 0,
+ // version needed to extract
+ 10, 0,
+ // GPB (EFS + *no* Data Descriptor)
+ 0, 8,
+ // method
+ 8, 0,
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ byte[] crc = new byte[4];
+ a.readFully(crc);
+ assertArrayEquals(new byte[] {
+ (byte) 0x9E, (byte) 0xCB,
+ (byte) 0x79, (byte) 0x12,
+ }, crc);
+ // skip compressed size
+ a.skipBytes(4);
+ byte[] rest = new byte[23];
+ a.readFully(rest);
+ assertArrayEquals(new byte[] {
+ // Original Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ 0, 0,
+ // comment length
+ 0, 0,
+ // disk number
+ 0, 0,
+ // attributes
+ 0, 0,
+ 0, 0, 0, 0,
+ // offset
+ 0, 0, 0, 0,
+ // file name
+ (byte) '0'
+ }, rest);
+
+ // and now validate local file header
+ a.seek(0);
+ header = new byte[10];
+ a.readFully(header);
+ assertArrayEquals(new byte[] {
+ // sig
+ (byte) 0x50, (byte) 0x4b, 3, 4,
+ // version needed to extract
+ 10, 0,
+ // GPB (EFS bit, no DD)
+ 0, 8,
+ // method
+ 8, 0,
+ }, header);
+ // ignore timestamp
+ a.skipBytes(4);
+ crc = new byte[4];
+ a.readFully(crc);
+ assertArrayEquals(new byte[] {
+ (byte) 0x9E, (byte) 0xCB,
+ (byte) 0x79, (byte) 0x12,
+ }, crc);
+ // skip compressed size
+ a.skipBytes(4);
+ rest = new byte[9];
+ a.readFully(rest);
+ assertArrayEquals(new byte[] {
+ // Original Size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ // file name length
+ 1, 0,
+ // extra field length
+ (byte) (knownSize ? 0 : 20), 0,
+ // file name
+ (byte) '0'
+ }, rest);
+ if (!knownSize) {
+ byte[] extra = new byte[12];
+ a.readFully(extra);
+ assertArrayEquals(new byte[] {
+ // Header-ID
+ 1, 0,
+ // size of extra
+ 16, 0,
+ // original size
+ (byte) 0x40, (byte) 0x42, (byte) 0x0F, 0,
+ 0, 0, 0, 0,
+ // don't know the
+ // compressed size,
+ // don't want to
+ // hard-code it
+ }, extra);
+ }
+ } finally {
+ a.close();
+ }
+ }
+ };
+ }
+
+ @Test public void writeSmallDeflatedEntryKnownSizeToFile()
+ throws Throwable {
+ withTemporaryArchive("writeSmallDeflatedEntryKnownSizeToFile",
+ writeSmallDeflatedEntryToFile(true),
+ true);
+ }
+
+ @Test public void writeSmallDeflatedEntryUnknownSizeToFile()
+ throws Throwable {
+ withTemporaryArchive("writeSmallDeflatedEntryUnknownSizeToFile",
+ writeSmallDeflatedEntryToFile(false),
+ true);
+ }
+
static interface ZipOutputTest {
void test(File f, ZipArchiveOutputStream zos) throws IOException;
}
@@ -897,6 +1346,15 @@ public class Zip64SupportTest {
private static long
getLengthAndPositionAtCentralDirectory(RandomAccessFile a)
throws IOException {
final long end = a.length();
+ a.seek(end - 22 - 20);
+ byte[] sig = new byte[4];
+ a.readFully(sig);
+ if (sig[0] != (byte) 0x50 || sig[1] != (byte) 0x4b
+ || sig[2] != 6 || sig[3] != 7) {
+ // not a ZIP64 archive
+ return getLengthAndPositionAtCentralDirectory32(a, end);
+ }
+
long cdOffsetLoc = end - 22 - 20 - 56 + 48;
// seek to central directory locator
a.seek(cdOffsetLoc);
@@ -905,4 +1363,13 @@ public class Zip64SupportTest {
a.seek(ZipEightByteInteger.getLongValue(cdOffset));
return end;
}
+
+ private static long
getLengthAndPositionAtCentralDirectory32(RandomAccessFile a, final long end)
+ throws IOException {
+ a.seek(end - 22 + 16);
+ byte[] cdOffset = new byte[4];
+ a.readFully(cdOffset);
+ a.seek(ZipLong.getValue(cdOffset));
+ return end;
+ }
}