1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.zip;
20
21 import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
22 import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
23 import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
24 import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
25
26 import java.io.ByteArrayInputStream;
27 import java.io.ByteArrayOutputStream;
28 import java.io.EOFException;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.PushbackInputStream;
32 import java.math.BigInteger;
33 import java.nio.ByteBuffer;
34 import java.nio.charset.StandardCharsets;
35 import java.util.Arrays;
36 import java.util.Objects;
37 import java.util.function.Function;
38 import java.util.zip.CRC32;
39 import java.util.zip.DataFormatException;
40 import java.util.zip.Inflater;
41 import java.util.zip.ZipEntry;
42 import java.util.zip.ZipException;
43
44 import org.apache.commons.compress.archivers.ArchiveEntry;
45 import org.apache.commons.compress.archivers.ArchiveInputStream;
46 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
47 import org.apache.commons.compress.compressors.deflate64.Deflate64CompressorInputStream;
48 import org.apache.commons.compress.compressors.zstandard.ZstdCompressorInputStream;
49 import org.apache.commons.compress.utils.ArchiveUtils;
50 import org.apache.commons.compress.utils.IOUtils;
51 import org.apache.commons.compress.utils.InputStreamStatistics;
52 import org.apache.commons.io.input.BoundedInputStream;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 public class ZipArchiveInputStream extends ArchiveInputStream<ZipArchiveEntry> implements InputStreamStatistics {
76
77
78
79
80 private final class BoundCountInputStream extends BoundedInputStream {
81
82
83
84
85
86
87
88
89
90 BoundCountInputStream(final InputStream in, final long max) {
91 super(in, max);
92 }
93
94 private boolean atMaxLength() {
95 return getMaxCount() >= 0 && getCount() >= getMaxCount();
96 }
97
98 @Override
99 public int read() throws IOException {
100 if (atMaxLength()) {
101 return -1;
102 }
103 final int result = super.read();
104 if (result != -1) {
105 readCount(1);
106 }
107 return result;
108 }
109
110 @Override
111 public int read(final byte[] b, final int off, final int len) throws IOException {
112 if (len == 0) {
113 return 0;
114 }
115 if (atMaxLength()) {
116 return -1;
117 }
118 final long maxRead = getMaxCount() >= 0 ? Math.min(len, getMaxCount() - getCount()) : len;
119 return readCount(super.read(b, off, (int) maxRead));
120 }
121
122 private int readCount(final int bytesRead) {
123 if (bytesRead != -1) {
124 count(bytesRead);
125 current.bytesReadFromStream += bytesRead;
126 }
127 return bytesRead;
128 }
129
130 }
131
132
133
134
135 private static final class CurrentEntry {
136
137
138
139
140 private final ZipArchiveEntry entry = new ZipArchiveEntry();
141
142
143
144
145 private boolean hasDataDescriptor;
146
147
148
149
150 private boolean usesZip64;
151
152
153
154
155 private long bytesRead;
156
157
158
159
160
161
162
163
164 private long bytesReadFromStream;
165
166
167
168
169 private final CRC32 crc = new CRC32();
170
171
172
173
174 private InputStream inputStream;
175
176 @SuppressWarnings("unchecked")
177 private <T extends InputStream> T checkInputStream() {
178 return (T) Objects.requireNonNull(inputStream, "inputStream");
179 }
180 }
181
182
183
184
185 public static final int PREAMBLE_GARBAGE_MAX_SIZE = 4096;
186
187 private static final int LFH_LEN = 30;
188
189
190
191
192
193 private static final int CFH_LEN = 46;
194
195
196
197
198
199
200 private static final long TWO_EXP_32 = ZIP64_MAGIC + 1;
201
202 private static final String USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER = " while reading a stored entry using data descriptor. Either the archive is broken"
203 + " or it cannot be read using ZipArchiveInputStream and you must use ZipFile."
204 + " A common cause for this is a ZIP archive containing a ZIP archive."
205 + " See https://commons.apache.org/proper/commons-compress/zip.html#ZipArchiveInputStream_vs_ZipFile";
206
207 private static final byte[] LFH = ZipLong.LFH_SIG.getBytes();
208
209 private static final byte[] CFH = ZipLong.CFH_SIG.getBytes();
210
211 private static final byte[] DD = ZipLong.DD_SIG.getBytes();
212
213 private static final byte[] APK_SIGNING_BLOCK_MAGIC = { 'A', 'P', 'K', ' ', 'S', 'i', 'g', ' ', 'B', 'l', 'o', 'c', 'k', ' ', '4', '2', };
214
215 private static final BigInteger LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE);
216
217 private static boolean checkSig(final byte[] expected, final byte[] signature) {
218 for (int i = 0; i < expected.length; i++) {
219 if (signature[i] != expected[i]) {
220 return false;
221 }
222 }
223 return true;
224 }
225
226
227
228
229
230
231
232
233 public static boolean matches(final byte[] signature, final int length) {
234 if (length < ZipArchiveOutputStream.LFH_SIG.length) {
235 return false;
236 }
237
238 return checkSig(ZipArchiveOutputStream.LFH_SIG, signature)
239 || checkSig(ZipArchiveOutputStream.EOCD_SIG, signature)
240 || checkSig(ZipArchiveOutputStream.DD_SIG, signature)
241 || checkSig(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER.getBytes(), signature);
242 }
243
244
245 private final ZipEncoding zipEncoding;
246
247
248 private final boolean useUnicodeExtraFields;
249
250
251 private final Inflater inf = new Inflater(true);
252
253
254 private final ByteBuffer buf = ByteBuffer.allocate(ZipArchiveOutputStream.BUFFER_SIZE);
255
256
257 private CurrentEntry current;
258
259
260 private boolean closed;
261
262
263 private boolean hitCentralDirectory;
264
265
266
267
268 private ByteArrayInputStream lastStoredEntry;
269
270
271
272
273
274
275
276
277
278
279 private final boolean allowStoredEntriesWithDataDescriptor;
280
281
282 private long uncompressedCount;
283
284
285 private final boolean skipSplitSig;
286
287
288 private final byte[] lfhBuf = new byte[LFH_LEN];
289
290 private final byte[] skipBuf = new byte[1024];
291
292 private final byte[] shortBuf = new byte[SHORT];
293
294 private final byte[] wordBuf = new byte[WORD];
295
296 private final byte[] twoDwordBuf = new byte[2 * DWORD];
297
298 private int entriesRead;
299
300
301
302
303
304
305
306
307
308
309
310 public ZipArchiveInputStream(final InputStream inputStream) {
311 this(inputStream, StandardCharsets.UTF_8.name());
312 }
313
314
315
316
317
318
319
320
321 public ZipArchiveInputStream(final InputStream inputStream, final String encoding) {
322 this(inputStream, encoding, true);
323 }
324
325
326
327
328
329
330
331
332 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields) {
333 this(inputStream, encoding, useUnicodeExtraFields, false);
334 }
335
336
337
338
339
340
341
342
343
344
345 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
346 final boolean allowStoredEntriesWithDataDescriptor) {
347 this(inputStream, encoding, useUnicodeExtraFields, allowStoredEntriesWithDataDescriptor, false);
348 }
349
350
351
352
353
354
355
356
357
358
359
360
361 public ZipArchiveInputStream(final InputStream inputStream, final String encoding, final boolean useUnicodeExtraFields,
362 final boolean allowStoredEntriesWithDataDescriptor, final boolean skipSplitSig) {
363 super(inputStream, encoding);
364 this.in = new PushbackInputStream(inputStream, buf.capacity());
365 this.zipEncoding = ZipEncodingHelper.getZipEncoding(encoding);
366 this.useUnicodeExtraFields = useUnicodeExtraFields;
367 this.allowStoredEntriesWithDataDescriptor = allowStoredEntriesWithDataDescriptor;
368 this.skipSplitSig = skipSplitSig;
369
370 buf.limit(0);
371 }
372
373
374
375
376
377
378
379
380 private boolean bufferContainsSignature(final ByteArrayOutputStream bos, final int offset, final int lastRead, final int expectedDDLen) throws IOException {
381
382 boolean done = false;
383 for (int i = 0; !done && i < offset + lastRead - 4; i++) {
384 if (buf.array()[i] == LFH[0] && buf.array()[i + 1] == LFH[1]) {
385 int expectDDPos = i;
386 if (i >= expectedDDLen && buf.array()[i + 2] == LFH[2] && buf.array()[i + 3] == LFH[3]
387 || buf.array()[i + 2] == CFH[2] && buf.array()[i + 3] == CFH[3]) {
388
389 expectDDPos = i - expectedDDLen;
390 done = true;
391 } else if (buf.array()[i + 2] == DD[2] && buf.array()[i + 3] == DD[3]) {
392
393 done = true;
394 }
395 if (done) {
396
397
398
399
400 pushback(buf.array(), expectDDPos, offset + lastRead - expectDDPos);
401 bos.write(buf.array(), 0, expectDDPos);
402 readDataDescriptor();
403 }
404 }
405 }
406 return done;
407 }
408
409
410
411
412
413
414
415
416 private int cacheBytesRead(final ByteArrayOutputStream bos, int offset, final int lastRead, final int expectedDDLen) {
417 final int cacheable = offset + lastRead - expectedDDLen - 3;
418 if (cacheable > 0) {
419 bos.write(buf.array(), 0, cacheable);
420 System.arraycopy(buf.array(), cacheable, buf.array(), 0, expectedDDLen + 3);
421 offset = expectedDDLen + 3;
422 } else {
423 offset += lastRead;
424 }
425 return offset;
426 }
427
428
429
430
431
432
433
434
435
436 @Override
437 public boolean canReadEntryData(final ArchiveEntry ae) {
438 if (ae instanceof ZipArchiveEntry) {
439 final ZipArchiveEntry ze = (ZipArchiveEntry) ae;
440 return ZipUtil.canHandleEntryData(ze) && supportsDataDescriptorFor(ze) && supportsCompressedSizeFor(ze);
441 }
442 return false;
443 }
444
445 @Override
446 public void close() throws IOException {
447 if (!closed) {
448 closed = true;
449 try {
450 in.close();
451 } finally {
452 inf.end();
453 }
454 }
455 }
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471 private void closeEntry() throws IOException {
472 if (closed) {
473 throw new IOException("The stream is closed");
474 }
475 if (current == null) {
476 return;
477 }
478
479
480 if (currentEntryHasOutstandingBytes()) {
481 drainCurrentEntryData();
482 } else {
483
484 if (skip(Long.MAX_VALUE) < 0) {
485 throw new IllegalStateException("Can't read the remainder of the stream");
486 }
487
488 final long inB = current.entry.getMethod() == ZipArchiveOutputStream.DEFLATED ? getBytesInflated() : current.bytesRead;
489
490
491
492 final int diff = (int) (current.bytesReadFromStream - inB);
493
494
495 if (diff > 0) {
496 pushback(buf.array(), buf.limit() - diff, diff);
497 current.bytesReadFromStream -= diff;
498 }
499
500
501 if (currentEntryHasOutstandingBytes()) {
502 drainCurrentEntryData();
503 }
504 }
505
506 if (lastStoredEntry == null && current.hasDataDescriptor) {
507 readDataDescriptor();
508 }
509
510 inf.reset();
511 buf.clear().flip();
512 current = null;
513 lastStoredEntry = null;
514 }
515
516
517
518
519
520
521
522
523
524 protected InputStream createZstdInputStream(final InputStream in) throws IOException {
525 return new ZstdCompressorInputStream(in);
526 }
527
528
529
530
531
532
533
534 private boolean currentEntryHasOutstandingBytes() {
535 return current.bytesReadFromStream <= current.entry.getCompressedSize() && !current.hasDataDescriptor;
536 }
537
538
539
540
541 private void drainCurrentEntryData() throws IOException {
542 long remaining = current.entry.getCompressedSize() - current.bytesReadFromStream;
543 while (remaining > 0) {
544 final long n = in.read(buf.array(), 0, (int) Math.min(buf.capacity(), remaining));
545 if (n < 0) {
546 throw new EOFException("Truncated ZIP entry: " + ArchiveUtils.sanitize(current.entry.getName()));
547 }
548 count(n);
549 remaining -= n;
550 }
551 }
552
553 private int fill() throws IOException {
554 if (closed) {
555 throw new IOException("The stream is closed");
556 }
557 final int length = in.read(buf.array());
558 if (length > 0) {
559 buf.limit(length);
560 count(buf.limit());
561 inf.setInput(buf.array(), 0, buf.limit());
562 }
563 return length;
564 }
565
566
567
568
569 private boolean findEocdRecord() throws IOException {
570 int currentByte = -1;
571 boolean skipReadCall = false;
572 while (skipReadCall || (currentByte = readOneByte()) > -1) {
573 skipReadCall = false;
574 if (!isFirstByteOfEocdSig(currentByte)) {
575 continue;
576 }
577 currentByte = readOneByte();
578 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[1]) {
579 if (currentByte == -1) {
580 break;
581 }
582 skipReadCall = isFirstByteOfEocdSig(currentByte);
583 continue;
584 }
585 currentByte = readOneByte();
586 if (currentByte != ZipArchiveOutputStream.EOCD_SIG[2]) {
587 if (currentByte == -1) {
588 break;
589 }
590 skipReadCall = isFirstByteOfEocdSig(currentByte);
591 continue;
592 }
593 currentByte = readOneByte();
594 if (currentByte == -1) {
595 break;
596 }
597 if (currentByte == ZipArchiveOutputStream.EOCD_SIG[3]) {
598 return true;
599 }
600 skipReadCall = isFirstByteOfEocdSig(currentByte);
601 }
602 return false;
603 }
604
605
606
607
608
609
610
611
612
613
614
615
616 private long getBytesInflated() {
617 long inB = inf.getBytesRead();
618 if (current.bytesReadFromStream >= TWO_EXP_32) {
619 while (inB + TWO_EXP_32 <= current.bytesReadFromStream) {
620 inB += TWO_EXP_32;
621 }
622 }
623 return inB;
624 }
625
626
627
628
629 @SuppressWarnings("resource")
630 @Override
631 public long getCompressedCount() {
632 if (current == null) {
633 return -1;
634 }
635 final int method = current.entry.getMethod();
636 if (method == ZipArchiveOutputStream.STORED) {
637 return current.bytesRead;
638 }
639 if (method == ZipArchiveOutputStream.DEFLATED) {
640 return getBytesInflated();
641 }
642 if (method == ZipMethod.UNSHRINKING.getCode() || method == ZipMethod.IMPLODING.getCode() || method == ZipMethod.ENHANCED_DEFLATED.getCode()
643 || method == ZipMethod.BZIP2.getCode()) {
644 return ((InputStreamStatistics) current.checkInputStream()).getCompressedCount();
645 }
646 return -1;
647 }
648
649 @Override
650 public ZipArchiveEntry getNextEntry() throws IOException {
651 return getNextZipEntry();
652 }
653
654
655
656
657
658
659
660
661 @Deprecated
662 public ZipArchiveEntry getNextZipEntry() throws IOException {
663 uncompressedCount = 0;
664
665 boolean firstEntry = true;
666 if (closed || hitCentralDirectory) {
667 return null;
668 }
669 if (current != null) {
670 closeEntry();
671 firstEntry = false;
672 }
673
674 final long currentHeaderOffset = getBytesRead();
675 try {
676 if (firstEntry) {
677
678
679
680
681 if (!readFirstLocalFileHeader()) {
682 hitCentralDirectory = true;
683 skipRemainderOfArchive();
684 return null;
685 }
686 } else {
687 readFully(lfhBuf);
688 }
689 } catch (final EOFException e) {
690 return null;
691 }
692
693 final ZipLong sig = new ZipLong(lfhBuf);
694 if (!sig.equals(ZipLong.LFH_SIG)) {
695 if (sig.equals(ZipLong.CFH_SIG) || sig.equals(ZipLong.AED_SIG) || isApkSigningBlock(lfhBuf)) {
696 hitCentralDirectory = true;
697 skipRemainderOfArchive();
698 return null;
699 }
700 throw new ZipException(String.format("Unexpected record signature: 0x%x", sig.getValue()));
701 }
702
703 int off = WORD;
704 current = new CurrentEntry();
705
706 final int versionMadeBy = ZipShort.getValue(lfhBuf, off);
707 off += SHORT;
708 current.entry.setPlatform(ZipFile.toPlatform(versionMadeBy));
709
710 final GeneralPurposeBit gpFlag = GeneralPurposeBit.parse(lfhBuf, off);
711 final boolean hasUTF8Flag = gpFlag.usesUTF8ForNames();
712 final ZipEncoding entryEncoding = hasUTF8Flag ? ZipEncodingHelper.ZIP_ENCODING_UTF_8 : zipEncoding;
713 current.hasDataDescriptor = gpFlag.usesDataDescriptor();
714 current.entry.setGeneralPurposeBit(gpFlag);
715
716 off += SHORT;
717
718 current.entry.setMethod(ZipShort.getValue(lfhBuf, off));
719 off += SHORT;
720
721 final long time = ZipUtil.dosToJavaTime(ZipLong.getValue(lfhBuf, off));
722 current.entry.setTime(time);
723 off += WORD;
724
725 ZipLong size = null;
726 ZipLong cSize = null;
727 if (!current.hasDataDescriptor) {
728 current.entry.setCrc(ZipLong.getValue(lfhBuf, off));
729 off += WORD;
730
731 cSize = new ZipLong(lfhBuf, off);
732 off += WORD;
733
734 size = new ZipLong(lfhBuf, off);
735 off += WORD;
736 } else {
737 off += 3 * WORD;
738 }
739
740 final int fileNameLen = ZipShort.getValue(lfhBuf, off);
741
742 off += SHORT;
743
744 final int extraLen = ZipShort.getValue(lfhBuf, off);
745 off += SHORT;
746
747 final byte[] fileName = readRange(fileNameLen);
748 current.entry.setName(entryEncoding.decode(fileName), fileName);
749 if (hasUTF8Flag) {
750 current.entry.setNameSource(ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
751 }
752
753 final byte[] extraData = readRange(extraLen);
754 try {
755 current.entry.setExtra(extraData);
756 } catch (final RuntimeException ex) {
757 throw ZipUtil.newZipException("Invalid extra data in entry " + current.entry.getName(), ex);
758 }
759
760 if (!hasUTF8Flag && useUnicodeExtraFields) {
761 ZipUtil.setNameAndCommentFromExtraFields(current.entry, fileName, null);
762 }
763
764 processZip64Extra(size, cSize);
765
766 current.entry.setLocalHeaderOffset(currentHeaderOffset);
767 current.entry.setDataOffset(getBytesRead());
768 current.entry.setStreamContiguous(true);
769
770 final ZipMethod m = ZipMethod.getMethodByCode(current.entry.getMethod());
771 if (current.entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN) {
772 if (ZipUtil.canHandleEntryData(current.entry) && m != ZipMethod.STORED && m != ZipMethod.DEFLATED) {
773 final InputStream bis = new BoundCountInputStream(in, current.entry.getCompressedSize());
774 switch (m) {
775 case UNSHRINKING:
776 current.inputStream = new UnshrinkingInputStream(bis);
777 break;
778 case IMPLODING:
779 try {
780 current.inputStream = new ExplodingInputStream(current.entry.getGeneralPurposeBit().getSlidingDictionarySize(),
781 current.entry.getGeneralPurposeBit().getNumberOfShannonFanoTrees(), bis);
782 } catch (final IllegalArgumentException ex) {
783 throw new IOException("bad IMPLODE data", ex);
784 }
785 break;
786 case BZIP2:
787 current.inputStream = new BZip2CompressorInputStream(bis);
788 break;
789 case ENHANCED_DEFLATED:
790 current.inputStream = new Deflate64CompressorInputStream(bis);
791 break;
792 case ZSTD:
793 case ZSTD_DEPRECATED:
794 current.inputStream = createZstdInputStream(bis);
795 break;
796 default:
797
798
799
800 break;
801 }
802 }
803 } else if (m == ZipMethod.ENHANCED_DEFLATED) {
804 current.inputStream = new Deflate64CompressorInputStream(in);
805 }
806
807 entriesRead++;
808 return current.entry;
809 }
810
811
812
813
814
815
816 @Override
817 public long getUncompressedCount() {
818 return uncompressedCount;
819 }
820
821
822
823
824
825
826
827
828
829
830
831
832 private boolean isApkSigningBlock(final byte[] suspectLocalFileHeader) throws IOException {
833
834 final BigInteger len = ZipEightByteInteger.getValue(suspectLocalFileHeader);
835
836
837 BigInteger toSkip = len.add(BigInteger.valueOf(DWORD - suspectLocalFileHeader.length - (long) APK_SIGNING_BLOCK_MAGIC.length));
838 final byte[] magic = new byte[APK_SIGNING_BLOCK_MAGIC.length];
839
840 try {
841 if (toSkip.signum() < 0) {
842
843 final int off = suspectLocalFileHeader.length + toSkip.intValue();
844
845 if (off < DWORD) {
846 return false;
847 }
848 final int bytesInBuffer = Math.abs(toSkip.intValue());
849 System.arraycopy(suspectLocalFileHeader, off, magic, 0, Math.min(bytesInBuffer, magic.length));
850 if (bytesInBuffer < magic.length) {
851 readFully(magic, bytesInBuffer);
852 }
853 } else {
854 while (toSkip.compareTo(LONG_MAX) > 0) {
855 realSkip(Long.MAX_VALUE);
856 toSkip = toSkip.add(LONG_MAX.negate());
857 }
858 realSkip(toSkip.longValue());
859 readFully(magic);
860 }
861 } catch (final EOFException ex) {
862
863 return false;
864 }
865 return Arrays.equals(magic, APK_SIGNING_BLOCK_MAGIC);
866 }
867
868 private boolean isFirstByteOfEocdSig(final int b) {
869 return b == ZipArchiveOutputStream.EOCD_SIG[0];
870 }
871
872
873
874
875 private void processZip64Extra(final ZipLong size, final ZipLong cSize) throws ZipException {
876 final ZipExtraField extra = current.entry.getExtraField(Zip64ExtendedInformationExtraField.HEADER_ID);
877 if (extra != null && !(extra instanceof Zip64ExtendedInformationExtraField)) {
878 throw new ZipException("archive contains unparseable zip64 extra field");
879 }
880 final Zip64ExtendedInformationExtraField z64 = (Zip64ExtendedInformationExtraField) extra;
881 current.usesZip64 = z64 != null;
882 if (!current.hasDataDescriptor) {
883 if (z64 != null
884 && (ZipLong.ZIP64_MAGIC.equals(cSize) || ZipLong.ZIP64_MAGIC.equals(size))) {
885 if (z64.getCompressedSize() == null || z64.getSize() == null) {
886
887 throw new ZipException("archive contains corrupted zip64 extra field");
888 }
889 long s = z64.getCompressedSize().getLongValue();
890 if (s < 0) {
891 throw new ZipException("broken archive, entry with negative compressed size");
892 }
893 current.entry.setCompressedSize(s);
894 s = z64.getSize().getLongValue();
895 if (s < 0) {
896 throw new ZipException("broken archive, entry with negative size");
897 }
898 current.entry.setSize(s);
899 } else if (cSize != null && size != null) {
900 if (cSize.getValue() < 0) {
901 throw new ZipException("broken archive, entry with negative compressed size");
902 }
903 current.entry.setCompressedSize(cSize.getValue());
904 if (size.getValue() < 0) {
905 throw new ZipException("broken archive, entry with negative size");
906 }
907 current.entry.setSize(size.getValue());
908 }
909 }
910 }
911
912 private void pushback(final byte[] buf, final int offset, final int length) throws IOException {
913 if (offset < 0) {
914
915 throw new IOException(String.format("Negative offset %,d into buffer", offset));
916 }
917 ((PushbackInputStream) in).unread(buf, offset, length);
918 pushedBackBytes(length);
919 }
920
921 @Override
922 public int read(final byte[] buffer, final int offset, final int length) throws IOException {
923 if (length == 0) {
924 return 0;
925 }
926 if (closed) {
927 throw new IOException("The stream is closed");
928 }
929
930 if (current == null) {
931 return -1;
932 }
933
934
935 if (offset > buffer.length || length < 0 || offset < 0 || buffer.length - offset < length) {
936 throw new ArrayIndexOutOfBoundsException();
937 }
938
939 ZipUtil.checkRequestedFeatures(current.entry);
940 if (!supportsDataDescriptorFor(current.entry)) {
941 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.DATA_DESCRIPTOR, current.entry);
942 }
943 if (!supportsCompressedSizeFor(current.entry)) {
944 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.UNKNOWN_COMPRESSED_SIZE, current.entry);
945 }
946
947 final int read;
948 final int method = current.entry.getMethod();
949 if (method == ZipArchiveOutputStream.STORED) {
950 read = readStored(buffer, offset, length);
951 } else if (method == ZipArchiveOutputStream.DEFLATED) {
952 read = readDeflated(buffer, offset, length);
953 } else if (method == ZipMethod.UNSHRINKING.getCode() || method == ZipMethod.IMPLODING.getCode()
954 || method == ZipMethod.ENHANCED_DEFLATED.getCode() || method == ZipMethod.BZIP2.getCode()
955 || ZipMethod.isZstd(method)
956 || method == ZipMethod.XZ.getCode()) {
957 read = current.inputStream.read(buffer, offset, length);
958 } else {
959 throw new UnsupportedZipFeatureException(ZipMethod.getMethodByCode(method), current.entry);
960 }
961
962 if (read >= 0) {
963 current.crc.update(buffer, offset, read);
964 uncompressedCount += read;
965 }
966
967 return read;
968 }
969
970 private void readDataDescriptor() throws IOException {
971 readFully(wordBuf);
972 ZipLong val = new ZipLong(wordBuf);
973 if (ZipLong.DD_SIG.equals(val)) {
974
975 readFully(wordBuf);
976 val = new ZipLong(wordBuf);
977 }
978 current.entry.setCrc(val.getValue());
979
980
981
982
983
984
985
986
987
988
989
990
991 readFully(twoDwordBuf);
992 final ZipLong potentialSig = new ZipLong(twoDwordBuf, DWORD);
993 if (potentialSig.equals(ZipLong.CFH_SIG) || potentialSig.equals(ZipLong.LFH_SIG)) {
994 pushback(twoDwordBuf, DWORD, DWORD);
995 long size = ZipLong.getValue(twoDwordBuf);
996 if (size < 0) {
997 throw new ZipException("broken archive, entry with negative compressed size");
998 }
999 current.entry.setCompressedSize(size);
1000 size = ZipLong.getValue(twoDwordBuf, WORD);
1001 if (size < 0) {
1002 throw new ZipException("broken archive, entry with negative size");
1003 }
1004 current.entry.setSize(size);
1005 } else {
1006 long size = ZipEightByteInteger.getLongValue(twoDwordBuf);
1007 if (size < 0) {
1008 throw new ZipException("broken archive, entry with negative compressed size");
1009 }
1010 current.entry.setCompressedSize(size);
1011 size = ZipEightByteInteger.getLongValue(twoDwordBuf, DWORD);
1012 if (size < 0) {
1013 throw new ZipException("broken archive, entry with negative size");
1014 }
1015 current.entry.setSize(size);
1016 }
1017 }
1018
1019
1020
1021
1022 private int readDeflated(final byte[] buffer, final int offset, final int length) throws IOException {
1023 final int read = readFromInflater(buffer, offset, length);
1024 if (read <= 0) {
1025 if (inf.finished()) {
1026 return -1;
1027 }
1028 if (inf.needsDictionary()) {
1029 throw new ZipException("This archive needs a preset dictionary which is not supported by Commons Compress.");
1030 }
1031 if (read == -1) {
1032 throw new IOException("Truncated ZIP file");
1033 }
1034 }
1035 return read;
1036 }
1037
1038
1039
1040
1041 private boolean readFirstLocalFileHeader() throws IOException {
1042
1043 final byte[] header = new byte[Math.min(LFH_LEN, ZipFile.MIN_EOCD_SIZE)];
1044 readFully(header);
1045 try {
1046 READ_LOOP: for (int i = 0; ; ) {
1047 for (int j = 0; i <= PREAMBLE_GARBAGE_MAX_SIZE - 4 && j <= header.length - 4; ++j, ++i) {
1048 final ZipLong sig = new ZipLong(header, j);
1049 if (sig.equals(ZipLong.LFH_SIG) ||
1050 sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) ||
1051 sig.equals(ZipLong.DD_SIG)) {
1052
1053 System.arraycopy(header, j, header, 0, header.length - j);
1054 readFully(header, header.length - j);
1055 break READ_LOOP;
1056 }
1057 if (sig.equals(new ZipLong(ZipArchiveOutputStream.EOCD_SIG))) {
1058
1059 pushback(header, j, header.length - j);
1060 return false;
1061 }
1062 }
1063 if (i >= PREAMBLE_GARBAGE_MAX_SIZE - 4) {
1064 throw new ZipException("Cannot find zip signature within the first " + PREAMBLE_GARBAGE_MAX_SIZE + " bytes");
1065 }
1066 System.arraycopy(header, header.length - 3, header, 0, 3);
1067 readFully(header, 3);
1068 }
1069 System.arraycopy(header, 0, lfhBuf, 0, header.length);
1070 readFully(lfhBuf, header.length);
1071 } catch (final EOFException ex) {
1072 throw new ZipException("Cannot find zip signature within the file");
1073 }
1074 final ZipLong sig = new ZipLong(lfhBuf);
1075
1076 if (!skipSplitSig && sig.equals(ZipLong.DD_SIG)) {
1077 throw new UnsupportedZipFeatureException(UnsupportedZipFeatureException.Feature.SPLITTING);
1078 }
1079
1080
1081 if (sig.equals(ZipLong.SINGLE_SEGMENT_SPLIT_MARKER) || sig.equals(ZipLong.DD_SIG)) {
1082
1083 System.arraycopy(lfhBuf, 4, lfhBuf, 0, lfhBuf.length - 4);
1084 readFully(lfhBuf, lfhBuf.length - 4);
1085 }
1086 return true;
1087 }
1088
1089
1090
1091
1092 private int readFromInflater(final byte[] buffer, final int offset, final int length) throws IOException {
1093 int read = 0;
1094 do {
1095 if (inf.needsInput()) {
1096 final int l = fill();
1097 if (l > 0) {
1098 current.bytesReadFromStream += buf.limit();
1099 } else if (l == -1) {
1100 return -1;
1101 } else {
1102 break;
1103 }
1104 }
1105 try {
1106 read = inf.inflate(buffer, offset, length);
1107 } catch (final DataFormatException e) {
1108 throw ZipUtil.newZipException(e.getMessage(), e);
1109 }
1110 } while (read == 0 && inf.needsInput());
1111 return read;
1112 }
1113
1114 private void readFully(final byte[] b) throws IOException {
1115 readFully(b, 0);
1116 }
1117
1118 private void readFully(final byte[] b, final int off) throws IOException {
1119 final int len = b.length - off;
1120 final int count = IOUtils.readFully(in, b, off, len);
1121 count(count);
1122 if (count < len) {
1123 throw new EOFException();
1124 }
1125 }
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149 private int readOneByte() throws IOException {
1150 final int b = in.read();
1151 if (b != -1) {
1152 count(1);
1153 }
1154 return b;
1155 }
1156
1157 private byte[] readRange(final int len) throws IOException {
1158 final byte[] ret = IOUtils.readRange(in, len);
1159 count(ret.length);
1160 if (ret.length < len) {
1161 throw new EOFException();
1162 }
1163 return ret;
1164 }
1165
1166
1167
1168
1169 private int readStored(final byte[] buffer, final int offset, final int length) throws IOException {
1170
1171 if (current.hasDataDescriptor) {
1172 if (lastStoredEntry == null) {
1173 readStoredEntry();
1174 }
1175 return lastStoredEntry.read(buffer, offset, length);
1176 }
1177
1178 final long csize = current.entry.getSize();
1179 if (current.bytesRead >= csize) {
1180 return -1;
1181 }
1182
1183 if (buf.position() >= buf.limit()) {
1184 buf.position(0);
1185 final int l = in.read(buf.array());
1186 if (l == -1) {
1187 buf.limit(0);
1188 throw new IOException("Truncated ZIP file");
1189 }
1190 buf.limit(l);
1191
1192 count(l);
1193 current.bytesReadFromStream += l;
1194 }
1195
1196 int toRead = Math.min(buf.remaining(), length);
1197 if (csize - current.bytesRead < toRead) {
1198
1199 toRead = (int) (csize - current.bytesRead);
1200 }
1201 buf.get(buffer, offset, toRead);
1202 current.bytesRead += toRead;
1203 return toRead;
1204 }
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220 private void readStoredEntry() throws IOException {
1221 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
1222 int off = 0;
1223 boolean done = false;
1224
1225
1226 final int ddLen = current.usesZip64 ? WORD + 2 * DWORD : 3 * WORD;
1227
1228 while (!done) {
1229 final int r = in.read(buf.array(), off, ZipArchiveOutputStream.BUFFER_SIZE - off);
1230 if (r <= 0) {
1231
1232
1233 throw new IOException("Truncated ZIP file");
1234 }
1235 if (r + off < 4) {
1236
1237 off += r;
1238 continue;
1239 }
1240
1241 done = bufferContainsSignature(bos, off, r, ddLen);
1242 if (!done) {
1243 off = cacheBytesRead(bos, off, r, ddLen);
1244 }
1245 }
1246 if (current.entry.getCompressedSize() != current.entry.getSize()) {
1247 throw new ZipException("compressed and uncompressed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1248 }
1249 final byte[] b = bos.toByteArray();
1250 if (b.length != current.entry.getSize()) {
1251 throw new ZipException("actual and claimed size don't match" + USE_ZIPFILE_INSTEAD_OF_STREAM_DISCLAIMER);
1252 }
1253 lastStoredEntry = new ByteArrayInputStream(b);
1254 }
1255
1256
1257
1258
1259
1260
1261 private void realSkip(final long value) throws IOException {
1262 if (value >= 0) {
1263 long skipped = 0;
1264 while (skipped < value) {
1265 final long rem = value - skipped;
1266 final int x = in.read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1267 if (x == -1) {
1268 return;
1269 }
1270 count(x);
1271 skipped += x;
1272 }
1273 return;
1274 }
1275 throw new IllegalArgumentException();
1276 }
1277
1278
1279
1280
1281
1282
1283
1284
1285 public ZipArchiveInputStream setExtraFieldSupport(final Function<ZipShort, ZipExtraField> extraFieldSupport) {
1286
1287 return this;
1288 }
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304 @Override
1305 public long skip(final long value) throws IOException {
1306 if (value >= 0) {
1307 long skipped = 0;
1308 while (skipped < value) {
1309 final long rem = value - skipped;
1310 final int x = read(skipBuf, 0, (int) (skipBuf.length > rem ? rem : skipBuf.length));
1311 if (x == -1) {
1312 return skipped;
1313 }
1314 skipped += x;
1315 }
1316 return skipped;
1317 }
1318 throw new IllegalArgumentException("Negative skip value");
1319 }
1320
1321
1322
1323
1324 private void skipRemainderOfArchive() throws IOException {
1325
1326
1327
1328 if (entriesRead > 0) {
1329 realSkip((long) entriesRead * CFH_LEN - LFH_LEN);
1330 }
1331 final boolean foundEocd = findEocdRecord();
1332 if (foundEocd) {
1333 realSkip((long) ZipFile.MIN_EOCD_SIZE - WORD - SHORT );
1334 readFully(shortBuf);
1335
1336 final int commentLen = ZipShort.getValue(shortBuf);
1337 if (commentLen >= 0) {
1338 realSkip(commentLen);
1339 return;
1340 }
1341 }
1342 throw new IOException("Truncated ZIP file");
1343 }
1344
1345
1346
1347
1348 private boolean supportsCompressedSizeFor(final ZipArchiveEntry entry) {
1349 final int method = entry.getMethod();
1350 return entry.getCompressedSize() != ArchiveEntry.SIZE_UNKNOWN || method == ZipEntry.DEFLATED
1351 || method == ZipMethod.ENHANCED_DEFLATED.getCode()
1352 || entry.getGeneralPurposeBit().usesDataDescriptor() && allowStoredEntriesWithDataDescriptor && method == ZipEntry.STORED
1353 || ZipMethod.isZstd(method)
1354 || method == ZipMethod.XZ.getCode();
1355 }
1356
1357
1358
1359
1360
1361
1362
1363 private boolean supportsDataDescriptorFor(final ZipArchiveEntry entry) {
1364 final int method = entry.getMethod();
1365 return !entry.getGeneralPurposeBit().usesDataDescriptor() || allowStoredEntriesWithDataDescriptor && method == ZipEntry.STORED
1366 || method == ZipEntry.DEFLATED || method == ZipMethod.ENHANCED_DEFLATED.getCode()
1367 || ZipMethod.isZstd(method)
1368 || method == ZipMethod.XZ.getCode();
1369 }
1370 }