89
93
private boolean closed = false;
97
* Helper function to open RandomAccessFile and throw the proper
98
* ZipException in case opening the file fails.
100
* @param name the file name, or null if file is provided
102
* @param file the file, or null if name is provided
104
* @return the newly open RandomAccessFile, never null
106
private RandomAccessFile openFile(String name,
108
throws ZipException, IOException
114
? new RandomAccessFile(name, "r")
115
: new RandomAccessFile(file, "r");
117
catch (FileNotFoundException f)
119
ZipException ze = new ZipException(f.getMessage());
92
127
* Opens a Zip file with the given name for reading.
93
128
* @exception IOException if a i/o error occured.
136
171
throw new IllegalArgumentException("invalid mode");
137
172
if ((mode & OPEN_DELETE) != 0)
138
173
file.deleteOnExit();
139
this.raf = new RandomAccessFile(file, "r");
174
this.raf = openFile(null,file);
140
175
this.name = file.getPath();
144
private void checkZipFile() throws IOException, ZipException
179
private void checkZipFile() throws ZipException
146
byte[] magicBuf = new byte[4];
147
boolean validRead = true;
181
boolean valid = false;
151
raf.readFully(magicBuf);
153
catch (EOFException eof)
185
byte[] buf = new byte[4];
187
int sig = buf[0] & 0xFF
188
| ((buf[1] & 0xFF) << 8)
189
| ((buf[2] & 0xFF) << 16)
190
| ((buf[3] & 0xFF) << 24);
191
valid = sig == LOCSIG;
193
catch (IOException _)
158
if (validRead == false || readLeInt(magicBuf, 0) != LOCSIG)
203
catch (IOException _)
161
206
throw new ZipException("Not a valid zip file");
175
* Read an unsigned short in little endian byte order from the given
176
* DataInput stream using the given byte buffer.
178
* @param di DataInput stream to read from.
179
* @param b the byte buffer to read in (must be at least 2 bytes long).
180
* @return The value read.
182
* @exception IOException if a i/o error occured.
183
* @exception EOFException if the file ends prematurely
185
private int readLeShort(DataInput di, byte[] b) throws IOException
187
di.readFully(b, 0, 2);
188
return (b[0] & 0xff) | (b[1] & 0xff) << 8;
192
* Read an int in little endian byte order from the given
193
* DataInput stream using the given byte buffer.
195
* @param di DataInput stream to read from.
196
* @param b the byte buffer to read in (must be at least 4 bytes long).
197
* @return The value read.
199
* @exception IOException if a i/o error occured.
200
* @exception EOFException if the file ends prematurely
202
private int readLeInt(DataInput di, byte[] b) throws IOException
204
di.readFully(b, 0, 4);
205
return ((b[0] & 0xff) | (b[1] & 0xff) << 8)
206
| ((b[2] & 0xff) | (b[3] & 0xff) << 8) << 16;
210
* Read an unsigned short in little endian byte order from the given
211
* byte buffer at the given offset.
213
* @param b the byte array to read from.
214
* @param off the offset to read from.
215
* @return The value read.
217
private int readLeShort(byte[] b, int off)
219
return (b[off] & 0xff) | (b[off+1] & 0xff) << 8;
223
* Read an int in little endian byte order from the given
224
* byte buffer at the given offset.
226
* @param b the byte array to read from.
227
* @param off the offset to read from.
228
* @return The value read.
230
private int readLeInt(byte[] b, int off)
232
return ((b[off] & 0xff) | (b[off+1] & 0xff) << 8)
233
| ((b[off+2] & 0xff) | (b[off+3] & 0xff) << 8) << 16;
238
220
* Read the central directory of a zip file and fill the entries
239
221
* array. This is called exactly once when first needed. It is called
240
222
* while holding the lock on <code>raf</code>.
247
229
/* Search for the End Of Central Directory. When a zip comment is
248
230
* present the directory may start earlier.
249
* FIXME: This searches the whole file in a very slow manner if the
250
* file isn't a zip file.
231
* Note that a comment has a maximum length of 64K, so that is the
232
* maximum we search backwards.
234
PartialInputStream inp = new PartialInputStream(raf, 4096);
252
235
long pos = raf.length() - ENDHDR;
253
byte[] ebs = new byte[CENHDR];
236
long top = Math.max(0, pos - 65536);
258
240
throw new ZipException
259
241
("central directory not found, probably not a zip file: " + name);
262
while (readLeInt(raf, ebs) != ENDSIG);
244
while (inp.readLeInt() != ENDSIG);
264
if (raf.skipBytes(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
265
throw new EOFException(name);
266
int count = readLeShort(raf, ebs);
267
if (raf.skipBytes(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
268
throw new EOFException(name);
269
int centralOffset = readLeInt(raf, ebs);
246
if (inp.skip(ENDTOT - ENDNRD) != ENDTOT - ENDNRD)
247
throw new EOFException(name);
248
int count = inp.readLeShort();
249
if (inp.skip(ENDOFF - ENDSIZ) != ENDOFF - ENDSIZ)
250
throw new EOFException(name);
251
int centralOffset = inp.readLeInt();
271
253
entries = new HashMap(count+count/2);
272
raf.seek(centralOffset);
254
inp.seek(centralOffset);
274
byte[] buffer = new byte[16];
275
256
for (int i = 0; i < count; i++)
278
if (readLeInt(ebs, 0) != CENSIG)
258
if (inp.readLeInt() != CENSIG)
279
259
throw new ZipException("Wrong Central Directory signature: " + name);
281
int method = readLeShort(ebs, CENHOW);
282
int dostime = readLeInt(ebs, CENTIM);
283
int crc = readLeInt(ebs, CENCRC);
284
int csize = readLeInt(ebs, CENSIZ);
285
int size = readLeInt(ebs, CENLEN);
286
int nameLen = readLeShort(ebs, CENNAM);
287
int extraLen = readLeShort(ebs, CENEXT);
288
int commentLen = readLeShort(ebs, CENCOM);
290
int offset = readLeInt(ebs, CENOFF);
292
int needBuffer = Math.max(nameLen, commentLen);
293
if (buffer.length < needBuffer)
294
buffer = new byte[needBuffer];
296
raf.readFully(buffer, 0, nameLen);
300
name = new String(buffer, 0, nameLen, "UTF-8");
302
catch (UnsupportedEncodingException uee)
304
throw new AssertionError(uee);
262
int method = inp.readLeShort();
263
int dostime = inp.readLeInt();
264
int crc = inp.readLeInt();
265
int csize = inp.readLeInt();
266
int size = inp.readLeInt();
267
int nameLen = inp.readLeShort();
268
int extraLen = inp.readLeShort();
269
int commentLen = inp.readLeShort();
271
int offset = inp.readLeInt();
272
String name = inp.readString(nameLen);
307
274
ZipEntry entry = new ZipEntry(name);
308
275
entry.setMethod(method);
433
//access should be protected by synchronized(raf)
434
private byte[] locBuf = new byte[LOCHDR];
437
* Checks, if the local header of the entry at index i matches the
438
* central directory, and returns the offset to the data.
440
* @param entry to check.
441
* @return the start offset of the (compressed) data.
443
* @exception IOException if a i/o error occured.
444
* @exception ZipException if the local header doesn't match the
445
* central directory header
447
private long checkLocalHeader(ZipEntry entry) throws IOException
451
raf.seek(entry.offset);
452
raf.readFully(locBuf);
454
if (readLeInt(locBuf, 0) != LOCSIG)
455
throw new ZipException("Wrong Local header signature: " + name);
457
if (entry.getMethod() != readLeShort(locBuf, LOCHOW))
458
throw new ZipException("Compression method mismatch: " + name);
460
if (entry.getName().length() != readLeShort(locBuf, LOCNAM))
461
throw new ZipException("file name length mismatch: " + name);
463
int extraLen = entry.getName().length() + readLeShort(locBuf, LOCEXT);
464
return entry.offset + LOCHDR + extraLen;
469
392
* Creates an input stream reading the given zip entry as
470
393
* uncompressed data. Normally zip entry should be an entry
497
420
if (zipEntry == null)
500
long start = checkLocalHeader(zipEntry);
423
PartialInputStream inp = new PartialInputStream(raf, 1024);
424
inp.seek(zipEntry.offset);
426
if (inp.readLeInt() != LOCSIG)
427
throw new ZipException("Wrong Local header signature: " + name);
431
if (zipEntry.getMethod() != inp.readLeShort())
432
throw new ZipException("Compression method mismatch: " + name);
436
int nameLen = inp.readLeShort();
437
int extraLen = inp.readLeShort();
438
inp.skip(nameLen + extraLen);
440
inp.setLength(zipEntry.getCompressedSize());
501
442
int method = zipEntry.getMethod();
502
InputStream is = new BufferedInputStream(new PartialInputStream
503
(raf, start, zipEntry.getCompressedSize()));
506
445
case ZipOutputStream.STORED:
508
447
case ZipOutputStream.DEFLATED:
509
return new InflaterInputStream(is, new Inflater(true));
448
final Inflater inf = new Inflater(true);
449
final int sz = (int) entry.getSize();
450
return new InflaterInputStream(inp, inf)
452
public int available() throws IOException
455
return super.available();
456
if (super.available() != 0)
457
return sz - inf.getTotalOut();
511
462
throw new ZipException("Unknown compression method " + method);
565
private static class PartialInputStream extends InputStream
516
private static final class PartialInputStream extends InputStream
567
518
private final RandomAccessFile raf;
519
private final byte[] buffer;
520
private long bufferOffset;
570
public PartialInputStream(RandomAccessFile raf, long start, long len)
524
public PartialInputStream(RandomAccessFile raf, int bufferSize)
528
buffer = new byte[bufferSize];
529
bufferOffset = -buffer.length;
534
void setLength(long length)
536
end = bufferOffset + pos + length;
539
private void fillBuffer() throws IOException
543
raf.seek(bufferOffset);
544
raf.readFully(buffer, 0, (int) Math.min(buffer.length, end - bufferOffset));
577
548
public int available()
579
long amount = end - filepos;
550
long amount = end - (bufferOffset + pos);
580
551
if (amount > Integer.MAX_VALUE)
581
552
return Integer.MAX_VALUE;
582
553
return (int) amount;
585
556
public int read() throws IOException
558
if (bufferOffset + pos >= end)
560
if (pos == buffer.length)
562
bufferOffset += buffer.length;
567
return buffer[pos++] & 0xFF;
596
570
public int read(byte[] b, int off, int len) throws IOException
598
if (len > end - filepos)
572
if (len > end - (bufferOffset + pos))
600
len = (int) (end - filepos);
574
len = (int) (end - (bufferOffset + pos));
607
int count = raf.read(b, off, len);
579
int totalBytesRead = Math.min(buffer.length - pos, len);
580
System.arraycopy(buffer, pos, b, off, totalBytesRead);
581
pos += totalBytesRead;
582
off += totalBytesRead;
583
len -= totalBytesRead;
587
bufferOffset += buffer.length;
590
int remain = Math.min(buffer.length, len);
591
System.arraycopy(buffer, pos, b, off, remain);
595
totalBytesRead += remain;
598
return totalBytesRead;
614
public long skip(long amount)
601
public long skip(long amount) throws IOException
617
throw new IllegalArgumentException();
618
if (amount > end - filepos)
619
amount = end - filepos;
605
if (amount > end - (bufferOffset + pos))
606
amount = end - (bufferOffset + pos);
607
seek(bufferOffset + pos + amount);
611
void seek(long newpos) throws IOException
613
long offset = newpos - bufferOffset;
614
if (offset >= 0 && offset <= buffer.length)
620
bufferOffset = newpos;
626
void readFully(byte[] buf) throws IOException
628
if (read(buf, 0, buf.length) != buf.length)
629
throw new EOFException();
632
void readFully(byte[] buf, int off, int len) throws IOException
634
if (read(buf, off, len) != len)
635
throw new EOFException();
638
int readLeShort() throws IOException
643
throw new EOFException();
644
return (b0 & 0xff) | (b1 & 0xff) << 8;
647
int readLeInt() throws IOException
654
throw new EOFException();
655
return ((b0 & 0xff) | (b1 & 0xff) << 8)
656
| ((b2 & 0xff) | (b3 & 0xff) << 8) << 16;
659
String readString(int length) throws IOException
661
if (length > end - (bufferOffset + pos))
662
throw new EOFException();
666
if (buffer.length - pos >= length)
668
String s = new String(buffer, pos, length, "UTF-8");
674
byte[] b = new byte[length];
676
return new String(b, 0, length, "UTF-8");
679
catch (UnsupportedEncodingException uee)
681
throw new AssertionError(uee);