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