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 
TestSignature(const Byte * p)283 static inline bool TestSignature(const Byte *p)
284 {
285   for (int i = 0; i < kSignatureSize; i++)
286     if (p[i] != kSignature[i])
287       return false;
288   return CrcCalc(p + 12, 20) == GetUi32(p + 8);
289 }
290 
291 #ifdef FORMAT_7Z_RECOVERY
TestSignature2(const Byte * p)292 static inline bool TestSignature2(const Byte *p)
293 {
294   int i;
295   for (i = 0; i < kSignatureSize; i++)
296     if (p[i] != kSignature[i])
297       return false;
298   if (CrcCalc(p + 12, 20) == GetUi32(p + 8))
299     return true;
300   for (i = 8; i < kHeaderSize; i++)
301     if (p[i] != 0)
302       return false;
303   return (p[6] != 0 || p[7] != 0);
304 }
305 #else
306 #define TestSignature2(p) TestSignature(p)
307 #endif
308 
FindAndReadSignature(IInStream * stream,const UInt64 * searchHeaderSizeLimit)309 HRESULT CInArchive::FindAndReadSignature(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
310 {
311   RINOK(ReadStream_FALSE(stream, _header, kHeaderSize));
312 
313   if (TestSignature2(_header))
314     return S_OK;
315 
316   CByteBuffer byteBuffer;
317   const UInt32 kBufferSize = (1 << 16);
318   byteBuffer.SetCapacity(kBufferSize);
319   Byte *buffer = byteBuffer;
320   UInt32 numPrevBytes = kHeaderSize;
321   memcpy(buffer, _header, kHeaderSize);
322   UInt64 curTestPos = _arhiveBeginStreamPosition;
323   for (;;)
324   {
325     if (searchHeaderSizeLimit != NULL)
326       if (curTestPos - _arhiveBeginStreamPosition > *searchHeaderSizeLimit)
327         break;
328     do
329     {
330       UInt32 numReadBytes = kBufferSize - numPrevBytes;
331       UInt32 processedSize;
332       RINOK(stream->Read(buffer + numPrevBytes, numReadBytes, &processedSize));
333       numPrevBytes += processedSize;
334       if (processedSize == 0)
335         return S_FALSE;
336     }
337     while (numPrevBytes <= kHeaderSize);
338     UInt32 numTests = numPrevBytes - kHeaderSize;
339     for (UInt32 pos = 0; pos < numTests; pos++)
340     {
341       for (; buffer[pos] != '7' && pos < numTests; pos++);
342       if (pos == numTests)
343         break;
344       if (TestSignature(buffer + pos))
345       {
346         memcpy(_header, buffer + pos, kHeaderSize);
347         curTestPos += pos;
348         _arhiveBeginStreamPosition = curTestPos;
349         return stream->Seek(curTestPos + kHeaderSize, STREAM_SEEK_SET, NULL);
350       }
351     }
352     curTestPos += numTests;
353     numPrevBytes -= numTests;
354     memmove(buffer, buffer + numTests, numPrevBytes);
355   }
356   return S_FALSE;
357 }
358 
359 // S_FALSE means that file is not archive
Open(IInStream * stream,const UInt64 * searchHeaderSizeLimit)360 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit)
361 {
362   HeadersSize = 0;
363   Close();
364   RINOK(stream->Seek(0, STREAM_SEEK_CUR, &_arhiveBeginStreamPosition))
365   RINOK(FindAndReadSignature(stream, searchHeaderSizeLimit));
366   _stream = stream;
367   return S_OK;
368 }
369 
Close()370 void CInArchive::Close()
371 {
372   _stream.Release();
373 }
374 
ReadArchiveProperties(CInArchiveInfo &)375 void CInArchive::ReadArchiveProperties(CInArchiveInfo & /* archiveInfo */)
376 {
377   for (;;)
378   {
379     if (ReadID() == NID::kEnd)
380       break;
381     SkipData();
382   }
383 }
384 
GetNextFolderItem(CFolder & folder)385 void CInArchive::GetNextFolderItem(CFolder &folder)
386 {
387   CNum numCoders = ReadNum();
388 
389   folder.Coders.Clear();
390   folder.Coders.Reserve((int)numCoders);
391   CNum numInStreams = 0;
392   CNum numOutStreams = 0;
393   CNum i;
394   for (i = 0; i < numCoders; i++)
395   {
396     folder.Coders.Add(CCoderInfo());
397     CCoderInfo &coder = folder.Coders.Back();
398 
399     {
400       Byte mainByte = ReadByte();
401       int idSize = (mainByte & 0xF);
402       Byte longID[15];
403       ReadBytes(longID, idSize);
404       if (idSize > 8)
405         ThrowUnsupported();
406       UInt64 id = 0;
407       for (int j = 0; j < idSize; j++)
408         id |= (UInt64)longID[idSize - 1 - j] << (8 * j);
409       coder.MethodID = id;
410 
411       if ((mainByte & 0x10) != 0)
412       {
413         coder.NumInStreams = ReadNum();
414         coder.NumOutStreams = ReadNum();
415       }
416       else
417       {
418         coder.NumInStreams = 1;
419         coder.NumOutStreams = 1;
420       }
421       if ((mainByte & 0x20) != 0)
422       {
423         CNum propsSize = ReadNum();
424         coder.Props.SetCapacity((size_t)propsSize);
425         ReadBytes((Byte *)coder.Props, (size_t)propsSize);
426       }
427       if ((mainByte & 0x80) != 0)
428         ThrowUnsupported();
429     }
430     numInStreams += coder.NumInStreams;
431     numOutStreams += coder.NumOutStreams;
432   }
433 
434   CNum numBindPairs = numOutStreams - 1;
435   folder.BindPairs.Clear();
436   folder.BindPairs.Reserve(numBindPairs);
437   for (i = 0; i < numBindPairs; i++)
438   {
439     CBindPair bp;
440     bp.InIndex = ReadNum();
441     bp.OutIndex = ReadNum();
442     folder.BindPairs.Add(bp);
443   }
444 
445   if (numInStreams < numBindPairs)
446     ThrowUnsupported();
447   CNum numPackStreams = numInStreams - numBindPairs;
448   folder.PackStreams.Reserve(numPackStreams);
449   if (numPackStreams == 1)
450   {
451     for (i = 0; i < numInStreams; i++)
452       if (folder.FindBindPairForInStream(i) < 0)
453       {
454         folder.PackStreams.Add(i);
455         break;
456       }
457     if (folder.PackStreams.Size() != 1)
458       ThrowUnsupported();
459   }
460   else
461     for (i = 0; i < numPackStreams; i++)
462       folder.PackStreams.Add(ReadNum());
463 }
464 
WaitAttribute(UInt64 attribute)465 void CInArchive::WaitAttribute(UInt64 attribute)
466 {
467   for (;;)
468   {
469     UInt64 type = ReadID();
470     if (type == attribute)
471       return;
472     if (type == NID::kEnd)
473       ThrowIncorrect();
474     SkipData();
475   }
476 }
477 
ReadHashDigests(int numItems,CBoolVector & digestsDefined,CRecordVector<UInt32> & digests)478 void CInArchive::ReadHashDigests(int numItems,
479     CBoolVector &digestsDefined,
480     CRecordVector<UInt32> &digests)
481 {
482   ReadBoolVector2(numItems, digestsDefined);
483   digests.Clear();
484   digests.Reserve(numItems);
485   for (int i = 0; i < numItems; i++)
486   {
487     UInt32 crc = 0;
488     if (digestsDefined[i])
489       crc = ReadUInt32();
490     digests.Add(crc);
491   }
492 }
493 
ReadPackInfo(UInt64 & dataOffset,CRecordVector<UInt64> & packSizes,CBoolVector & packCRCsDefined,CRecordVector<UInt32> & packCRCs)494 void CInArchive::ReadPackInfo(
495     UInt64 &dataOffset,
496     CRecordVector<UInt64> &packSizes,
497     CBoolVector &packCRCsDefined,
498     CRecordVector<UInt32> &packCRCs)
499 {
500   dataOffset = ReadNumber();
501   CNum numPackStreams = ReadNum();
502 
503   WaitAttribute(NID::kSize);
504   packSizes.Clear();
505   packSizes.Reserve(numPackStreams);
506   for (CNum i = 0; i < numPackStreams; i++)
507     packSizes.Add(ReadNumber());
508 
509   UInt64 type;
510   for (;;)
511   {
512     type = ReadID();
513     if (type == NID::kEnd)
514       break;
515     if (type == NID::kCRC)
516     {
517       ReadHashDigests(numPackStreams, packCRCsDefined, packCRCs);
518       continue;
519     }
520     SkipData();
521   }
522   if (packCRCsDefined.IsEmpty())
523   {
524     BoolVector_Fill_False(packCRCsDefined, numPackStreams);
525     packCRCs.Reserve(numPackStreams);
526     packCRCs.Clear();
527     for (CNum i = 0; i < numPackStreams; i++)
528       packCRCs.Add(0);
529   }
530 }
531 
ReadUnpackInfo(const CObjectVector<CByteBuffer> * dataVector,CObjectVector<CFolder> & folders)532 void CInArchive::ReadUnpackInfo(
533     const CObjectVector<CByteBuffer> *dataVector,
534     CObjectVector<CFolder> &folders)
535 {
536   WaitAttribute(NID::kFolder);
537   CNum numFolders = ReadNum();
538 
539   {
540     CStreamSwitch streamSwitch;
541     streamSwitch.Set(this, dataVector);
542     folders.Clear();
543     folders.Reserve(numFolders);
544     for (CNum i = 0; i < numFolders; i++)
545     {
546       folders.Add(CFolder());
547       GetNextFolderItem(folders.Back());
548     }
549   }
550 
551   WaitAttribute(NID::kCodersUnpackSize);
552 
553   CNum i;
554   for (i = 0; i < numFolders; i++)
555   {
556     CFolder &folder = folders[i];
557     CNum numOutStreams = folder.GetNumOutStreams();
558     folder.UnpackSizes.Reserve(numOutStreams);
559     for (CNum j = 0; j < numOutStreams; j++)
560       folder.UnpackSizes.Add(ReadNumber());
561   }
562 
563   for (;;)
564   {
565     UInt64 type = ReadID();
566     if (type == NID::kEnd)
567       return;
568     if (type == NID::kCRC)
569     {
570       CBoolVector crcsDefined;
571       CRecordVector<UInt32> crcs;
572       ReadHashDigests(numFolders, crcsDefined, crcs);
573       for (i = 0; i < numFolders; i++)
574       {
575         CFolder &folder = folders[i];
576         folder.UnpackCRCDefined = crcsDefined[i];
577         folder.UnpackCRC = crcs[i];
578       }
579       continue;
580     }
581     SkipData();
582   }
583 }
584 
ReadSubStreamsInfo(const CObjectVector<CFolder> & folders,CRecordVector<CNum> & numUnpackStreamsInFolders,CRecordVector<UInt64> & unpackSizes,CBoolVector & digestsDefined,CRecordVector<UInt32> & digests)585 void CInArchive::ReadSubStreamsInfo(
586     const CObjectVector<CFolder> &folders,
587     CRecordVector<CNum> &numUnpackStreamsInFolders,
588     CRecordVector<UInt64> &unpackSizes,
589     CBoolVector &digestsDefined,
590     CRecordVector<UInt32> &digests)
591 {
592   numUnpackStreamsInFolders.Clear();
593   numUnpackStreamsInFolders.Reserve(folders.Size());
594   UInt64 type;
595   for (;;)
596   {
597     type = ReadID();
598     if (type == NID::kNumUnpackStream)
599     {
600       for (int i = 0; i < folders.Size(); i++)
601         numUnpackStreamsInFolders.Add(ReadNum());
602       continue;
603     }
604     if (type == NID::kCRC || type == NID::kSize)
605       break;
606     if (type == NID::kEnd)
607       break;
608     SkipData();
609   }
610 
611   if (numUnpackStreamsInFolders.IsEmpty())
612     for (int i = 0; i < folders.Size(); i++)
613       numUnpackStreamsInFolders.Add(1);
614 
615   int i;
616   for (i = 0; i < numUnpackStreamsInFolders.Size(); i++)
617   {
618     // v3.13 incorrectly worked with empty folders
619     // v4.07: we check that folder is empty
620     CNum numSubstreams = numUnpackStreamsInFolders[i];
621     if (numSubstreams == 0)
622       continue;
623     UInt64 sum = 0;
624     for (CNum j = 1; j < numSubstreams; j++)
625       if (type == NID::kSize)
626       {
627         UInt64 size = ReadNumber();
628         unpackSizes.Add(size);
629         sum += size;
630       }
631     unpackSizes.Add(folders[i].GetUnpackSize() - sum);
632   }
633   if (type == NID::kSize)
634     type = ReadID();
635 
636   int numDigests = 0;
637   int numDigestsTotal = 0;
638   for (i = 0; i < folders.Size(); i++)
639   {
640     CNum numSubstreams = numUnpackStreamsInFolders[i];
641     if (numSubstreams != 1 || !folders[i].UnpackCRCDefined)
642       numDigests += numSubstreams;
643     numDigestsTotal += numSubstreams;
644   }
645 
646   for (;;)
647   {
648     if (type == NID::kCRC)
649     {
650       CBoolVector digestsDefined2;
651       CRecordVector<UInt32> digests2;
652       ReadHashDigests(numDigests, digestsDefined2, digests2);
653       int digestIndex = 0;
654       for (i = 0; i < folders.Size(); i++)
655       {
656         CNum numSubstreams = numUnpackStreamsInFolders[i];
657         const CFolder &folder = folders[i];
658         if (numSubstreams == 1 && folder.UnpackCRCDefined)
659         {
660           digestsDefined.Add(true);
661           digests.Add(folder.UnpackCRC);
662         }
663         else
664           for (CNum j = 0; j < numSubstreams; j++, digestIndex++)
665           {
666             digestsDefined.Add(digestsDefined2[digestIndex]);
667             digests.Add(digests2[digestIndex]);
668           }
669       }
670     }
671     else if (type == NID::kEnd)
672     {
673       if (digestsDefined.IsEmpty())
674       {
675         BoolVector_Fill_False(digestsDefined, numDigestsTotal);
676         digests.Clear();
677         for (int i = 0; i < numDigestsTotal; i++)
678           digests.Add(0);
679       }
680       return;
681     }
682     else
683       SkipData();
684     type = ReadID();
685   }
686 }
687 
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)688 void CInArchive::ReadStreamsInfo(
689     const CObjectVector<CByteBuffer> *dataVector,
690     UInt64 &dataOffset,
691     CRecordVector<UInt64> &packSizes,
692     CBoolVector &packCRCsDefined,
693     CRecordVector<UInt32> &packCRCs,
694     CObjectVector<CFolder> &folders,
695     CRecordVector<CNum> &numUnpackStreamsInFolders,
696     CRecordVector<UInt64> &unpackSizes,
697     CBoolVector &digestsDefined,
698     CRecordVector<UInt32> &digests)
699 {
700   for (;;)
701   {
702     UInt64 type = ReadID();
703     if (type > ((UInt32)1 << 30))
704       ThrowIncorrect();
705     switch((UInt32)type)
706     {
707       case NID::kEnd:
708         return;
709       case NID::kPackInfo:
710       {
711         ReadPackInfo(dataOffset, packSizes, packCRCsDefined, packCRCs);
712         break;
713       }
714       case NID::kUnpackInfo:
715       {
716         ReadUnpackInfo(dataVector, folders);
717         break;
718       }
719       case NID::kSubStreamsInfo:
720       {
721         ReadSubStreamsInfo(folders, numUnpackStreamsInFolders,
722             unpackSizes, digestsDefined, digests);
723         break;
724       }
725       default:
726         ThrowIncorrect();
727     }
728   }
729 }
730 
ReadBoolVector(int numItems,CBoolVector & v)731 void CInArchive::ReadBoolVector(int numItems, CBoolVector &v)
732 {
733   v.Clear();
734   v.Reserve(numItems);
735   Byte b = 0;
736   Byte mask = 0;
737   for (int i = 0; i < numItems; i++)
738   {
739     if (mask == 0)
740     {
741       b = ReadByte();
742       mask = 0x80;
743     }
744     v.Add((b & mask) != 0);
745     mask >>= 1;
746   }
747 }
748 
ReadBoolVector2(int numItems,CBoolVector & v)749 void CInArchive::ReadBoolVector2(int numItems, CBoolVector &v)
750 {
751   Byte allAreDefined = ReadByte();
752   if (allAreDefined == 0)
753   {
754     ReadBoolVector(numItems, v);
755     return;
756   }
757   v.Clear();
758   v.Reserve(numItems);
759   for (int i = 0; i < numItems; i++)
760     v.Add(true);
761 }
762 
ReadUInt64DefVector(const CObjectVector<CByteBuffer> & dataVector,CUInt64DefVector & v,int numFiles)763 void CInArchive::ReadUInt64DefVector(const CObjectVector<CByteBuffer> &dataVector,
764     CUInt64DefVector &v, int numFiles)
765 {
766   ReadBoolVector2(numFiles, v.Defined);
767 
768   CStreamSwitch streamSwitch;
769   streamSwitch.Set(this, &dataVector);
770   v.Values.Reserve(numFiles);
771 
772   for (int i = 0; i < numFiles; i++)
773   {
774     UInt64 t = 0;
775     if (v.Defined[i])
776       t = ReadUInt64();
777     v.Values.Add(t);
778   }
779 }
780 
ReadAndDecodePackedStreams(DECL_EXTERNAL_CODECS_LOC_VARS UInt64 baseOffset,UInt64 & dataOffset,CObjectVector<CByteBuffer> & dataVector,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)781 HRESULT CInArchive::ReadAndDecodePackedStreams(
782     DECL_EXTERNAL_CODECS_LOC_VARS
783     UInt64 baseOffset,
784     UInt64 &dataOffset, CObjectVector<CByteBuffer> &dataVector
785     #ifndef _NO_CRYPTO
786     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
787     #endif
788     )
789 {
790   CRecordVector<UInt64> packSizes;
791   CBoolVector packCRCsDefined;
792   CRecordVector<UInt32> packCRCs;
793   CObjectVector<CFolder> folders;
794 
795   CRecordVector<CNum> numUnpackStreamsInFolders;
796   CRecordVector<UInt64> unpackSizes;
797   CBoolVector digestsDefined;
798   CRecordVector<UInt32> digests;
799 
800   ReadStreamsInfo(NULL,
801     dataOffset,
802     packSizes,
803     packCRCsDefined,
804     packCRCs,
805     folders,
806     numUnpackStreamsInFolders,
807     unpackSizes,
808     digestsDefined,
809     digests);
810 
811   // db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
812 
813   CNum packIndex = 0;
814   CDecoder decoder(
815     #ifdef _ST_MODE
816     false
817     #else
818     true
819     #endif
820     );
821   UInt64 dataStartPos = baseOffset + dataOffset;
822   for (int i = 0; i < folders.Size(); i++)
823   {
824     const CFolder &folder = folders[i];
825     dataVector.Add(CByteBuffer());
826     CByteBuffer &data = dataVector.Back();
827     UInt64 unpackSize64 = folder.GetUnpackSize();
828     size_t unpackSize = (size_t)unpackSize64;
829     if (unpackSize != unpackSize64)
830       ThrowUnsupported();
831     data.SetCapacity(unpackSize);
832 
833     CBufPtrSeqOutStream *outStreamSpec = new CBufPtrSeqOutStream;
834     CMyComPtr<ISequentialOutStream> outStream = outStreamSpec;
835     outStreamSpec->Init(data, unpackSize);
836 
837     HRESULT result = decoder.Decode(
838       EXTERNAL_CODECS_LOC_VARS
839       _stream, dataStartPos,
840       &packSizes[packIndex], folder, outStream, NULL
841       #ifndef _NO_CRYPTO
842       , getTextPassword, passwordIsDefined
843       #endif
844       #if !defined(_7ZIP_ST) && !defined(_SFX)
845       , false, 1
846       #endif
847       );
848     RINOK(result);
849 
850     if (folder.UnpackCRCDefined)
851       if (CrcCalc(data, unpackSize) != folder.UnpackCRC)
852         ThrowIncorrect();
853     for (int j = 0; j < folder.PackStreams.Size(); j++)
854     {
855       UInt64 packSize = packSizes[packIndex++];
856       dataStartPos += packSize;
857       HeadersSize += packSize;
858     }
859   }
860   return S_OK;
861 }
862 
ReadHeader(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)863 HRESULT CInArchive::ReadHeader(
864     DECL_EXTERNAL_CODECS_LOC_VARS
865     CArchiveDatabaseEx &db
866     #ifndef _NO_CRYPTO
867     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
868     #endif
869     )
870 {
871   UInt64 type = ReadID();
872 
873   if (type == NID::kArchiveProperties)
874   {
875     ReadArchiveProperties(db.ArchiveInfo);
876     type = ReadID();
877   }
878 
879   CObjectVector<CByteBuffer> dataVector;
880 
881   if (type == NID::kAdditionalStreamsInfo)
882   {
883     HRESULT result = ReadAndDecodePackedStreams(
884         EXTERNAL_CODECS_LOC_VARS
885         db.ArchiveInfo.StartPositionAfterHeader,
886         db.ArchiveInfo.DataStartPosition2,
887         dataVector
888         #ifndef _NO_CRYPTO
889         , getTextPassword, passwordIsDefined
890         #endif
891         );
892     RINOK(result);
893     db.ArchiveInfo.DataStartPosition2 += db.ArchiveInfo.StartPositionAfterHeader;
894     type = ReadID();
895   }
896 
897   CRecordVector<UInt64> unpackSizes;
898   CBoolVector digestsDefined;
899   CRecordVector<UInt32> digests;
900 
901   if (type == NID::kMainStreamsInfo)
902   {
903     ReadStreamsInfo(&dataVector,
904         db.ArchiveInfo.DataStartPosition,
905         db.PackSizes,
906         db.PackCRCsDefined,
907         db.PackCRCs,
908         db.Folders,
909         db.NumUnpackStreamsVector,
910         unpackSizes,
911         digestsDefined,
912         digests);
913     db.ArchiveInfo.DataStartPosition += db.ArchiveInfo.StartPositionAfterHeader;
914     type = ReadID();
915   }
916   else
917   {
918     for (int i = 0; i < db.Folders.Size(); i++)
919     {
920       db.NumUnpackStreamsVector.Add(1);
921       CFolder &folder = db.Folders[i];
922       unpackSizes.Add(folder.GetUnpackSize());
923       digestsDefined.Add(folder.UnpackCRCDefined);
924       digests.Add(folder.UnpackCRC);
925     }
926   }
927 
928   db.Files.Clear();
929 
930   if (type == NID::kEnd)
931     return S_OK;
932   if (type != NID::kFilesInfo)
933     ThrowIncorrect();
934 
935   CNum numFiles = ReadNum();
936   db.Files.Reserve(numFiles);
937   CNum i;
938   for (i = 0; i < numFiles; i++)
939     db.Files.Add(CFileItem());
940 
941   db.ArchiveInfo.FileInfoPopIDs.Add(NID::kSize);
942   if (!db.PackSizes.IsEmpty())
943     db.ArchiveInfo.FileInfoPopIDs.Add(NID::kPackInfo);
944   if (numFiles > 0  && !digests.IsEmpty())
945     db.ArchiveInfo.FileInfoPopIDs.Add(NID::kCRC);
946 
947   CBoolVector emptyStreamVector;
948   BoolVector_Fill_False(emptyStreamVector, (int)numFiles);
949   CBoolVector emptyFileVector;
950   CBoolVector antiFileVector;
951   CNum numEmptyStreams = 0;
952 
953   for (;;)
954   {
955     UInt64 type = ReadID();
956     if (type == NID::kEnd)
957       break;
958     UInt64 size = ReadNumber();
959     size_t ppp = _inByteBack->_pos;
960     bool addPropIdToList = true;
961     bool isKnownType = true;
962     if (type > ((UInt32)1 << 30))
963       isKnownType = false;
964     else switch((UInt32)type)
965     {
966       case NID::kName:
967       {
968         CStreamSwitch streamSwitch;
969         streamSwitch.Set(this, &dataVector);
970         for (int i = 0; i < db.Files.Size(); i++)
971           _inByteBack->ReadString(db.Files[i].Name);
972         break;
973       }
974       case NID::kWinAttributes:
975       {
976         CBoolVector boolVector;
977         ReadBoolVector2(db.Files.Size(), boolVector);
978         CStreamSwitch streamSwitch;
979         streamSwitch.Set(this, &dataVector);
980         for (i = 0; i < numFiles; i++)
981         {
982           CFileItem &file = db.Files[i];
983           file.AttribDefined = boolVector[i];
984           if (file.AttribDefined)
985             file.Attrib = ReadUInt32();
986         }
987         break;
988       }
989       case NID::kEmptyStream:
990       {
991         ReadBoolVector(numFiles, emptyStreamVector);
992         for (i = 0; i < (CNum)emptyStreamVector.Size(); i++)
993           if (emptyStreamVector[i])
994             numEmptyStreams++;
995 
996         BoolVector_Fill_False(emptyFileVector, numEmptyStreams);
997         BoolVector_Fill_False(antiFileVector, numEmptyStreams);
998 
999         break;
1000       }
1001       case NID::kEmptyFile:  ReadBoolVector(numEmptyStreams, emptyFileVector); break;
1002       case NID::kAnti:  ReadBoolVector(numEmptyStreams, antiFileVector); break;
1003       case NID::kStartPos:  ReadUInt64DefVector(dataVector, db.StartPos, (int)numFiles); break;
1004       case NID::kCTime:  ReadUInt64DefVector(dataVector, db.CTime, (int)numFiles); break;
1005       case NID::kATime:  ReadUInt64DefVector(dataVector, db.ATime, (int)numFiles); break;
1006       case NID::kMTime:  ReadUInt64DefVector(dataVector, db.MTime, (int)numFiles); break;
1007       case NID::kDummy:
1008       {
1009         for (UInt64 j = 0; j < size; j++)
1010           if (ReadByte() != 0)
1011             ThrowIncorrect();
1012         addPropIdToList = false;
1013         break;
1014       }
1015       default:
1016         addPropIdToList = isKnownType = false;
1017     }
1018     if (isKnownType)
1019     {
1020       if(addPropIdToList)
1021         db.ArchiveInfo.FileInfoPopIDs.Add(type);
1022     }
1023     else
1024       SkipData(size);
1025     bool checkRecordsSize = (db.ArchiveInfo.Version.Major > 0 ||
1026         db.ArchiveInfo.Version.Minor > 2);
1027     if (checkRecordsSize && _inByteBack->_pos - ppp != size)
1028       ThrowIncorrect();
1029   }
1030 
1031   CNum emptyFileIndex = 0;
1032   CNum sizeIndex = 0;
1033 
1034   CNum numAntiItems = 0;
1035   for (i = 0; i < numEmptyStreams; i++)
1036     if (antiFileVector[i])
1037       numAntiItems++;
1038 
1039   for (i = 0; i < numFiles; i++)
1040   {
1041     CFileItem &file = db.Files[i];
1042     bool isAnti;
1043     file.HasStream = !emptyStreamVector[i];
1044     if (file.HasStream)
1045     {
1046       file.IsDir = false;
1047       isAnti = false;
1048       file.Size = unpackSizes[sizeIndex];
1049       file.Crc = digests[sizeIndex];
1050       file.CrcDefined = digestsDefined[sizeIndex];
1051       sizeIndex++;
1052     }
1053     else
1054     {
1055       file.IsDir = !emptyFileVector[emptyFileIndex];
1056       isAnti = antiFileVector[emptyFileIndex];
1057       emptyFileIndex++;
1058       file.Size = 0;
1059       file.CrcDefined = false;
1060     }
1061     if (numAntiItems != 0)
1062       db.IsAnti.Add(isAnti);
1063   }
1064   return S_OK;
1065 }
1066 
1067 
FillFolderStartPackStream()1068 void CArchiveDatabaseEx::FillFolderStartPackStream()
1069 {
1070   FolderStartPackStreamIndex.Clear();
1071   FolderStartPackStreamIndex.Reserve(Folders.Size());
1072   CNum startPos = 0;
1073   for (int i = 0; i < Folders.Size(); i++)
1074   {
1075     FolderStartPackStreamIndex.Add(startPos);
1076     startPos += (CNum)Folders[i].PackStreams.Size();
1077   }
1078 }
1079 
FillStartPos()1080 void CArchiveDatabaseEx::FillStartPos()
1081 {
1082   PackStreamStartPositions.Clear();
1083   PackStreamStartPositions.Reserve(PackSizes.Size());
1084   UInt64 startPos = 0;
1085   for (int i = 0; i < PackSizes.Size(); i++)
1086   {
1087     PackStreamStartPositions.Add(startPos);
1088     startPos += PackSizes[i];
1089   }
1090 }
1091 
FillFolderStartFileIndex()1092 void CArchiveDatabaseEx::FillFolderStartFileIndex()
1093 {
1094   FolderStartFileIndex.Clear();
1095   FolderStartFileIndex.Reserve(Folders.Size());
1096   FileIndexToFolderIndexMap.Clear();
1097   FileIndexToFolderIndexMap.Reserve(Files.Size());
1098 
1099   int folderIndex = 0;
1100   CNum indexInFolder = 0;
1101   for (int i = 0; i < Files.Size(); i++)
1102   {
1103     const CFileItem &file = Files[i];
1104     bool emptyStream = !file.HasStream;
1105     if (emptyStream && indexInFolder == 0)
1106     {
1107       FileIndexToFolderIndexMap.Add(kNumNoIndex);
1108       continue;
1109     }
1110     if (indexInFolder == 0)
1111     {
1112       // v3.13 incorrectly worked with empty folders
1113       // v4.07: Loop for skipping empty folders
1114       for (;;)
1115       {
1116         if (folderIndex >= Folders.Size())
1117           ThrowIncorrect();
1118         FolderStartFileIndex.Add(i); // check it
1119         if (NumUnpackStreamsVector[folderIndex] != 0)
1120           break;
1121         folderIndex++;
1122       }
1123     }
1124     FileIndexToFolderIndexMap.Add(folderIndex);
1125     if (emptyStream)
1126       continue;
1127     indexInFolder++;
1128     if (indexInFolder >= NumUnpackStreamsVector[folderIndex])
1129     {
1130       folderIndex++;
1131       indexInFolder = 0;
1132     }
1133   }
1134 }
1135 
ReadDatabase2(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)1136 HRESULT CInArchive::ReadDatabase2(
1137     DECL_EXTERNAL_CODECS_LOC_VARS
1138     CArchiveDatabaseEx &db
1139     #ifndef _NO_CRYPTO
1140     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
1141     #endif
1142     )
1143 {
1144   db.Clear();
1145   db.ArchiveInfo.StartPosition = _arhiveBeginStreamPosition;
1146 
1147   db.ArchiveInfo.Version.Major = _header[6];
1148   db.ArchiveInfo.Version.Minor = _header[7];
1149 
1150   if (db.ArchiveInfo.Version.Major != kMajorVersion)
1151     ThrowUnsupportedVersion();
1152 
1153   UInt32 crcFromArchive = Get32(_header + 8);
1154   UInt64 nextHeaderOffset = Get64(_header + 0xC);
1155   UInt64 nextHeaderSize = Get64(_header + 0x14);
1156   UInt32 nextHeaderCRC = Get32(_header + 0x1C);
1157   UInt32 crc = CrcCalc(_header + 0xC, 20);
1158 
1159   #ifdef FORMAT_7Z_RECOVERY
1160   if (crcFromArchive == 0 && nextHeaderOffset == 0 && nextHeaderSize == 0 && nextHeaderCRC == 0)
1161   {
1162     UInt64 cur, cur2;
1163     RINOK(_stream->Seek(0, STREAM_SEEK_CUR, &cur));
1164     const int kCheckSize = 500;
1165     Byte buf[kCheckSize];
1166     RINOK(_stream->Seek(0, STREAM_SEEK_END, &cur2));
1167     int checkSize = kCheckSize;
1168     if (cur2 - cur < kCheckSize)
1169       checkSize = (int)(cur2 - cur);
1170     RINOK(_stream->Seek(-checkSize, STREAM_SEEK_END, &cur2));
1171 
1172     RINOK(ReadStream_FALSE(_stream, buf, (size_t)checkSize));
1173 
1174     int i;
1175     for (i = (int)checkSize - 2; i >= 0; i--)
1176       if (buf[i] == 0x17 && buf[i + 1] == 0x6 || buf[i] == 0x01 && buf[i + 1] == 0x04)
1177         break;
1178     if (i < 0)
1179       return S_FALSE;
1180     nextHeaderSize = checkSize - i;
1181     nextHeaderOffset = cur2 - cur + i;
1182     nextHeaderCRC = CrcCalc(buf + i, (size_t)nextHeaderSize);
1183     RINOK(_stream->Seek(cur, STREAM_SEEK_SET, NULL));
1184   }
1185   else
1186   #endif
1187   {
1188     if (crc != crcFromArchive)
1189       ThrowIncorrect();
1190   }
1191 
1192   db.ArchiveInfo.StartPositionAfterHeader = _arhiveBeginStreamPosition + kHeaderSize;
1193 
1194   if (nextHeaderSize == 0)
1195     return S_OK;
1196 
1197   if (nextHeaderSize > (UInt64)0xFFFFFFFF)
1198     return S_FALSE;
1199 
1200   if ((Int64)nextHeaderOffset < 0)
1201     return S_FALSE;
1202 
1203   RINOK(_stream->Seek(nextHeaderOffset, STREAM_SEEK_CUR, NULL));
1204 
1205   CByteBuffer buffer2;
1206   buffer2.SetCapacity((size_t)nextHeaderSize);
1207 
1208   RINOK(ReadStream_FALSE(_stream, buffer2, (size_t)nextHeaderSize));
1209   HeadersSize += kHeaderSize + nextHeaderSize;
1210   db.PhySize = kHeaderSize + nextHeaderOffset + nextHeaderSize;
1211 
1212   if (CrcCalc(buffer2, (UInt32)nextHeaderSize) != nextHeaderCRC)
1213     ThrowIncorrect();
1214 
1215   CStreamSwitch streamSwitch;
1216   streamSwitch.Set(this, buffer2);
1217 
1218   CObjectVector<CByteBuffer> dataVector;
1219 
1220   UInt64 type = ReadID();
1221   if (type != NID::kHeader)
1222   {
1223     if (type != NID::kEncodedHeader)
1224       ThrowIncorrect();
1225     HRESULT result = ReadAndDecodePackedStreams(
1226         EXTERNAL_CODECS_LOC_VARS
1227         db.ArchiveInfo.StartPositionAfterHeader,
1228         db.ArchiveInfo.DataStartPosition2,
1229         dataVector
1230         #ifndef _NO_CRYPTO
1231         , getTextPassword, passwordIsDefined
1232         #endif
1233         );
1234     RINOK(result);
1235     if (dataVector.Size() == 0)
1236       return S_OK;
1237     if (dataVector.Size() > 1)
1238       ThrowIncorrect();
1239     streamSwitch.Remove();
1240     streamSwitch.Set(this, dataVector.Front());
1241     if (ReadID() != NID::kHeader)
1242       ThrowIncorrect();
1243   }
1244 
1245   db.HeadersSize = HeadersSize;
1246 
1247   return ReadHeader(
1248     EXTERNAL_CODECS_LOC_VARS
1249     db
1250     #ifndef _NO_CRYPTO
1251     , getTextPassword, passwordIsDefined
1252     #endif
1253     );
1254 }
1255 
ReadDatabase(DECL_EXTERNAL_CODECS_LOC_VARS CArchiveDatabaseEx & db,ICryptoGetTextPassword * getTextPassword,bool & passwordIsDefined)1256 HRESULT CInArchive::ReadDatabase(
1257     DECL_EXTERNAL_CODECS_LOC_VARS
1258     CArchiveDatabaseEx &db
1259     #ifndef _NO_CRYPTO
1260     , ICryptoGetTextPassword *getTextPassword, bool &passwordIsDefined
1261     #endif
1262     )
1263 {
1264   try
1265   {
1266     return ReadDatabase2(
1267       EXTERNAL_CODECS_LOC_VARS db
1268       #ifndef _NO_CRYPTO
1269       , getTextPassword, passwordIsDefined
1270       #endif
1271       );
1272   }
1273   catch(CInArchiveException &) { return S_FALSE; }
1274 }
1275 
1276 }}
1277