summaryrefslogtreecommitdiff
path: root/java/util/zip/ZipFile.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/util/zip/ZipFile.java')
-rw-r--r--java/util/zip/ZipFile.java397
1 files changed, 204 insertions, 193 deletions
diff --git a/java/util/zip/ZipFile.java b/java/util/zip/ZipFile.java
index 4be845ea7..7307ee9a4 100644
--- a/java/util/zip/ZipFile.java
+++ b/java/util/zip/ZipFile.java
@@ -1,5 +1,5 @@
/* ZipFile.java --
- Copyright (C) 2001, 2002, 2003, 2004, 2005
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -41,8 +41,6 @@ package java.util.zip;
import gnu.java.util.EmptyEnumeration;
-import java.io.BufferedInputStream;
-import java.io.DataInput;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
@@ -141,23 +139,33 @@ public class ZipFile implements ZipConstants
checkZipFile();
}
- private void checkZipFile() throws IOException, ZipException
+ private void checkZipFile() throws ZipException
{
- byte[] magicBuf = new byte[4];
- boolean validRead = true;
+ boolean valid = false;
try
{
- raf.readFully(magicBuf);
- }
- catch (EOFException eof)
+ byte[] buf = new byte[4];
+ raf.readFully(buf);
+ int sig = buf[0] & 0xFF
+ | ((buf[1] & 0xFF) << 8)
+ | ((buf[2] & 0xFF) << 16)
+ | ((buf[3] & 0xFF) << 24);
+ valid = sig == LOCSIG;
+ }
+ catch (IOException _)
{
- validRead = false;
}
- if (validRead == false || readLeInt(magicBuf, 0) != LOCSIG)
+ if (!valid)
{
- raf.close();
+ try
+ {
+ raf.close();
+ }
+ catch (IOException _)
+ {
+ }
throw new ZipException("Not a valid zip file");
}
}
@@ -172,69 +180,6 @@ public class ZipFile implements ZipConstants
}
/**
- * Read an unsigned short in little endian byte order from the given
- * DataInput stream using the given byte buffer.
- *
- * @param di DataInput stream to read from.
- * @param b the byte buffer to read in (must be at least 2 bytes long).
- * @return The value read.
- *
- * @exception IOException if a i/o error occured.
- * @exception EOFException if the file ends prematurely
- */
- private int readLeShort(DataInput di, byte[] b) throws IOException
- {
- di.readFully(b, 0, 2);
- return (b[0] & 0xff) | (b[1] & 0xff) << 8;
- }
-
- /**
- * Read an int in little endian byte order from the given
- * DataInput stream using the given byte buffer.
- *
- * @param di DataInput stream to read from.
- * @param b the byte buffer to read in (must be at least 4 bytes long).
- * @return The value read.
- *
- * @exception IOException if a i/o error occured.
- * @exception EOFException if the file ends prematurely
- */
- private int readLeInt(DataInput di, byte[] b) throws IOException
- {
- di.readFully(b, 0, 4);
- return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
- | ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
- }
-
- /**
- * Read an unsigned short in little endian byte order from the given
- * byte buffer at the given offset.
- *
- * @param b the byte array to read from.
- * @param off the offset to read from.
- * @return The value read.
- */
- private int readLeShort(byte[] b, int off)
- {
- return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
- }
-
- /**
- * Read an int in little endian byte order from the given
- * byte buffer at the given offset.
- *
- * @param b the byte array to read from.
- * @param off the offset to read from.
- * @return The value read.
- */
- private int readLeInt(byte[] b, int off)
- {
- return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
- | ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
- }
-
-
- /**
* Read the central directory of a zip file and fill the entries
* array. This is called exactly once when first needed. It is called
* while holding the lock on <code>raf</code>.
@@ -246,63 +191,48 @@ public class ZipFile implements ZipConstants
{
/* Search for the End Of Central Directory. When a zip comment is
* present the directory may start earlier.
- * FIXME: This searches the whole file in a very slow manner if the
- * file isn't a zip file.
+ * Note that a comment has a maximum length of 64K, so that is the
+ * maximum we search backwards.
*/
+ PartialInputStream inp = new PartialInputStream(raf, 4096);
long pos = raf.length() - ENDHDR;
- byte[] ebs = new byte[CENHDR];
-
+ long top = Math.max(0, pos - 65536);
do
{
- if (pos < 0)
+ if (pos < top)
throw new ZipException
("central directory not found, probably not a zip file: " + name);
- raf.seek(pos--);
+ inp.seek(pos--);
}
- while (readLeInt(raf, ebs) != ENDSIG);
+ while (inp.readLeInt() != ENDSIG);
- if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
+ if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
throw new EOFException(name);
- int count = readLeShort(raf, ebs);
- if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
+ int count = inp.readLeShort();
+ if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
throw new EOFException(name);
- int centralOffset = readLeInt(raf, ebs);
+ int centralOffset = inp.readLeInt();
entries = new HashMap(count+count/2);
- raf.seek(centralOffset);
+ inp.seek(centralOffset);
- byte[] buffer = new byte[16];
for (int i = 0; i < count; i++)
{
- raf.readFully(ebs);
- if (readLeInt(ebs, 0) != CENSIG)
+ if (inp.readLeInt() != CENSIG)
throw new ZipException("Wrong Central Directory signature: " + name);
- int method = readLeShort(ebs, CENHOW);
- int dostime = readLeInt(ebs, CENTIM);
- int crc = readLeInt(ebs, CENCRC);
- int csize = readLeInt(ebs, CENSIZ);
- int size = readLeInt(ebs, CENLEN);
- int nameLen = readLeShort(ebs, CENNAM);
- int extraLen = readLeShort(ebs, CENEXT);
- int commentLen = readLeShort(ebs, CENCOM);
-
- int offset = readLeInt(ebs, CENOFF);
-
- int needBuffer = Math.max(nameLen, commentLen);
- if (buffer.length < needBuffer)
- buffer = new byte[needBuffer];
-
- raf.readFully(buffer, 0, nameLen);
- String name;
- try
- {
- name = new String(buffer, 0, nameLen, "UTF-8");
- }
- catch (UnsupportedEncodingException uee)
- {
- throw new AssertionError(uee);
- }
+ inp.skip(6);
+ int method = inp.readLeShort();
+ int dostime = inp.readLeInt();
+ int crc = inp.readLeInt();
+ int csize = inp.readLeInt();
+ int size = inp.readLeInt();
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ int commentLen = inp.readLeShort();
+ inp.skip(8);
+ int offset = inp.readLeInt();
+ String name = inp.readString(nameLen);
ZipEntry entry = new ZipEntry(name);
entry.setMethod(method);
@@ -313,20 +243,12 @@ public class ZipFile implements ZipConstants
if (extraLen > 0)
{
byte[] extra = new byte[extraLen];
- raf.readFully(extra);
+ inp.readFully(extra);
entry.setExtra(extra);
}
if (commentLen > 0)
{
- raf.readFully(buffer, 0, commentLen);
- try
- {
- entry.setComment(new String(buffer, 0, commentLen, "UTF-8"));
- }
- catch (UnsupportedEncodingException uee)
- {
- throw new AssertionError(uee);
- }
+ entry.setComment(inp.readString(commentLen));
}
entry.offset = offset;
entries.put(name, entry);
@@ -429,42 +351,6 @@ public class ZipFile implements ZipConstants
}
}
-
- //access should be protected by synchronized(raf)
- private byte[] locBuf = new byte[LOCHDR];
-
- /**
- * Checks, if the local header of the entry at index i matches the
- * central directory, and returns the offset to the data.
- *
- * @param entry to check.
- * @return the start offset of the (compressed) data.
- *
- * @exception IOException if a i/o error occured.
- * @exception ZipException if the local header doesn't match the
- * central directory header
- */
- private long checkLocalHeader(ZipEntry entry) throws IOException
- {
- synchronized (raf)
- {
- raf.seek(entry.offset);
- raf.readFully(locBuf);
-
- if (readLeInt(locBuf, 0) != LOCSIG)
- throw new ZipException("Wrong Local header signature: " + name);
-
- if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
- throw new ZipException("Compression method mismatch: " + name);
-
- if (entry.getName().length() != readLeShort(locBuf, LOCNAM))
- throw new ZipException("file name length mismatch: " + name);
-
- int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT);
- return entry.offset + LOCHDR + extraLen;
- }
- }
-
/**
* Creates an input stream reading the given zip entry as
* uncompressed data. Normally zip entry should be an entry
@@ -497,16 +383,32 @@ public class ZipFile implements ZipConstants
if (zipEntry == null)
return null;
- long start = checkLocalHeader(zipEntry);
+ PartialInputStream inp = new PartialInputStream(raf, 1024);
+ inp.seek(zipEntry.offset);
+
+ if (inp.readLeInt() != LOCSIG)
+ throw new ZipException("Wrong Local header signature: " + name);
+
+ inp.skip(4);
+
+ if (zipEntry.getMethod() != inp.readLeShort())
+ throw new ZipException("Compression method mismatch: " + name);
+
+ inp.skip(16);
+
+ int nameLen = inp.readLeShort();
+ int extraLen = inp.readLeShort();
+ inp.skip(nameLen + extraLen);
+
+ inp.setLength(zipEntry.getCompressedSize());
+
int method = zipEntry.getMethod();
- InputStream is = new BufferedInputStream(new PartialInputStream
- (raf, start, zipEntry.getCompressedSize()));
switch (method)
{
case ZipOutputStream.STORED:
- return is;
+ return inp;
case ZipOutputStream.DEFLATED:
- return new InflaterInputStream(is, new Inflater(true));
+ return new InflaterInputStream(inp, new Inflater(true));
default:
throw new ZipException("Unknown compression method " + method);
}
@@ -562,21 +464,41 @@ public class ZipFile implements ZipConstants
}
}
- private static class PartialInputStream extends InputStream
+ private static final class PartialInputStream extends InputStream
{
private final RandomAccessFile raf;
- long filepos, end;
+ private final byte[] buffer;
+ private long bufferOffset;
+ private int pos;
+ private long end;
- public PartialInputStream(RandomAccessFile raf, long start, long len)
+ public PartialInputStream(RandomAccessFile raf, int bufferSize)
+ throws IOException
{
this.raf = raf;
- filepos = start;
- end = start + len;
+ buffer = new byte[bufferSize];
+ bufferOffset = -buffer.length;
+ pos = buffer.length;
+ end = raf.length();
+ }
+
+ void setLength(long length)
+ {
+ end = bufferOffset + pos + length;
+ }
+
+ private void fillBuffer() throws IOException
+ {
+ synchronized (raf)
+ {
+ raf.seek(bufferOffset);
+ raf.readFully(buffer, 0, (int) Math.min(buffer.length, end - bufferOffset));
+ }
}
public int available()
{
- long amount = end - filepos;
+ long amount = end - (bufferOffset + pos);
if (amount > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
return (int) amount;
@@ -584,41 +506,130 @@ public class ZipFile implements ZipConstants
public int read() throws IOException
{
- if (filepos == end)
+ if (bufferOffset + pos >= end)
return -1;
- synchronized (raf)
- {
- raf.seek(filepos++);
- return raf.read();
- }
+ if (pos == buffer.length)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ }
+ return buffer[pos++] & 0xFF;
}
public int read(byte[] b, int off, int len) throws IOException
{
- if (len > end - filepos)
+ if (len > end - (bufferOffset + pos))
{
- len = (int) (end - filepos);
+ len = (int) (end - (bufferOffset + pos));
if (len == 0)
return -1;
}
- synchronized (raf)
- {
- raf.seek(filepos);
- int count = raf.read(b, off, len);
- if (count > 0)
- filepos += len;
- return count;
- }
+
+ int totalBytesRead = Math.min(buffer.length - pos, len);
+ System.arraycopy(buffer, pos, b, off, totalBytesRead);
+ pos += totalBytesRead;
+ off += totalBytesRead;
+ len -= totalBytesRead;
+
+ while (len > 0)
+ {
+ bufferOffset += buffer.length;
+ pos = 0;
+ fillBuffer();
+ int remain = Math.min(buffer.length, len);
+ System.arraycopy(buffer, pos, b, off, remain);
+ pos += remain;
+ off += remain;
+ len -= remain;
+ totalBytesRead += remain;
+ }
+
+ return totalBytesRead;
}
- public long skip(long amount)
+ public long skip(long amount) throws IOException
{
if (amount < 0)
- throw new IllegalArgumentException();
- if (amount > end - filepos)
- amount = end - filepos;
- filepos += amount;
+ return 0;
+ if (amount > end - (bufferOffset + pos))
+ amount = end - (bufferOffset + pos);
+ seek(bufferOffset + pos + amount);
return amount;
}
+
+ void seek(long newpos) throws IOException
+ {
+ long offset = newpos - bufferOffset;
+ if (offset >= 0 && offset <= buffer.length)
+ {
+ pos = (int) offset;
+ }
+ else
+ {
+ bufferOffset = newpos;
+ pos = 0;
+ fillBuffer();
+ }
+ }
+
+ void readFully(byte[] buf) throws IOException
+ {
+ if (read(buf, 0, buf.length) != buf.length)
+ throw new EOFException();
+ }
+
+ void readFully(byte[] buf, int off, int len) throws IOException
+ {
+ if (read(buf, off, len) != len)
+ throw new EOFException();
+ }
+
+ int readLeShort() throws IOException
+ {
+ int b0 = read();
+ int b1 = read();
+ if (b1 == -1)
+ throw new EOFException();
+ return (b0 & 0xff) | (b1 & 0xff) << 8;
+ }
+
+ int readLeInt() throws IOException
+ {
+ int b0 = read();
+ int b1 = read();
+ int b2 = read();
+ int b3 = read();
+ if (b3 == -1)
+ throw new EOFException();
+ return ((b0 & 0xff) | (b1 & 0xff) << 8)
+ | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
+ }
+
+ String readString(int length) throws IOException
+ {
+ if (length > end - (bufferOffset + pos))
+ throw new EOFException();
+
+ try
+ {
+ if (buffer.length - pos >= length)
+ {
+ String s = new String(buffer, pos, length, "UTF-8");
+ pos += length;
+ return s;
+ }
+ else
+ {
+ byte[] b = new byte[length];
+ readFully(b);
+ return new String(b, 0, length, "UTF-8");
+ }
+ }
+ catch (UnsupportedEncodingException uee)
+ {
+ throw new AssertionError(uee);
+ }
+ }
}
}