提交 c731c28a 编写于 作者: S sherman

4681995: Add support for large (> 4GB) zip/jar files

Summary: The ZIP64 format support is added for > 4GB jar/zip files
Reviewed-by: alanb, martin
上级 7082d981
/*
* 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() {}
}
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -144,11 +144,13 @@ class ZipEntry implements ZipConstants, Cloneable { ...@@ -144,11 +144,13 @@ class ZipEntry implements ZipConstants, Cloneable {
* Sets the uncompressed size of the entry data. * Sets the uncompressed size of the entry data.
* @param size the uncompressed size in bytes * @param size the uncompressed size in bytes
* @exception IllegalArgumentException if the specified size is less * @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() * @see #getSize()
*/ */
public void setSize(long size) { public void setSize(long size) {
if (size < 0 || size > 0xFFFFFFFFL) { if (size < 0) {
throw new IllegalArgumentException("invalid entry size"); throw new IllegalArgumentException("invalid entry size");
} }
this.size = size; this.size = size;
......
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ import java.io.InputStream; ...@@ -29,6 +29,7 @@ import java.io.InputStream;
import java.io.IOException; import java.io.IOException;
import java.io.EOFException; import java.io.EOFException;
import java.io.PushbackInputStream; import java.io.PushbackInputStream;
import static java.util.zip.ZipConstants64.*;
/** /**
* This class implements an input stream filter for reading files in the * This class implements an input stream filter for reading files in the
...@@ -285,6 +286,29 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { ...@@ -285,6 +286,29 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
byte[] bb = new byte[len]; byte[] bb = new byte[len];
readFully(bb, 0, len); readFully(bb, 0, len);
e.setExtra(bb); 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; return e;
} }
...@@ -375,6 +399,23 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { ...@@ -375,6 +399,23 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
} }
if ((flag & 8) == 8) { if ((flag & 8) == 8) {
/* "Data Descriptor" present */ /* "Data Descriptor" present */
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 {
readFully(tmpbuf, 0, EXTHDR); readFully(tmpbuf, 0, EXTHDR);
long sig = get32(tmpbuf, 0); long sig = get32(tmpbuf, 0);
if (sig != EXTSIG) { // no EXTSIG present if (sig != EXTSIG) { // no EXTSIG present
...@@ -389,6 +430,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { ...@@ -389,6 +430,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
e.size = get32(tmpbuf, EXTLEN); e.size = get32(tmpbuf, EXTLEN);
} }
} }
}
if (e.size != inf.getBytesWritten()) { if (e.size != inf.getBytesWritten()) {
throw new ZipException( throw new ZipException(
"invalid entry size (expected " + e.size + "invalid entry size (expected " + e.size +
...@@ -433,6 +475,14 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants { ...@@ -433,6 +475,14 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
* The bytes are assumed to be in Intel (little-endian) byte order. * The bytes are assumed to be in Intel (little-endian) byte order.
*/ */
private static final long get32(byte b[], int off) { 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);
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -29,6 +29,7 @@ import java.io.OutputStream; ...@@ -29,6 +29,7 @@ import java.io.OutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Vector; import java.util.Vector;
import java.util.HashSet; import java.util.HashSet;
import static java.util.zip.ZipConstants64.*;
/** /**
* This class implements an output stream filter for writing files in the * This class implements an output stream filter for writing files in the
...@@ -343,26 +344,52 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -343,26 +344,52 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
private void writeLOC(XEntry xentry) throws IOException { private void writeLOC(XEntry xentry) throws IOException {
ZipEntry e = xentry.entry; ZipEntry e = xentry.entry;
int flag = xentry.flag; int flag = xentry.flag;
int elen = (e.extra != null) ? e.extra.length : 0;
boolean hasZip64 = false;
writeInt(LOCSIG); // LOC header signature writeInt(LOCSIG); // LOC header signature
if ((flag & 8) == 8) {
writeShort(version(e)); // version needed to extract writeShort(version(e)); // version needed to extract
writeShort(flag); // general purpose bit flag writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method writeShort(e.method); // compression method
writeInt(e.time); // last modification time writeInt(e.time); // last modification time
if ((flag & 8) == 8) {
// store size, uncompressed size, and crc-32 in data descriptor // store size, uncompressed size, and crc-32 in data descriptor
// immediately following compressed entry data // immediately following compressed entry data
writeInt(0); writeInt(0);
writeInt(0); writeInt(0);
writeInt(0); writeInt(0);
} else { } else {
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 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.csize); // compressed size
writeInt(e.size); // uncompressed size writeInt(e.size); // uncompressed size
} }
}
byte[] nameBytes = getUTF8Bytes(e.name); byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length); writeShort(nameBytes.length);
writeShort(e.extra != null ? e.extra.length : 0); writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length); writeBytes(nameBytes, 0, nameBytes.length);
if (hasZip64) {
writeShort(ZIP64_EXTID);
writeShort(16);
writeLong(e.size);
writeLong(e.csize);
}
if (e.extra != null) { if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length); writeBytes(e.extra, 0, e.extra.length);
} }
...@@ -375,9 +402,14 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -375,9 +402,14 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
private void writeEXT(ZipEntry e) throws IOException { private void writeEXT(ZipEntry e) throws IOException {
writeInt(EXTSIG); // EXT header signature writeInt(EXTSIG); // EXT header signature
writeInt(e.crc); // crc-32 writeInt(e.crc); // crc-32
if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
writeLong(e.csize);
writeLong(e.size);
} else {
writeInt(e.csize); // compressed size writeInt(e.csize); // compressed size
writeInt(e.size); // uncompressed size writeInt(e.size); // uncompressed size
} }
}
/* /*
* Write central directory (CEN) header for specified entry. * Write central directory (CEN) header for specified entry.
...@@ -387,18 +419,49 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -387,18 +419,49 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
ZipEntry e = xentry.entry; ZipEntry e = xentry.entry;
int flag = xentry.flag; int flag = xentry.flag;
int version = version(e); 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 writeInt(CENSIG); // CEN header signature
if (hasZip64) {
writeShort(45); // ver 4.5 for zip64
writeShort(45);
} else {
writeShort(version); // version made by writeShort(version); // version made by
writeShort(version); // version needed to extract writeShort(version); // version needed to extract
}
writeShort(flag); // general purpose bit flag writeShort(flag); // general purpose bit flag
writeShort(e.method); // compression method writeShort(e.method); // compression method
writeInt(e.time); // last modification time writeInt(e.time); // last modification time
writeInt(e.crc); // crc-32 writeInt(e.crc); // crc-32
writeInt(e.csize); // compressed size writeInt(csize); // compressed size
writeInt(e.size); // uncompressed size writeInt(size); // uncompressed size
byte[] nameBytes = getUTF8Bytes(e.name); byte[] nameBytes = getUTF8Bytes(e.name);
writeShort(nameBytes.length); writeShort(nameBytes.length);
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); writeShort(e.extra != null ? e.extra.length : 0);
}
byte[] commentBytes; byte[] commentBytes;
if (e.comment != null) { if (e.comment != null) {
commentBytes = getUTF8Bytes(e.comment); commentBytes = getUTF8Bytes(e.comment);
...@@ -410,8 +473,18 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -410,8 +473,18 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
writeShort(0); // starting disk number writeShort(0); // starting disk number
writeShort(0); // internal file attributes (unused) writeShort(0); // internal file attributes (unused)
writeInt(0); // external 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); 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) { if (e.extra != null) {
writeBytes(e.extra, 0, e.extra.length); writeBytes(e.extra, 0, e.extra.length);
} }
...@@ -424,14 +497,49 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -424,14 +497,49 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
* Writes end of central directory (END) header. * Writes end of central directory (END) header.
*/ */
private void writeEND(long off, long len) throws IOException { 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(); int count = xentries.size();
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 writeInt(ENDSIG); // END record signature
writeShort(0); // number of this disk writeShort(0); // number of this disk
writeShort(0); // central directory start disk writeShort(0); // central directory start disk
writeShort(count); // number of directory entries on disk writeShort(count); // number of directory entries on disk
writeShort(count); // total number of directory entries writeShort(count); // total number of directory entries
writeInt(len); // length of central directory writeInt(xlen); // length of central directory
writeInt(off); // offset of central directory writeInt(xoff); // offset of central directory
if (comment != null) { // zip file comment if (comment != null) { // zip file comment
byte[] b = getUTF8Bytes(comment); byte[] b = getUTF8Bytes(comment);
writeShort(b.length); writeShort(b.length);
...@@ -463,6 +571,22 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants { ...@@ -463,6 +571,22 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
written += 4; written += 4;
} }
/*
* 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. * Writes an array of bytes to the output stream.
*/ */
......
...@@ -45,6 +45,13 @@ input streams. ...@@ -45,6 +45,13 @@ input streams.
Info-ZIP Application Note 970311 Info-ZIP Application Note 970311
</a> - a detailed description of the Info-ZIP format upon which </a> - a detailed description of the Info-ZIP format upon which
the <code>java.util.zip</code> classes are based. 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> <p>
<li><a href="http://www.isi.edu/in-notes/rfc1950.txt"> <li><a href="http://www.isi.edu/in-notes/rfc1950.txt">
ZLIB Compressed Data Format Specification version 3.3</a> ZLIB Compressed Data Format Specification version 3.3</a>
...@@ -70,7 +77,6 @@ input streams. ...@@ -70,7 +77,6 @@ input streams.
<li>CRC-32 checksum is described in RFC 1952 (above) <li>CRC-32 checksum is described in RFC 1952 (above)
<p> <p>
<li>Adler-32 checksum is described in RFC 1950 (above) <li>Adler-32 checksum is described in RFC 1950 (above)
</ul> </ul>
......
...@@ -312,6 +312,38 @@ findEND(jzfile *zip, void *endbuf) ...@@ -312,6 +312,38 @@ findEND(jzfile *zip, void *endbuf)
return -1; /* END header not found */ return -1; /* END header not found */
} }
/*
* 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. * Returns a hash code value for a C-style NUL-terminated string.
*/ */
...@@ -463,7 +495,7 @@ static jlong ...@@ -463,7 +495,7 @@ static jlong
readCEN(jzfile *zip, jint knownTotal) readCEN(jzfile *zip, jint knownTotal)
{ {
/* Following are unsigned 32-bit */ /* Following are unsigned 32-bit */
jlong endpos, cenpos, cenlen; jlong endpos, end64pos, cenpos, cenlen, cenoff;
/* Following are unsigned 16-bit */ /* Following are unsigned 16-bit */
jint total, tablelen, i, j; jint total, tablelen, i, j;
unsigned char *cenbuf = NULL; unsigned char *cenbuf = NULL;
...@@ -474,6 +506,7 @@ readCEN(jzfile *zip, jint knownTotal) ...@@ -474,6 +506,7 @@ readCEN(jzfile *zip, jint knownTotal)
jlong offset; jlong offset;
#endif #endif
unsigned char endbuf[ENDHDR]; unsigned char endbuf[ENDHDR];
jint endhdrlen = ENDHDR;
jzcell *entries; jzcell *entries;
jint *table; jint *table;
...@@ -490,13 +523,27 @@ readCEN(jzfile *zip, jint knownTotal) ...@@ -490,13 +523,27 @@ readCEN(jzfile *zip, jint knownTotal)
/* Get position and length of central directory */ /* Get position and length of central directory */
cenlen = ENDSIZ(endbuf); 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) if (cenlen > endpos)
ZIP_FORMAT_ERROR("invalid END header (bad central directory size)"); ZIP_FORMAT_ERROR("invalid END header (bad central directory size)");
cenpos = endpos - cenlen; cenpos = endpos - cenlen;
/* Get position of first local file (LOC) header, taking into /* Get position of first local file (LOC) header, taking into
* account that there may be a stub prefixed to the zip file. */ * 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) if (zip->locpos < 0)
ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)"); ZIP_FORMAT_ERROR("invalid END header (bad central directory offset)");
...@@ -527,7 +574,7 @@ readCEN(jzfile *zip, jint knownTotal) ...@@ -527,7 +574,7 @@ readCEN(jzfile *zip, jint knownTotal)
out the page size in order to make offset to be multiples of out the page size in order to make offset to be multiples of
page size. page size.
*/ */
zip->mlen = cenpos - offset + cenlen + ENDHDR; zip->mlen = cenpos - offset + cenlen + endhdrlen;
zip->offset = offset; zip->offset = offset;
mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset); mappedAddr = mmap64(0, zip->mlen, PROT_READ, MAP_SHARED, zip->zfd, (off64_t) offset);
zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL : zip->maddr = (mappedAddr == (void*) MAP_FAILED) ? NULL :
...@@ -551,8 +598,13 @@ readCEN(jzfile *zip, jint knownTotal) ...@@ -551,8 +598,13 @@ readCEN(jzfile *zip, jint knownTotal)
* is a 2-byte field, but we (and other zip implementations) * is a 2-byte field, but we (and other zip implementations)
* support approx. 2**31 entries, we do not trust ENDTOT, but * support approx. 2**31 entries, we do not trust ENDTOT, but
* treat it only as a strong hint. When we call ourselves * treat it only as a strong hint. When we call ourselves
* recursively, knownTotal will have the "true" value. */ * recursively, knownTotal will have the "true" value.
total = (knownTotal != -1) ? knownTotal : ENDTOT(endbuf); *
* 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])); entries = zip->entries = calloc(total, sizeof(entries[0]));
tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions tablelen = zip->tablelen = ((total/2) | 1); // Odd -> fewer collisions
table = zip->table = malloc(tablelen * sizeof(table[0])); table = zip->table = malloc(tablelen * sizeof(table[0]));
...@@ -854,6 +906,7 @@ typedef enum { ACCESS_RANDOM, ACCESS_SEQUENTIAL } AccessHint; ...@@ -854,6 +906,7 @@ typedef enum { ACCESS_RANDOM, ACCESS_SEQUENTIAL } AccessHint;
static jzentry * static jzentry *
newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint) newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
{ {
jlong locoff;
jint nlen, elen, clen; jint nlen, elen, clen;
jzentry *ze; jzentry *ze;
char *cen; char *cen;
...@@ -880,18 +933,55 @@ newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint) ...@@ -880,18 +933,55 @@ newEntry(jzfile *zip, jzcell *zc, AccessHint accessHint)
ze->size = CENLEN(cen); ze->size = CENLEN(cen);
ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen); ze->csize = (CENHOW(cen) == STORED) ? 0 : CENSIZ(cen);
ze->crc = CENCRC(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; if ((ze->name = malloc(nlen + 1)) == NULL) goto Catch;
memcpy(ze->name, cen + CENHDR, nlen); memcpy(ze->name, cen + CENHDR, nlen);
ze->name[nlen] = '\0'; ze->name[nlen] = '\0';
if (elen > 0) { if (elen > 0) {
char *extra = cen + CENHDR + nlen;
/* This entry has "extra" data */ /* This entry has "extra" data */
if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch; if ((ze->extra = malloc(elen + 2)) == NULL) goto Catch;
ze->extra[0] = (unsigned char) elen; ze->extra[0] = (unsigned char) elen;
ze->extra[1] = (unsigned char) (elen >> 8); 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) { if (clen > 0) {
......
...@@ -38,9 +38,13 @@ ...@@ -38,9 +38,13 @@
#define CENSIG 0x02014b50L /* "PK\001\002" */ #define CENSIG 0x02014b50L /* "PK\001\002" */
#define ENDSIG 0x06054b50L /* "PK\005\006" */ #define ENDSIG 0x06054b50L /* "PK\005\006" */
#define ZIP64_ENDSIG 0x06064b50L /* "PK\006\006" */
#define ZIP64_LOCSIG 0x07064b50L /* "PK\006\007" */
/* /*
* Header sizes including signatures * Header sizes including signatures
*/ */
#ifdef USE_MMAP #ifdef USE_MMAP
#define SIGSIZ 4 #define SIGSIZ 4
#endif #endif
...@@ -49,12 +53,22 @@ ...@@ -49,12 +53,22 @@
#define CENHDR 46 #define CENHDR 46
#define ENDHDR 22 #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 * Header field access macros
*/ */
#define CH(b, n) (((unsigned char *)(b))[n]) #define CH(b, n) (((unsigned char *)(b))[n])
#define SH(b, n) (CH(b, n) | (CH(b, n+1) << 8)) #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) #define GETSIG(b) LG(b, 0)
/* /*
...@@ -105,6 +119,26 @@ ...@@ -105,6 +119,26 @@
#define ENDOFF(b) LG(b, 16) /* central directory offset */ #define ENDOFF(b) LG(b, 16) /* central directory offset */
#define ENDCOM(b) SH(b, 20) /* size of zip file comment */ #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 * Supported compression methods
*/ */
...@@ -145,7 +179,7 @@ typedef struct jzentry { /* Zip file entry */ ...@@ -145,7 +179,7 @@ typedef struct jzentry { /* Zip file entry */
*/ */
typedef struct jzcell { typedef struct jzcell {
unsigned int hash; /* 32 bit hashcode on name */ 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 */ unsigned int next; /* hash chain: index into jzfile->entries */
} jzcell; } jzcell;
......
...@@ -106,11 +106,11 @@ struct internal_state; ...@@ -106,11 +106,11 @@ struct internal_state;
typedef struct z_stream_s { typedef struct z_stream_s {
Bytef *next_in; /* next input byte */ Bytef *next_in; /* next input byte */
uInt avail_in; /* number of bytes available at next_in */ 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 */ Bytef *next_out; /* next output byte should be put there */
uInt avail_out; /* remaining free space at next_out */ 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 */ char *msg; /* last error message, NULL if no error */
struct internal_state FAR *state; /* not visible by applications */ struct internal_state FAR *state; /* not visible by applications */
......
/*
* 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");}
}
...@@ -158,4 +158,3 @@ public class LargeZipFile { ...@@ -158,4 +158,3 @@ public class LargeZipFile {
System.out.println("\nPassed = " + passed + " failed = " + failed); System.out.println("\nPassed = " + passed + " failed = " + failed);
if (failed > 0) throw new AssertionError("Some tests failed");} if (failed > 0) throw new AssertionError("Some tests failed");}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册