308 // The outstanding inputstreams that need to be closed,
309 // mapped to the inflater objects they use.
310 private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
311
312 /**
313 * Returns an input stream for reading the contents of the specified
314 * zip file entry.
315 * <p>
316 * Closing this ZIP file will, in turn, close all input streams that
317 * have been returned by invocations of this method.
318 *
319 * @param entry the zip file entry
320 * @return the input stream for reading the contents of the specified
321 * zip file entry.
322 * @throws ZipException if a ZIP format error has occurred
323 * @throws IOException if an I/O error has occurred
324 * @throws IllegalStateException if the zip file has been closed
325 */
326 public InputStream getInputStream(ZipEntry entry) throws IOException {
327 Objects.requireNonNull(entry, "entry");
328 int pos = -1;
329 ZipFileInputStream in = null;
330 synchronized (this) {
331 ensureOpen();
332 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
333 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
334 } else {
335 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
336 }
337 if (pos == -1) {
338 return null;
339 }
340 in = new ZipFileInputStream(zsrc.cen, pos);
341 switch (CENHOW(zsrc.cen, pos)) {
342 case STORED:
343 synchronized (streams) {
344 streams.put(in, null);
345 }
346 return in;
347 case DEFLATED:
348 // Inflater likes a bit of slack
349 // MORE: Compute good size for inflater stream:
350 long size = CENLEN(zsrc.cen, pos) + 2;
351 if (size > 65536) {
352 size = 8192;
353 }
354 if (size <= 0) {
355 size = 4096;
356 }
357 Inflater inf = getInflater();
358 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
359 synchronized (streams) {
360 streams.put(is, inf);
361 }
362 return is;
363 default:
364 throw new ZipException("invalid compression method");
365 }
366 }
367 }
368
369 private class ZipFileInflaterInputStream extends InflaterInputStream {
370 private volatile boolean closeRequested;
371 private boolean eof = false;
372 private final ZipFileInputStream zfin;
373
374 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
375 int size) {
376 super(zfin, inf, size);
377 this.zfin = zfin;
641 if (zsrc == null) {
642 throw new IllegalStateException("The object is not initialized.");
643 }
644 }
645
646 private void ensureOpenOrZipException() throws IOException {
647 if (closeRequested) {
648 throw new ZipException("ZipFile closed");
649 }
650 }
651
652 /*
653 * Inner class implementing the input stream used to read a
654 * (possibly compressed) zip file entry.
655 */
656 private class ZipFileInputStream extends InputStream {
657 private volatile boolean closeRequested;
658 private long pos; // current position within entry data
659 protected long rem; // number of remaining bytes within entry
660 protected long size; // uncompressed size of this entry
661
662 ZipFileInputStream(byte[] cen, int cenpos) throws IOException {
663 rem = CENSIZ(cen, cenpos);
664 size = CENLEN(cen, cenpos);
665 pos = CENOFF(cen, cenpos);
666 // zip64
667 if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
668 pos == ZIP64_MAGICVAL) {
669 checkZIP64(cen, cenpos);
670 }
671 // negative for lazy initialization, see getDataOffset();
672 pos = - (pos + ZipFile.this.zsrc.locpos);
673 }
674
675 private void checkZIP64(byte[] cen, int cenpos) throws IOException {
676 int off = cenpos + CENHDR + CENNAM(cen, cenpos);
677 int end = off + CENEXT(cen, cenpos);
678 while (off + 4 < end) {
679 int tag = get16(cen, off);
680 int sz = get16(cen, off + 2);
681 off += 4;
682 if (off + sz > end) // invalid data
683 break;
684 if (tag == EXTID_ZIP64) {
685 if (size == ZIP64_MAGICVAL) {
686 if (sz < 8 || (off + 8) > end)
687 break;
688 size = get64(cen, off);
689 sz -= 8;
690 off += 8;
691 }
692 if (rem == ZIP64_MAGICVAL) {
714 * the CEN extra data size, we need to read the LOC to determine
715 * the entry data offset.
716 */
717 private long initDataOffset() throws IOException {
718 if (pos <= 0) {
719 byte[] loc = new byte[LOCHDR];
720 pos = -pos;
721 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
722 if (len != LOCHDR) {
723 throw new ZipException("ZipFile error reading zip file");
724 }
725 if (LOCSIG(loc) != LOCSIG) {
726 throw new ZipException("ZipFile invalid LOC header (bad signature)");
727 }
728 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
729 }
730 return pos;
731 }
732
733 public int read(byte b[], int off, int len) throws IOException {
734 synchronized (ZipFile.this) {
735 ensureOpenOrZipException();
736 initDataOffset();
737 if (rem == 0) {
738 return -1;
739 }
740 if (len > rem) {
741 len = (int) rem;
742 }
743 if (len <= 0) {
744 return 0;
745 }
746 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
747 if (len > 0) {
748 pos += len;
749 rem -= len;
750 }
751 }
752 if (rem == 0) {
753 close();
1163
1164 // Iterate through the entries in the central directory
1165 int i = 0;
1166 int hsh = 0;
1167 int pos = 0;
1168 int limit = cen.length - ENDHDR;
1169 while (pos + CENHDR <= limit) {
1170 if (i >= total) {
1171 // This will only happen if the zip file has an incorrect
1172 // ENDTOT field, which usually means it contains more than
1173 // 65535 entries.
1174 initCEN(countCENHeaders(cen, limit));
1175 return;
1176 }
1177 if (CENSIG(cen, pos) != CENSIG)
1178 zerror("invalid CEN header (bad signature)");
1179 int method = CENHOW(cen, pos);
1180 int nlen = CENNAM(cen, pos);
1181 int elen = CENEXT(cen, pos);
1182 int clen = CENCOM(cen, pos);
1183 if ((CENFLG(cen, pos) & 1) != 0)
1184 zerror("invalid CEN header (encrypted entry)");
1185 if (method != STORED && method != DEFLATED)
1186 zerror("invalid CEN header (bad compression method: " + method + ")");
1187 if (pos + CENHDR + nlen > limit)
1188 zerror("invalid CEN header (bad header size)");
1189 // Record the CEN offset and the name hash in our hash cell.
1190 hash = hashN(cen, pos + CENHDR, nlen);
1191 hsh = (hash & 0x7fffffff) % tablelen;
1192 next = table[hsh];
1193 table[hsh] = idx;
1194 idx = addEntry(idx, hash, next, pos);
1195 // Adds name to metanames.
1196 if (isMetaName(cen, pos + CENHDR, nlen)) {
1197 metanames.add(pos);
1198 }
1199 // skip ext and comment
1200 pos += (CENHDR + nlen + elen + clen);
1201 i++;
1202 }
1203 total = i;
1204 if (pos + ENDHDR != cen.length) {
|
308 // The outstanding inputstreams that need to be closed,
309 // mapped to the inflater objects they use.
310 private final Map<InputStream, Inflater> streams = new WeakHashMap<>();
311
312 /**
313 * Returns an input stream for reading the contents of the specified
314 * zip file entry.
315 * <p>
316 * Closing this ZIP file will, in turn, close all input streams that
317 * have been returned by invocations of this method.
318 *
319 * @param entry the zip file entry
320 * @return the input stream for reading the contents of the specified
321 * zip file entry.
322 * @throws ZipException if a ZIP format error has occurred
323 * @throws IOException if an I/O error has occurred
324 * @throws IllegalStateException if the zip file has been closed
325 */
326 public InputStream getInputStream(ZipEntry entry) throws IOException {
327 Objects.requireNonNull(entry, "entry");
328 ZipCryption zipCryption = null;
329
330 if (entry.isPassphraseRequired()) {
331 zipCryption = entry.zipCryption;
332 Objects.requireNonNull(zipCryption, "Passphrase is required.");
333 }
334
335 int pos = -1;
336 ZipFileInputStream in = null;
337 synchronized (this) {
338 ensureOpen();
339 if (!zc.isUTF8() && (entry.flag & EFS) != 0) {
340 pos = zsrc.getEntryPos(zc.getBytesUTF8(entry.name), false);
341 } else {
342 pos = zsrc.getEntryPos(zc.getBytes(entry.name), false);
343 }
344 if (pos == -1) {
345 return null;
346 }
347 in = new ZipFileInputStream(zsrc.cen, pos, zipCryption);
348 switch (CENHOW(zsrc.cen, pos)) {
349 case STORED:
350 if (entry.isPassphraseRequired()) {
351 entry.encryptionHeader =
352 new byte[TraditionalZipCryption.ENCRYPTION_HEADER_SIZE];
353 in.readRaw(entry.encryptionHeader, 0,
354 entry.encryptionHeader.length);
355 if (!entry.isValidPassphrase()) {
356 throw new ZipException("possibly incorrect passphrase");
357 }
358 }
359
360 synchronized (streams) {
361 streams.put(in, null);
362 }
363 return in;
364 case DEFLATED:
365 // Inflater likes a bit of slack
366 // MORE: Compute good size for inflater stream:
367 long size = CENLEN(zsrc.cen, pos) + 2;
368 if (size > 65536) {
369 size = 8192;
370 }
371 if (size <= 0) {
372 size = 4096;
373 }
374 Inflater inf = getInflater();
375
376 if (entry.isPassphraseRequired()) {
377 entry.encryptionHeader =
378 new byte[TraditionalZipCryption.ENCRYPTION_HEADER_SIZE];
379 in.readRaw(entry.encryptionHeader, 0,
380 entry.encryptionHeader.length);
381 if (!entry.isValidPassphrase()) {
382 throw new ZipException("possibly incorrect passphrase");
383 }
384 }
385
386 InputStream is = new ZipFileInflaterInputStream(in, inf, (int)size);
387 synchronized (streams) {
388 streams.put(is, inf);
389 }
390 return is;
391 default:
392 throw new ZipException("invalid compression method");
393 }
394 }
395 }
396
397 private class ZipFileInflaterInputStream extends InflaterInputStream {
398 private volatile boolean closeRequested;
399 private boolean eof = false;
400 private final ZipFileInputStream zfin;
401
402 ZipFileInflaterInputStream(ZipFileInputStream zfin, Inflater inf,
403 int size) {
404 super(zfin, inf, size);
405 this.zfin = zfin;
669 if (zsrc == null) {
670 throw new IllegalStateException("The object is not initialized.");
671 }
672 }
673
674 private void ensureOpenOrZipException() throws IOException {
675 if (closeRequested) {
676 throw new ZipException("ZipFile closed");
677 }
678 }
679
680 /*
681 * Inner class implementing the input stream used to read a
682 * (possibly compressed) zip file entry.
683 */
684 private class ZipFileInputStream extends InputStream {
685 private volatile boolean closeRequested;
686 private long pos; // current position within entry data
687 protected long rem; // number of remaining bytes within entry
688 protected long size; // uncompressed size of this entry
689 private ZipCryption zipCryption; // ZIP encrypt/decrypt engine
690
691 ZipFileInputStream(byte[] cen, int cenpos, ZipCryption zipCryption)
692 throws IOException {
693 rem = CENSIZ(cen, cenpos);
694 size = CENLEN(cen, cenpos);
695 pos = CENOFF(cen, cenpos);
696 // zip64
697 if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
698 pos == ZIP64_MAGICVAL) {
699 checkZIP64(cen, cenpos);
700 }
701 // negative for lazy initialization, see getDataOffset();
702 pos = - (pos + ZipFile.this.zsrc.locpos);
703 this.zipCryption = zipCryption;
704 }
705
706 private void checkZIP64(byte[] cen, int cenpos) throws IOException {
707 int off = cenpos + CENHDR + CENNAM(cen, cenpos);
708 int end = off + CENEXT(cen, cenpos);
709 while (off + 4 < end) {
710 int tag = get16(cen, off);
711 int sz = get16(cen, off + 2);
712 off += 4;
713 if (off + sz > end) // invalid data
714 break;
715 if (tag == EXTID_ZIP64) {
716 if (size == ZIP64_MAGICVAL) {
717 if (sz < 8 || (off + 8) > end)
718 break;
719 size = get64(cen, off);
720 sz -= 8;
721 off += 8;
722 }
723 if (rem == ZIP64_MAGICVAL) {
745 * the CEN extra data size, we need to read the LOC to determine
746 * the entry data offset.
747 */
748 private long initDataOffset() throws IOException {
749 if (pos <= 0) {
750 byte[] loc = new byte[LOCHDR];
751 pos = -pos;
752 int len = ZipFile.this.zsrc.readFullyAt(loc, 0, loc.length, pos);
753 if (len != LOCHDR) {
754 throw new ZipException("ZipFile error reading zip file");
755 }
756 if (LOCSIG(loc) != LOCSIG) {
757 throw new ZipException("ZipFile invalid LOC header (bad signature)");
758 }
759 pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
760 }
761 return pos;
762 }
763
764 public int read(byte b[], int off, int len) throws IOException {
765 len = readRaw(b, off, len);
766
767 if (zipCryption != null) {
768 zipCryption.decryptBytes(b, off, len);
769 }
770
771 return len;
772 }
773
774 public int readRaw(byte b[], int off, int len) throws IOException {
775 synchronized (ZipFile.this) {
776 ensureOpenOrZipException();
777 initDataOffset();
778 if (rem == 0) {
779 return -1;
780 }
781 if (len > rem) {
782 len = (int) rem;
783 }
784 if (len <= 0) {
785 return 0;
786 }
787 len = ZipFile.this.zsrc.readAt(b, off, len, pos);
788 if (len > 0) {
789 pos += len;
790 rem -= len;
791 }
792 }
793 if (rem == 0) {
794 close();
1204
1205 // Iterate through the entries in the central directory
1206 int i = 0;
1207 int hsh = 0;
1208 int pos = 0;
1209 int limit = cen.length - ENDHDR;
1210 while (pos + CENHDR <= limit) {
1211 if (i >= total) {
1212 // This will only happen if the zip file has an incorrect
1213 // ENDTOT field, which usually means it contains more than
1214 // 65535 entries.
1215 initCEN(countCENHeaders(cen, limit));
1216 return;
1217 }
1218 if (CENSIG(cen, pos) != CENSIG)
1219 zerror("invalid CEN header (bad signature)");
1220 int method = CENHOW(cen, pos);
1221 int nlen = CENNAM(cen, pos);
1222 int elen = CENEXT(cen, pos);
1223 int clen = CENCOM(cen, pos);
1224 if (method != STORED && method != DEFLATED)
1225 zerror("invalid CEN header (bad compression method: " + method + ")");
1226 if (pos + CENHDR + nlen > limit)
1227 zerror("invalid CEN header (bad header size)");
1228 // Record the CEN offset and the name hash in our hash cell.
1229 hash = hashN(cen, pos + CENHDR, nlen);
1230 hsh = (hash & 0x7fffffff) % tablelen;
1231 next = table[hsh];
1232 table[hsh] = idx;
1233 idx = addEntry(idx, hash, next, pos);
1234 // Adds name to metanames.
1235 if (isMetaName(cen, pos + CENHDR, nlen)) {
1236 metanames.add(pos);
1237 }
1238 // skip ext and comment
1239 pos += (CENHDR + nlen + elen + clen);
1240 i++;
1241 }
1242 total = i;
1243 if (pos + ENDHDR != cen.length) {
|