1 // 7zIn.cpp
2 
3 #include "StdAfx.h"
4 
5 #include "../../../../C/7zCrc.h"
6 #include "../../../../C/CpuArch.h"
7 
8 #include "../../Common/StreamObjects.h"
9 #include "../../Common/StreamUtils.h"
10 
11 #include "7zDecode.h"
12 #include "7zIn.h"
13 
14 #define Get16(p) GetUi16(p)
15 #define Get32(p) GetUi32(p)
16 #define Get64(p) GetUi64(p)
17 
18 // define FORMAT_7Z_RECOVERY if you want to recover multivolume archives with empty StartHeader
19 #ifndef _SFX
20 #define FORMAT_7Z_RECOVERY
21 #endif
22 
23 namespace NArchive {
24 namespace N7z {
25 
BoolVector_Fill_False(CBoolVector & v,int size)26 static void BoolVector_Fill_False(CBoolVector &v, int size)
27 {
28   v.Clear();
29   v.Reserve(size);
30   for (int i = 0; i < size; i++)
31     v.Add(false);
32 }
33 
BoolVector_GetAndSet(CBoolVector & v,UInt32 index)34 static bool BoolVector_GetAndSet(CBoolVector &v, UInt32 index)
35 {
36   if (index >= (UInt32)v.Size())
37     return true;
38   bool res = v[index];
39   v[index] = true;
40   return res;
41 }
42 
CheckStructure() const43 bool CFolder::CheckStructure() const
44 {
45   const int kNumCodersMax = sizeof(UInt32) * 8; // don't change it
46   const int kMaskSize = sizeof(UInt32) * 8; // it must be >= kNumCodersMax
47   const int kNumBindsMax = 32;
48 
49   if (Coders.Size() > kNumCodersMax || BindPairs.Size() > kNumBindsMax)
50     return false;
51 
52   {
53     CBoolVector v;
54     BoolVector_Fill_False(v, BindPairs.Size() + PackStreams.Size());
55 
56     int i;
57     for (i = 0; i < BindPairs.Size(); i++)
58       if (BoolVector_GetAndSet(v, BindPairs[i].InIndex))
59         return false;
60     for (i = 0; i < PackStreams.Size(); i++)
61       if (BoolVector_GetAndSet(v, PackStreams[i]))
62         return false;
63 
64     BoolVector_Fill_False(v, UnpackSizes.Size());
65     for (i = 0; i < BindPairs.Size(); i++)
66       if (BoolVector_GetAndSet(v, BindPairs[i].OutIndex))
67         return false;
68   }
69 
70   UInt32 mask[kMaskSize];
71   int i;
72   for (i = 0; i < kMaskSize; i++)
73     mask[i] = 0;
74 
75   {
76     CIntVector inStreamToCoder, outStreamToCoder;
77     for (i = 0; i < Coders.Size(); i++)
78     {
79       CNum j;
80       const CCoderInfo &coder = Coders[i];
81       for (j = 0; j < coder.NumInStreams; j++)
82         inStreamToCoder.Add(i);
83       for (j = 0; j < coder.NumOutStreams; j++)
84         outStreamToCoder.Add(i);
85     }
86 
87     for (i = 0; i < BindPairs.Size(); i++)
88     {
89       const CBindPair &bp = BindPairs[i];
90       mask[inStreamToCoder[bp.InIndex]] |= (1 << outStreamToCoder[bp.OutIndex]);
91     }
92   }
93 
94   for (i = 0; i < kMaskSize; i++)
95     for (int j = 0; j < kMaskSize; j++)
96       if (((1 << j) & mask[i]) != 0)
97         mask[i] |= mask[j];
98 
99   for (i = 0; i < kMaskSize; i++)
100     if (((1 << i) & mask[i]) != 0)
101       return false;
102 
103   return true;
104 }
105 
106 class CInArchiveException {};
107 
ThrowException()108 static void ThrowException() { throw CInArchiveException(); }
ThrowEndOfData()109 static inline void ThrowEndOfData()   { ThrowException(); }
ThrowUnsupported()110 static inline void ThrowUnsupported() { ThrowException(); }
ThrowIncorrect()111 static inline void ThrowIncorrect()   { ThrowException(); }
ThrowUnsupportedVersion()112 static inline void ThrowUnsupportedVersion() { ThrowException(); }
113 
114 /*
115 class CInArchiveException
116 {
117 public:
118   enum CCauseType
119   {
120     kUnsupportedVersion = 0,
121     kUnsupported,
122     kIncorrect,
123     kEndOfData,
124   } Cause;
125   CInArchiveException(CCauseType cause): Cause(cause) {};
126 };
127 
128 static void ThrowException(CInArchiveException::CCauseType c) { throw CInArchiveException(c); }
129 static void ThrowEndOfData()   { ThrowException(CInArchiveException::kEndOfData); }
130 static void ThrowUnsupported() { ThrowException(CInArchiveException::kUnsupported); }
131 static void ThrowIncorrect()   { ThrowException(CInArchiveException::kIncorrect); }
132 static void ThrowUnsupportedVersion() { ThrowException(CInArchiveException::kUnsupportedVersion); }
133 */
134 
135 class CStreamSwitch
136 {
137   CInArchive *_archive;
138   bool _needRemove;
139 public:
CStreamSwitch()140   CStreamSwitch(): _needRemove(false) {}
~CStreamSwitch()141   ~CStreamSwitch() { Remove(); }
142   void Remove();
143   void Set(CInArchive *archive, const Byte *data, size_t size);
144   void Set(CInArchive *archive, const CByteBuffer &byteBuffer);
145   void Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector);
146 };
147 
Remove()148 void CStreamSwitch::Remove()
149 {
150   if (_needRemove)
151   {
152     _archive->DeleteByteStream();
153     _needRemove = false;
154   }
155 }
156 
Set(CInArchive * archive,const Byte * data,size_t size)157 void CStreamSwitch::Set(CInArchive *archive, const Byte *data, size_t size)
158 {
159   Remove();
160   _archive = archive;
161   _archive->AddByteStream(data, size);
162   _needRemove = true;
163 }
164 
Set(CInArchive * archive,const CByteBuffer & byteBuffer)165 void CStreamSwitch::Set(CInArchive *archive, const CByteBuffer &byteBuffer)
166 {
167   Set(archive, byteBuffer, byteBuffer.GetCapacity());
168 }
169 
Set(CInArchive * archive,const CObjectVector<CByteBuffer> * dataVector)170 void CStreamSwitch::Set(CInArchive *archive, const CObjectVector<CByteBuffer> *dataVector)
171 {
172   Remove();
173   Byte external = archive->ReadByte();
174   if (external != 0)
175   {
176     int dataIndex = (int)archive->ReadNum();
177     if (dataIndex < 0 || dataIndex >= dataVector->Size())
178       ThrowIncorrect();
179     Set(archive, (*dataVector)[dataIndex]);
180   }
181 }
182 
ReadByte()183 Byte CInByte2::ReadByte()
184 {
185   if (_pos >= _size)
186     ThrowEndOfData();
187   return _buffer[_pos++];
188 }
189 
ReadBytes(Byte * data,size_t size)190 void CInByte2::ReadBytes(Byte *data, size_t size)
191 {
192   if (size > _size - _pos)
193     ThrowEndOfData();
194   for (size_t i = 0; i < size; i++)
195     data[i] = _buffer[_pos++];
196 }
197 
SkipData(UInt64 size)198 void CInByte2::SkipData(UInt64 size)
199 {
200   if (size > _size - _pos)
201     ThrowEndOfData();
202   _pos += (size_t)size;
203 }
204 
SkipData()205 void CInByte2::SkipData()
206 {
207   SkipData(ReadNumber());
208 }
209 
ReadNumber()210 UInt64 CInByte2::ReadNumber()
211 {
212   if (_pos >= _size)
213     ThrowEndOfData();
214   Byte firstByte = _buffer[_pos++];
215   Byte mask = 0x80;
216   UInt64 value = 0;
217   for (int i = 0; i < 8; i++)
218   {
219     if ((firstByte & mask) == 0)
220     {
221       UInt64 highPart = firstByte & (mask - 1);
222       value += (highPart << (i * 8));
223       return value;
224     }
225     if (_pos >= _size)
226       ThrowEndOfData();
227     value |= ((UInt64)_buffer[_pos++] << (8 * i));
228     mask >>= 1;
229   }
230   return value;
231 }
232 
ReadNum()233 CNum CInByte2::ReadNum()
234 {
235   UInt64 value = ReadNumber();
236   if (value > kNumMax)
237     ThrowUnsupported();
238   return (CNum)value;
239 }
240 
ReadUInt32()241 UInt32 CInByte2::ReadUInt32()
242 {
243   if (_pos + 4 > _size)
244     ThrowEndOfData();
245   UInt32 res = Get32(_buffer + _pos);
246   _pos += 4;
247   return res;
248 }
249 
ReadUInt64()250 UInt64 CInByte2::ReadUInt64()
251 {
252   if (_pos + 8 > _size)
253     ThrowEndOfData();
254   UInt64 res = Get64(_buffer + _pos);
255   _pos += 8;
256   return res;
257 }
258 
ReadString(UString & s)259 void CInByte2::ReadString(UString &s)
260 {
261   const Byte *buf = _buffer + _pos;
262   size_t rem = (_size - _pos) / 2 * 2;
263   {
264     size_t i;
265     for (i = 0; i < rem; i += 2)
266       if (buf[i] == 0 && buf[i + 1] == 0)
267         break;
268     if (i == rem)
269       ThrowEndOfData();
270     rem = i;
271   }
272   int len = (int)(rem / 2);
273   if (len < 0 || (size_t)len * 2 != rem)
274     ThrowUnsupported();
275   wchar_t *p = s.GetBuffer(len);
276   int i;
277   for (i = 0; i < len; i++, buf += 2)
278     p[i] = (wchar_t)Get16(buf);
279   s.ReleaseBuffer(len);
280   _pos += rem + 2;
281 }
282 
TestSignatureCandidate(const Byte * p)283 static inline bool TestSignatureCandidate(const Byte *p)
284 {
285   for (int i = 0; i < kSignatureSize; i++)
286     if (p[i] != kSignature[i])
287       return false;
288   return (p[0x1A] == 0 && p[0x1B] == 0);
289 }
290 
FindAndReadSignature(IInStream * stream,const UInt64 * searchHeaderSizeLimit)291 HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
292 {
293   RINOK(ReadStream_FALSE(stream, _header, kHeaderSize));
294 
295   if (TestSignatureCandidate(_header))
296     return S_OK;
297 
298   CByteBuffer byteBuffer;
299   const UInt32 kBufferSize = (1 << 16);
300   byteBuffer.SetCapacity(kBufferSize);
301   Byte *buffer = byteBuffer;
302   UInt32 numPrevBytes = kHeaderSize - 1;
303   memcpy(buffer, _header + 1, numPrevBytes);
304   UInt64 curTestPos = _arhiveBeginStreamPosition + 1;
305   for (;;)
306   {
307     if (searchHeaderSizeLimit != NULL)
308       if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit)
309         break;
310     do
311     {
312       UInt32 numReadBytes = kBufferSize - numPrevBytes;
313       UInt32 processedSize;
314       RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize));
315       numPrevBytes += processedSize;
316       if (processedSize == 0)
317         return S_FALSE;
318     }
319     while (numPrevBytes < kHeaderSize);
320     UInt32 numTests = numPrevBytes - kHeaderSize + 1;
321     for (UInt32 pos = 0; pos < numTests; pos++)
322     {
323       for (; buffer[pos] != '7' && pos < numTests; pos++);
324       if (pos == numTests)
325         break;
326       if (TestSignatureCandidate(buffer + pos))
327       {
328         memcpy(_header, buffer + pos, kHeaderSize);
329         curTestPos += pos;
330         _arhiveBeginStreamPosition = curTestPos;
331         return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL);
332       }
333     }
334     curTestPos += numTests;
335     numPrevBytes -= numTests;
336     memmove(buffer, buffer + numTests, numPrevBytes);
337   }
338   return S_FALSE;
339 }
340 
341 // S_FALSE means that file is not archive
Open(IInStream * stream,const UInt64 * searchHeaderSizeLimit)342 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
343 {
344   HeadersSize = 0;
345   Close();
346   RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition))
347   RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit));
348   _stream = stream;
349   return S_OK;
350 }
351 
Close()352 void CInArchive::Close()
353 {
354   _stream.Release();
355 }
356 
ReadArchiveProperties(CInArchiveInfo &)357 void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */)
358 {
359   for (;;)
360   {
361     if (ReadID() == NID::kEnd)
362       break;
363     SkipData();
364   }
365 }
366 
GetNextFolderItem(CFolder & folder)367 void CInArchive::GetNextFolderItem(CFolder &folder)
368 {
369   CNum numCoders = ReadNum();
370 
371   folder.Coders.Clear();
372   folder.Coders.Reserve((int)numCoders);
373   CNum numInStreams = 0;
374   CNum numOutStreams = 0;
375   CNum i;
376   for (i = 0; i < numCoders; i++)
377   {
378     folder.Coders.Add(CCoderInfo());
379     CCoderInfo &coder = folder.Coders.Back();
380 
381     {
382       Byte mainByte = ReadByte();
383       int idSize = (mainByte & 0xF);
384       Byte longID[15];
385       ReadBytes(longID, idSize);
386       if (idSize > 8)
387         ThrowUnsupported();
388       UInt64 id = 0;
389       for (int j = 0; j < idSize; j++)
390         id |= (UInt64)longID[idSize - 1 - j] << (8 * j);
391       coder.MethodID = id;
392 
393       if ((mainByte & 0x10) != 0)
394       {
395         coder.NumInStreams = ReadNum();
396         coder.NumOutStreams = ReadNum();
397       }
398       else
399       {
400         coder.NumInStreams = 1;
401         coder.NumOutStreams = 1;
402       }
403       if ((mainByte & 0x20) != 0)
404       {
405         CNum propsSize = ReadNum();
406         coder.Props.SetCapacity((size_t)propsSize);
407         ReadBytes((Byte *)coder.Props, (size_t)propsSize);
408       }
409       if ((mainByte & 0x80) != 0)
410         ThrowUnsupported();
411     }
412     numInStreams += coder.NumInStreams;
413     numOutStreams += coder.NumOutStreams;
414   }
415 
416   CNum numBindPairs = numOutStreams - 1;
417   folder.BindPairs.Clear();
418   folder.BindPairs.Reserve(numBindPairs);
419   for (i = 0; i < numBindPairs; i++)
420   {
421     CBindPair bp;
422     bp.InIndex = ReadNum();
423     bp.OutIndex = ReadNum();
424     folder.BindPairs.Add(bp);
425   }
426 
427   if (numInStreams < numBindPairs)
428     ThrowUnsupported();
429   CNum numPackStreams = numInStreams - numBindPairs;
430   folder.PackStreams.Reserve(numPackStreams);
431   if (numPackStreams == 1)
432   {
433     for (i = 0; i < numInStreams; i++)
434       if (folder.FindBindPairForInStream(i) < 0)
435       {
436         folder.PackStreams.Add(i);
437         break;
438       }
439     if (folder.PackStreams.Size() != 1)
440       ThrowUnsupported();
441   }
442   else
443     for (i = 0; i < numPackStreams; i++)
444       folder.PackStreams.Add(ReadNum());
445 }
446 
WaitAttribute(UInt64 attribute)447 void CInArchive::WaitAttribute(UInt64 attribute)
448 {
449   for (;;)
450   {
451     UInt64 type = ReadID();
452     if (type == attribute)
453       return;
454     if (type == NID::kEnd)
455       ThrowIncorrect();
456     SkipData();
457   }
458 }
459 
ReadHashDigests(int numItems,CBoolVector & digestsDefined,CRecordVector<UInt32> & digests)460 void CInArchive::ReadHashDigests(int numItems,
461     CBoolVector &digestsDefined,
462     CRecordVector<UInt32> &digests)
463 {
464   ReadBoolVector2(numItems, digestsDefined);
465   digests.Clear();
466   digests.Reserve(numItems);
467   for (int i = 0; i < numItems; i++)
468   {
469     UInt32 crc = 0;
470     if (digestsDefined[i])
471       crc = ReadUInt32();
472     digests.Add(crc);
473   }
474 }
475 
ReadPackInfo(UInt64 & dataOffset,CRecordVector<UInt64> & packSizes,CBoolVector & packCRCsDefined,CRecordVector<UInt32> & packCRCs)476 void CInArchive::ReadPackInfo(
477     UInt64 &dataOffset,
478     CRecordVector<UInt64> &packSizes,
479     CBoolVector &packCRCsDefined,
480     CRecordVector<UInt32> &packCRCs)
481 {
482   dataOffset = ReadNumber();
483   CNum numPackStreams = ReadNum();
484 
485   WaitAttribute(NID::kSize);
486   packSizes.Clear();
487   packSizes.Reserve(numPackStreams);
488   for (CNum i = 0; i < numPackStreams; i++)
489     packSizes.Add(ReadNumber());
490 
491   UInt64 type;
492   for (;;)
493   {
494     type = ReadID();
495     if (type == NID::kEnd)
496       break;
497     if (type == NID::kCRC)
498     {
499       ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs);
500       continue;
501     }
502     SkipData();
503   }
504   if (packCRCsDefined.IsEmpty())
505   {
506     BoolVector_Fill_False(packCRCsDefined, numPackStreams);
507     packCRCs.Reserve(numPackStreams);
508     packCRCs.Clear();
509     for (CNum i = 0; i < numPackStreams; i++)
510       packCRCs.Add(0);
511   }
512 }
513 
ReadUnpackInfo(const CObjectVector<CByteBuffer> * dataVector,CObjectVector<CFolder> & folders)514 void CInArchive::ReadUnpackInfo(
515     const CObjectVector<CByteBuffer> *dataVector,
516     CObjectVector<CFolder> &folders)
517 {
518   WaitAttribute(NID::kFolder);
519   CNum numFolders = ReadNum();
520 
521   {
522     CStreamSwitch streamSwitch;
523     streamSwitch.Set(this, dataVector);
524     folders.Clear();
525     folders.Reserve(numFolders);
526     for (CNum i = 0; i < numFolders; i++)
527     {
528       folders.Add(CFolder());
529       GetNextFolderItem(folders.Back());
530     }
531   }
532 
533   WaitAttribute(NID::kCodersUnpackSize);
534 
535   CNum i;
536   for (i = 0; i < numFolders; i++)
537   {
538     CFolder &folder = folders[i];
539     CNum numOutStreams = folder.GetNumOutStreams();
540     folder.UnpackSizes.Reserve(numOutStreams);
541     for (CNum j = 0; j < numOutStreams; j++)
542       folder.UnpackSizes.Add(ReadNumber());
543   }
544 
545   for (;;)
546   {
547     UInt64 type = ReadID();
548     if (type == NID::kEnd)
549       return;
550     if (type == NID::kCRC)
551     {
552       CBoolVector crcsDefined;
553       CRecordVector<UInt32> crcs;
554       ReadHashDigests(numFolders, crcsDefined, crcs);
555       for (i = 0; i < numFolders; i++)
556       {
557         CFolder &folder = folders[i];
558         folder.UnpackCRCDefined = crcsDefined[i];
559         folder.UnpackCRC = crcs[i];
560       }
561       continue;
562     }
563     SkipData();
564   }
565 }
566 
ReadSubStreamsInfo(const CObjectVector<CFolder> & folders,CRecordVector<CNum> & numUnpackStreamsInFolders,CRecordVector<UInt64> & unpackSizes,CBoolVector & digestsDefined,CRecordVector<UInt32> & digests)567 void CInArchive::ReadSubStreamsInfo(
568     const CObjectVector<CFolder> &folders,
569     CRecordVector<CNum> &numUnpackStreamsInFolders,
570     CRecordVector<UInt64> &unpackSizes,
571     CBoolVector &digestsDefined,
572     CRecordVector<UInt32> &digests)
573 {
574   numUnpackStreamsInFolders.Clear();
575   numUnpackStreamsInFolders.Reserve(folders.Size());
576   UInt64 type;
577   for (;;)
578   {
579     type = ReadID();
580     if (type == NID::kNumUnpackStream)
581     {
582       for (int i = 0; i < folders.Size(); i++)
583         numUnpackStreamsInFolders.Add(ReadNum());
584       continue;
585     }
586     if (type == NID::kCRC || type == NID::kSize)
587       break;
588     if (type == NID::kEnd)
589       break;
590     SkipData();
591   }
592 
593   if (numUnpackStreamsInFolders.IsEmpty())
594     for (int i = 0; i < folders.Size(); i++)
595       numUnpackStreamsInFolders.Add(1);
596 
597   int i;
598   for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
599   {
600     // v3.13 incorrectly worked with empty folders
601     // v4.07: we check that folder is empty
602     CNum numSubstreams = numUnpackStreamsInFolders[i];
603     if (numSubstreams == 0)
604       continue;
605     UInt64 sum = 0;
606     for (CNum j = 1; j < numSubstreams; j++)
607       if (type == NID::kSize)
608       {
609         UInt64 size = ReadNumber();
610         unpackSizes.Add(size);
611         sum += size;
612       }
613     unpackSizes.Add(folders[i].GetUnpackSize() - sum);
614   }
615   if (type == NID::kSize)
616     type = ReadID();
617 
618   int numDigests = 0;
619   int numDigestsTotal = 0;
620   for (i = 0; i < folders.Size(); i++)
621   {
622     CNum numSubstreams = numUnpackStreamsInFolders[i];
623     if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
624       numDigests += numSubstreams;
625     numDigestsTotal += numSubstreams;
626   }
627 
628   for (;;)
629   {
630     if (type == NID::kCRC)
631     {
632       CBoolVector digestsDefined2;
633       CRecordVector<UInt32> digests2;
634       ReadHashDigests(numDigests, digestsDefined2, digests2);
635       int digestIndex = 0;
636       for (i = 0; i < folders.Size(); i++)
637       {
638         CNum numSubstreams = numUnpackStreamsInFolders[i];
639         const CFolder &folder = folders[i];
640         if (numSubstreams == 1 && folder.UnpackCRCDefined)
641         {
642           digestsDefined.Add(true);
643           digests.Add(folder.UnpackCRC);
644         }
645         else
646           for (CNum j = 0; j < numSubstreams; j++, digestIndex++)
647           {
648             digestsDefined.Add(digestsDefined2[digestIndex]);
649             digests.Add(digests2[digestIndex]);
650           }
651       }
652     }
653     else if (type == NID::kEnd)
654     {
655       if (digestsDefined.IsEmpty())
656       {
657         BoolVector_Fill_False(digestsDefined, numDigestsTotal);
658         digests.Clear();
659         for (int i = 0; i < numDigestsTotal; i++)
660           digests.Add(0);
661       }
662       return;
663     }
664     else
665       SkipData();
666     type = ReadID();
667   }
668 }
669 
ReadStreamsInfo(const CObjectVector<CByteBuffer> * dataVector,UInt64 & dataOffset,CRecordVector<UInt64> & packSizes,CBoolVector & packCRCsDefined,CRecordVector<UInt32> & packCRCs,CObjectVector<CFolder> & folders,CRecordVector<CNum> & numUnpackStreamsInFolders,CRecordVector<UInt64> & unpackSizes,CBoolVector & digestsDefined,CRecordVector<UInt32> & digests)670 void CInArchive::ReadStreamsInfo(
671     const CObjectVector<CByteBuffer> *dataVector,
672     UInt64 &dataOffset,
673     CRecordVector<UInt64> &packSizes,
674     CBoolVector &packCRCsDefined,
675     CRecordVector<UInt32> &packCRCs,
676     CObjectVector<CFolder> &folders,
677     CRecordVector<CNum> &numUnpackStreamsInFolders,
678     CRecordVector<UInt64> &unpackSizes,
679     CBoolVector &digestsDefined,
680     CRecordVector<UInt32> &digests)
681 {
682   for (;;)
683   {
684     UInt64 type = ReadID();
685     if (type > ((UInt32)1 << 30))
686       ThrowIncorrect();
687     switch((UInt32)type)
688     {
689       case NID::kEnd:
690         return;
691       case NID::kPackInfo:
692       {
693         ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs);
694         break;
695       }
696       case NID::kUnpackInfo:
697       {
698         ReadUnpackInfo(dataVector, folders);
699         break;
700       }
701       case NID::kSubStreamsInfo:
702       {
703         ReadSubStreamsInfo(folders, numUnpackStreamsInFolders,
704             unpackSizes, digestsDefined, digests);
705         break;
706       }
707       default:
708         ThrowIncorrect();
709     }
710   }
711 }
712 
ReadBoolVector(int numItems,CBoolVector & v)713 void CInArchive::ReadBoolVector(int numItems, CBoolVector &v)
714 {
715   v.Clear();
716   v.Reserve(numItems);
717   Byte b = 0;
718   Byte mask = 0;
719   for (int i = 0; i < numItems; i++)
720   {
721     if (mask == 0)
722     {
723       b = ReadByte();
724       mask = 0x80;
725     }
726     v.Add((b & mask) != 0);
727     mask >>= 1;
728   }
729 }
730 
ReadBoolVector2(int numItems,CBoolVector & v)731 void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v)
732 {
733   Byte allAreDefined = ReadByte();
734   if (allAreDefined == 0)
735   {
736     ReadBoolVector(numItems, v);
737     return;
738   }
739   v.Clear();
740   v.Reserve(numItems);
741   for (int i = 0; i < numItems; i++)
742     v.Add(true);
743 }
744 
ReadUInt64DefVector(const CObjectVector<CByteBuffer> & dataVector,CUInt64DefVector & v,int numFiles)745 void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector,
746     CUInt64DefVector &v, int numFiles)
747 {
748   ReadBoolVector2(numFiles, v.Defined);
749 
750   CStreamSwitch streamSwitch;
751   streamSwitch.Set(this, &dataVector);
752   v.Values.Reserve(numFiles);
753 
754   for (int i = 0; i < numFiles; i++)
755   {
756     UInt64 t = 0;
757     if (v.Defined[i])
758       t = ReadUInt64();
759     v.Values.Add(t);
760   }
761 }
762 
ReadAndDecodePackedStreams(DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset,UInt64 & dataOffset,CObjectVector<CByteBuffer> & dataVector,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)763 HRESULT CInArchive::ReadAndDecodePackedStreams(
764     DECL_EXTERNAL_CODECS_LOC_VARS
765     UInt64 baseOffset,
766     UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector
767     #ifndef _NO_CRYPTO
768     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
769     #endif
770     )
771 {
772   CRecordVector<UInt64> packSizes;
773   CBoolVector packCRCsDefined;
774   CRecordVector<UInt32> packCRCs;
775   CObjectVector<CFolder> folders;
776 
777   CRecordVector<CNum> numUnpackStreamsInFolders;
778   CRecordVector<UInt64> unpackSizes;
779   CBoolVector digestsDefined;
780   CRecordVector<UInt32> digests;
781 
782   ReadStreamsInfo(NULL,
783     dataOffset,
784     packSizes,
785     packCRCsDefined,
786     packCRCs,
787     folders,
788     numUnpackStreamsInFolders,
789     unpackSizes,
790     digestsDefined,
791     digests);
792 
793   // db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
794 
795   CNum packIndex = 0;
796   CDecoder decoder(
797     #ifdef _ST_MODE
798     false
799     #else
800     true
801     #endif
802     );
803   UInt64 dataStartPos = baseOffset + dataOffset;
804   for (int i = 0; i < folders.Size(); i++)
805   {
806     const CFolder &folder = folders[i];
807     dataVector.Add(CByteBuffer());
808     CByteBuffer &data = dataVector.Back();
809     UInt64 unpackSize64 = folder.GetUnpackSize();
810     size_t unpackSize = (size_t)unpackSize64;
811     if (unpackSize != unpackSize64)
812       ThrowUnsupported();
813     data.SetCapacity(unpackSize);
814 
815     CSequentialOutStreamImp2 *outStreamSpec = new CSequentialOutStreamImp2;
816     CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
817     outStreamSpec->Init(data, unpackSize);
818 
819     HRESULT result = decoder.Decode(
820       EXTERNAL_CODECS_LOC_VARS
821       _stream, dataStartPos,
822       &packSizes[packIndex], folder, outStream, NULL
823       #ifndef _NO_CRYPTO
824       , getTextPassword, passwordIsDefined
825       #endif
826       #ifdef COMPRESS_MT
827       , false, 1
828       #endif
829       );
830     RINOK(result);
831 
832     if (folder.UnpackCRCDefined)
833       if (CrcCalc(data, unpackSize) != folder.UnpackCRC)
834         ThrowIncorrect();
835     for (int j = 0; j < folder.PackStreams.Size(); j++)
836     {
837       UInt64 packSize = packSizes[packIndex++];
838       dataStartPos += packSize;
839       HeadersSize += packSize;
840     }
841   }
842   return S_OK;
843 }
844 
ReadHeader(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)845 HRESULT CInArchive::ReadHeader(
846     DECL_EXTERNAL_CODECS_LOC_VARS
847     CArchiveDatabaseEx &db
848     #ifndef _NO_CRYPTO
849     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
850     #endif
851     )
852 {
853   UInt64 type = ReadID();
854 
855   if (type == NID::kArchiveProperties)
856   {
857     ReadArchiveProperties(db.ArchiveInfo);
858     type = ReadID();
859   }
860 
861   CObjectVector<CByteBuffer> dataVector;
862 
863   if (type == NID::kAdditionalStreamsInfo)
864   {
865     HRESULT result = ReadAndDecodePackedStreams(
866         EXTERNAL_CODECS_LOC_VARS
867         db.ArchiveInfo.StartPositionAfterHeader,
868         db.ArchiveInfo.DataStartPosition2,
869         dataVector
870         #ifndef _NO_CRYPTO
871         , getTextPassword, passwordIsDefined
872         #endif
873         );
874     RINOK(result);
875     db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
876     type = ReadID();
877   }
878 
879   CRecordVector<UInt64> unpackSizes;
880   CBoolVector digestsDefined;
881   CRecordVector<UInt32> digests;
882 
883   if (type == NID::kMainStreamsInfo)
884   {
885     ReadStreamsInfo(&dataVector,
886         db.ArchiveInfo.DataStartPosition,
887         db.PackSizes,
888         db.PackCRCsDefined,
889         db.PackCRCs,
890         db.Folders,
891         db.NumUnpackStreamsVector,
892         unpackSizes,
893         digestsDefined,
894         digests);
895     db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader;
896     type = ReadID();
897   }
898   else
899   {
900     for (int i = 0; i < db.Folders.Size(); i++)
901     {
902       db.NumUnpackStreamsVector.Add(1);
903       CFolder &folder = db.Folders[i];
904       unpackSizes.Add(folder.GetUnpackSize());
905       digestsDefined.Add(folder.UnpackCRCDefined);
906       digests.Add(folder.UnpackCRC);
907     }
908   }
909 
910   db.Files.Clear();
911 
912   if (type == NID::kEnd)
913     return S_OK;
914   if (type != NID::kFilesInfo)
915     ThrowIncorrect();
916 
917   CNum numFiles = ReadNum();
918   db.Files.Reserve(numFiles);
919   CNum i;
920   for (i = 0; i < numFiles; i++)
921     db.Files.Add(CFileItem());
922 
923   db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize);
924   if (!db.PackSizes.IsEmpty())
925     db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo);
926   if (numFiles > 0  && !digests.IsEmpty())
927     db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC);
928 
929   CBoolVector emptyStreamVector;
930   BoolVector_Fill_False(emptyStreamVector, (int)numFiles);
931   CBoolVector emptyFileVector;
932   CBoolVector antiFileVector;
933   CNum numEmptyStreams = 0;
934 
935   for (;;)
936   {
937     UInt64 type = ReadID();
938     if (type == NID::kEnd)
939       break;
940     UInt64 size = ReadNumber();
941     size_t ppp = _inByteBack->_pos;
942     bool addPropIdToList = true;
943     bool isKnownType = true;
944     if (type > ((UInt32)1 << 30))
945       isKnownType = false;
946     else switch((UInt32)type)
947     {
948       case NID::kName:
949       {
950         CStreamSwitch streamSwitch;
951         streamSwitch.Set(this, &dataVector);
952         for (int i = 0; i < db.Files.Size(); i++)
953           _inByteBack->ReadString(db.Files[i].Name);
954         break;
955       }
956       case NID::kWinAttributes:
957       {
958         CBoolVector boolVector;
959         ReadBoolVector2(db.Files.Size(), boolVector);
960         CStreamSwitch streamSwitch;
961         streamSwitch.Set(this, &dataVector);
962         for (i = 0; i < numFiles; i++)
963         {
964           CFileItem &file = db.Files[i];
965           file.AttribDefined = boolVector[i];
966           if (file.AttribDefined)
967             file.Attrib = ReadUInt32();
968         }
969         break;
970       }
971       case NID::kEmptyStream:
972       {
973         ReadBoolVector(numFiles, emptyStreamVector);
974         for (i = 0; i < (CNum)emptyStreamVector.Size(); i++)
975           if (emptyStreamVector[i])
976             numEmptyStreams++;
977 
978         BoolVector_Fill_False(emptyFileVector, numEmptyStreams);
979         BoolVector_Fill_False(antiFileVector, numEmptyStreams);
980 
981         break;
982       }
983       case NID::kEmptyFile:  ReadBoolVector(numEmptyStreams, emptyFileVector); break;
984       case NID::kAnti:  ReadBoolVector(numEmptyStreams, antiFileVector); break;
985       case NID::kStartPos:  ReadUInt64DefVector(dataVector, db.StartPos, (int)numFiles); break;
986       case NID::kCTime:  ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break;
987       case NID::kATime:  ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break;
988       case NID::kMTime:  ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break;
989       case NID::kDummy:
990       {
991         for (UInt64 j = 0; j < size; j++)
992           if (ReadByte() != 0)
993             ThrowIncorrect();
994         addPropIdToList = false;
995         break;
996       }
997       default:
998         addPropIdToList = isKnownType = false;
999     }
1000     if (isKnownType)
1001     {
1002       if(addPropIdToList)
1003         db.ArchiveInfo.FileInfoPopIDs.Add(type);
1004     }
1005     else
1006       SkipData(size);
1007     bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 ||
1008         db.ArchiveInfo.Version.Minor > 2);
1009     if (checkRecordsSize && _inByteBack->_pos - ppp != size)
1010       ThrowIncorrect();
1011   }
1012 
1013   CNum emptyFileIndex = 0;
1014   CNum sizeIndex = 0;
1015 
1016   CNum numAntiItems = 0;
1017   for (i = 0; i < numEmptyStreams; i++)
1018     if (antiFileVector[i])
1019       numAntiItems++;
1020 
1021   for (i = 0; i < numFiles; i++)
1022   {
1023     CFileItem &file = db.Files[i];
1024     bool isAnti;
1025     file.HasStream = !emptyStreamVector[i];
1026     if (file.HasStream)
1027     {
1028       file.IsDir = false;
1029       isAnti = false;
1030       file.Size = unpackSizes[sizeIndex];
1031       file.Crc = digests[sizeIndex];
1032       file.CrcDefined = digestsDefined[sizeIndex];
1033       sizeIndex++;
1034     }
1035     else
1036     {
1037       file.IsDir = !emptyFileVector[emptyFileIndex];
1038       isAnti = antiFileVector[emptyFileIndex];
1039       emptyFileIndex++;
1040       file.Size = 0;
1041       file.CrcDefined = false;
1042     }
1043     if (numAntiItems != 0)
1044       db.IsAnti.Add(isAnti);
1045   }
1046   return S_OK;
1047 }
1048 
1049 
FillFolderStartPackStream()1050 void CArchiveDatabaseEx::FillFolderStartPackStream()
1051 {
1052   FolderStartPackStreamIndex.Clear();
1053   FolderStartPackStreamIndex.Reserve(Folders.Size());
1054   CNum startPos = 0;
1055   for (int i = 0; i < Folders.Size(); i++)
1056   {
1057     FolderStartPackStreamIndex.Add(startPos);
1058     startPos += (CNum)Folders[i].PackStreams.Size();
1059   }
1060 }
1061 
FillStartPos()1062 void CArchiveDatabaseEx::FillStartPos()
1063 {
1064   PackStreamStartPositions.Clear();
1065   PackStreamStartPositions.Reserve(PackSizes.Size());
1066   UInt64 startPos = 0;
1067   for (int i = 0; i < PackSizes.Size(); i++)
1068   {
1069     PackStreamStartPositions.Add(startPos);
1070     startPos += PackSizes[i];
1071   }
1072 }
1073 
FillFolderStartFileIndex()1074 void CArchiveDatabaseEx::FillFolderStartFileIndex()
1075 {
1076   FolderStartFileIndex.Clear();
1077   FolderStartFileIndex.Reserve(Folders.Size());
1078   FileIndexToFolderIndexMap.Clear();
1079   FileIndexToFolderIndexMap.Reserve(Files.Size());
1080 
1081   int folderIndex = 0;
1082   CNum indexInFolder = 0;
1083   for (int i = 0; i < Files.Size(); i++)
1084   {
1085     const CFileItem &file = Files[i];
1086     bool emptyStream = !file.HasStream;
1087     if (emptyStream && indexInFolder == 0)
1088     {
1089       FileIndexToFolderIndexMap.Add(kNumNoIndex);
1090       continue;
1091     }
1092     if (indexInFolder == 0)
1093     {
1094       // v3.13 incorrectly worked with empty folders
1095       // v4.07: Loop for skipping empty folders
1096       for (;;)
1097       {
1098         if (folderIndex >= Folders.Size())
1099           ThrowIncorrect();
1100         FolderStartFileIndex.Add(i); // check it
1101         if (NumUnpackStreamsVector[folderIndex] != 0)
1102           break;
1103         folderIndex++;
1104       }
1105     }
1106     FileIndexToFolderIndexMap.Add(folderIndex);
1107     if (emptyStream)
1108       continue;
1109     indexInFolder++;
1110     if (indexInFolder >= NumUnpackStreamsVector[folderIndex])
1111     {
1112       folderIndex++;
1113       indexInFolder = 0;
1114     }
1115   }
1116 }
1117 
ReadDatabase2(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)1118 HRESULT CInArchive::ReadDatabase2(
1119     DECL_EXTERNAL_CODECS_LOC_VARS
1120     CArchiveDatabaseEx &db
1121     #ifndef _NO_CRYPTO
1122     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
1123     #endif
1124     )
1125 {
1126   db.Clear();
1127   db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;
1128 
1129   db.ArchiveInfo.Version.Major = _header[6];
1130   db.ArchiveInfo.Version.Minor = _header[7];
1131 
1132   if (db.ArchiveInfo.Version.Major != kMajorVersion)
1133     ThrowUnsupportedVersion();
1134 
1135   UInt32 crcFromArchive = Get32(_header + 8);
1136   UInt64 nextHeaderOffset = Get64(_header + 0xC);
1137   UInt64 nextHeaderSize = Get64(_header + 0x14);
1138   UInt32 nextHeaderCRC = Get32(_header + 0x1C);
1139   UInt32 crc = CrcCalc(_header + 0xC, 20);
1140 
1141   #ifdef FORMAT_7Z_RECOVERY
1142   if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)
1143   {
1144     UInt64 cur, cur2;
1145     RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));
1146     const int kCheckSize = 500;
1147     Byte buf[kCheckSize];
1148     RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2));
1149     int checkSize = kCheckSize;
1150     if (cur2 - cur < kCheckSize)
1151       checkSize = (int)(cur2 - cur);
1152     RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2));
1153 
1154     RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize));
1155 
1156     int i;
1157     for (i = (int)checkSize - 2; i >= 0; i--)
1158       if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)
1159         break;
1160     if (i < 0)
1161       return S_FALSE;
1162     nextHeaderSize = checkSize - i;
1163     nextHeaderOffset = cur2 - cur + i;
1164     nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);
1165     RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));
1166   }
1167   #endif
1168 
1169   #ifdef FORMAT_7Z_RECOVERY
1170   crcFromArchive = crc;
1171   #endif
1172 
1173   db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;
1174 
1175   if (crc != crcFromArchive)
1176     ThrowIncorrect();
1177 
1178   if (nextHeaderSize == 0)
1179     return S_OK;
1180 
1181   if (nextHeaderSize > (UInt64)0xFFFFFFFF)
1182     return S_FALSE;
1183 
1184   RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL));
1185 
1186   CByteBuffer buffer2;
1187   buffer2.SetCapacity((size_t)nextHeaderSize);
1188 
1189   RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize));
1190   HeadersSize += kHeaderSize + nextHeaderSize;
1191   db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize;
1192 
1193   if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC)
1194     ThrowIncorrect();
1195 
1196   CStreamSwitch streamSwitch;
1197   streamSwitch.Set(this, buffer2);
1198 
1199   CObjectVector<CByteBuffer> dataVector;
1200 
1201   UInt64 type = ReadID();
1202   if (type != NID::kHeader)
1203   {
1204     if (type != NID::kEncodedHeader)
1205       ThrowIncorrect();
1206     HRESULT result = ReadAndDecodePackedStreams(
1207         EXTERNAL_CODECS_LOC_VARS
1208         db.ArchiveInfo.StartPositionAfterHeader,
1209         db.ArchiveInfo.DataStartPosition2,
1210         dataVector
1211         #ifndef _NO_CRYPTO
1212         , getTextPassword, passwordIsDefined
1213         #endif
1214         );
1215     RINOK(result);
1216     if (dataVector.Size() == 0)
1217       return S_OK;
1218     if (dataVector.Size() > 1)
1219       ThrowIncorrect();
1220     streamSwitch.Remove();
1221     streamSwitch.Set(this, dataVector.Front());
1222     if (ReadID() != NID::kHeader)
1223       ThrowIncorrect();
1224   }
1225 
1226   db.HeadersSize = HeadersSize;
1227 
1228   return ReadHeader(
1229     EXTERNAL_CODECS_LOC_VARS
1230     db
1231     #ifndef _NO_CRYPTO
1232     , getTextPassword, passwordIsDefined
1233     #endif
1234     );
1235 }
1236 
ReadDatabase(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)1237 HRESULT CInArchive::ReadDatabase(
1238     DECL_EXTERNAL_CODECS_LOC_VARS
1239     CArchiveDatabaseEx &db
1240     #ifndef _NO_CRYPTO
1241     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
1242     #endif
1243     )
1244 {
1245   try
1246   {
1247     return ReadDatabase2(
1248       EXTERNAL_CODECS_LOC_VARS db
1249       #ifndef _NO_CRYPTO
1250       , getTextPassword, passwordIsDefined
1251       #endif
1252       );
1253   }
1254   catch(CInArchiveException &) { return S_FALSE; }
1255 }
1256 
1257 }}
1258