aboutsummaryrefslogtreecommitdiff
path: root/CPP/7zip/Archive/Zip/ZipIn.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'CPP/7zip/Archive/Zip/ZipIn.cpp')
-rw-r--r--CPP/7zip/Archive/Zip/ZipIn.cpp3367
1 files changed, 3367 insertions, 0 deletions
diff --git a/CPP/7zip/Archive/Zip/ZipIn.cpp b/CPP/7zip/Archive/Zip/ZipIn.cpp
new file mode 100644
index 0000000..076d6bb
--- /dev/null
+++ b/CPP/7zip/Archive/Zip/ZipIn.cpp
@@ -0,0 +1,3367 @@
1// Archive/ZipIn.cpp
2
3#include "StdAfx.h"
4
5// #include <stdio.h>
6
7#include "../../../Common/DynamicBuffer.h"
8#include "../../../Common/IntToString.h"
9#include "../../../Common/MyException.h"
10#include "../../../Common/StringToInt.h"
11
12#include "../../../Windows/PropVariant.h"
13
14#include "../../Common/StreamUtils.h"
15
16#include "../IArchive.h"
17
18#include "ZipIn.h"
19
20#define Get16(p) GetUi16(p)
21#define Get32(p) GetUi32(p)
22#define Get64(p) GetUi64(p)
23
24#define G16(offs, v) v = Get16(p + (offs))
25#define G32(offs, v) v = Get32(p + (offs))
26#define G64(offs, v) v = Get64(p + (offs))
27
28namespace NArchive {
29namespace NZip {
30
31// (kBufferSize >= kDataDescriptorSize64 + 4)
32
33static const size_t kSeqBufferSize = (size_t)1 << 14;
34
35/*
36 if (not defined ZIP_SELF_CHECK) : it reads CD and if error in first pass CD reading, it reads LOCALS-CD-MODE
37 if ( defined ZIP_SELF_CHECK) : it always reads CD and LOCALS-CD-MODE
38 use ZIP_SELF_CHECK to check LOCALS-CD-MODE for any zip archive
39*/
40
41// #define ZIP_SELF_CHECK
42
43
44struct CEcd
45{
46 UInt16 ThisDisk;
47 UInt16 CdDisk;
48 UInt16 NumEntries_in_ThisDisk;
49 UInt16 NumEntries;
50 UInt32 Size;
51 UInt32 Offset;
52 UInt16 CommentSize;
53
54 bool IsEmptyArc() const
55 {
56 return ThisDisk == 0
57 && CdDisk == 0
58 && NumEntries_in_ThisDisk == 0
59 && NumEntries == 0
60 && Size == 0
61 && Offset == 0 // test it
62 ;
63 }
64
65 void Parse(const Byte *p); // (p) doesn't include signature
66};
67
68void CEcd::Parse(const Byte *p)
69{
70 // (p) doesn't include signature
71 G16(0, ThisDisk);
72 G16(2, CdDisk);
73 G16(4, NumEntries_in_ThisDisk);
74 G16(6, NumEntries);
75 G32(8, Size);
76 G32(12, Offset);
77 G16(16, CommentSize);
78}
79
80
81void CCdInfo::ParseEcd32(const Byte *p)
82{
83 IsFromEcd64 = false;
84 // (p) includes signature
85 p += 4;
86 G16(0, ThisDisk);
87 G16(2, CdDisk);
88 G16(4, NumEntries_in_ThisDisk);
89 G16(6, NumEntries);
90 G32(8, Size);
91 G32(12, Offset);
92 G16(16, CommentSize);
93}
94
95void CCdInfo::ParseEcd64e(const Byte *p)
96{
97 IsFromEcd64 = true;
98 // (p) exclude signature
99 G16(0, VersionMade);
100 G16(2, VersionNeedExtract);
101 G32(4, ThisDisk);
102 G32(8, CdDisk);
103
104 G64(12, NumEntries_in_ThisDisk);
105 G64(20, NumEntries);
106 G64(28, Size);
107 G64(36, Offset);
108}
109
110
111struct CLocator
112{
113 UInt32 Ecd64Disk;
114 UInt32 NumDisks;
115 UInt64 Ecd64Offset;
116
117 CLocator(): Ecd64Disk(0), NumDisks(0), Ecd64Offset(0) {}
118
119 void Parse(const Byte *p)
120 {
121 G32(0, Ecd64Disk);
122 G64(4, Ecd64Offset);
123 G32(12, NumDisks);
124 }
125
126 bool IsEmptyArc() const
127 {
128 return Ecd64Disk == 0 && NumDisks == 0 && Ecd64Offset == 0;
129 }
130};
131
132
133
134
135void CInArchive::ClearRefs()
136{
137 StreamRef.Release();
138 Stream = NULL;
139 StartStream = NULL;
140 Callback = NULL;
141
142 Vols.Clear();
143}
144
145void CInArchive::Close()
146{
147 _cnt = 0;
148 DisableBufMode();
149
150 IsArcOpen = false;
151
152 IsArc = false;
153 IsZip64 = false;
154
155 IsApk = false;
156 IsCdUnsorted = false;
157
158 HeadersError = false;
159 HeadersWarning = false;
160 ExtraMinorError = false;
161
162 UnexpectedEnd = false;
163 LocalsWereRead = false;
164 LocalsCenterMerged = false;
165 NoCentralDir = false;
166 Overflow32bit = false;
167 Cd_NumEntries_Overflow_16bit = false;
168
169 MarkerIsFound = false;
170 MarkerIsSafe = false;
171
172 IsMultiVol = false;
173 UseDisk_in_SingleVol = false;
174 EcdVolIndex = 0;
175
176 ArcInfo.Clear();
177
178 ClearRefs();
179}
180
181
182
183HRESULT CInArchive::Seek_SavePos(UInt64 offset)
184{
185 // InitBuf();
186 // if (!Stream) return S_FALSE;
187 return Stream->Seek((Int64)offset, STREAM_SEEK_SET, &_streamPos);
188}
189
190HRESULT CInArchive::SeekToVol(int volIndex, UInt64 offset)
191{
192 if (volIndex != Vols.StreamIndex)
193 {
194 InitBuf();
195 if (IsMultiVol && volIndex >= 0)
196 {
197 if ((unsigned)volIndex >= Vols.Streams.Size())
198 return S_FALSE;
199 if (!Vols.Streams[(unsigned)volIndex].Stream)
200 return S_FALSE;
201 Stream = Vols.Streams[(unsigned)volIndex].Stream;
202 }
203 else if (volIndex == -2)
204 {
205 if (!Vols.ZipStream)
206 return S_FALSE;
207 Stream = Vols.ZipStream;
208 }
209 else
210 Stream = StartStream;
211 Vols.StreamIndex = volIndex;
212 }
213 else
214 {
215 if (offset <= _streamPos)
216 {
217 const UInt64 back = _streamPos - offset;
218 if (back <= _bufCached)
219 {
220 _bufPos = _bufCached - (size_t)back;
221 return S_OK;
222 }
223 }
224 InitBuf();
225 }
226 return Seek_SavePos(offset);
227}
228
229
230// ---------- ReadFromCache ----------
231// reads from cache and from Stream
232// move to next volume can be allowed if (CanStartNewVol) and only before first byte reading
233
234HRESULT CInArchive::ReadFromCache(Byte *data, unsigned size, unsigned &processed)
235{
236 HRESULT result = S_OK;
237 processed = 0;
238
239 for (;;)
240 {
241 if (size == 0)
242 return S_OK;
243
244 const size_t avail = GetAvail();
245
246 if (avail != 0)
247 {
248 unsigned cur = size;
249 if (cur > avail)
250 cur = (unsigned)avail;
251 memcpy(data, (const Byte *)Buffer + _bufPos, cur);
252
253 data += cur;
254 size -= cur;
255 processed += cur;
256
257 _bufPos += cur;
258 _cnt += cur;
259
260 CanStartNewVol = false;
261
262 continue;
263 }
264
265 InitBuf();
266
267 if (_inBufMode)
268 {
269 UInt32 cur = 0;
270 result = Stream->Read(Buffer, (UInt32)Buffer.Size(), &cur);
271 _bufPos = 0;
272 _bufCached = cur;
273 _streamPos += cur;
274 if (cur != 0)
275 CanStartNewVol = false;
276 if (result != S_OK)
277 break;
278 if (cur != 0)
279 continue;
280 }
281 else
282 {
283 size_t cur = size;
284 result = ReadStream(Stream, data, &cur);
285 data += cur;
286 size -= (unsigned)cur;
287 processed += (unsigned)cur;
288 _streamPos += cur;
289 _cnt += cur;
290 if (cur != 0)
291 {
292 CanStartNewVol = false;
293 break;
294 }
295 if (result != S_OK)
296 break;
297 }
298
299 if ( !IsMultiVol
300 || !CanStartNewVol
301 || Vols.StreamIndex < 0
302 || (unsigned)Vols.StreamIndex + 1 >= Vols.Streams.Size())
303 break;
304
305 const CVols::CSubStreamInfo &s = Vols.Streams[(unsigned)Vols.StreamIndex + 1];
306 if (!s.Stream)
307 break;
308 result = s.SeekToStart();
309 if (result != S_OK)
310 break;
311 Vols.StreamIndex++;
312 _streamPos = 0;
313 // Vols.NeedSeek = false;
314
315 Stream = s.Stream;
316 }
317
318 return result;
319}
320
321
322HRESULT CInArchive::ReadFromCache_FALSE(Byte *data, unsigned size)
323{
324 unsigned processed;
325 HRESULT res = ReadFromCache(data, size, processed);
326 if (res == S_OK && size != processed)
327 return S_FALSE;
328 return res;
329}
330
331
332static bool CheckDosTime(UInt32 dosTime)
333{
334 if (dosTime == 0)
335 return true;
336 unsigned month = (dosTime >> 21) & 0xF;
337 unsigned day = (dosTime >> 16) & 0x1F;
338 unsigned hour = (dosTime >> 11) & 0x1F;
339 unsigned min = (dosTime >> 5) & 0x3F;
340 unsigned sec = (dosTime & 0x1F) * 2;
341 if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || min > 59 || sec > 59)
342 return false;
343 return true;
344}
345
346API_FUNC_IsArc IsArc_Zip(const Byte *p, size_t size)
347{
348 if (size < 8)
349 return k_IsArc_Res_NEED_MORE;
350 if (p[0] != 'P')
351 return k_IsArc_Res_NO;
352
353 UInt32 sig = Get32(p);
354
355 if (sig == NSignature::kNoSpan || sig == NSignature::kSpan)
356 {
357 p += 4;
358 size -= 4;
359 }
360
361 sig = Get32(p);
362
363 if (sig == NSignature::kEcd64)
364 {
365 if (size < kEcd64_FullSize)
366 return k_IsArc_Res_NEED_MORE;
367
368 const UInt64 recordSize = Get64(p + 4);
369 if ( recordSize < kEcd64_MainSize
370 || recordSize > kEcd64_MainSize + (1 << 20))
371 return k_IsArc_Res_NO;
372 CCdInfo cdInfo;
373 cdInfo.ParseEcd64e(p + 12);
374 if (!cdInfo.IsEmptyArc())
375 return k_IsArc_Res_NO;
376 return k_IsArc_Res_YES; // k_IsArc_Res_YES_2;
377 }
378
379 if (sig == NSignature::kEcd)
380 {
381 if (size < kEcdSize)
382 return k_IsArc_Res_NEED_MORE;
383 CEcd ecd;
384 ecd.Parse(p + 4);
385 // if (ecd.cdSize != 0)
386 if (!ecd.IsEmptyArc())
387 return k_IsArc_Res_NO;
388 return k_IsArc_Res_YES; // k_IsArc_Res_YES_2;
389 }
390
391 if (sig != NSignature::kLocalFileHeader)
392 return k_IsArc_Res_NO;
393
394 if (size < kLocalHeaderSize)
395 return k_IsArc_Res_NEED_MORE;
396
397 p += 4;
398
399 {
400 const unsigned kPureHeaderSize = kLocalHeaderSize - 4;
401 unsigned i;
402 for (i = 0; i < kPureHeaderSize && p[i] == 0; i++);
403 if (i == kPureHeaderSize)
404 return k_IsArc_Res_NEED_MORE;
405 }
406
407 /*
408 if (p[0] >= 128) // ExtractVersion.Version;
409 return k_IsArc_Res_NO;
410 */
411
412 // ExtractVersion.Version = p[0];
413 // ExtractVersion.HostOS = p[1];
414 // Flags = Get16(p + 2);
415 // Method = Get16(p + 4);
416 /*
417 // 9.33: some zip archives contain incorrect value in timestamp. So we don't check it now
418 UInt32 dosTime = Get32(p + 6);
419 if (!CheckDosTime(dosTime))
420 return k_IsArc_Res_NO;
421 */
422 // Crc = Get32(p + 10);
423 // PackSize = Get32(p + 14);
424 // Size = Get32(p + 18);
425 const unsigned nameSize = Get16(p + 22);
426 unsigned extraSize = Get16(p + 24);
427 const UInt32 extraOffset = kLocalHeaderSize + (UInt32)nameSize;
428
429 /*
430 // 21.02: fixed. we don't use the following check
431 if (extraOffset + extraSize > (1 << 16))
432 return k_IsArc_Res_NO;
433 */
434
435 p -= 4;
436
437 {
438 size_t rem = size - kLocalHeaderSize;
439 if (rem > nameSize)
440 rem = nameSize;
441 const Byte *p2 = p + kLocalHeaderSize;
442 for (size_t i = 0; i < rem; i++)
443 if (p2[i] == 0)
444 {
445 // we support some "bad" zip archives that contain zeros after name
446 for (size_t k = i + 1; k < rem; k++)
447 if (p2[k] != 0)
448 return k_IsArc_Res_NO;
449 break;
450 /*
451 if (i != nameSize - 1)
452 return k_IsArc_Res_NO;
453 */
454 }
455 }
456
457 if (size < extraOffset)
458 return k_IsArc_Res_NEED_MORE;
459
460 if (extraSize > 0)
461 {
462 p += extraOffset;
463 size -= extraOffset;
464 while (extraSize != 0)
465 {
466 if (extraSize < 4)
467 {
468 // 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers.
469 // so we return k_IsArc_Res_YES to support such archives.
470 // return k_IsArc_Res_NO; // do we need to support such extra ?
471 return k_IsArc_Res_YES;
472 }
473 if (size < 4)
474 return k_IsArc_Res_NEED_MORE;
475 unsigned dataSize = Get16(p + 2);
476 size -= 4;
477 extraSize -= 4;
478 p += 4;
479 if (dataSize > extraSize)
480 {
481 // It can be error on header.
482 // We want to support such rare case bad archives.
483 // We use additional checks to reduce false-positive probability.
484 if (nameSize == 0
485 || nameSize > (1 << 9)
486 || extraSize > (1 << 9))
487 return k_IsArc_Res_NO;
488 return k_IsArc_Res_YES;
489 }
490 if (dataSize > size)
491 return k_IsArc_Res_NEED_MORE;
492 size -= dataSize;
493 extraSize -= dataSize;
494 p += dataSize;
495 }
496 }
497
498 return k_IsArc_Res_YES;
499}
500
501static UInt32 IsArc_Zip_2(const Byte *p, size_t size, bool isFinal)
502{
503 UInt32 res = IsArc_Zip(p, size);
504 if (res == k_IsArc_Res_NEED_MORE && isFinal)
505 return k_IsArc_Res_NO;
506 return res;
507}
508
509
510
511MY_NO_INLINE
512static const Byte *FindPK(const Byte *p, const Byte *limit)
513{
514 for (;;)
515 {
516 for (;;)
517 {
518 Byte b0;
519 b0 = p[0]; if (p >= limit) return p; p++; if (b0 == 0x50) break;
520 b0 = p[0]; if (p >= limit) return p; p++; if (b0 == 0x50) break;
521 }
522 if (p[0] == 0x4B)
523 return p - 1;
524 }
525}
526
527
528/*
529---------- FindMarker ----------
530returns:
531 S_OK:
532 ArcInfo.MarkerVolIndex : volume of marker
533 ArcInfo.MarkerPos : Pos of first signature
534 ArcInfo.MarkerPos2 : Pos of main signature (local item signature in most cases)
535 _streamPos : stream pos
536 _cnt : The number of virtal Bytes after start of search to offset after signature
537 _signature : main signature
538
539 S_FALSE: can't find marker, or there is some non-zip data after marker
540
541 Error code: stream reading error.
542*/
543
544HRESULT CInArchive::FindMarker(const UInt64 *searchLimit)
545{
546 ArcInfo.MarkerPos = GetVirtStreamPos();
547 ArcInfo.MarkerPos2 = ArcInfo.MarkerPos;
548 ArcInfo.MarkerVolIndex = Vols.StreamIndex;
549
550 _cnt = 0;
551
552 CanStartNewVol = false;
553
554 if (searchLimit && *searchLimit == 0)
555 {
556 Byte startBuf[kMarkerSize];
557 RINOK(ReadFromCache_FALSE(startBuf, kMarkerSize));
558
559 UInt32 marker = Get32(startBuf);
560 _signature = marker;
561
562 if ( marker == NSignature::kNoSpan
563 || marker == NSignature::kSpan)
564 {
565 RINOK(ReadFromCache_FALSE(startBuf, kMarkerSize));
566 _signature = Get32(startBuf);
567 }
568
569 if ( _signature != NSignature::kEcd
570 && _signature != NSignature::kEcd64
571 && _signature != NSignature::kLocalFileHeader)
572 return S_FALSE;
573
574 ArcInfo.MarkerPos2 = GetVirtStreamPos() - 4;
575 ArcInfo.IsSpanMode = (marker == NSignature::kSpan);
576
577 // we use weak test in case of (*searchLimit == 0)
578 // since error will be detected later in Open function
579 return S_OK;
580 }
581
582 const size_t kCheckSize = (size_t)1 << 16; // must be smaller than kBufSize
583 const size_t kBufSize = (size_t)1 << 17; // must be larger than kCheckSize
584
585 if (Buffer.Size() < kBufSize)
586 {
587 InitBuf();
588 Buffer.AllocAtLeast(kBufSize);
589 if (!Buffer.IsAllocated())
590 return E_OUTOFMEMORY;
591 }
592
593 _inBufMode = true;
594
595 UInt64 progressPrev = 0;
596
597 for (;;)
598 {
599 RINOK(LookAhead(kBufSize));
600
601 const size_t avail = GetAvail();
602
603 size_t limitPos;
604 const bool isFinished = (avail != kBufSize);
605 if (isFinished)
606 {
607 const unsigned kMinAllowed = 4;
608 if (avail <= kMinAllowed)
609 {
610 if ( !IsMultiVol
611 || Vols.StreamIndex < 0
612 || (unsigned)Vols.StreamIndex + 1 >= Vols.Streams.Size())
613 break;
614
615 SkipLookahed(avail);
616
617 const CVols::CSubStreamInfo &s = Vols.Streams[(unsigned)Vols.StreamIndex + 1];
618 if (!s.Stream)
619 break;
620
621 RINOK(s.SeekToStart());
622
623 InitBuf();
624 Vols.StreamIndex++;
625 _streamPos = 0;
626 Stream = s.Stream;
627 continue;
628 }
629 limitPos = avail - kMinAllowed;
630 }
631 else
632 limitPos = (avail - kCheckSize);
633
634 // we don't check at (limitPos) for good fast aligned operations
635
636 if (searchLimit)
637 {
638 if (_cnt > *searchLimit)
639 break;
640 UInt64 rem = *searchLimit - _cnt;
641 if (limitPos > rem)
642 limitPos = (size_t)rem + 1;
643 }
644
645 if (limitPos == 0)
646 break;
647
648 const Byte * const pStart = Buffer + _bufPos;
649 const Byte * p = pStart;
650 const Byte * const limit = pStart + limitPos;
651
652 for (;; p++)
653 {
654 p = FindPK(p, limit);
655 if (p >= limit)
656 break;
657 const size_t rem = (size_t)(pStart + avail - p);
658 UInt32 res = IsArc_Zip_2(p, rem, isFinished);
659 if (res != k_IsArc_Res_NO)
660 {
661 if (rem < kMarkerSize)
662 return S_FALSE;
663 _signature = Get32(p);
664 SkipLookahed((size_t)(p - pStart));
665 ArcInfo.MarkerVolIndex = Vols.StreamIndex;
666 ArcInfo.MarkerPos = GetVirtStreamPos();
667 ArcInfo.MarkerPos2 = ArcInfo.MarkerPos;
668 SkipLookahed(4);
669 if ( _signature == NSignature::kNoSpan
670 || _signature == NSignature::kSpan)
671 {
672 if (rem < kMarkerSize * 2)
673 return S_FALSE;
674 ArcInfo.IsSpanMode = (_signature == NSignature::kSpan);
675 _signature = Get32(p + 4);
676 ArcInfo.MarkerPos2 += 4;
677 SkipLookahed(4);
678 }
679 return S_OK;
680 }
681 }
682
683 if (!IsMultiVol && isFinished)
684 break;
685
686 SkipLookahed((size_t)(p - pStart));
687
688 if (Callback && (_cnt - progressPrev) >= ((UInt32)1 << 23))
689 {
690 progressPrev = _cnt;
691 // const UInt64 numFiles64 = 0;
692 RINOK(Callback->SetCompleted(NULL, &_cnt));
693 }
694 }
695
696 return S_FALSE;
697}
698
699
700/*
701---------- IncreaseRealPosition ----------
702moves virtual offset in virtual stream.
703changing to new volumes is allowed
704*/
705
706HRESULT CInArchive::IncreaseRealPosition(UInt64 offset, bool &isFinished)
707{
708 isFinished = false;
709
710 for (;;)
711 {
712 const size_t avail = GetAvail();
713
714 if (offset <= avail)
715 {
716 _bufPos += (size_t)offset;
717 _cnt += offset;
718 return S_OK;
719 }
720
721 _cnt += avail;
722 offset -= avail;
723
724 _bufCached = 0;
725 _bufPos = 0;
726
727 if (!_inBufMode)
728 break;
729
730 CanStartNewVol = true;
731 LookAhead(1);
732
733 if (GetAvail() == 0)
734 return S_OK;
735 }
736
737 if (!IsMultiVol)
738 {
739 _cnt += offset;
740 return Stream->Seek((Int64)offset, STREAM_SEEK_CUR, &_streamPos);
741 }
742
743 for (;;)
744 {
745 if (offset == 0)
746 return S_OK;
747
748 if (Vols.StreamIndex < 0)
749 return S_FALSE;
750 if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size())
751 {
752 isFinished = true;
753 return S_OK;
754 }
755 {
756 const CVols::CSubStreamInfo &s = Vols.Streams[(unsigned)Vols.StreamIndex];
757 if (!s.Stream)
758 {
759 isFinished = true;
760 return S_OK;
761 }
762 if (_streamPos > s.Size)
763 return S_FALSE;
764 const UInt64 rem = s.Size - _streamPos;
765 if ((UInt64)offset <= rem)
766 {
767 _cnt += offset;
768 return Stream->Seek((Int64)offset, STREAM_SEEK_CUR, &_streamPos);
769 }
770 RINOK(Seek_SavePos(s.Size));
771 offset -= rem;
772 _cnt += rem;
773 }
774
775 Stream = NULL;
776 _streamPos = 0;
777 Vols.StreamIndex++;
778 if ((unsigned)Vols.StreamIndex >= Vols.Streams.Size())
779 {
780 isFinished = true;
781 return S_OK;
782 }
783 const CVols::CSubStreamInfo &s2 = Vols.Streams[(unsigned)Vols.StreamIndex];
784 if (!s2.Stream)
785 {
786 isFinished = true;
787 return S_OK;
788 }
789 Stream = s2.Stream;
790 RINOK(Seek_SavePos(0));
791 }
792}
793
794
795
796/*
797---------- LookAhead ----------
798Reads data to buffer, if required.
799
800It can read from volumes as long as Buffer.Size().
801But it moves to new volume, only if it's required to provide minRequired bytes in buffer.
802
803in:
804 (minRequired <= Buffer.Size())
805
806return:
807 S_OK : if (GetAvail() < minRequired) after function return, it's end of stream(s) data, or no new volume stream.
808 Error codes: IInStream::Read() error or IInStream::Seek() error for multivol
809*/
810
811HRESULT CInArchive::LookAhead(size_t minRequired)
812{
813 for (;;)
814 {
815 const size_t avail = GetAvail();
816
817 if (minRequired <= avail)
818 return S_OK;
819
820 if (_bufPos != 0)
821 {
822 if (avail != 0)
823 memmove(Buffer, Buffer + _bufPos, avail);
824 _bufPos = 0;
825 _bufCached = avail;
826 }
827
828 const size_t pos = _bufCached;
829 UInt32 processed = 0;
830 HRESULT res = Stream->Read(Buffer + pos, (UInt32)(Buffer.Size() - pos), &processed);
831 _streamPos += processed;
832 _bufCached += processed;
833
834 if (res != S_OK)
835 return res;
836
837 if (processed != 0)
838 continue;
839
840 if ( !IsMultiVol
841 || !CanStartNewVol
842 || Vols.StreamIndex < 0
843 || (unsigned)Vols.StreamIndex + 1 >= Vols.Streams.Size())
844 return S_OK;
845
846 const CVols::CSubStreamInfo &s = Vols.Streams[(unsigned)Vols.StreamIndex + 1];
847 if (!s.Stream)
848 return S_OK;
849
850 RINOK(s.SeekToStart());
851
852 Vols.StreamIndex++;
853 _streamPos = 0;
854 Stream = s.Stream;
855 // Vols.NeedSeek = false;
856 }
857}
858
859
860class CUnexpectEnd {};
861
862
863/*
864---------- SafeRead ----------
865
866reads data of exact size from stream(s)
867
868in:
869 _inBufMode
870 if (CanStartNewVol) it can go to next volume before first byte reading, if there is end of volume data.
871
872in, out:
873 _streamPos : position in Stream
874 Stream
875 Vols : if (IsMultiVol)
876 _cnt
877
878out:
879 (CanStartNewVol == false), if some data was read
880
881return:
882 S_OK : success reading of requested data
883
884exceptions:
885 CSystemException() - stream reading error
886 CUnexpectEnd() : could not read data of requested size
887*/
888
889void CInArchive::SafeRead(Byte *data, unsigned size)
890{
891 unsigned processed;
892 HRESULT result = ReadFromCache(data, size, processed);
893 if (result != S_OK)
894 throw CSystemException(result);
895 if (size != processed)
896 throw CUnexpectEnd();
897}
898
899void CInArchive::ReadBuffer(CByteBuffer &buffer, unsigned size)
900{
901 buffer.Alloc(size);
902 if (size != 0)
903 SafeRead(buffer, size);
904}
905
906// Byte CInArchive::ReadByte () { Byte b; SafeRead(&b, 1); return b; }
907// UInt16 CInArchive::ReadUInt16() { Byte buf[2]; SafeRead(buf, 2); return Get16(buf); }
908UInt32 CInArchive::ReadUInt32() { Byte buf[4]; SafeRead(buf, 4); return Get32(buf); }
909UInt64 CInArchive::ReadUInt64() { Byte buf[8]; SafeRead(buf, 8); return Get64(buf); }
910
911void CInArchive::ReadSignature()
912{
913 CanStartNewVol = true;
914 _signature = ReadUInt32();
915 // CanStartNewVol = false; // it's already changed in SafeRead
916}
917
918
919// we Skip() inside headers only, so no need for stream change in multivol.
920
921void CInArchive::Skip(size_t num)
922{
923 while (num != 0)
924 {
925 const unsigned kBufSize = (size_t)1 << 10;
926 Byte buf[kBufSize];
927 unsigned step = kBufSize;
928 if (step > num)
929 step = (unsigned)num;
930 SafeRead(buf, step);
931 num -= step;
932 }
933}
934
935/*
936HRESULT CInArchive::Callback_Completed(unsigned numFiles)
937{
938 const UInt64 numFiles64 = numFiles;
939 return Callback->SetCompleted(&numFiles64, &_cnt);
940}
941*/
942
943HRESULT CInArchive::Skip64(UInt64 num, unsigned numFiles)
944{
945 if (num == 0)
946 return S_OK;
947
948 for (;;)
949 {
950 size_t step = (size_t)1 << 24;
951 if (step > num)
952 step = (size_t)num;
953 Skip(step);
954 num -= step;
955 if (num == 0)
956 return S_OK;
957 if (Callback)
958 {
959 const UInt64 numFiles64 = numFiles;
960 RINOK(Callback->SetCompleted(&numFiles64, &_cnt));
961 }
962 }
963}
964
965
966bool CInArchive::ReadFileName(unsigned size, AString &s)
967{
968 if (size == 0)
969 {
970 s.Empty();
971 return true;
972 }
973 char *p = s.GetBuf(size);
974 SafeRead((Byte *)p, size);
975 unsigned i = size;
976 do
977 {
978 if (p[i - 1] != 0)
979 break;
980 }
981 while (--i);
982 s.ReleaseBuf_CalcLen(size);
983 return s.Len() == i;
984}
985
986
987#define ZIP64_IS_32_MAX(n) ((n) == 0xFFFFFFFF)
988#define ZIP64_IS_16_MAX(n) ((n) == 0xFFFF)
989
990
991bool CInArchive::ReadExtra(const CLocalItem &item, unsigned extraSize, CExtraBlock &extra,
992 UInt64 &unpackSize, UInt64 &packSize,
993 CItem *cdItem)
994{
995 extra.Clear();
996
997 while (extraSize >= 4)
998 {
999 CExtraSubBlock subBlock;
1000 const UInt32 pair = ReadUInt32();
1001 subBlock.ID = (pair & 0xFFFF);
1002 unsigned size = (unsigned)(pair >> 16);
1003
1004 extraSize -= 4;
1005
1006 if (size > extraSize)
1007 {
1008 // it's error in extra
1009 HeadersWarning = true;
1010 extra.Error = true;
1011 Skip(extraSize);
1012 return false;
1013 }
1014
1015 extraSize -= size;
1016
1017 if (subBlock.ID == NFileHeader::NExtraID::kZip64)
1018 {
1019 extra.IsZip64 = true;
1020 bool isOK = true;
1021
1022 if (!cdItem
1023 && size == 16
1024 && !ZIP64_IS_32_MAX(unpackSize)
1025 && !ZIP64_IS_32_MAX(packSize))
1026 {
1027 /* Win10 Explorer's "Send to Zip" for big (3500 MiB) files
1028 creates Zip64 Extra in local file header.
1029 But if both uncompressed and compressed sizes are smaller than 4 GiB,
1030 Win10 doesn't store 0xFFFFFFFF in 32-bit fields as expected by zip specification.
1031 21.04: we ignore these minor errors in Win10 zip archives. */
1032 if (ReadUInt64() != unpackSize)
1033 isOK = false;
1034 if (ReadUInt64() != packSize)
1035 isOK = false;
1036 size = 0;
1037 }
1038 else
1039 {
1040 if (ZIP64_IS_32_MAX(unpackSize))
1041 { if (size < 8) isOK = false; else { size -= 8; unpackSize = ReadUInt64(); }}
1042
1043 if (isOK && ZIP64_IS_32_MAX(packSize))
1044 { if (size < 8) isOK = false; else { size -= 8; packSize = ReadUInt64(); }}
1045
1046 if (cdItem)
1047 {
1048 if (isOK && ZIP64_IS_32_MAX(cdItem->LocalHeaderPos))
1049 { if (size < 8) isOK = false; else { size -= 8; cdItem->LocalHeaderPos = ReadUInt64(); }}
1050
1051 if (isOK && ZIP64_IS_16_MAX(cdItem->Disk))
1052 { if (size < 4) isOK = false; else { size -= 4; cdItem->Disk = ReadUInt32(); }}
1053 }
1054 }
1055
1056 if (!isOK || size != 0)
1057 {
1058 HeadersWarning = true;
1059 extra.Error = true;
1060 extra.IsZip64_Error = true;
1061 Skip(size);
1062 }
1063 }
1064 else
1065 {
1066 ReadBuffer(subBlock.Data, size);
1067 extra.SubBlocks.Add(subBlock);
1068 if (subBlock.ID == NFileHeader::NExtraID::kIzUnicodeName)
1069 {
1070 if (!subBlock.CheckIzUnicode(item.Name))
1071 extra.Error = true;
1072 }
1073 }
1074 }
1075
1076 if (extraSize != 0)
1077 {
1078 ExtraMinorError = true;
1079 extra.MinorError = true;
1080 // 7-Zip before 9.31 created incorrect WsAES Extra in folder's local headers.
1081 // so we don't return false, but just set warning flag
1082 // return false;
1083 Skip(extraSize);
1084 }
1085
1086 return true;
1087}
1088
1089
1090bool CInArchive::ReadLocalItem(CItemEx &item)
1091{
1092 item.Disk = 0;
1093 if (IsMultiVol && Vols.StreamIndex >= 0)
1094 item.Disk = (UInt32)Vols.StreamIndex;
1095 const unsigned kPureHeaderSize = kLocalHeaderSize - 4;
1096 Byte p[kPureHeaderSize];
1097 SafeRead(p, kPureHeaderSize);
1098 {
1099 unsigned i;
1100 for (i = 0; i < kPureHeaderSize && p[i] == 0; i++);
1101 if (i == kPureHeaderSize)
1102 return false;
1103 }
1104
1105 item.ExtractVersion.Version = p[0];
1106 item.ExtractVersion.HostOS = p[1];
1107 G16(2, item.Flags);
1108 G16(4, item.Method);
1109 G32(6, item.Time);
1110 G32(10, item.Crc);
1111 G32(14, item.PackSize);
1112 G32(18, item.Size);
1113 const unsigned nameSize = Get16(p + 22);
1114 const unsigned extraSize = Get16(p + 24);
1115 bool isOkName = ReadFileName(nameSize, item.Name);
1116 item.LocalFullHeaderSize = kLocalHeaderSize + (UInt32)nameSize + extraSize;
1117 item.DescriptorWasRead = false;
1118
1119 /*
1120 if (item.IsDir())
1121 item.Size = 0; // check It
1122 */
1123
1124 if (extraSize > 0)
1125 {
1126 if (!ReadExtra(item, extraSize, item.LocalExtra, item.Size, item.PackSize, NULL))
1127 {
1128 /* Most of archives are OK for Extra. But there are some rare cases
1129 that have error. And if error in first item, it can't open archive.
1130 So we ignore that error */
1131 // return false;
1132 }
1133 }
1134
1135 if (!CheckDosTime(item.Time))
1136 {
1137 HeadersWarning = true;
1138 // return false;
1139 }
1140
1141 if (item.Name.Len() != nameSize)
1142 {
1143 // we support some "bad" zip archives that contain zeros after name
1144 if (!isOkName)
1145 return false;
1146 HeadersWarning = true;
1147 }
1148
1149 // return item.LocalFullHeaderSize <= ((UInt32)1 << 16);
1150 return true;
1151}
1152
1153
1154static bool FlagsAreSame(const CItem &i1, const CItem &i2_cd)
1155{
1156 if (i1.Method != i2_cd.Method)
1157 return false;
1158
1159 UInt32 mask = i1.Flags ^ i2_cd.Flags;
1160 if (mask == 0)
1161 return true;
1162 switch (i1.Method)
1163 {
1164 case NFileHeader::NCompressionMethod::kDeflate:
1165 mask &= 0x7FF9;
1166 break;
1167 default:
1168 if (i1.Method <= NFileHeader::NCompressionMethod::kImplode)
1169 mask &= 0x7FFF;
1170 }
1171
1172 // we can ignore utf8 flag, if name is ascii
1173 if (mask & NFileHeader::NFlags::kUtf8)
1174 if (i1.Name.IsAscii() && i2_cd.Name.IsAscii())
1175 mask &= ~NFileHeader::NFlags::kUtf8;
1176
1177 // some bad archive in rare case can use descriptor without descriptor flag in Central Dir
1178 // if (i1.HasDescriptor())
1179 mask &= ~NFileHeader::NFlags::kDescriptorUsedMask;
1180
1181 return (mask == 0);
1182}
1183
1184
1185// #ifdef _WIN32
1186static bool AreEqualPaths_IgnoreSlashes(const char *s1, const char *s2)
1187{
1188 for (;;)
1189 {
1190 char c1 = *s1++;
1191 char c2 = *s2++;
1192 if (c1 == c2)
1193 {
1194 if (c1 == 0)
1195 return true;
1196 }
1197 else
1198 {
1199 if (c1 == '\\') c1 = '/';
1200 if (c2 == '\\') c2 = '/';
1201 if (c1 != c2)
1202 return false;
1203 }
1204 }
1205}
1206// #endif
1207
1208
1209static bool AreItemsEqual(const CItemEx &localItem, const CItemEx &cdItem)
1210{
1211 if (!FlagsAreSame(localItem, cdItem))
1212 return false;
1213 if (!localItem.HasDescriptor())
1214 {
1215 if (cdItem.PackSize != localItem.PackSize
1216 || cdItem.Size != localItem.Size
1217 || (cdItem.Crc != localItem.Crc && cdItem.Crc != 0)) // some program writes 0 to crc field in central directory
1218 return false;
1219 }
1220 /* pkzip 2.50 creates incorrect archives. It uses
1221 - WIN encoding for name in local header
1222 - OEM encoding for name in central header
1223 We don't support these strange items. */
1224
1225 /* if (cdItem.Name.Len() != localItem.Name.Len())
1226 return false;
1227 */
1228 if (cdItem.Name != localItem.Name)
1229 {
1230 // #ifdef _WIN32
1231 // some xap files use backslash in central dir items.
1232 // we can ignore such errors in windows, where all slashes are converted to backslashes
1233 unsigned hostOs = cdItem.GetHostOS();
1234
1235 if (hostOs == NFileHeader::NHostOS::kFAT ||
1236 hostOs == NFileHeader::NHostOS::kNTFS)
1237 {
1238 if (!AreEqualPaths_IgnoreSlashes(cdItem.Name, localItem.Name))
1239 {
1240 // pkzip 2.50 uses DOS encoding in central dir and WIN encoding in local header.
1241 // so we ignore that error
1242 if (hostOs != NFileHeader::NHostOS::kFAT
1243 || cdItem.MadeByVersion.Version < 25
1244 || cdItem.MadeByVersion.Version > 40)
1245 return false;
1246 }
1247 }
1248 /*
1249 else
1250 #endif
1251 return false;
1252 */
1253 }
1254 return true;
1255}
1256
1257
1258HRESULT CInArchive::ReadLocalItemAfterCdItem(CItemEx &item, bool &isAvail, bool &headersError)
1259{
1260 InitBuf();
1261 _inBufMode = false;
1262
1263 isAvail = true;
1264 headersError = false;
1265 if (item.FromLocal)
1266 return S_OK;
1267 try
1268 {
1269 UInt64 offset = item.LocalHeaderPos;
1270
1271 if (IsMultiVol)
1272 {
1273 if (item.Disk >= Vols.Streams.Size())
1274 {
1275 isAvail = false;
1276 return S_FALSE;
1277 }
1278 Stream = Vols.Streams[item.Disk].Stream;
1279 Vols.StreamIndex = (int)item.Disk;
1280 if (!Stream)
1281 {
1282 isAvail = false;
1283 return S_FALSE;
1284 }
1285 }
1286 else
1287 {
1288 if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex)
1289 {
1290 isAvail = false;
1291 return S_FALSE;
1292 }
1293 Stream = StreamRef;
1294
1295 offset = (UInt64)((Int64)offset + ArcInfo.Base);
1296 if (ArcInfo.Base < 0 && (Int64)offset < 0)
1297 {
1298 isAvail = false;
1299 return S_FALSE;
1300 }
1301 }
1302
1303 RINOK(Seek_SavePos(offset));
1304
1305 /*
1306 // we can check buf mode
1307 InitBuf();
1308 _inBufMode = true;
1309 Buffer.AllocAtLeast(1 << 10);
1310 */
1311
1312 CItemEx localItem;
1313 if (ReadUInt32() != NSignature::kLocalFileHeader)
1314 return S_FALSE;
1315 ReadLocalItem(localItem);
1316 if (!AreItemsEqual(localItem, item))
1317 return S_FALSE;
1318 item.LocalFullHeaderSize = localItem.LocalFullHeaderSize;
1319 item.LocalExtra = localItem.LocalExtra;
1320 if (item.Crc != localItem.Crc && !localItem.HasDescriptor())
1321 {
1322 item.Crc = localItem.Crc;
1323 headersError = true;
1324 }
1325 if ((item.Flags ^ localItem.Flags) & NFileHeader::NFlags::kDescriptorUsedMask)
1326 {
1327 item.Flags = (UInt16)(item.Flags ^ NFileHeader::NFlags::kDescriptorUsedMask);
1328 headersError = true;
1329 }
1330 item.FromLocal = true;
1331 }
1332 catch(...) { return S_FALSE; }
1333 return S_OK;
1334}
1335
1336
1337/*
1338---------- FindDescriptor ----------
1339
1340in:
1341 _streamPos : position in Stream
1342 Stream :
1343 Vols : if (IsMultiVol)
1344
1345action:
1346 searches descriptor in input stream(s).
1347 sets
1348 item.DescriptorWasRead = true;
1349 item.Size
1350 item.PackSize
1351 item.Crc
1352 if descriptor was found
1353
1354out:
1355 S_OK:
1356 if ( item.DescriptorWasRead) : if descriptor was found
1357 if (!item.DescriptorWasRead) : if descriptor was not found : unexpected end of stream(s)
1358
1359 S_FALSE: if no items or there is just one item with strange properies that doesn't look like real archive.
1360
1361 another error code: Callback error.
1362
1363exceptions :
1364 CSystemException() : stream reading error
1365*/
1366
1367HRESULT CInArchive::FindDescriptor(CItemEx &item, unsigned numFiles)
1368{
1369 // const size_t kBufSize = (size_t)1 << 5; // don't increase it too much. It reads data look ahead.
1370
1371 // Buffer.Alloc(kBufSize);
1372 // Byte *buf = Buffer;
1373
1374 UInt64 packedSize = 0;
1375
1376 UInt64 progressPrev = _cnt;
1377
1378 for (;;)
1379 {
1380 /* appnote specification claims that we must use 64-bit descriptor, if there is zip64 extra.
1381 But some old third-party xps archives used 64-bit descriptor without zip64 extra. */
1382 // unsigned descriptorSize = kDataDescriptorSize64 + kNextSignatureSize;
1383
1384 // const unsigned kNextSignatureSize = 0; // we can disable check for next signatuire
1385 const unsigned kNextSignatureSize = 4; // we check also for signature for next File headear
1386
1387 const unsigned descriptorSize4 = item.GetDescriptorSize() + kNextSignatureSize;
1388
1389 if (descriptorSize4 > Buffer.Size()) return E_FAIL;
1390
1391 // size_t processedSize;
1392 CanStartNewVol = true;
1393 RINOK(LookAhead(descriptorSize4));
1394 const size_t avail = GetAvail();
1395
1396 if (avail < descriptorSize4)
1397 {
1398 // we write to packSize all these available bytes.
1399 // later it's simpler to work with such value than with 0
1400 // if (item.PackSize == 0)
1401 item.PackSize = packedSize + avail;
1402 if (item.Method == 0)
1403 item.Size = item.PackSize;
1404 SkipLookahed(avail);
1405 return S_OK;
1406 }
1407
1408 const Byte * const pStart = Buffer + _bufPos;
1409 const Byte * p = pStart;
1410 const Byte * const limit = pStart + (avail - descriptorSize4);
1411
1412 for (; p <= limit; p++)
1413 {
1414 // descriptor signature field is Info-ZIP's extension to pkware Zip specification.
1415 // New ZIP specification also allows descriptorSignature.
1416
1417 p = FindPK(p, limit + 1);
1418 if (p > limit)
1419 break;
1420
1421 /*
1422 if (*p != 0x50)
1423 continue;
1424 */
1425
1426 if (Get32(p) != NSignature::kDataDescriptor)
1427 continue;
1428
1429 // we check next signatuire after descriptor
1430 // maybe we need check only 2 bytes "PK" instead of 4 bytes, if some another type of header is possible after descriptor
1431 const UInt32 sig = Get32(p + descriptorSize4 - kNextSignatureSize);
1432 if ( sig != NSignature::kLocalFileHeader
1433 && sig != NSignature::kCentralFileHeader)
1434 continue;
1435
1436 const UInt64 packSizeCur = packedSize + (size_t)(p - pStart);
1437 if (descriptorSize4 == kDataDescriptorSize64 + kNextSignatureSize) // if (item.LocalExtra.IsZip64)
1438 {
1439 const UInt64 descriptorPackSize = Get64(p + 8);
1440 if (descriptorPackSize != packSizeCur)
1441 continue;
1442 item.Size = Get64(p + 16);
1443 }
1444 else
1445 {
1446 const UInt32 descriptorPackSize = Get32(p + 8);
1447 if (descriptorPackSize != (UInt32)packSizeCur)
1448 continue;
1449 item.Size = Get32(p + 12);
1450 // that item.Size can be truncated to 32-bit value here
1451 }
1452 // We write calculated 64-bit packSize, even if descriptor64 was not used
1453 item.PackSize = packSizeCur;
1454
1455 item.DescriptorWasRead = true;
1456 item.Crc = Get32(p + 4);
1457
1458 const size_t skip = (size_t)(p - pStart) + descriptorSize4 - kNextSignatureSize;
1459
1460 SkipLookahed(skip);
1461
1462 return S_OK;
1463 }
1464
1465 const size_t skip = (size_t)(p - pStart);
1466 SkipLookahed(skip);
1467
1468 packedSize += skip;
1469
1470 if (Callback)
1471 if (_cnt - progressPrev >= ((UInt32)1 << 22))
1472 {
1473 progressPrev = _cnt;
1474 const UInt64 numFiles64 = numFiles;
1475 RINOK(Callback->SetCompleted(&numFiles64, &_cnt));
1476 }
1477 }
1478}
1479
1480
1481HRESULT CInArchive::CheckDescriptor(const CItemEx &item)
1482{
1483 if (!item.HasDescriptor())
1484 return S_OK;
1485
1486 // pkzip's version without descriptor signature is not supported
1487
1488 bool isFinished = false;
1489 RINOK(IncreaseRealPosition(item.PackSize, isFinished));
1490 if (isFinished)
1491 return S_FALSE;
1492
1493 /*
1494 if (!IsMultiVol)
1495 {
1496 RINOK(Seek_SavePos(ArcInfo.Base + item.GetDataPosition() + item.PackSize));
1497 }
1498 */
1499
1500 Byte buf[kDataDescriptorSize64];
1501 try
1502 {
1503 CanStartNewVol = true;
1504 SafeRead(buf, item.GetDescriptorSize());
1505 }
1506 catch (const CSystemException &e) { return e.ErrorCode; }
1507 // catch (const CUnexpectEnd &)
1508 catch(...)
1509 {
1510 return S_FALSE;
1511 }
1512 // RINOK(ReadStream_FALSE(Stream, buf, item.GetDescriptorSize()));
1513
1514 if (Get32(buf) != NSignature::kDataDescriptor)
1515 return S_FALSE;
1516 UInt32 crc = Get32(buf + 4);
1517 UInt64 packSize, unpackSize;
1518
1519 if (item.LocalExtra.IsZip64)
1520 {
1521 packSize = Get64(buf + 8);
1522 unpackSize = Get64(buf + 16);
1523 }
1524 else
1525 {
1526 packSize = Get32(buf + 8);
1527 unpackSize = Get32(buf + 12);
1528 }
1529
1530 if (crc != item.Crc || item.PackSize != packSize || item.Size != unpackSize)
1531 return S_FALSE;
1532 return S_OK;
1533}
1534
1535
1536HRESULT CInArchive::ReadLocalItemAfterCdItemFull(CItemEx &item)
1537{
1538 if (item.FromLocal)
1539 return S_OK;
1540 try
1541 {
1542 bool isAvail = true;
1543 bool headersError = false;
1544 RINOK(ReadLocalItemAfterCdItem(item, isAvail, headersError));
1545 if (headersError)
1546 return S_FALSE;
1547 if (item.HasDescriptor())
1548 return CheckDescriptor(item);
1549 }
1550 catch(...) { return S_FALSE; }
1551 return S_OK;
1552}
1553
1554
1555HRESULT CInArchive::ReadCdItem(CItemEx &item)
1556{
1557 item.FromCentral = true;
1558 Byte p[kCentralHeaderSize - 4];
1559 SafeRead(p, kCentralHeaderSize - 4);
1560
1561 item.MadeByVersion.Version = p[0];
1562 item.MadeByVersion.HostOS = p[1];
1563 item.ExtractVersion.Version = p[2];
1564 item.ExtractVersion.HostOS = p[3];
1565 G16(4, item.Flags);
1566 G16(6, item.Method);
1567 G32(8, item.Time);
1568 G32(12, item.Crc);
1569 G32(16, item.PackSize);
1570 G32(20, item.Size);
1571 const unsigned nameSize = Get16(p + 24);
1572 const unsigned extraSize = Get16(p + 26);
1573 const unsigned commentSize = Get16(p + 28);
1574 G16(30, item.Disk);
1575 G16(32, item.InternalAttrib);
1576 G32(34, item.ExternalAttrib);
1577 G32(38, item.LocalHeaderPos);
1578 ReadFileName(nameSize, item.Name);
1579
1580 if (extraSize > 0)
1581 ReadExtra(item, extraSize, item.CentralExtra, item.Size, item.PackSize, &item);
1582
1583 // May be these strings must be deleted
1584 /*
1585 if (item.IsDir())
1586 item.Size = 0;
1587 */
1588
1589 ReadBuffer(item.Comment, commentSize);
1590 return S_OK;
1591}
1592
1593
1594HRESULT CInArchive::TryEcd64(UInt64 offset, CCdInfo &cdInfo)
1595{
1596 if (offset >= ((UInt64)1 << 63))
1597 return S_FALSE;
1598 Byte buf[kEcd64_FullSize];
1599
1600 RINOK(SeekToVol(Vols.StreamIndex, offset));
1601 RINOK(ReadFromCache_FALSE(buf, kEcd64_FullSize));
1602
1603 if (Get32(buf) != NSignature::kEcd64)
1604 return S_FALSE;
1605 UInt64 mainSize = Get64(buf + 4);
1606 if (mainSize < kEcd64_MainSize || mainSize > ((UInt64)1 << 40))
1607 return S_FALSE;
1608 cdInfo.ParseEcd64e(buf + 12);
1609 return S_OK;
1610}
1611
1612
1613HRESULT CInArchive::FindCd(bool checkOffsetMode)
1614{
1615 CCdInfo &cdInfo = Vols.ecd;
1616
1617 UInt64 endPos;
1618
1619 // There are no useful data in cache in most cases here.
1620 // So here we don't use cache data from previous operations .
1621
1622 InitBuf();
1623 RINOK(Stream->Seek(0, STREAM_SEEK_END, &endPos));
1624 _streamPos = endPos;
1625
1626 // const UInt32 kBufSizeMax2 = ((UInt32)1 << 16) + kEcdSize + kEcd64Locator_Size + kEcd64_FullSize;
1627 const size_t kBufSizeMax = ((size_t)1 << 17); // must be larger than kBufSizeMax2
1628
1629 const size_t bufSize = (endPos < kBufSizeMax) ? (size_t)endPos : kBufSizeMax;
1630 if (bufSize < kEcdSize)
1631 return S_FALSE;
1632 // CByteArr byteBuffer(bufSize);
1633
1634 if (Buffer.Size() < kBufSizeMax)
1635 {
1636 // InitBuf();
1637 Buffer.AllocAtLeast(kBufSizeMax);
1638 if (!Buffer.IsAllocated())
1639 return E_OUTOFMEMORY;
1640 }
1641
1642 RINOK(Seek_SavePos(endPos - bufSize));
1643
1644 size_t processed = bufSize;
1645 HRESULT res = ReadStream(Stream, Buffer, &processed);
1646 _streamPos += processed;
1647 _bufCached = processed;
1648 _bufPos = 0;
1649 _cnt += processed;
1650 if (res != S_OK)
1651 return res;
1652 if (processed != bufSize)
1653 return S_FALSE;
1654
1655
1656 for (size_t i = bufSize - kEcdSize + 1;;)
1657 {
1658 if (i == 0)
1659 return S_FALSE;
1660
1661 const Byte *buf = Buffer;
1662
1663 for (;;)
1664 {
1665 i--;
1666 if (buf[i] == 0x50)
1667 break;
1668 if (i == 0)
1669 return S_FALSE;
1670 }
1671
1672 if (Get32(buf + i) != NSignature::kEcd)
1673 continue;
1674
1675 cdInfo.ParseEcd32(buf + i);
1676
1677 if (i >= kEcd64Locator_Size)
1678 {
1679 const size_t locatorIndex = i - kEcd64Locator_Size;
1680 if (Get32(buf + locatorIndex) == NSignature::kEcd64Locator)
1681 {
1682 CLocator locator;
1683 locator.Parse(buf + locatorIndex + 4);
1684 UInt32 numDisks = locator.NumDisks;
1685 // we ignore the error, where some zip creators use (NumDisks == 0)
1686 if (numDisks == 0)
1687 numDisks = 1;
1688 if ((cdInfo.ThisDisk == numDisks - 1 || ZIP64_IS_16_MAX(cdInfo.ThisDisk))
1689 && locator.Ecd64Disk < numDisks)
1690 {
1691 if (locator.Ecd64Disk != cdInfo.ThisDisk && !ZIP64_IS_16_MAX(cdInfo.ThisDisk))
1692 return E_NOTIMPL;
1693
1694 // Most of the zip64 use fixed size Zip64 ECD
1695 // we try relative backward reading.
1696
1697 UInt64 absEcd64 = endPos - bufSize + i - (kEcd64Locator_Size + kEcd64_FullSize);
1698
1699 if (locatorIndex >= kEcd64_FullSize)
1700 if (checkOffsetMode || absEcd64 == locator.Ecd64Offset)
1701 {
1702 const Byte *ecd64 = buf + locatorIndex - kEcd64_FullSize;
1703 if (Get32(ecd64) == NSignature::kEcd64)
1704 {
1705 UInt64 mainEcd64Size = Get64(ecd64 + 4);
1706 if (mainEcd64Size == kEcd64_MainSize)
1707 {
1708 cdInfo.ParseEcd64e(ecd64 + 12);
1709 ArcInfo.Base = (Int64)(absEcd64 - locator.Ecd64Offset);
1710 // ArcInfo.BaseVolIndex = cdInfo.ThisDisk;
1711 return S_OK;
1712 }
1713 }
1714 }
1715
1716 // some zip64 use variable size Zip64 ECD.
1717 // we try to use absolute offset from locator.
1718
1719 if (absEcd64 != locator.Ecd64Offset)
1720 {
1721 if (TryEcd64(locator.Ecd64Offset, cdInfo) == S_OK)
1722 {
1723 ArcInfo.Base = 0;
1724 // ArcInfo.BaseVolIndex = cdInfo.ThisDisk;
1725 return S_OK;
1726 }
1727 }
1728
1729 // for variable Zip64 ECD with for archives with offset != 0.
1730
1731 if (checkOffsetMode
1732 && ArcInfo.MarkerPos != 0
1733 && ArcInfo.MarkerPos + locator.Ecd64Offset != absEcd64)
1734 {
1735 if (TryEcd64(ArcInfo.MarkerPos + locator.Ecd64Offset, cdInfo) == S_OK)
1736 {
1737 ArcInfo.Base = (Int64)ArcInfo.MarkerPos;
1738 // ArcInfo.BaseVolIndex = cdInfo.ThisDisk;
1739 return S_OK;
1740 }
1741 }
1742 }
1743 }
1744 }
1745
1746 // bool isVolMode = (Vols.EndVolIndex != -1);
1747 // UInt32 searchDisk = (isVolMode ? Vols.EndVolIndex : 0);
1748
1749 if (/* searchDisk == thisDisk && */ cdInfo.CdDisk <= cdInfo.ThisDisk)
1750 {
1751 // if (isVolMode)
1752 {
1753 if (cdInfo.CdDisk != cdInfo.ThisDisk)
1754 return S_OK;
1755 }
1756
1757 UInt64 absEcdPos = endPos - bufSize + i;
1758 UInt64 cdEnd = cdInfo.Size + cdInfo.Offset;
1759 ArcInfo.Base = 0;
1760 // ArcInfo.BaseVolIndex = cdInfo.ThisDisk;
1761 if (absEcdPos != cdEnd)
1762 {
1763 /*
1764 if (cdInfo.Offset <= 16 && cdInfo.Size != 0)
1765 {
1766 // here we support some rare ZIP files with Central directory at the start
1767 ArcInfo.Base = 0;
1768 }
1769 else
1770 */
1771 ArcInfo.Base = (Int64)(absEcdPos - cdEnd);
1772 }
1773 return S_OK;
1774 }
1775 }
1776}
1777
1778
1779HRESULT CInArchive::TryReadCd(CObjectVector<CItemEx> &items, const CCdInfo &cdInfo, UInt64 cdOffset, UInt64 cdSize)
1780{
1781 items.Clear();
1782 IsCdUnsorted = false;
1783
1784 // _startLocalFromCd_Disk = (UInt32)(Int32)-1;
1785 // _startLocalFromCd_Offset = (UInt64)(Int64)-1;
1786
1787 RINOK(SeekToVol(IsMultiVol ? (int)cdInfo.CdDisk : -1, cdOffset));
1788
1789 _inBufMode = true;
1790 _cnt = 0;
1791
1792 if (Callback)
1793 {
1794 RINOK(Callback->SetTotal(&cdInfo.NumEntries, IsMultiVol ? &Vols.TotalBytesSize : NULL));
1795 }
1796 UInt64 numFileExpected = cdInfo.NumEntries;
1797 const UInt64 *totalFilesPtr = &numFileExpected;
1798 bool isCorrect_NumEntries = (cdInfo.IsFromEcd64 || numFileExpected >= ((UInt32)1 << 16));
1799
1800 while (_cnt < cdSize)
1801 {
1802 CanStartNewVol = true;
1803 if (ReadUInt32() != NSignature::kCentralFileHeader)
1804 return S_FALSE;
1805 CanStartNewVol = false;
1806 {
1807 CItemEx cdItem;
1808 RINOK(ReadCdItem(cdItem));
1809
1810 /*
1811 if (cdItem.Disk < _startLocalFromCd_Disk ||
1812 cdItem.Disk == _startLocalFromCd_Disk &&
1813 cdItem.LocalHeaderPos < _startLocalFromCd_Offset)
1814 {
1815 _startLocalFromCd_Disk = cdItem.Disk;
1816 _startLocalFromCd_Offset = cdItem.LocalHeaderPos;
1817 }
1818 */
1819
1820 if (items.Size() > 0 && !IsCdUnsorted)
1821 {
1822 const CItemEx &prev = items.Back();
1823 if (cdItem.Disk < prev.Disk
1824 || (cdItem.Disk == prev.Disk &&
1825 cdItem.LocalHeaderPos < prev.LocalHeaderPos))
1826 IsCdUnsorted = true;
1827 }
1828
1829 items.Add(cdItem);
1830 }
1831 if (Callback && (items.Size() & 0xFFF) == 0)
1832 {
1833 const UInt64 numFiles = items.Size();
1834
1835 if (numFiles > numFileExpected && totalFilesPtr)
1836 {
1837 if (isCorrect_NumEntries)
1838 totalFilesPtr = NULL;
1839 else
1840 while (numFiles > numFileExpected)
1841 numFileExpected += (UInt32)1 << 16;
1842 RINOK(Callback->SetTotal(totalFilesPtr, NULL));
1843 }
1844
1845 RINOK(Callback->SetCompleted(&numFiles, &_cnt));
1846 }
1847 }
1848
1849 CanStartNewVol = true;
1850
1851 return (_cnt == cdSize) ? S_OK : S_FALSE;
1852}
1853
1854
1855/*
1856static int CompareCdItems(void *const *elem1, void *const *elem2, void *)
1857{
1858 const CItemEx *i1 = *(const CItemEx **)elem1;
1859 const CItemEx *i2 = *(const CItemEx **)elem2;
1860
1861 if (i1->Disk < i2->Disk) return -1;
1862 if (i1->Disk > i2->Disk) return 1;
1863 if (i1->LocalHeaderPos < i2->LocalHeaderPos) return -1;
1864 if (i1->LocalHeaderPos > i2->LocalHeaderPos) return 1;
1865 if (i1 < i2) return -1;
1866 if (i1 > i2) return 1;
1867 return 0;
1868}
1869*/
1870
1871HRESULT CInArchive::ReadCd(CObjectVector<CItemEx> &items, UInt32 &cdDisk, UInt64 &cdOffset, UInt64 &cdSize)
1872{
1873 bool checkOffsetMode = true;
1874
1875 if (IsMultiVol)
1876 {
1877 if (Vols.EndVolIndex == -1)
1878 return S_FALSE;
1879 Stream = Vols.Streams[(unsigned)Vols.EndVolIndex].Stream;
1880 if (!Vols.StartIsZip)
1881 checkOffsetMode = false;
1882 }
1883 else
1884 Stream = StartStream;
1885
1886 if (!Vols.ecd_wasRead)
1887 {
1888 RINOK(FindCd(checkOffsetMode));
1889 }
1890
1891 CCdInfo &cdInfo = Vols.ecd;
1892
1893 HRESULT res = S_FALSE;
1894
1895 cdSize = cdInfo.Size;
1896 cdOffset = cdInfo.Offset;
1897 cdDisk = cdInfo.CdDisk;
1898
1899 if (!IsMultiVol)
1900 {
1901 if (cdInfo.ThisDisk != cdInfo.CdDisk)
1902 return S_FALSE;
1903 }
1904
1905 const UInt64 base = (IsMultiVol ? 0 : (UInt64)ArcInfo.Base);
1906 res = TryReadCd(items, cdInfo, base + cdOffset, cdSize);
1907
1908 if (res == S_FALSE && !IsMultiVol && base != ArcInfo.MarkerPos)
1909 {
1910 // do we need that additional attempt to read cd?
1911 res = TryReadCd(items, cdInfo, ArcInfo.MarkerPos + cdOffset, cdSize);
1912 if (res == S_OK)
1913 ArcInfo.Base = (Int64)ArcInfo.MarkerPos;
1914 }
1915
1916 // Some rare case files are unsorted
1917 // items.Sort(CompareCdItems, NULL);
1918 return res;
1919}
1920
1921
1922static int FindItem(const CObjectVector<CItemEx> &items, const CItemEx &item)
1923{
1924 unsigned left = 0, right = items.Size();
1925 for (;;)
1926 {
1927 if (left >= right)
1928 return -1;
1929 const unsigned index = (left + right) / 2;
1930 const CItemEx &item2 = items[index];
1931 if (item.Disk < item2.Disk)
1932 right = index;
1933 else if (item.Disk > item2.Disk)
1934 left = index + 1;
1935 else if (item.LocalHeaderPos == item2.LocalHeaderPos)
1936 return (int)index;
1937 else if (item.LocalHeaderPos < item2.LocalHeaderPos)
1938 right = index;
1939 else
1940 left = index + 1;
1941 }
1942}
1943
1944static bool IsStrangeItem(const CItem &item)
1945{
1946 return item.Name.Len() > (1 << 14) || item.Method > (1 << 8);
1947}
1948
1949
1950
1951/*
1952 ---------- ReadLocals ----------
1953
1954in:
1955 (_signature == NSignature::kLocalFileHeader)
1956 VirtStreamPos : after _signature : position in Stream
1957 Stream :
1958 Vols : if (IsMultiVol)
1959 (_inBufMode == false)
1960
1961action:
1962 it parses local items.
1963
1964 if ( IsMultiVol) it writes absolute offsets to CItemEx::LocalHeaderPos
1965 if (!IsMultiVol) it writes relative (from ArcInfo.Base) offsets to CItemEx::LocalHeaderPos
1966 later we can correct CItemEx::LocalHeaderPos values, if
1967 some new value for ArcInfo.Base will be detected
1968out:
1969 S_OK:
1970 (_signature != NSignature::kLocalFileHeade)
1971 _streamPos : after _signature
1972
1973 S_FALSE: if no items or there is just one item with strange properies that doesn't look like real archive.
1974
1975 another error code: stream reading error or Callback error.
1976
1977 CUnexpectEnd() exception : it's not fatal exception here.
1978 It means that reading was interrupted by unexpected end of input stream,
1979 but some CItemEx items were parsed OK.
1980 We can stop further archive parsing.
1981 But we can use all filled CItemEx items.
1982*/
1983
1984HRESULT CInArchive::ReadLocals(CObjectVector<CItemEx> &items)
1985{
1986 items.Clear();
1987
1988 UInt64 progressPrev = _cnt;
1989
1990 if (Callback)
1991 {
1992 RINOK(Callback->SetTotal(NULL, IsMultiVol ? &Vols.TotalBytesSize : NULL));
1993 }
1994
1995 while (_signature == NSignature::kLocalFileHeader)
1996 {
1997 CItemEx item;
1998
1999 item.LocalHeaderPos = GetVirtStreamPos() - 4;
2000 if (!IsMultiVol)
2001 item.LocalHeaderPos = (UInt64)((Int64)item.LocalHeaderPos - ArcInfo.Base);
2002
2003 try
2004 {
2005 ReadLocalItem(item);
2006 item.FromLocal = true;
2007 bool isFinished = false;
2008
2009 if (item.HasDescriptor())
2010 {
2011 RINOK(FindDescriptor(item, items.Size()));
2012 isFinished = !item.DescriptorWasRead;
2013 }
2014 else
2015 {
2016 if (item.PackSize >= ((UInt64)1 << 62))
2017 throw CUnexpectEnd();
2018 RINOK(IncreaseRealPosition(item.PackSize, isFinished));
2019 }
2020
2021 items.Add(item);
2022
2023 if (isFinished)
2024 throw CUnexpectEnd();
2025
2026 ReadSignature();
2027 }
2028 catch (CUnexpectEnd &)
2029 {
2030 if (items.IsEmpty() || (items.Size() == 1 && IsStrangeItem(items[0])))
2031 return S_FALSE;
2032 throw;
2033 }
2034
2035
2036 if (Callback)
2037 if ((items.Size() & 0xFF) == 0
2038 || _cnt - progressPrev >= ((UInt32)1 << 22))
2039 {
2040 progressPrev = _cnt;
2041 const UInt64 numFiles = items.Size();
2042 RINOK(Callback->SetCompleted(&numFiles, &_cnt));
2043 }
2044 }
2045
2046 if (items.Size() == 1 && _signature != NSignature::kCentralFileHeader)
2047 if (IsStrangeItem(items[0]))
2048 return S_FALSE;
2049
2050 return S_OK;
2051}
2052
2053
2054
2055HRESULT CVols::ParseArcName(IArchiveOpenVolumeCallback *volCallback)
2056{
2057 UString name;
2058 {
2059 NWindows::NCOM::CPropVariant prop;
2060 RINOK(volCallback->GetProperty(kpidName, &prop));
2061 if (prop.vt != VT_BSTR)
2062 return S_OK;
2063 name = prop.bstrVal;
2064 }
2065
2066 const int dotPos = name.ReverseFind_Dot();
2067 if (dotPos < 0)
2068 return S_OK;
2069 const UString ext = name.Ptr((unsigned)(dotPos + 1));
2070 name.DeleteFrom((unsigned)(dotPos + 1));
2071
2072 StartVolIndex = (Int32)(-1);
2073
2074 if (ext.IsEmpty())
2075 return S_OK;
2076 {
2077 wchar_t c = ext[0];
2078 IsUpperCase = (c >= 'A' && c <= 'Z');
2079 if (ext.IsEqualTo_Ascii_NoCase("zip"))
2080 {
2081 BaseName = name;
2082 StartIsZ = true;
2083 StartIsZip = true;
2084 return S_OK;
2085 }
2086 else if (ext.IsEqualTo_Ascii_NoCase("exe"))
2087 {
2088 /* possible cases:
2089 - exe with zip inside
2090 - sfx: a.exe, a.z02, a.z03,... , a.zip
2091 a.exe is start volume.
2092 - zip renamed to exe
2093 */
2094
2095 StartIsExe = true;
2096 BaseName = name;
2097 StartVolIndex = 0;
2098 /* sfx-zip can use both arc.exe and arc.zip
2099 We can open arc.zip, if it was requesed to open arc.exe.
2100 But it's possible that arc.exe and arc.zip are not parts of same archive.
2101 So we can disable such operation */
2102
2103 // 18.04: we still want to open zip renamed to exe.
2104 /*
2105 {
2106 UString volName = name;
2107 volName += IsUpperCase ? "Z01" : "z01";
2108 {
2109 CMyComPtr<IInStream> stream;
2110 HRESULT res2 = volCallback->GetStream(volName, &stream);
2111 if (res2 == S_OK)
2112 DisableVolsSearch = true;
2113 }
2114 }
2115 */
2116 DisableVolsSearch = true;
2117 return S_OK;
2118 }
2119 else if (ext[0] == 'z' || ext[0] == 'Z')
2120 {
2121 if (ext.Len() < 3)
2122 return S_OK;
2123 const wchar_t *end = NULL;
2124 UInt32 volNum = ConvertStringToUInt32(ext.Ptr(1), &end);
2125 if (*end != 0 || volNum < 1 || volNum > ((UInt32)1 << 30))
2126 return S_OK;
2127 StartVolIndex = (Int32)(volNum - 1);
2128 BaseName = name;
2129 StartIsZ = true;
2130 }
2131 else
2132 return S_OK;
2133 }
2134
2135 UString volName = BaseName;
2136 volName += (IsUpperCase ? "ZIP" : "zip");
2137
2138 HRESULT res = volCallback->GetStream(volName, &ZipStream);
2139
2140 if (res == S_FALSE || !ZipStream)
2141 {
2142 if (MissingName.IsEmpty())
2143 {
2144 MissingZip = true;
2145 MissingName = volName;
2146 }
2147 return S_OK;
2148 }
2149
2150 return res;
2151}
2152
2153
2154HRESULT CInArchive::ReadVols2(IArchiveOpenVolumeCallback *volCallback,
2155 unsigned start, int lastDisk, int zipDisk, unsigned numMissingVolsMax, unsigned &numMissingVols)
2156{
2157 if (Vols.DisableVolsSearch)
2158 return S_OK;
2159
2160 numMissingVols = 0;
2161
2162 for (unsigned i = start;; i++)
2163 {
2164 if (lastDisk >= 0 && i >= (unsigned)lastDisk)
2165 break;
2166
2167 if (i < Vols.Streams.Size())
2168 if (Vols.Streams[i].Stream)
2169 continue;
2170
2171 CMyComPtr<IInStream> stream;
2172
2173 if ((int)i == zipDisk)
2174 {
2175 stream = Vols.ZipStream;
2176 }
2177 else if ((int)i == Vols.StartVolIndex)
2178 {
2179 stream = StartStream;
2180 }
2181 else
2182 {
2183 UString volName = Vols.BaseName;
2184 {
2185 volName += (char)(Vols.IsUpperCase ? 'Z' : 'z');
2186 unsigned v = i + 1;
2187 if (v < 10)
2188 volName += '0';
2189 volName.Add_UInt32(v);
2190 }
2191
2192 HRESULT res = volCallback->GetStream(volName, &stream);
2193 if (res != S_OK && res != S_FALSE)
2194 return res;
2195 if (res == S_FALSE || !stream)
2196 {
2197 if (i == 0)
2198 {
2199 UString volName_exe = Vols.BaseName;
2200 volName_exe += (Vols.IsUpperCase ? "EXE" : "exe");
2201
2202 HRESULT res2 = volCallback->GetStream(volName_exe, &stream);
2203 if (res2 != S_OK && res2 != S_FALSE)
2204 return res2;
2205 res = res2;
2206 }
2207 }
2208 if (res == S_FALSE || !stream)
2209 {
2210 if (i == 1 && Vols.StartIsExe)
2211 return S_OK;
2212 if (Vols.MissingName.IsEmpty())
2213 Vols.MissingName = volName;
2214 numMissingVols++;
2215 if (numMissingVols > numMissingVolsMax)
2216 return S_OK;
2217 if (lastDisk == -1 && numMissingVols != 0)
2218 return S_OK;
2219 continue;
2220 }
2221 }
2222
2223 UInt64 size;
2224 UInt64 pos;
2225 RINOK(stream->Seek(0, STREAM_SEEK_CUR, &pos));
2226 RINOK(stream->Seek(0, STREAM_SEEK_END, &size));
2227 RINOK(stream->Seek((Int64)pos, STREAM_SEEK_SET, NULL));
2228
2229 while (i >= Vols.Streams.Size())
2230 Vols.Streams.AddNew();
2231
2232 CVols::CSubStreamInfo &ss = Vols.Streams[i];
2233 Vols.NumVols++;
2234 Vols.TotalBytesSize += size;
2235
2236 ss.Stream = stream;
2237 ss.Size = size;
2238
2239 if ((int)i == zipDisk)
2240 {
2241 Vols.EndVolIndex = (int)(Vols.Streams.Size() - 1);
2242 break;
2243 }
2244 }
2245
2246 return S_OK;
2247}
2248
2249
2250HRESULT CInArchive::ReadVols()
2251{
2252 CMyComPtr<IArchiveOpenVolumeCallback> volCallback;
2253
2254 Callback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&volCallback);
2255 if (!volCallback)
2256 return S_OK;
2257
2258 RINOK(Vols.ParseArcName(volCallback));
2259
2260 // const int startZIndex = Vols.StartVolIndex;
2261
2262 if (!Vols.StartIsZ)
2263 {
2264 if (!Vols.StartIsExe)
2265 return S_OK;
2266 }
2267
2268 int zipDisk = -1;
2269 int cdDisk = -1;
2270
2271 if (Vols.StartIsZip)
2272 Vols.ZipStream = StartStream;
2273
2274 if (Vols.ZipStream)
2275 {
2276 Stream = Vols.ZipStream;
2277
2278 if (Vols.StartIsZip)
2279 Vols.StreamIndex = -1;
2280 else
2281 {
2282 Vols.StreamIndex = -2;
2283 InitBuf();
2284 }
2285
2286 HRESULT res = FindCd(true);
2287
2288 CCdInfo &ecd = Vols.ecd;
2289 if (res == S_OK)
2290 {
2291 zipDisk = (int)ecd.ThisDisk;
2292 Vols.ecd_wasRead = true;
2293
2294 // if is not multivol or bad multivol, we return to main single stream code
2295 if (ecd.ThisDisk == 0
2296 || ecd.ThisDisk >= ((UInt32)1 << 30)
2297 || ecd.ThisDisk < ecd.CdDisk)
2298 return S_OK;
2299
2300 cdDisk = (int)ecd.CdDisk;
2301 if (Vols.StartVolIndex < 0)
2302 Vols.StartVolIndex = (Int32)ecd.ThisDisk;
2303 else if ((UInt32)Vols.StartVolIndex >= ecd.ThisDisk)
2304 return S_OK;
2305
2306 // Vols.StartVolIndex = ecd.ThisDisk;
2307 // Vols.EndVolIndex = ecd.ThisDisk;
2308 unsigned numMissingVols;
2309 if (cdDisk != zipDisk)
2310 {
2311 // get volumes required for cd.
2312 RINOK(ReadVols2(volCallback, (unsigned)cdDisk, zipDisk, zipDisk, 0, numMissingVols));
2313 if (numMissingVols != 0)
2314 {
2315 // cdOK = false;
2316 }
2317 }
2318 }
2319 else if (res != S_FALSE)
2320 return res;
2321 }
2322
2323 if (Vols.StartVolIndex < 0)
2324 {
2325 // is not mutivol;
2326 return S_OK;
2327 }
2328
2329 /*
2330 if (!Vols.Streams.IsEmpty())
2331 IsMultiVol = true;
2332 */
2333
2334 unsigned numMissingVols;
2335
2336 if (cdDisk != 0)
2337 {
2338 // get volumes that were no requested still
2339 const unsigned kNumMissingVolsMax = 1 << 12;
2340 RINOK(ReadVols2(volCallback, 0, cdDisk < 0 ? -1 : cdDisk, zipDisk, kNumMissingVolsMax, numMissingVols));
2341 }
2342
2343 // if (Vols.StartVolIndex >= 0)
2344 {
2345 if (Vols.Streams.IsEmpty())
2346 if (Vols.StartVolIndex > (1 << 20))
2347 return S_OK;
2348 if ((unsigned)Vols.StartVolIndex >= Vols.Streams.Size()
2349 || !Vols.Streams[(unsigned)Vols.StartVolIndex].Stream)
2350 {
2351 // we get volumes starting from StartVolIndex, if they we not requested before know the volume index (if FindCd() was ok)
2352 RINOK(ReadVols2(volCallback, (unsigned)Vols.StartVolIndex, zipDisk, zipDisk, 0, numMissingVols));
2353 }
2354 }
2355
2356 if (Vols.ZipStream)
2357 {
2358 // if there is no another volumes and volumeIndex is too big, we don't use multivol mode
2359 if (Vols.Streams.IsEmpty())
2360 if (zipDisk > (1 << 10))
2361 return S_OK;
2362 if (zipDisk >= 0)
2363 {
2364 // we create item in Streams for ZipStream, if we know the volume index (if FindCd() was ok)
2365 RINOK(ReadVols2(volCallback, (unsigned)zipDisk, zipDisk + 1, zipDisk, 0, numMissingVols));
2366 }
2367 }
2368
2369 if (!Vols.Streams.IsEmpty())
2370 {
2371 IsMultiVol = true;
2372 /*
2373 if (cdDisk)
2374 IsMultiVol = true;
2375 */
2376 const int startZIndex = Vols.StartVolIndex;
2377 if (startZIndex >= 0)
2378 {
2379 // if all volumes before start volume are OK, we can start parsing from 0
2380 // if there are missing volumes before startZIndex, we start parsing in current startZIndex
2381 if ((unsigned)startZIndex < Vols.Streams.Size())
2382 {
2383 for (unsigned i = 0; i <= (unsigned)startZIndex; i++)
2384 if (!Vols.Streams[i].Stream)
2385 {
2386 Vols.StartParsingVol = startZIndex;
2387 break;
2388 }
2389 }
2390 }
2391 }
2392
2393 return S_OK;
2394}
2395
2396
2397
2398HRESULT CVols::Read(void *data, UInt32 size, UInt32 *processedSize)
2399{
2400 if (processedSize)
2401 *processedSize = 0;
2402 if (size == 0)
2403 return S_OK;
2404
2405 for (;;)
2406 {
2407 if (StreamIndex < 0)
2408 return S_OK;
2409 if ((unsigned)StreamIndex >= Streams.Size())
2410 return S_OK;
2411 const CVols::CSubStreamInfo &s = Streams[(unsigned)StreamIndex];
2412 if (!s.Stream)
2413 return S_FALSE;
2414 if (NeedSeek)
2415 {
2416 RINOK(s.SeekToStart());
2417 NeedSeek = false;
2418 }
2419 UInt32 realProcessedSize = 0;
2420 HRESULT res = s.Stream->Read(data, size, &realProcessedSize);
2421 if (processedSize)
2422 *processedSize = realProcessedSize;
2423 if (res != S_OK)
2424 return res;
2425 if (realProcessedSize != 0)
2426 return res;
2427 StreamIndex++;
2428 NeedSeek = true;
2429 }
2430}
2431
2432STDMETHODIMP CVolStream::Read(void *data, UInt32 size, UInt32 *processedSize)
2433{
2434 return Vols->Read(data, size, processedSize);
2435}
2436
2437
2438
2439
2440#define COPY_ECD_ITEM_16(n) if (!isZip64 || !ZIP64_IS_16_MAX(ecd. n)) cdInfo. n = ecd. n;
2441#define COPY_ECD_ITEM_32(n) if (!isZip64 || !ZIP64_IS_32_MAX(ecd. n)) cdInfo. n = ecd. n;
2442
2443
2444HRESULT CInArchive::ReadHeaders(CObjectVector<CItemEx> &items)
2445{
2446 if (Buffer.Size() < kSeqBufferSize)
2447 {
2448 InitBuf();
2449 Buffer.AllocAtLeast(kSeqBufferSize);
2450 if (!Buffer.IsAllocated())
2451 return E_OUTOFMEMORY;
2452 }
2453
2454 _inBufMode = false;
2455
2456 HRESULT res = S_OK;
2457
2458 bool localsWereRead = false;
2459
2460 /* we try to open archive with the following modes:
2461 1) CD-MODE : fast mode : we read backward ECD and CD, compare CD items with first Local item.
2462 2) LOCALS-CD-MODE : slow mode, if CD-MODE fails : we sequentially read all Locals and then CD.
2463 Then we read sequentially ECD64, Locator, ECD again at the end.
2464
2465 - in LOCALS-CD-MODE we use use the following
2466 variables (with real cd properties) to set Base archive offset
2467 and check real cd properties with values from ECD/ECD64.
2468 */
2469
2470 UInt64 cdSize = 0;
2471 UInt64 cdRelatOffset = 0;
2472 UInt32 cdDisk = 0;
2473
2474 UInt64 cdAbsOffset = 0; // absolute cd offset, for LOCALS-CD-MODE only.
2475
2476 if (!MarkerIsFound || !MarkerIsSafe)
2477 {
2478 IsArc = true;
2479 res = ReadCd(items, cdDisk, cdRelatOffset, cdSize);
2480 if (res == S_OK)
2481 ReadSignature();
2482 else if (res != S_FALSE)
2483 return res;
2484 }
2485 else
2486 {
2487
2488 // _signature must be kLocalFileHeader or kEcd or kEcd64
2489
2490 SeekToVol(ArcInfo.MarkerVolIndex, ArcInfo.MarkerPos2 + 4);
2491
2492 CanStartNewVol = false;
2493
2494 if (_signature == NSignature::kEcd64)
2495 {
2496 // UInt64 ecd64Offset = GetVirtStreamPos() - 4;
2497 IsZip64 = true;
2498
2499 {
2500 const UInt64 recordSize = ReadUInt64();
2501 if (recordSize < kEcd64_MainSize)
2502 return S_FALSE;
2503 if (recordSize >= ((UInt64)1 << 62))
2504 return S_FALSE;
2505
2506 {
2507 const unsigned kBufSize = kEcd64_MainSize;
2508 Byte buf[kBufSize];
2509 SafeRead(buf, kBufSize);
2510 CCdInfo cdInfo;
2511 cdInfo.ParseEcd64e(buf);
2512 if (!cdInfo.IsEmptyArc())
2513 return S_FALSE;
2514 }
2515
2516 RINOK(Skip64(recordSize - kEcd64_MainSize, 0));
2517 }
2518
2519 ReadSignature();
2520 if (_signature != NSignature::kEcd64Locator)
2521 return S_FALSE;
2522
2523 {
2524 const unsigned kBufSize = 16;
2525 Byte buf[kBufSize];
2526 SafeRead(buf, kBufSize);
2527 CLocator locator;
2528 locator.Parse(buf);
2529 if (!locator.IsEmptyArc())
2530 return S_FALSE;
2531 }
2532
2533 ReadSignature();
2534 if (_signature != NSignature::kEcd)
2535 return S_FALSE;
2536 }
2537
2538 if (_signature == NSignature::kEcd)
2539 {
2540 // It must be empty archive or backware archive
2541 // we don't support backware archive still
2542
2543 const unsigned kBufSize = kEcdSize - 4;
2544 Byte buf[kBufSize];
2545 SafeRead(buf, kBufSize);
2546 CEcd ecd;
2547 ecd.Parse(buf);
2548 // if (ecd.cdSize != 0)
2549 // Do we need also to support the case where empty zip archive with PK00 uses cdOffset = 4 ??
2550 if (!ecd.IsEmptyArc())
2551 return S_FALSE;
2552
2553 ArcInfo.Base = (Int64)ArcInfo.MarkerPos;
2554 IsArc = true; // check it: we need more tests?
2555
2556 RINOK(SeekToVol(ArcInfo.MarkerVolIndex, ArcInfo.MarkerPos2));
2557 ReadSignature();
2558 }
2559 else
2560 {
2561 CItemEx firstItem;
2562 try
2563 {
2564 try
2565 {
2566 if (!ReadLocalItem(firstItem))
2567 return S_FALSE;
2568 }
2569 catch(CUnexpectEnd &)
2570 {
2571 return S_FALSE;
2572 }
2573
2574 IsArc = true;
2575 res = ReadCd(items, cdDisk, cdRelatOffset, cdSize);
2576 if (res == S_OK)
2577 ReadSignature();
2578 }
2579 catch(CUnexpectEnd &) { res = S_FALSE; }
2580
2581 if (res != S_FALSE && res != S_OK)
2582 return res;
2583
2584 if (res == S_OK && items.Size() == 0)
2585 res = S_FALSE;
2586
2587 if (res == S_OK)
2588 {
2589 // we can't read local items here to keep _inBufMode state
2590 if ((Int64)ArcInfo.MarkerPos2 < ArcInfo.Base)
2591 res = S_FALSE;
2592 else
2593 {
2594 firstItem.LocalHeaderPos = (UInt64)((Int64)ArcInfo.MarkerPos2 - ArcInfo.Base);
2595 int index = -1;
2596
2597 UInt32 min_Disk = (UInt32)(Int32)-1;
2598 UInt64 min_LocalHeaderPos = (UInt64)(Int64)-1;
2599
2600 if (!IsCdUnsorted)
2601 index = FindItem(items, firstItem);
2602 else
2603 {
2604 FOR_VECTOR (i, items)
2605 {
2606 const CItemEx &cdItem = items[i];
2607 if (cdItem.Disk == firstItem.Disk
2608 && (cdItem.LocalHeaderPos == firstItem.LocalHeaderPos))
2609 index = (int)i;
2610
2611 if (i == 0
2612 || cdItem.Disk < min_Disk
2613 || (cdItem.Disk == min_Disk && cdItem.LocalHeaderPos < min_LocalHeaderPos))
2614 {
2615 min_Disk = cdItem.Disk;
2616 min_LocalHeaderPos = cdItem.LocalHeaderPos;
2617 }
2618 }
2619 }
2620
2621 if (index == -1)
2622 res = S_FALSE;
2623 else if (!AreItemsEqual(firstItem, items[(unsigned)index]))
2624 res = S_FALSE;
2625 else
2626 {
2627 ArcInfo.CdWasRead = true;
2628 if (IsCdUnsorted)
2629 ArcInfo.FirstItemRelatOffset = min_LocalHeaderPos;
2630 else
2631 ArcInfo.FirstItemRelatOffset = items[0].LocalHeaderPos;
2632
2633 // ArcInfo.FirstItemRelatOffset = _startLocalFromCd_Offset;
2634 }
2635 }
2636 }
2637 }
2638 }
2639
2640
2641
2642 CObjectVector<CItemEx> cdItems;
2643
2644 bool needSetBase = false; // we set needSetBase only for LOCALS_CD_MODE
2645 unsigned numCdItems = items.Size();
2646
2647 #ifdef ZIP_SELF_CHECK
2648 res = S_FALSE; // if uncommented, it uses additional LOCALS-CD-MODE mode to check the code
2649 #endif
2650
2651 if (res != S_OK)
2652 {
2653 // ---------- LOCALS-CD-MODE ----------
2654 // CD doesn't match firstItem,
2655 // so we clear items and read Locals and CD.
2656
2657 items.Clear();
2658 localsWereRead = true;
2659
2660 HeadersError = false;
2661 HeadersWarning = false;
2662 ExtraMinorError = false;
2663
2664 // we can use any mode: with buffer and without buffer
2665 // without buffer : skips packed data : fast for big files : slow for small files
2666 // with buffer : reads packed data : slow for big files : fast for small files
2667
2668 _inBufMode = false;
2669 // _inBufMode = true;
2670
2671 InitBuf();
2672
2673 ArcInfo.Base = 0;
2674
2675 if (!MarkerIsFound)
2676 {
2677 if (!IsMultiVol)
2678 return S_FALSE;
2679 if (Vols.StartParsingVol != 0)
2680 return S_FALSE;
2681 // if (StartParsingVol == 0) and we didn't find marker, we use default zero marker.
2682 // so we suppose that there is no sfx stub
2683 RINOK(SeekToVol(0, ArcInfo.MarkerPos2));
2684 }
2685 else
2686 {
2687 if (ArcInfo.MarkerPos != 0)
2688 {
2689 /*
2690 If multi-vol or there is (No)Span-marker at start of stream, we set (Base) as 0.
2691 In another caes:
2692 (No)Span-marker is supposed as false positive. So we set (Base) as main marker (MarkerPos2).
2693 The (Base) can be corrected later after ECD reading.
2694 But sfx volume with stub and (No)Span-marker in (!IsMultiVol) mode will have incorrect (Base) here.
2695 */
2696 ArcInfo.Base = (Int64)ArcInfo.MarkerPos2;
2697 }
2698
2699 RINOK(SeekToVol(ArcInfo.MarkerVolIndex, ArcInfo.MarkerPos2));
2700 }
2701
2702 _cnt = 0;
2703
2704 ReadSignature();
2705
2706 LocalsWereRead = true;
2707
2708 RINOK(ReadLocals(items));
2709
2710 if (_signature != NSignature::kCentralFileHeader)
2711 {
2712 // GetVirtStreamPos() - 4
2713 if (items.IsEmpty())
2714 return S_FALSE;
2715
2716 bool isError = true;
2717
2718 const UInt32 apkSize = _signature;
2719 const unsigned kApkFooterSize = 16 + 8;
2720 if (apkSize >= kApkFooterSize && apkSize <= (1 << 20))
2721 {
2722 if (ReadUInt32() == 0)
2723 {
2724 CByteBuffer apk;
2725 apk.Alloc(apkSize);
2726 SafeRead(apk, apkSize);
2727 ReadSignature();
2728 const Byte *footer = apk + apkSize - kApkFooterSize;
2729 if (_signature == NSignature::kCentralFileHeader)
2730 if (GetUi64(footer) == apkSize)
2731 if (memcmp(footer + 8, "APK Sig Block 42", 16) == 0)
2732 {
2733 isError = false;
2734 IsApk = true;
2735 }
2736 }
2737 }
2738
2739 if (isError)
2740 {
2741 NoCentralDir = true;
2742 HeadersError = true;
2743 return S_OK;
2744 }
2745 }
2746
2747 _inBufMode = true;
2748
2749 cdAbsOffset = GetVirtStreamPos() - 4;
2750 cdDisk = (UInt32)Vols.StreamIndex;
2751
2752 #ifdef ZIP_SELF_CHECK
2753 if (!IsMultiVol && _cnt != GetVirtStreamPos() - ArcInfo.MarkerPos2)
2754 return E_FAIL;
2755 #endif
2756
2757 const UInt64 processedCnt_start = _cnt;
2758
2759 for (;;)
2760 {
2761 CItemEx cdItem;
2762
2763 RINOK(ReadCdItem(cdItem));
2764
2765 cdItems.Add(cdItem);
2766 if (Callback && (cdItems.Size() & 0xFFF) == 0)
2767 {
2768 const UInt64 numFiles = items.Size();
2769 const UInt64 numBytes = _cnt;
2770 RINOK(Callback->SetCompleted(&numFiles, &numBytes));
2771 }
2772 ReadSignature();
2773 if (_signature != NSignature::kCentralFileHeader)
2774 break;
2775 }
2776
2777 cdSize = _cnt - processedCnt_start;
2778
2779 #ifdef ZIP_SELF_CHECK
2780 if (!IsMultiVol)
2781 {
2782 if (_cnt != GetVirtStreamPos() - ArcInfo.MarkerPos2)
2783 return E_FAIL;
2784 if (cdSize != (GetVirtStreamPos() - 4) - cdAbsOffset)
2785 return E_FAIL;
2786 }
2787 #endif
2788
2789 needSetBase = true;
2790 numCdItems = cdItems.Size();
2791 cdRelatOffset = (UInt64)((Int64)cdAbsOffset - ArcInfo.Base);
2792
2793 if (!cdItems.IsEmpty())
2794 {
2795 ArcInfo.CdWasRead = true;
2796 ArcInfo.FirstItemRelatOffset = cdItems[0].LocalHeaderPos;
2797 }
2798 }
2799
2800
2801
2802 CCdInfo cdInfo;
2803 CLocator locator;
2804 bool isZip64 = false;
2805 const UInt64 ecd64AbsOffset = GetVirtStreamPos() - 4;
2806 int ecd64Disk = -1;
2807
2808 if (_signature == NSignature::kEcd64)
2809 {
2810 ecd64Disk = Vols.StreamIndex;
2811
2812 IsZip64 = isZip64 = true;
2813
2814 {
2815 const UInt64 recordSize = ReadUInt64();
2816 if (recordSize < kEcd64_MainSize
2817 || recordSize >= ((UInt64)1 << 62))
2818 {
2819 HeadersError = true;
2820 return S_OK;
2821 }
2822
2823 {
2824 const unsigned kBufSize = kEcd64_MainSize;
2825 Byte buf[kBufSize];
2826 SafeRead(buf, kBufSize);
2827 cdInfo.ParseEcd64e(buf);
2828 }
2829
2830 RINOK(Skip64(recordSize - kEcd64_MainSize, items.Size()));
2831 }
2832
2833
2834 ReadSignature();
2835
2836 if (_signature != NSignature::kEcd64Locator)
2837 {
2838 HeadersError = true;
2839 return S_OK;
2840 }
2841
2842 {
2843 const unsigned kBufSize = 16;
2844 Byte buf[kBufSize];
2845 SafeRead(buf, kBufSize);
2846 locator.Parse(buf);
2847 // we ignore the error, where some zip creators use (NumDisks == 0)
2848 // if (locator.NumDisks == 0) HeadersWarning = true;
2849 }
2850
2851 ReadSignature();
2852 }
2853
2854
2855 if (_signature != NSignature::kEcd)
2856 {
2857 HeadersError = true;
2858 return S_OK;
2859 }
2860
2861
2862 CanStartNewVol = false;
2863
2864 // ---------- ECD ----------
2865
2866 CEcd ecd;
2867 {
2868 const unsigned kBufSize = kEcdSize - 4;
2869 Byte buf[kBufSize];
2870 SafeRead(buf, kBufSize);
2871 ecd.Parse(buf);
2872 }
2873
2874 COPY_ECD_ITEM_16(ThisDisk);
2875 COPY_ECD_ITEM_16(CdDisk);
2876 COPY_ECD_ITEM_16(NumEntries_in_ThisDisk);
2877 COPY_ECD_ITEM_16(NumEntries);
2878 COPY_ECD_ITEM_32(Size);
2879 COPY_ECD_ITEM_32(Offset);
2880
2881 bool cdOK = true;
2882
2883 if ((UInt32)cdInfo.Size != (UInt32)cdSize)
2884 {
2885 // return S_FALSE;
2886 cdOK = false;
2887 }
2888
2889 if (isZip64)
2890 {
2891 if (cdInfo.NumEntries != numCdItems
2892 || cdInfo.Size != cdSize)
2893 {
2894 cdOK = false;
2895 }
2896 }
2897
2898
2899 if (IsMultiVol)
2900 {
2901 if (cdDisk != cdInfo.CdDisk)
2902 HeadersError = true;
2903 }
2904 else if (needSetBase && cdOK)
2905 {
2906 const UInt64 oldBase = (UInt64)ArcInfo.Base;
2907 // localsWereRead == true
2908 // ArcInfo.Base == ArcInfo.MarkerPos2
2909 // cdRelatOffset == (cdAbsOffset - ArcInfo.Base)
2910
2911 if (isZip64)
2912 {
2913 if (ecd64Disk == Vols.StartVolIndex)
2914 {
2915 const Int64 newBase = (Int64)ecd64AbsOffset - (Int64)locator.Ecd64Offset;
2916 if (newBase <= (Int64)ecd64AbsOffset)
2917 {
2918 if (!localsWereRead || newBase <= (Int64)ArcInfo.MarkerPos2)
2919 {
2920 ArcInfo.Base = newBase;
2921 cdRelatOffset = (UInt64)((Int64)cdAbsOffset - newBase);
2922 }
2923 else
2924 cdOK = false;
2925 }
2926 }
2927 }
2928 else if (numCdItems != 0) // we can't use ecd.Offset in empty archive?
2929 {
2930 if ((int)cdDisk == Vols.StartVolIndex)
2931 {
2932 const Int64 newBase = (Int64)cdAbsOffset - (Int64)cdInfo.Offset;
2933 if (newBase <= (Int64)cdAbsOffset)
2934 {
2935 if (!localsWereRead || newBase <= (Int64)ArcInfo.MarkerPos2)
2936 {
2937 // cd can be more accurate, when it points before Locals
2938 // so we change Base and cdRelatOffset
2939 ArcInfo.Base = newBase;
2940 cdRelatOffset = cdInfo.Offset;
2941 }
2942 else
2943 {
2944 // const UInt64 delta = ((UInt64)cdRelatOffset - cdInfo.Offset);
2945 const UInt64 delta = ((UInt64)(newBase - ArcInfo.Base));
2946 if ((UInt32)delta == 0)
2947 {
2948 // we set Overflow32bit mode, only if there is (x<<32) offset
2949 // between real_CD_offset_from_MarkerPos and CD_Offset_in_ECD.
2950 // Base and cdRelatOffset unchanged
2951 Overflow32bit = true;
2952 }
2953 else
2954 cdOK = false;
2955 }
2956 }
2957 else
2958 cdOK = false;
2959 }
2960 }
2961 // cdRelatOffset = cdAbsOffset - ArcInfo.Base;
2962
2963 if (localsWereRead)
2964 {
2965 const UInt64 delta = (UInt64)((Int64)oldBase - ArcInfo.Base);
2966 if (delta != 0)
2967 {
2968 FOR_VECTOR (i, items)
2969 items[i].LocalHeaderPos += delta;
2970 }
2971 }
2972 }
2973
2974 if (!cdOK)
2975 HeadersError = true;
2976
2977 EcdVolIndex = cdInfo.ThisDisk;
2978
2979 if (!IsMultiVol)
2980 {
2981 if (EcdVolIndex == 0 && Vols.MissingZip && Vols.StartIsExe)
2982 {
2983 Vols.MissingName.Empty();
2984 Vols.MissingZip = false;
2985 }
2986
2987 if (localsWereRead)
2988 {
2989 if (EcdVolIndex != 0)
2990 {
2991 FOR_VECTOR (i, items)
2992 items[i].Disk = EcdVolIndex;
2993 }
2994 }
2995
2996 UseDisk_in_SingleVol = true;
2997 }
2998
2999 if (isZip64)
3000 {
3001 if ((cdInfo.ThisDisk == 0 && ecd64AbsOffset != (UInt64)(ArcInfo.Base + (Int64)locator.Ecd64Offset))
3002 // || cdInfo.NumEntries_in_ThisDisk != numCdItems
3003 || cdInfo.NumEntries != numCdItems
3004 || cdInfo.Size != cdSize
3005 || (cdInfo.Offset != cdRelatOffset && !items.IsEmpty()))
3006 {
3007 HeadersError = true;
3008 return S_OK;
3009 }
3010 }
3011
3012 if (cdOK && !cdItems.IsEmpty())
3013 {
3014 // ---------- merge Central Directory Items ----------
3015
3016 CRecordVector<unsigned> items2;
3017
3018 int nextLocalIndex = 0;
3019
3020 LocalsCenterMerged = true;
3021
3022 FOR_VECTOR (i, cdItems)
3023 {
3024 if (Callback)
3025 if ((i & 0x3FFF) == 0)
3026 {
3027 const UInt64 numFiles64 = items.Size() + items2.Size();
3028 RINOK(Callback->SetCompleted(&numFiles64, &_cnt));
3029 }
3030
3031 const CItemEx &cdItem = cdItems[i];
3032
3033 int index = -1;
3034
3035 if (nextLocalIndex != -1)
3036 {
3037 if ((unsigned)nextLocalIndex < items.Size())
3038 {
3039 CItemEx &item = items[(unsigned)nextLocalIndex];
3040 if (item.Disk == cdItem.Disk &&
3041 (item.LocalHeaderPos == cdItem.LocalHeaderPos
3042 || (Overflow32bit && (UInt32)item.LocalHeaderPos == cdItem.LocalHeaderPos)))
3043 index = nextLocalIndex++;
3044 else
3045 nextLocalIndex = -1;
3046 }
3047 }
3048
3049 if (index == -1)
3050 index = FindItem(items, cdItem);
3051
3052 // index = -1;
3053
3054 if (index == -1)
3055 {
3056 items2.Add(i);
3057 HeadersError = true;
3058 continue;
3059 }
3060
3061 CItemEx &item = items[(unsigned)index];
3062 if (item.Name != cdItem.Name
3063 // || item.Name.Len() != cdItem.Name.Len()
3064 || item.PackSize != cdItem.PackSize
3065 || item.Size != cdItem.Size
3066 // item.ExtractVersion != cdItem.ExtractVersion
3067 || !FlagsAreSame(item, cdItem)
3068 || item.Crc != cdItem.Crc)
3069 {
3070 HeadersError = true;
3071 continue;
3072 }
3073
3074 // item.Name = cdItem.Name;
3075 item.MadeByVersion = cdItem.MadeByVersion;
3076 item.CentralExtra = cdItem.CentralExtra;
3077 item.InternalAttrib = cdItem.InternalAttrib;
3078 item.ExternalAttrib = cdItem.ExternalAttrib;
3079 item.Comment = cdItem.Comment;
3080 item.FromCentral = cdItem.FromCentral;
3081 }
3082
3083 FOR_VECTOR (k, items2)
3084 items.Add(cdItems[items2[k]]);
3085 }
3086
3087 if (ecd.NumEntries < ecd.NumEntries_in_ThisDisk)
3088 HeadersError = true;
3089
3090 if (ecd.ThisDisk == 0)
3091 {
3092 // if (isZip64)
3093 {
3094 if (ecd.NumEntries != ecd.NumEntries_in_ThisDisk)
3095 HeadersError = true;
3096 }
3097 }
3098
3099 if (isZip64)
3100 {
3101 if (cdInfo.NumEntries != items.Size()
3102 || (ecd.NumEntries != items.Size() && ecd.NumEntries != 0xFFFF))
3103 HeadersError = true;
3104 }
3105 else
3106 {
3107 // old 7-zip could store 32-bit number of CD items to 16-bit field.
3108 // if (ecd.NumEntries != items.Size())
3109 if (ecd.NumEntries > items.Size())
3110 HeadersError = true;
3111
3112 if (cdInfo.NumEntries != numCdItems)
3113 {
3114 if ((UInt16)cdInfo.NumEntries != (UInt16)numCdItems)
3115 HeadersError = true;
3116 else
3117 Cd_NumEntries_Overflow_16bit = true;
3118 }
3119 }
3120
3121 ReadBuffer(ArcInfo.Comment, ecd.CommentSize);
3122
3123 _inBufMode = false;
3124
3125 // DisableBufMode();
3126 // Buffer.Free();
3127 /* we can't clear buf varibles. we need them to calculate PhySize of archive */
3128
3129 if ((UInt16)cdInfo.NumEntries != (UInt16)numCdItems
3130 || (UInt32)cdInfo.Size != (UInt32)cdSize
3131 || ((UInt32)cdInfo.Offset != (UInt32)cdRelatOffset && !items.IsEmpty()))
3132 {
3133 // return S_FALSE;
3134 HeadersError = true;
3135 }
3136
3137 #ifdef ZIP_SELF_CHECK
3138 if (localsWereRead)
3139 {
3140 const UInt64 endPos = ArcInfo.MarkerPos2 + _cnt;
3141 if (endPos != (IsMultiVol ? Vols.TotalBytesSize : ArcInfo.FileEndPos))
3142 {
3143 // there are some data after the end of archive or error in code;
3144 return E_FAIL;
3145 }
3146 }
3147 #endif
3148
3149 // printf("\nOpen OK");
3150 return S_OK;
3151}
3152
3153
3154
3155HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchLimit,
3156 IArchiveOpenCallback *callback, CObjectVector<CItemEx> &items)
3157{
3158 items.Clear();
3159
3160 Close();
3161
3162 UInt64 startPos;
3163 RINOK(stream->Seek(0, STREAM_SEEK_CUR, &startPos));
3164 RINOK(stream->Seek(0, STREAM_SEEK_END, &ArcInfo.FileEndPos));
3165 _streamPos = ArcInfo.FileEndPos;
3166
3167 StartStream = stream;
3168 Stream = stream;
3169 Callback = callback;
3170
3171 DisableBufMode();
3172
3173 bool volWasRequested = false;
3174
3175 if (callback
3176 && (startPos == 0 || !searchLimit || *searchLimit != 0))
3177 {
3178 // we try to read volumes only if it's first call (offset == 0) or scan is allowed.
3179 volWasRequested = true;
3180 RINOK(ReadVols());
3181 }
3182
3183 if (IsMultiVol && Vols.StartParsingVol == 0 && (unsigned)Vols.StartParsingVol < Vols.Streams.Size())
3184 {
3185 // only StartParsingVol = 0 is safe search.
3186 RINOK(SeekToVol(0, 0));
3187 // if (Stream)
3188 {
3189 // UInt64 limit = 1 << 22; // for sfx
3190 UInt64 limit = 0; // without sfx
3191
3192 HRESULT res = FindMarker(&limit);
3193
3194 if (res == S_OK)
3195 {
3196 MarkerIsFound = true;
3197 MarkerIsSafe = true;
3198 }
3199 else if (res != S_FALSE)
3200 return res;
3201 }
3202 }
3203 else
3204 {
3205 // printf("\nOpen offset = %u\n", (unsigned)startPos);
3206 if (IsMultiVol
3207 && (unsigned)Vols.StartParsingVol < Vols.Streams.Size()
3208 && Vols.Streams[(unsigned)Vols.StartParsingVol].Stream)
3209 {
3210 RINOK(SeekToVol(Vols.StartParsingVol, Vols.StreamIndex == Vols.StartVolIndex ? startPos : 0));
3211 }
3212 else
3213 {
3214 RINOK(SeekToVol(-1, startPos));
3215 }
3216
3217 // UInt64 limit = 1 << 22;
3218 // HRESULT res = FindMarker(&limit);
3219
3220 HRESULT res = FindMarker(searchLimit);
3221
3222 // const UInt64 curPos = GetVirtStreamPos();
3223 const UInt64 curPos = ArcInfo.MarkerPos2 + 4;
3224
3225 if (res == S_OK)
3226 MarkerIsFound = true;
3227 else if (!IsMultiVol)
3228 {
3229 /*
3230 // if (startPos != 0), probably CD copuld be already tested with another call with (startPos == 0).
3231 // so we don't want to try to open CD again in that ase.
3232 if (startPos != 0)
3233 return res;
3234 // we can try to open CD, if there is no Marker and (startPos == 0).
3235 // is it OK to open such files as ZIP, or big number of false positive, when CD can be find in end of file ?
3236 */
3237 return res;
3238 }
3239
3240 if (ArcInfo.IsSpanMode && !volWasRequested)
3241 {
3242 RINOK(ReadVols());
3243 if (IsMultiVol && MarkerIsFound && ArcInfo.MarkerVolIndex < 0)
3244 ArcInfo.MarkerVolIndex = Vols.StartVolIndex;
3245 }
3246
3247 MarkerIsSafe = !IsMultiVol
3248 || (ArcInfo.MarkerVolIndex == 0 && ArcInfo.MarkerPos == 0)
3249 ;
3250
3251
3252 if (IsMultiVol)
3253 {
3254 if ((unsigned)Vols.StartVolIndex < Vols.Streams.Size())
3255 {
3256 Stream = Vols.Streams[(unsigned)Vols.StartVolIndex].Stream;
3257 if (Stream)
3258 {
3259 RINOK(Seek_SavePos(curPos));
3260 }
3261 else
3262 IsMultiVol = false;
3263 }
3264 else
3265 IsMultiVol = false;
3266 }
3267
3268 if (!IsMultiVol)
3269 {
3270 if (Vols.StreamIndex != -1)
3271 {
3272 Stream = StartStream;
3273 Vols.StreamIndex = -1;
3274 InitBuf();
3275 RINOK(Seek_SavePos(curPos));
3276 }
3277
3278 ArcInfo.MarkerVolIndex = -1;
3279 StreamRef = stream;
3280 Stream = stream;
3281 }
3282 }
3283
3284
3285 if (!IsMultiVol)
3286 Vols.ClearRefs();
3287
3288 {
3289 HRESULT res;
3290 try
3291 {
3292 res = ReadHeaders(items);
3293 }
3294 catch (const CSystemException &e) { res = e.ErrorCode; }
3295 catch (const CUnexpectEnd &)
3296 {
3297 if (items.IsEmpty())
3298 return S_FALSE;
3299 UnexpectedEnd = true;
3300 res = S_OK;
3301 }
3302 catch (...)
3303 {
3304 DisableBufMode();
3305 throw;
3306 }
3307
3308 if (IsMultiVol)
3309 {
3310 ArcInfo.FinishPos = ArcInfo.FileEndPos;
3311 if ((unsigned)Vols.StreamIndex < Vols.Streams.Size())
3312 if (GetVirtStreamPos() < Vols.Streams[(unsigned)Vols.StreamIndex].Size)
3313 ArcInfo.ThereIsTail = true;
3314 }
3315 else
3316 {
3317 ArcInfo.FinishPos = GetVirtStreamPos();
3318 ArcInfo.ThereIsTail = (ArcInfo.FileEndPos > ArcInfo.FinishPos);
3319 }
3320
3321 DisableBufMode();
3322
3323 IsArcOpen = true;
3324 if (!IsMultiVol)
3325 Vols.Streams.Clear();
3326 return res;
3327 }
3328}
3329
3330
3331HRESULT CInArchive::GetItemStream(const CItemEx &item, bool seekPackData, CMyComPtr<ISequentialInStream> &stream)
3332{
3333 stream.Release();
3334
3335 UInt64 pos = item.LocalHeaderPos;
3336 if (seekPackData)
3337 pos += item.LocalFullHeaderSize;
3338
3339 if (!IsMultiVol)
3340 {
3341 if (UseDisk_in_SingleVol && item.Disk != EcdVolIndex)
3342 return S_OK;
3343 pos = (UInt64)((Int64)pos + ArcInfo.Base);
3344 RINOK(StreamRef->Seek((Int64)pos, STREAM_SEEK_SET, NULL));
3345 stream = StreamRef;
3346 return S_OK;
3347 }
3348
3349 if (item.Disk >= Vols.Streams.Size())
3350 return S_OK;
3351
3352 IInStream *str2 = Vols.Streams[item.Disk].Stream;
3353 if (!str2)
3354 return S_OK;
3355 RINOK(str2->Seek((Int64)pos, STREAM_SEEK_SET, NULL));
3356
3357 Vols.NeedSeek = false;
3358 Vols.StreamIndex = (int)item.Disk;
3359
3360 CVolStream *volsStreamSpec = new CVolStream;
3361 volsStreamSpec->Vols = &Vols;
3362 stream = volsStreamSpec;
3363
3364 return S_OK;
3365}
3366
3367}}