4681995: Add support for large (> 4GB) zip/jar files
The ZIP64 format support is added for > 4GB jar/zip files
Reviewed-by: alanb, martin
diff --git a/jdk/src/share/classes/java/util/zip/ZipConstants64.java b/jdk/src/share/classes/java/util/zip/ZipConstants64.java
new file mode 100644
index 0000000..1bf3b18
--- /dev/null
+++ b/jdk/src/share/classes/java/util/zip/ZipConstants64.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1995-1996 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This class defines the constants that are used by the classes
+ * which manipulate Zip64 files.
+ */
+
+class ZipConstants64 {
+
+ /*
+ * ZIP64 constants
+ */
+ static final long ZIP64_ENDSIG = 0x06064b50L; // "PK\006\006"
+ static final long ZIP64_LOCSIG = 0x07064b50L; // "PK\006\007"
+ static final int ZIP64_ENDHDR = 56; // ZIP64 end header size
+ static final int ZIP64_LOCHDR = 20; // ZIP64 end loc header size
+ static final int ZIP64_EXTHDR = 24; // EXT header size
+ static final int ZIP64_EXTID = 0x0001; // Extra field Zip64 header ID
+
+ static final int ZIP64_MAGICCOUNT = 0xFFFF;
+ static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
+
+ /*
+ * Zip64 End of central directory (END) header field offsets
+ */
+ static final int ZIP64_ENDLEN = 4; // size of zip64 end of central dir
+ static final int ZIP64_ENDVEM = 12; // version made by
+ static final int ZIP64_ENDVER = 14; // version needed to extract
+ static final int ZIP64_ENDNMD = 16; // number of this disk
+ static final int ZIP64_ENDDSK = 20; // disk number of start
+ static final int ZIP64_ENDTOD = 24; // total number of entries on this disk
+ static final int ZIP64_ENDTOT = 32; // total number of entries
+ static final int ZIP64_ENDSIZ = 40; // central directory size in bytes
+ static final int ZIP64_ENDOFF = 48; // offset of first CEN header
+ static final int ZIP64_ENDEXT = 56; // zip64 extensible data sector
+
+ /*
+ * Zip64 End of central directory locator field offsets
+ */
+ static final int ZIP64_LOCDSK = 4; // disk number start
+ static final int ZIP64_LOCOFF = 8; // offset of zip64 end
+ static final int ZIP64_LOCTOT = 16; // total number of disks
+
+ /*
+ * Zip64 Extra local (EXT) header field offsets
+ */
+ static final int ZIP64_EXTCRC = 4; // uncompressed file crc-32 value
+ static final int ZIP64_EXTSIZ = 8; // compressed size, 8-byte
+ static final int ZIP64_EXTLEN = 16; // uncompressed size, 8-byte
+
+ private ZipConstants64() {}
+}
diff --git a/jdk/src/share/classes/java/util/zip/ZipEntry.java b/jdk/src/share/classes/java/util/zip/ZipEntry.java
index bcc27e6..6c16a9a 100644
--- a/jdk/src/share/classes/java/util/zip/ZipEntry.java
+++ b/jdk/src/share/classes/java/util/zip/ZipEntry.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1995-2005 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1995-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -144,11 +144,13 @@
* Sets the uncompressed size of the entry data.
* @param size the uncompressed size in bytes
* @exception IllegalArgumentException if the specified size is less
- * than 0 or greater than 0xFFFFFFFF bytes
+ * than 0, is greater than 0xFFFFFFFF when
+ * <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+ * or is less than 0 when ZIP64 is supported
* @see #getSize()
*/
public void setSize(long size) {
- if (size < 0 || size > 0xFFFFFFFFL) {
+ if (size < 0) {
throw new IllegalArgumentException("invalid entry size");
}
this.size = size;
diff --git a/jdk/src/share/classes/java/util/zip/ZipInputStream.java b/jdk/src/share/classes/java/util/zip/ZipInputStream.java
index e8f0ebd..1b9b934 100644
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
+import static java.util.zip.ZipConstants64.*;
/**
* This class implements an input stream filter for reading files in the
@@ -285,6 +286,29 @@
byte[] bb = new byte[len];
readFully(bb, 0, len);
e.setExtra(bb);
+ // extra fields are in "HeaderID(2)DataSize(2)Data... format
+ if (e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL) {
+ int off = 0;
+ while (off + 4 < len) {
+ int sz = get16(bb, off + 2);
+ if (get16(bb, off) == ZIP64_EXTID) {
+ off += 4;
+ // LOC extra zip64 entry MUST include BOTH original and
+ // compressed file size fields
+ if (sz < 16 || (off + sz) > len ) {
+ // Invalid zip64 extra fields, simply skip. Even it's
+ // rare, it's possible the entry size happens to be
+ // the magic value and it "accidnetly" has some bytes
+ // in extra match the id.
+ return e;
+ }
+ e.size = get64(bb, off);
+ e.csize = get64(bb, off + 8);
+ break;
+ }
+ off += (sz + 4);
+ }
+ }
}
return e;
}
@@ -375,18 +399,36 @@
}
if ((flag & 8) == 8) {
/* "Data Descriptor" present */
- readFully(tmpbuf, 0, EXTHDR);
- long sig = get32(tmpbuf, 0);
- if (sig != EXTSIG) { // no EXTSIG present
- e.crc = sig;
- e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
- e.size = get32(tmpbuf, EXTLEN - EXTCRC);
- ((PushbackInputStream)in).unread(
- tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+ if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
+ inf.getBytesRead() > ZIP64_MAGICVAL) {
+ // ZIP64 format
+ readFully(tmpbuf, 0, ZIP64_EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC - 1, ZIP64_EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, ZIP64_EXTCRC);
+ e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
+ e.size = get64(tmpbuf, ZIP64_EXTLEN);
+ }
} else {
- e.crc = get32(tmpbuf, EXTCRC);
- e.csize = get32(tmpbuf, EXTSIZ);
- e.size = get32(tmpbuf, EXTLEN);
+ readFully(tmpbuf, 0, EXTHDR);
+ long sig = get32(tmpbuf, 0);
+ if (sig != EXTSIG) { // no EXTSIG present
+ e.crc = sig;
+ e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
+ e.size = get32(tmpbuf, EXTLEN - EXTCRC);
+ ((PushbackInputStream)in).unread(
+ tmpbuf, EXTHDR - EXTCRC - 1, EXTCRC);
+ } else {
+ e.crc = get32(tmpbuf, EXTCRC);
+ e.csize = get32(tmpbuf, EXTSIZ);
+ e.size = get32(tmpbuf, EXTLEN);
+ }
}
}
if (e.size != inf.getBytesWritten()) {
@@ -433,6 +475,14 @@
* The bytes are assumed to be in Intel (little-endian) byte order.
*/
private static final long get32(byte b[], int off) {
- return get16(b, off) | ((long)get16(b, off+2) << 16);
+ return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
+ }
+
+ /*
+ * Fetches signed 64-bit value from byte array at specified offset.
+ * The bytes are assumed to be in Intel (little-endian) byte order.
+ */
+ private static final long get64(byte b[], int off) {
+ return get32(b, off) | (get32(b, off+4) << 32);
}
}
diff --git a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
index 797a373..bd44d32 100644
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1996-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright 1996-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.util.Vector;
import java.util.HashSet;
+import static java.util.zip.ZipConstants64.*;
/**
* This class implements an output stream filter for writing files in the
@@ -343,26 +344,52 @@
private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry;
int flag = xentry.flag;
+ int elen = (e.extra != null) ? e.extra.length : 0;
+ boolean hasZip64 = false;
+
writeInt(LOCSIG); // LOC header signature
- writeShort(version(e)); // version needed to extract
- writeShort(flag); // general purpose bit flag
- writeShort(e.method); // compression method
- writeInt(e.time); // last modification time
+
if ((flag & 8) == 8) {
+ writeShort(version(e)); // version needed to extract
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.time); // last modification time
+
// store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data
writeInt(0);
writeInt(0);
writeInt(0);
} else {
- writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ hasZip64 = true;
+ writeShort(45); // ver 4.5 for zip64
+ } else {
+ writeShort(version(e)); // version needed to extract
+ }
+ writeShort(flag); // general purpose bit flag
+ writeShort(e.method); // compression method
+ writeInt(e.time); // last modification time
+ writeInt(e.crc); // crc-32
+ if (hasZip64) {
+ writeInt(ZIP64_MAGICVAL);
+ writeInt(ZIP64_MAGICVAL);
+ elen += 20; //headid(2) + size(2) + size(8) + csize(8)
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
}
byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length);
- writeShort(e.extra != null ? e.extra.length : 0);
+ writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length);
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);
+ writeShort(16);
+ writeLong(e.size);
+ writeLong(e.csize);
+ }
if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length);
}
@@ -375,8 +402,13 @@
private void writeEXT(ZipEntry e) throws IOException {
writeInt(EXTSIG); // EXT header signature
writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+ writeLong(e.csize);
+ writeLong(e.size);
+ } else {
+ writeInt(e.csize); // compressed size
+ writeInt(e.size); // uncompressed size
+ }
}
/*
@@ -387,18 +419,49 @@
ZipEntry e = xentry.entry;
int flag = xentry.flag;
int version = version(e);
+
+ long csize = e.csize;
+ long size = e.size;
+ long offset = xentry.offset;
+ int e64len = 0;
+ boolean hasZip64 = false;
+ if (e.csize >= ZIP64_MAGICVAL) {
+ csize = ZIP64_MAGICVAL;
+ e64len += 8; // csize(8)
+ hasZip64 = true;
+ }
+ if (e.size >= ZIP64_MAGICVAL) {
+ size = ZIP64_MAGICVAL; // size(8)
+ e64len += 8;
+ hasZip64 = true;
+ }
+ if (xentry.offset >= ZIP64_MAGICVAL) {
+ offset = ZIP64_MAGICVAL;
+ e64len += 8; // offset(8)
+ hasZip64 = true;
+ }
writeInt(CENSIG); // CEN header signature
- writeShort(version); // version made by
- writeShort(version); // version needed to extract
+ if (hasZip64) {
+ writeShort(45); // ver 4.5 for zip64
+ writeShort(45);
+ } else {
+ writeShort(version); // version made by
+ writeShort(version); // version needed to extract
+ }
writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method
writeInt(e.time); // last modification time
writeInt(e.crc); // crc-32
- writeInt(e.csize); // compressed size
- writeInt(e.size); // uncompressed size
+ writeInt(csize); // compressed size
+ writeInt(size); // uncompressed size
byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length);
- writeShort(e.extra != null ? e.extra.length : 0);
+ if (hasZip64) {
+ // + headid(2) + datasize(2)
+ writeShort(e64len + 4 + (e.extra != null ? e.extra.length : 0));
+ } else {
+ writeShort(e.extra != null ? e.extra.length : 0);
+ }
byte[] commentBytes;
if (e.comment != null) {
commentBytes = getUTF8Bytes(e.comment);
@@ -410,8 +473,18 @@
writeShort(0); // starting disk number
writeShort(0); // internal file attributes (unused)
writeInt(0); // external file attributes (unused)
- writeInt(xentry.offset); // relative offset of local header
+ writeInt(offset); // relative offset of local header
writeBytes(nameBytes, 0, nameBytes.length);
+ if (hasZip64) {
+ writeShort(ZIP64_EXTID);// Zip64 extra
+ writeShort(e64len);
+ if (size == ZIP64_MAGICVAL)
+ writeLong(e.size);
+ if (csize == ZIP64_MAGICVAL)
+ writeLong(e.csize);
+ if (offset == ZIP64_MAGICVAL)
+ writeLong(xentry.offset);
+ }
if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length);
}
@@ -424,15 +497,50 @@
* Writes end of central directory (END) header.
*/
private void writeEND(long off, long len) throws IOException {
+ boolean hasZip64 = false;
+ long xlen = len;
+ long xoff = off;
+ if (xlen >= ZIP64_MAGICVAL) {
+ xlen = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
+ if (xoff >= ZIP64_MAGICVAL) {
+ xoff = ZIP64_MAGICVAL;
+ hasZip64 = true;
+ }
int count = xentries.size();
- writeInt(ENDSIG); // END record signature
- writeShort(0); // number of this disk
- writeShort(0); // central directory start disk
- writeShort(count); // number of directory entries on disk
- writeShort(count); // total number of directory entries
- writeInt(len); // length of central directory
- writeInt(off); // offset of central directory
- if (comment != null) { // zip file comment
+ if (count >= ZIP64_MAGICCOUNT) {
+ count = ZIP64_MAGICCOUNT;
+ hasZip64 = true;
+ }
+ if (hasZip64) {
+ long off64 = written;
+ //zip64 end of central directory record
+ writeInt(ZIP64_ENDSIG); // zip64 END record signature
+ writeLong(ZIP64_ENDHDR - 12); // size of zip64 end
+ writeShort(45); // version made by
+ writeShort(45); // version needed to extract
+ writeInt(0); // number of this disk
+ writeInt(0); // central directory start disk
+ writeLong(xentries.size()); // number of directory entires on disk
+ writeLong(xentries.size()); // number of directory entires
+ writeLong(len); // length of central directory
+ writeLong(off); // offset of central directory
+
+ //zip64 end of central directory locator
+ writeInt(ZIP64_LOCSIG); // zip64 END locator signature
+ writeInt(0); // zip64 END start disk
+ writeLong(off64); // offset of zip64 END
+ writeInt(1); // total number of disks (?)
+ }
+ writeInt(ENDSIG); // END record signature
+ writeShort(0); // number of this disk
+ writeShort(0); // central directory start disk
+ writeShort(count); // number of directory entries on disk
+ writeShort(count); // total number of directory entries
+ writeInt(xlen); // length of central directory
+ writeInt(xoff); // offset of central directory
+ if (comment != null) { // zip file comment
byte[] b = getUTF8Bytes(comment);
writeShort(b.length);
writeBytes(b, 0, b.length);
@@ -464,6 +572,22 @@
}
/*
+ * Writes a 64-bit int to the output stream in little-endian byte order.
+ */
+ private void writeLong(long v) throws IOException {
+ OutputStream out = this.out;
+ out.write((int)((v >>> 0) & 0xff));
+ out.write((int)((v >>> 8) & 0xff));
+ out.write((int)((v >>> 16) & 0xff));
+ out.write((int)((v >>> 24) & 0xff));
+ out.write((int)((v >>> 32) & 0xff));
+ out.write((int)((v >>> 40) & 0xff));
+ out.write((int)((v >>> 48) & 0xff));
+ out.write((int)((v >>> 56) & 0xff));
+ written += 8;
+ }
+
+ /*
* Writes an array of bytes to the output stream.
*/
private void writeBytes(byte[] b, int off, int len) throws IOException {
diff --git a/jdk/src/share/classes/java/util/zip/package.html b/jdk/src/share/classes/java/util/zip/package.html
index d32e0fa..d4b5926 100644
--- a/jdk/src/share/classes/java/util/zip/package.html
+++ b/jdk/src/share/classes/java/util/zip/package.html
@@ -46,6 +46,13 @@
</a> - a detailed description of the Info-ZIP format upon which
the <code>java.util.zip</code> classes are based.
<p>
+ <a name="zip64">
+ <li>An implementation may optionally support the ZIP64(tm) format extensions
+ defined by the
+ <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format extensions
+ are used to overcome the size limitations of the original ZIP format.
+<p>
<li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
ZLIB Compressed Data Format Specification version 3.3</a>
@@ -70,7 +77,6 @@
<li>CRC-32 checksum is described in RFC 1952 (above)
<p>
<li>Adler-32 checksum is described in RFC 1950 (above)
-
</ul>
diff --git a/jdk/src/share/native/java/util/zip/zip_util.c b/jdk/src/share/native/java/util/zip/zip_util.c
index f381629..89f52ae 100644
--- a/jdk/src/share/native/java/util/zip/zip_util.c
+++ b/jdk/src/share/native/java/util/zip/zip_util.c
@@ -313,6 +313,38 @@
}
/*
+ * Searches for the ZIP64 end of central directory (END) header. The
+ * contents of the ZIP64 END header will be read and placed in end64buf.
+ * Returns the file position of the ZIP64 END header, otherwise returns
+ * -1 if the END header was not found or an error occurred.
+ *
+ * The ZIP format specifies the "position" of each related record as
+ * ...
+ * [central directory]
+ * [zip64 end of central directory record]
+ * [zip64 end of central directory locator]
+ * [end of central directory record]
+ *
+ * The offset of zip64 end locator can be calculated from endpos as
+ * "endpos - ZIP64_LOCHDR".
+ * The "offset" of zip64 end record is stored in zip64 end locator.
+ */
+static jlong
+findEND64(jzfile *zip, void *end64buf, jlong endpos)
+{
+ char loc64[ZIP64_LOCHDR];
+ jlong end64pos;
+ if (readFullyAt(zip->zfd, loc64, ZIP64_LOCHDR, endpos - ZIP64_LOCHDR) == -1) {
+ return -1; // end64 locator not found
+ }
+ end64pos = ZIP64_LOCOFF(loc64);
+ if (readFullyAt(zip->zfd, end64buf, ZIP64_ENDHDR, end64pos) == -1) {
+ return -1; // end64 record not found
+ }
+ return end64pos;
+}
+
+/*
* Returns a hash code value for a C-style NUL-terminated string.
*/
static unsigned int
@@ -463,7 +495,7 @@
readCEN(jzfile *zip, jint knownTotal)
{
/* Following are unsigned 32-bit */
- jlong endpos, cenpos, cenlen;
+ jlong endpos, end64pos, cenpos, cenlen, cenoff;
/* Following are unsigned 16-bit */
jint total, tablelen, i, j;
unsigned char *cenbuf = NULL;
@@ -474,6 +506,7 @@
jlong offset;
#endif
unsigned char endbuf[ENDHDR];
+ jint endhdrlen = ENDHDR;
jzcell *entries;
jint *table;
@@ -490,13 +523,27 @@
/* Get position and length of central directory */
cenlen = ENDSIZ(endbuf);
+ cenoff = ENDOFF(endbuf);
+ total = ENDTOT(endbuf);
+ if (cenlen == ZIP64_MAGICVAL || cenoff == ZIP64_MAGICVAL ||
+ total == ZIP64_MAGICCOUNT) {
+ unsigned char end64buf[ZIP64_ENDHDR];
+ if ((end64pos = findEND64(zip, end64buf, endpos)) != -1) {
+ cenlen = ZIP64_ENDSIZ(end64buf);
+ cenoff = ZIP64_ENDOFF(end64buf);
+ total = (jint)ZIP64_ENDTOT(end64buf);
+ endpos = end64pos;
+ endhdrlen = ZIP64_ENDHDR;
+ }
+ }
+
if (cenlen > endpos)
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
cenpos = endpos - cenlen;
/* Get position of first local file (LOC) header, taking into
* account that there may be a stub prefixed to the zip file. */
- zip->locpos = cenpos - ENDOFF(endbuf);
+ zip->locpos = cenpos - cenoff;
if (zip->locpos < 0)
ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
@@ -527,7 +574,7 @@
out the page size in order to make offset to be multiples of
page size.
*/
- zip->mlen = cenpos - offset + cenlen + ENDHDR;
+ zip->mlen = cenpos - offset + cenlen + endhdrlen;
zip->offset = offset;
mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
@@ -551,8 +598,13 @@
* is a 2-byte field, but we (and other zip implementations)
* support approx. 2**31 entries, we do not trust ENDTOT, but
* treat it only as a strong hint. When we call ourselves
- * recursively, knownTotal will have the "true" value. */
- total = (knownTotal != -1) ? knownTotal : ENDTOT(endbuf);
+ * recursively, knownTotal will have the "true" value.
+ *
+ * Keep this path alive even with the Zip64 END support added, just
+ * for zip files that have more than 0xffff entries but don't have
+ * the Zip64 enabled.
+ */
+ total = (knownTotal != -1) ? knownTotal : total;
entries = zip->entries = calloc(total, sizeof(entries[0]));
tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
table = zip->table = malloc(tablelen * sizeof(table[0]));
@@ -854,6 +906,7 @@
static jzentry *
newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
{
+ jlong locoff;
jint nlen, elen, clen;
jzentry *ze;
char *cen;
@@ -880,18 +933,55 @@
ze->size = CENLEN(cen);
ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen);
ze->crc = CENCRC(cen);
- ze->pos = -(zip->locpos + CENOFF(cen));
+ locoff = CENOFF(cen);
+ ze->pos = -(zip->locpos + locoff);
if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen);
ze->name[nlen] = '\0';
if (elen > 0) {
+ char *extra = cen + CENHDR + nlen;
+
/* This entry has "extra" data */
if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch;
ze->extra[0] = (unsigned char) elen;
ze->extra[1] = (unsigned char) (elen >> 8);
- memcpy(ze->extra+2, cen + CENHDR + nlen, elen);
+ memcpy(ze->extra+2, extra, elen);
+ if (ze->csize == ZIP64_MAGICVAL || ze->size == ZIP64_MAGICVAL ||
+ locoff == ZIP64_MAGICVAL) {
+ jint off = 0;
+ while ((off + 4) < elen) { // spec: HeaderID+DataSize+Data
+ jint sz = SH(extra, off + 2);
+ if (SH(extra, off) == ZIP64_EXTID) {
+ off += 4;
+ if (ze->size == ZIP64_MAGICVAL) {
+ // if invalid zip64 extra fields, just skip
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->size = LL(extra, off);
+ sz -= 8;
+ off += 8;
+ }
+ if (ze->csize == ZIP64_MAGICVAL) {
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->csize = LL(extra, off);
+ sz -= 8;
+ off += 8;
+ }
+ if (locoff == ZIP64_MAGICVAL) {
+ if (sz < 8 || (off + 8) > elen)
+ break;
+ ze->pos = -(zip->locpos + LL(extra, off));
+ sz -= 8;
+ off += 8;
+ }
+ break;
+ }
+ off += (sz + 4);
+ }
+ }
}
if (clen > 0) {
diff --git a/jdk/src/share/native/java/util/zip/zip_util.h b/jdk/src/share/native/java/util/zip/zip_util.h
index 3814ad0..7ad1171 100644
--- a/jdk/src/share/native/java/util/zip/zip_util.h
+++ b/jdk/src/share/native/java/util/zip/zip_util.h
@@ -38,9 +38,13 @@
#define CENSIG 0x02014b50L /* "PK\001\002" */
#define ENDSIG 0x06054b50L /* "PK\005\006" */
+#define ZIP64_ENDSIG 0x06064b50L /* "PK\006\006" */
+#define ZIP64_LOCSIG 0x07064b50L /* "PK\006\007" */
+
/*
* Header sizes including signatures
*/
+
#ifdef USE_MMAP
#define SIGSIZ 4
#endif
@@ -49,12 +53,22 @@
#define CENHDR 46
#define ENDHDR 22
+#define ZIP64_ENDHDR 56 // ZIP64 end header size
+#define ZIP64_LOCHDR 20 // ZIP64 end loc header size
+#define ZIP64_EXTHDR 24 // EXT header size
+#define ZIP64_EXTID 1 // Extra field Zip64 header ID
+
+#define ZIP64_MAGICVAL 0xffffffffLL
+#define ZIP64_MAGICCOUNT 0xffff
+
+
/*
* Header field access macros
*/
#define CH(b, n) (((unsigned char *)(b))[n])
#define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8))
-#define LG(b, n) (SH(b, n) | (SH(b, n+2) << 16))
+#define LG(b, n) ((SH(b, n) | (SH(b, n+2) << 16)) &0xffffffffUL)
+#define LL(b, n) (((jlong)LG(b, n)) | (((jlong)LG(b, n+4)) << 32))
#define GETSIG(b) LG(b, 0)
/*
@@ -106,6 +120,26 @@
#define ENDCOM(b) SH(b, 20) /* size of zip file comment */
/*
+ * Macros for getting Zip64 end of central directory header fields
+ */
+#define ZIP64_ENDLEN(b) LL(b, 4) /* size of zip64 end of central dir */
+#define ZIP64_ENDVEM(b) SH(b, 12) /* version made by */
+#define ZIP64_ENDVER(b) SH(b, 14) /* version needed to extract */
+#define ZIP64_ENDNMD(b) LG(b, 16) /* number of this disk */
+#define ZIP64_ENDDSK(b) LG(b, 20) /* disk number of start */
+#define ZIP64_ENDTOD(b) LL(b, 24) /* total number of entries on this disk */
+#define ZIP64_ENDTOT(b) LL(b, 32) /* total number of entries */
+#define ZIP64_ENDSIZ(b) LL(b, 40) /* central directory size in bytes */
+#define ZIP64_ENDOFF(b) LL(b, 48) /* offset of first CEN header */
+
+/*
+ * Macros for getting Zip64 end of central directory locator fields
+ */
+#define ZIP64_LOCDSK(b) LG(b, 4) /* disk number start */
+#define ZIP64_LOCOFF(b) LL(b, 8) /* offset of zip64 end */
+#define ZIP64_LOCTOT(b) LG(b, 16) /* total number of disks */
+
+/*
* Supported compression methods
*/
#define STORED 0
@@ -145,7 +179,7 @@
*/
typedef struct jzcell {
unsigned int hash; /* 32 bit hashcode on name */
- unsigned int cenpos; /* Offset of central directory file header */
+ jlong cenpos; /* Offset of central directory file header */
unsigned int next; /* hash chain: index into jzfile->entries */
} jzcell;
diff --git a/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h b/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h
index 9576715..00fdf06 100644
--- a/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h
+++ b/jdk/src/share/native/java/util/zip/zlib-1.1.3/zlib.h
@@ -106,11 +106,11 @@
typedef struct z_stream_s {
Bytef *next_in; /* next input byte */
uInt avail_in; /* number of bytes available at next_in */
- uLong total_in; /* total nb of input bytes read so far */
+ long long total_in; /* total nb of input bytes read so far */
Bytef *next_out; /* next output byte should be put there */
uInt avail_out; /* remaining free space at next_out */
- uLong total_out; /* total nb of bytes output so far */
+ long long total_out; /* total nb of bytes output so far */
char *msg; /* last error message, NULL if no error */
struct internal_state FAR *state; /* not visible by applications */
diff --git a/jdk/test/java/util/zip/LargeZip.java b/jdk/test/java/util/zip/LargeZip.java
new file mode 100644
index 0000000..e499502
--- /dev/null
+++ b/jdk/test/java/util/zip/LargeZip.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.zip.*;
+
+public class LargeZip {
+ // If true, don't delete large ZIP file created for test.
+ static final boolean debug = System.getProperty("debug") != null;
+
+ //static final int DATA_LEN = 1024 * 1024;
+ static final int DATA_LEN = 80 * 1024;
+ static final int DATA_SIZE = 8;
+
+ static long fileSize = 6L * 1024L * 1024L * 1024L; // 6GB
+
+ static boolean userFile = false;
+
+ static byte[] data;
+ static File largeFile;
+ static String lastEntryName;
+
+ /* args can be empty, in which case check a 3 GB file which is created for
+ * this test (and then deleted). Or it can be a number, in which case
+ * that designates the size of the file that's created for this test (and
+ * then deleted). Or it can be the name of a file to use for the test, in
+ * which case it is *not* deleted. Note that in this last case, the data
+ * comparison might fail.
+ */
+ static void realMain (String[] args) throws Throwable {
+ if (args.length > 0) {
+ try {
+ fileSize = Long.parseLong(args[0]);
+ System.out.println("Testing with file of size " + fileSize);
+ } catch (NumberFormatException ex) {
+ largeFile = new File(args[0]);
+ if (!largeFile.exists()) {
+ throw new Exception("Specified file " + args[0] + " does not exist");
+ }
+ userFile = true;
+ System.out.println("Testing with user-provided file " + largeFile);
+ }
+ }
+ File testDir = null;
+ if (largeFile == null) {
+ testDir = new File(System.getProperty("test.scratch", "."),
+ "LargeZip");
+ if (testDir.exists()) {
+ if (!testDir.delete()) {
+ throw new Exception("Cannot delete already-existing test directory");
+ }
+ }
+ check(!testDir.exists() && testDir.mkdirs());
+ largeFile = new File(testDir, "largezip.zip");
+ createLargeZip();
+ }
+
+ readLargeZip1();
+ readLargeZip2();
+
+ if (!userFile && !debug) {
+ check(largeFile.delete());
+ check(testDir.delete());
+ }
+ }
+
+ static void createLargeZip() throws Throwable {
+ int iterations = DATA_LEN / DATA_SIZE;
+ ByteBuffer bb = ByteBuffer.allocate(DATA_SIZE);
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ for (int i = 0; i < iterations; i++) {
+ bb.putDouble(0, Math.random());
+ baos.write(bb.array(), 0, DATA_SIZE);
+ }
+ data = baos.toByteArray();
+
+ ZipOutputStream zos = new ZipOutputStream(
+ new BufferedOutputStream(new FileOutputStream(largeFile)));
+ long length = 0;
+ while (length < fileSize) {
+ ZipEntry ze = new ZipEntry("entry-" + length);
+ lastEntryName = ze.getName();
+ zos.putNextEntry(ze);
+ zos.write(data, 0, data.length);
+ zos.closeEntry();
+ length = largeFile.length();
+ }
+ System.out.println("Last entry written is " + lastEntryName);
+ zos.close();
+ }
+
+ static void readLargeZip1() throws Throwable {
+ ZipFile zipFile = new ZipFile(largeFile);
+ ZipEntry entry = null;
+ String entryName = null;
+ int count = 0;
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ entry = entries.nextElement();
+ entryName = entry.getName();
+ count++;
+ }
+ System.out.println("Number of entries read: " + count);
+ System.out.println("Last entry read is " + entryName);
+ check(!entry.isDirectory());
+ if (check(entryName.equals(lastEntryName))) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ InputStream is = zipFile.getInputStream(entry);
+ byte buf[] = new byte[4096];
+ int len;
+ while ((len = is.read(buf)) >= 0) {
+ baos.write(buf, 0, len);
+ }
+ baos.close();
+ is.close();
+ check(Arrays.equals(data, baos.toByteArray()));
+ }
+ }
+
+
+ static void readLargeZip2() throws Throwable {
+ ZipInputStream zis = new ZipInputStream(
+ new BufferedInputStream(new FileInputStream(largeFile)));
+ ZipEntry entry = null;
+ String entryName = null;
+ int count = 0;
+ while ((entry = zis.getNextEntry()) != null) {
+ entryName = entry.getName();
+ if (entryName.equals(lastEntryName)) {
+ break;
+ }
+ count++;
+ }
+ System.out.println("Number of entries read: " + count);
+ System.out.println("Last entry read is " + entryName);
+ check(!entry.isDirectory());
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ byte buf[] = new byte[4096];
+ int len;
+ while ((len = zis.read(buf)) >= 0) {
+ baos.write(buf, 0, len);
+ }
+ baos.close();
+ check(Arrays.equals(data, baos.toByteArray()));
+ check(zis.getNextEntry() == null);
+ zis.close();
+ }
+
+
+ //--------------------- Infrastructure ---------------------------
+ static volatile int passed = 0, failed = 0;
+ static void pass() {passed++;}
+ static void pass(String msg) {System.out.println(msg); passed++;}
+ static void fail() {failed++; Thread.dumpStack();}
+ static void fail(String msg) {System.out.println(msg); fail();}
+ static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+ static void unexpected(Throwable t, String msg) {
+ System.out.println(msg); failed++; t.printStackTrace();}
+ static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;}
+ static void equal(Object x, Object y) {
+ if (x == null ? y == null : x.equals(y)) pass();
+ else fail(x + " not equal to " + y);}
+ public static void main(String[] args) throws Throwable {
+ try {realMain(args);} catch (Throwable t) {unexpected(t);}
+ System.out.println("\nPassed = " + passed + " failed = " + failed);
+ if (failed > 0) throw new AssertionError("Some tests failed");}
+}
diff --git a/jdk/test/java/util/zip/ZipFile/LargeZipFile.java b/jdk/test/java/util/zip/ZipFile/LargeZipFile.java
index 479bac4..d228a5f 100644
--- a/jdk/test/java/util/zip/ZipFile/LargeZipFile.java
+++ b/jdk/test/java/util/zip/ZipFile/LargeZipFile.java
@@ -158,4 +158,3 @@
System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}
-