1 // Rar5Handler.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/7zCrc.h"
6 #include "../../../../C/CpuArch.h"
7
8 #include "../../../Common/ComTry.h"
9 #include "../../../Common/IntToString.h"
10 #include "../../../Common/MyBuffer2.h"
11 #include "../../../Common/UTFConvert.h"
12
13 #include "../../../Windows/PropVariantUtils.h"
14 #include "../../../Windows/TimeUtils.h"
15
16 #include "../../IPassword.h"
17
18 #include "../../Common/FilterCoder.h"
19 #include "../../Common/LimitedStreams.h"
20 #include "../../Common/MethodProps.h"
21 #include "../../Common/ProgressUtils.h"
22 #include "../../Common/RegisterArc.h"
23 #include "../../Common/StreamObjects.h"
24 #include "../../Common/StreamUtils.h"
25
26 #include "../../Common/RegisterCodec.h"
27
28 #include "../../Compress/CopyCoder.h"
29
30 #include "../../Crypto/Rar5Aes.h"
31
32 #include "../../Archive/Common/FindSignature.h"
33 #include "../../Archive/Common/ItemNameUtils.h"
34 #include "../../Archive/Common/HandlerOut.h"
35
36 #include "../../Archive/HandlerCont.h"
37
38 #include "../../Archive/Rar/RarVol.h"
39 #include "Rar5Handler.h"
40
41 using namespace NWindows;
42
43 #define Get32(p) GetUi32(p)
44
45 namespace NArchive {
46 namespace NRar5 {
47
48 static const unsigned kMarkerSize = 8;
49
50 static const Byte kMarker[kMarkerSize] =
51 { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0 };
52
53 // Comment length is limited to 256 KB in rar-encoder.
54 // So we use same limitation
55 static const size_t kCommentSize_Max = (size_t)1 << 18;
56
57
58 static const char * const kHostOS[] =
59 {
60 "Windows"
61 , "Unix"
62 };
63
64
65 static const char * const k_ArcFlags[] =
66 {
67 "Volume"
68 , "VolumeField"
69 , "Solid"
70 , "Recovery"
71 , "Lock" // 4
72 };
73
74
75 static const char * const k_FileFlags[] =
76 {
77 "Dir"
78 , "UnixTime"
79 , "CRC"
80 , "UnknownSize"
81 };
82
83
84 static const char * const g_ExtraTypes[] =
85 {
86 "0"
87 , "Crypto"
88 , "Hash"
89 , "Time"
90 , "Version"
91 , "Link"
92 , "UnixOwner"
93 , "Subdata"
94 };
95
96
97 static const char * const g_LinkTypes[] =
98 {
99 "0"
100 , "UnixSymLink"
101 , "WinSymLink"
102 , "WinJunction"
103 , "HardLink"
104 , "FileCopy"
105 };
106
107
108 static const char g_ExtraTimeFlags[] = { 'u', 'M', 'C', 'A', 'n' };
109
110
111 static
112 Z7_NO_INLINE
ReadVarInt(const Byte * p,size_t maxSize,UInt64 * val_ptr)113 unsigned ReadVarInt(const Byte *p, size_t maxSize, UInt64 *val_ptr)
114 {
115 if (maxSize > 10)
116 maxSize = 10;
117 UInt64 val = 0;
118 unsigned i;
119 for (i = 0; i < maxSize;)
120 {
121 const unsigned b = p[i];
122 val |= (UInt64)(b & 0x7F) << (7 * i);
123 i++;
124 if ((b & 0x80) == 0)
125 {
126 *val_ptr = val;
127 return i;
128 }
129 }
130 *val_ptr = 0;
131 #if 1
132 return 0; // 7zip-unrar : strict check of error
133 #else
134 return i; // original-unrar : ignore error
135 #endif
136 }
137
138
139 #define PARSE_VAR_INT(p, size, dest) \
140 { const unsigned num_ = ReadVarInt(p, size, &dest); \
141 if (num_ == 0) return false; \
142 p += num_; \
143 size -= num_; \
144 }
145
146
Parse(const Byte * p,unsigned size)147 bool CLinkInfo::Parse(const Byte *p, unsigned size)
148 {
149 const Byte *pStart = p;
150 UInt64 len;
151 PARSE_VAR_INT(p, size, Type)
152 PARSE_VAR_INT(p, size, Flags)
153 PARSE_VAR_INT(p, size, len)
154 if (size != len)
155 return false;
156 NameLen = (unsigned)len;
157 NameOffset = (unsigned)(size_t)(p - pStart);
158 return true;
159 }
160
161
AddHex64(AString & s,UInt64 v)162 static void AddHex64(AString &s, UInt64 v)
163 {
164 char sz[32];
165 sz[0] = '0';
166 sz[1] = 'x';
167 ConvertUInt64ToHex(v, sz + 2);
168 s += sz;
169 }
170
171
PrintType(AString & s,const char * const table[],unsigned num,UInt64 val)172 static void PrintType(AString &s, const char * const table[], unsigned num, UInt64 val)
173 {
174 char sz[32];
175 const char *p = NULL;
176 if (val < num)
177 p = table[(unsigned)val];
178 if (!p)
179 {
180 ConvertUInt64ToString(val, sz);
181 p = sz;
182 }
183 s += p;
184 }
185
186
FindExtra(unsigned extraID,unsigned & recordDataSize) const187 int CItem::FindExtra(unsigned extraID, unsigned &recordDataSize) const
188 {
189 recordDataSize = 0;
190 size_t offset = 0;
191
192 for (;;)
193 {
194 size_t rem = Extra.Size() - offset;
195 if (rem == 0)
196 return -1;
197
198 {
199 UInt64 size;
200 const unsigned num = ReadVarInt(Extra + offset, rem, &size);
201 if (num == 0)
202 return -1;
203 offset += num;
204 rem -= num;
205 if (size > rem)
206 return -1;
207 rem = (size_t)size;
208 }
209 {
210 UInt64 id;
211 const unsigned num = ReadVarInt(Extra + offset, rem, &id);
212 if (num == 0)
213 return -1;
214 offset += num;
215 rem -= num;
216
217 // There was BUG in RAR 5.21- : it stored (size-1) instead of (size)
218 // for Subdata record in Service header.
219 // That record always was last in bad archives, so we can fix that case.
220 if (id == NExtraID::kSubdata
221 && RecordType == NHeaderType::kService
222 && rem + 1 == Extra.Size() - offset)
223 rem++;
224
225 if (id == extraID)
226 {
227 recordDataSize = (unsigned)rem;
228 return (int)offset;
229 }
230
231 offset += rem;
232 }
233 }
234 }
235
236
PrintInfo(AString & s) const237 void CItem::PrintInfo(AString &s) const
238 {
239 size_t offset = 0;
240
241 for (;;)
242 {
243 size_t rem = Extra.Size() - offset;
244 if (rem == 0)
245 return;
246
247 {
248 UInt64 size;
249 unsigned num = ReadVarInt(Extra + offset, rem, &size);
250 if (num == 0)
251 return;
252 offset += num;
253 rem -= num;
254 if (size > rem)
255 break;
256 rem = (size_t)size;
257 }
258 {
259 UInt64 id;
260 {
261 unsigned num = ReadVarInt(Extra + offset, rem, &id);
262 if (num == 0)
263 break;
264 offset += num;
265 rem -= num;
266 }
267
268 // There was BUG in RAR 5.21- : it stored (size-1) instead of (size)
269 // for Subdata record in Service header.
270 // That record always was last in bad archives, so we can fix that case.
271 if (id == NExtraID::kSubdata
272 && RecordType == NHeaderType::kService
273 && rem + 1 == Extra.Size() - offset)
274 rem++;
275
276 s.Add_Space_if_NotEmpty();
277 PrintType(s, g_ExtraTypes, Z7_ARRAY_SIZE(g_ExtraTypes), id);
278
279 if (id == NExtraID::kTime)
280 {
281 const Byte *p = Extra + offset;
282 UInt64 flags;
283 const unsigned num = ReadVarInt(p, rem, &flags);
284 if (num != 0)
285 {
286 s.Add_Colon();
287 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_ExtraTimeFlags); i++)
288 if ((flags & ((UInt64)1 << i)) != 0)
289 s.Add_Char(g_ExtraTimeFlags[i]);
290 flags &= ~(((UInt64)1 << Z7_ARRAY_SIZE(g_ExtraTimeFlags)) - 1);
291 if (flags != 0)
292 {
293 s.Add_Char('_');
294 AddHex64(s, flags);
295 }
296 }
297 }
298 else if (id == NExtraID::kLink)
299 {
300 CLinkInfo linkInfo;
301 if (linkInfo.Parse(Extra + offset, (unsigned)rem))
302 {
303 s.Add_Colon();
304 PrintType(s, g_LinkTypes, Z7_ARRAY_SIZE(g_LinkTypes), linkInfo.Type);
305 UInt64 flags = linkInfo.Flags;
306 if (flags != 0)
307 {
308 s.Add_Colon();
309 if (flags & NLinkFlags::kTargetIsDir)
310 {
311 s.Add_Char('D');
312 flags &= ~((UInt64)NLinkFlags::kTargetIsDir);
313 }
314 if (flags != 0)
315 {
316 s.Add_Char('_');
317 AddHex64(s, flags);
318 }
319 }
320 }
321 }
322
323 offset += rem;
324 }
325 }
326
327 s.Add_OptSpaced("ERROR");
328 }
329
330
Parse(const Byte * p,size_t size)331 bool CCryptoInfo::Parse(const Byte *p, size_t size)
332 {
333 Algo = 0;
334 Flags = 0;
335 Cnt = 0;
336 PARSE_VAR_INT(p, size, Algo)
337 PARSE_VAR_INT(p, size, Flags)
338 if (size > 0)
339 Cnt = p[0];
340 if (size != 1 + 16 + 16 + (unsigned)(IsThereCheck() ? 12 : 0))
341 return false;
342 return true;
343 }
344
345
FindExtra_Version(UInt64 & version) const346 bool CItem::FindExtra_Version(UInt64 &version) const
347 {
348 unsigned size;
349 const int offset = FindExtra(NExtraID::kVersion, size);
350 if (offset < 0)
351 return false;
352 const Byte *p = Extra + (unsigned)offset;
353
354 UInt64 flags;
355 PARSE_VAR_INT(p, size, flags)
356 PARSE_VAR_INT(p, size, version)
357 return size == 0;
358 }
359
FindExtra_Link(CLinkInfo & link) const360 bool CItem::FindExtra_Link(CLinkInfo &link) const
361 {
362 unsigned size;
363 const int offset = FindExtra(NExtraID::kLink, size);
364 if (offset < 0)
365 return false;
366 if (!link.Parse(Extra + (unsigned)offset, size))
367 return false;
368 link.NameOffset += (unsigned)offset;
369 return true;
370 }
371
Is_CopyLink() const372 bool CItem::Is_CopyLink() const
373 {
374 CLinkInfo link;
375 return FindExtra_Link(link) && link.Type == NLinkType::kFileCopy;
376 }
377
Is_HardLink() const378 bool CItem::Is_HardLink() const
379 {
380 CLinkInfo link;
381 return FindExtra_Link(link) && link.Type == NLinkType::kHardLink;
382 }
383
Is_CopyLink_or_HardLink() const384 bool CItem::Is_CopyLink_or_HardLink() const
385 {
386 CLinkInfo link;
387 return FindExtra_Link(link) && (link.Type == NLinkType::kFileCopy || link.Type == NLinkType::kHardLink);
388 }
389
Link_to_Prop(unsigned linkType,NWindows::NCOM::CPropVariant & prop) const390 void CItem::Link_to_Prop(unsigned linkType, NWindows::NCOM::CPropVariant &prop) const
391 {
392 CLinkInfo link;
393 if (!FindExtra_Link(link))
394 return;
395
396 if (link.Type != linkType)
397 {
398 if (linkType != NLinkType::kUnixSymLink)
399 return;
400 switch ((unsigned)link.Type)
401 {
402 case NLinkType::kUnixSymLink:
403 case NLinkType::kWinSymLink:
404 case NLinkType::kWinJunction:
405 break;
406 default: return;
407 }
408 }
409
410 AString s;
411 s.SetFrom_CalcLen((const char *)(Extra + link.NameOffset), link.NameLen);
412
413 UString unicode;
414 ConvertUTF8ToUnicode(s, unicode);
415 prop = NItemName::GetOsPath(unicode);
416 }
417
GetAltStreamName(AString & name) const418 bool CItem::GetAltStreamName(AString &name) const
419 {
420 name.Empty();
421 unsigned size;
422 const int offset = FindExtra(NExtraID::kSubdata, size);
423 if (offset < 0)
424 return false;
425 name.SetFrom_CalcLen((const char *)(Extra + (unsigned)offset), size);
426 return true;
427 }
428
429
430 class CHash
431 {
432 bool _calcCRC;
433 UInt32 _crc;
434 int _blakeOffset;
435 CAlignedBuffer1 _buf;
436 // CBlake2sp _blake;
BlakeObj()437 CBlake2sp *BlakeObj() { return (CBlake2sp *)(void *)(Byte *)_buf; }
438 public:
CHash()439 CHash():
440 _buf(sizeof(CBlake2sp))
441 {}
442
Init_NoCalc()443 void Init_NoCalc()
444 {
445 _calcCRC = false;
446 _crc = CRC_INIT_VAL;
447 _blakeOffset = -1;
448 }
449
450 void Init(const CItem &item);
451 void Update(const void *data, size_t size);
GetCRC() const452 UInt32 GetCRC() const { return CRC_GET_DIGEST(_crc); }
453
454 bool Check(const CItem &item, NCrypto::NRar5::CDecoder *cryptoDecoder);
455 };
456
Init(const CItem & item)457 void CHash::Init(const CItem &item)
458 {
459 _crc = CRC_INIT_VAL;
460 _calcCRC = item.Has_CRC();
461 _blakeOffset = item.FindExtra_Blake();
462 if (_blakeOffset >= 0)
463 Blake2sp_Init(BlakeObj());
464 }
465
Update(const void * data,size_t size)466 void CHash::Update(const void *data, size_t size)
467 {
468 if (_calcCRC)
469 _crc = CrcUpdate(_crc, data, size);
470 if (_blakeOffset >= 0)
471 Blake2sp_Update(BlakeObj(), (const Byte *)data, size);
472 }
473
Check(const CItem & item,NCrypto::NRar5::CDecoder * cryptoDecoder)474 bool CHash::Check(const CItem &item, NCrypto::NRar5::CDecoder *cryptoDecoder)
475 {
476 if (_calcCRC)
477 {
478 UInt32 crc = GetCRC();
479 if (cryptoDecoder)
480 crc = cryptoDecoder->Hmac_Convert_Crc32(crc);
481 if (crc != item.CRC)
482 return false;
483 }
484 if (_blakeOffset >= 0)
485 {
486 UInt32 digest[Z7_BLAKE2S_DIGEST_SIZE / sizeof(UInt32)];
487 Blake2sp_Final(BlakeObj(), (Byte *)(void *)digest);
488 if (cryptoDecoder)
489 cryptoDecoder->Hmac_Convert_32Bytes((Byte *)(void *)digest);
490 if (memcmp(digest, item.Extra + (unsigned)_blakeOffset, Z7_BLAKE2S_DIGEST_SIZE) != 0)
491 return false;
492 }
493 return true;
494 }
495
496
497 Z7_CLASS_IMP_NOQIB_1(
498 COutStreamWithHash
499 , ISequentialOutStream
500 )
501 bool _size_Defined;
502 ISequentialOutStream *_stream;
503 UInt64 _pos;
504 UInt64 _size;
505 Byte *_destBuf;
506 public:
507 CHash _hash;
508
509 COutStreamWithHash(): _destBuf(NULL) {}
510
511 void SetStream(ISequentialOutStream *stream) { _stream = stream; }
512 void Init(const CItem &item, Byte *destBuf, bool needChecksumCheck)
513 {
514 _size_Defined = false;
515 _size = 0;
516 _destBuf = NULL;
517 if (!item.Is_UnknownSize())
518 {
519 _size_Defined = true;
520 _size = item.Size;
521 _destBuf = destBuf;
522 }
523 _pos = 0;
524 if (needChecksumCheck)
525 _hash.Init(item);
526 else
527 _hash.Init_NoCalc();
528 }
529 UInt64 GetPos() const { return _pos; }
530 };
531
532
533 Z7_COM7F_IMF(COutStreamWithHash::Write(const void *data, UInt32 size, UInt32 *processedSize))
534 {
535 HRESULT result = S_OK;
536 if (_size_Defined)
537 {
538 const UInt64 rem = _size - _pos;
539 if (size > rem)
540 size = (UInt32)rem;
541 }
542 if (_stream)
543 result = _stream->Write(data, size, &size);
544 if (_destBuf)
545 memcpy(_destBuf + (size_t)_pos, data, size);
546 _hash.Update(data, size);
547 _pos += size;
548 if (processedSize)
549 *processedSize = size;
550 return result;
551 }
552
553
554
555
556
557 class CInArchive
558 {
559 CAlignedBuffer _buf;
560 size_t _bufSize;
561 size_t _bufPos;
562 ISequentialInStream *_stream;
563
564 CMyComPtr2<ICompressFilter, NCrypto::NRar5::CDecoder> m_CryptoDecoder;
565
566 Z7_CLASS_NO_COPY(CInArchive)
567
568 HRESULT ReadStream_Check(void *data, size_t size);
569
570 public:
571 bool m_CryptoMode;
572
573 bool WrongPassword;
574 bool IsArc;
575 bool UnexpectedEnd;
576
577 UInt64 StreamStartPosition;
578 UInt64 Position;
579
580 size_t Get_Buf_RemainSize() const { return _bufSize - _bufPos; }
581 bool Is_Buf_Finished() const { return _bufPos == _bufSize; }
582 const Byte *Get_Buf_Data() const { return _buf + _bufPos; }
583 void Move_BufPos(size_t num) { _bufPos += num; }
584 bool ReadVar(UInt64 &val);
585
586 struct CHeader
587 {
588 UInt64 Type;
589 UInt64 Flags;
590 size_t ExtraSize;
591 UInt64 DataSize;
592 };
593
594 CInArchive() {}
595
596 HRESULT ReadBlockHeader(CHeader &h);
597 bool ReadFileHeader(const CHeader &header, CItem &item);
598 void AddToSeekValue(UInt64 addValue)
599 {
600 Position += addValue;
601 }
602
603 HRESULT Open(IInStream *inStream, const UInt64 *searchHeaderSizeLimit, ICryptoGetTextPassword *getTextPassword,
604 CInArcInfo &info);
605 };
606
607
608 static HRESULT MySetPassword(ICryptoGetTextPassword *getTextPassword, NCrypto::NRar5::CDecoder *cryptoDecoder)
609 {
610 CMyComBSTR_Wipe password;
611 RINOK(getTextPassword->CryptoGetTextPassword(&password))
612 AString_Wipe utf8;
613 const unsigned kPasswordLen_MAX = 127;
614 UString_Wipe unicode;
615 unicode.SetFromBstr(password);
616 if (unicode.Len() > kPasswordLen_MAX)
617 unicode.DeleteFrom(kPasswordLen_MAX);
618 ConvertUnicodeToUTF8(unicode, utf8);
619 cryptoDecoder->SetPassword((const Byte *)(const char *)utf8, utf8.Len());
620 return S_OK;
621 }
622
623
624 bool CInArchive::ReadVar(UInt64 &val)
625 {
626 const unsigned offset = ReadVarInt(Get_Buf_Data(), Get_Buf_RemainSize(), &val);
627 Move_BufPos(offset);
628 return (offset != 0);
629 }
630
631
632 HRESULT CInArchive::ReadStream_Check(void *data, size_t size)
633 {
634 size_t size2 = size;
635 RINOK(ReadStream(_stream, data, &size2))
636 if (size2 == size)
637 return S_OK;
638 UnexpectedEnd = true;
639 return S_FALSE;
640 }
641
642
643 HRESULT CInArchive::ReadBlockHeader(CHeader &h)
644 {
645 h.Type = 0;
646 h.Flags = 0;
647 h.ExtraSize = 0;
648 h.DataSize = 0;
649
650 Byte buf[AES_BLOCK_SIZE];
651 unsigned filled;
652
653 if (m_CryptoMode)
654 {
655 _buf.AllocAtLeast(1 << 12); // at least (AES_BLOCK_SIZE * 2)
656 if (!(Byte *)_buf)
657 return E_OUTOFMEMORY;
658 RINOK(ReadStream_Check(_buf, AES_BLOCK_SIZE * 2))
659 memcpy(m_CryptoDecoder->_iv, _buf, AES_BLOCK_SIZE);
660 RINOK(m_CryptoDecoder->Init())
661 if (m_CryptoDecoder->Filter(_buf + AES_BLOCK_SIZE, AES_BLOCK_SIZE) != AES_BLOCK_SIZE)
662 return E_FAIL;
663 memcpy(buf, _buf + AES_BLOCK_SIZE, AES_BLOCK_SIZE);
664 filled = AES_BLOCK_SIZE;
665 }
666 else
667 {
668 const unsigned kStartSize = 4 + 3;
669 RINOK(ReadStream_Check(buf, kStartSize))
670 filled = kStartSize;
671 }
672
673 {
674 UInt64 val;
675 unsigned offset = ReadVarInt(buf + 4, 3, &val);
676 if (offset == 0)
677 return S_FALSE;
678 size_t size = (size_t)val;
679 if (size < 2)
680 return S_FALSE;
681 offset += 4;
682 _bufPos = offset;
683 size += offset;
684 _bufSize = size;
685 if (m_CryptoMode)
686 size = (size + AES_BLOCK_SIZE - 1) & ~(size_t)(AES_BLOCK_SIZE - 1);
687 _buf.AllocAtLeast(size);
688 if (!(Byte *)_buf)
689 return E_OUTOFMEMORY;
690 memcpy(_buf, buf, filled);
691 const size_t rem = size - filled;
692 AddToSeekValue(size + (m_CryptoMode ? AES_BLOCK_SIZE : 0));
693 RINOK(ReadStream_Check(_buf + filled, rem))
694 if (m_CryptoMode)
695 {
696 if (m_CryptoDecoder->Filter(_buf + filled, (UInt32)rem) != rem)
697 return E_FAIL;
698 #if 1
699 // optional 7zip-unrar check : remainder must contain zeros.
700 const size_t pad = size - _bufSize;
701 const Byte *p = _buf + _bufSize;
702 for (size_t i = 0; i < pad; i++)
703 if (p[i])
704 return S_FALSE;
705 #endif
706 }
707 }
708
709 if (CrcCalc(_buf + 4, _bufSize - 4) != Get32(buf))
710 return S_FALSE;
711
712 if (!ReadVar(h.Type)) return S_FALSE;
713 if (!ReadVar(h.Flags)) return S_FALSE;
714
715 if (h.Flags & NHeaderFlags::kExtra)
716 {
717 UInt64 extraSize;
718 if (!ReadVar(extraSize))
719 return S_FALSE;
720 if (extraSize >= (1u << 21))
721 return S_FALSE;
722 h.ExtraSize = (size_t)extraSize;
723 }
724
725 if (h.Flags & NHeaderFlags::kData)
726 {
727 if (!ReadVar(h.DataSize))
728 return S_FALSE;
729 }
730
731 if (h.ExtraSize > Get_Buf_RemainSize())
732 return S_FALSE;
733 return S_OK;
734 }
735
736
737 bool CInArcInfo::CLocator::Parse(const Byte *p, size_t size)
738 {
739 Flags = 0;
740 QuickOpen = 0;
741 Recovery = 0;
742
743 PARSE_VAR_INT(p, size, Flags)
744
745 if (Is_QuickOpen())
746 {
747 PARSE_VAR_INT(p, size, QuickOpen)
748 }
749 if (Is_Recovery())
750 {
751 PARSE_VAR_INT(p, size, Recovery)
752 }
753 #if 0
754 // another records are possible in future rar formats.
755 if (size != 0)
756 return false;
757 #endif
758 return true;
759 }
760
761
762 bool CInArcInfo::CMetadata::Parse(const Byte *p, size_t size)
763 {
764 PARSE_VAR_INT(p, size, Flags)
765 if (Flags & NMetadataFlags::kArcName)
766 {
767 UInt64 nameLen;
768 PARSE_VAR_INT(p, size, nameLen)
769 if (nameLen > size)
770 return false;
771 ArcName.SetFrom_CalcLen((const char *)(const void *)p, (unsigned)nameLen);
772 p += (size_t)nameLen;
773 size -= (size_t)nameLen;
774 }
775 if (Flags & NMetadataFlags::kCTime)
776 {
777 if ((Flags & NMetadataFlags::kUnixTime) &&
778 (Flags & NMetadataFlags::kNanoSec) == 0)
779 {
780 if (size < 4)
781 return false;
782 CTime = GetUi32(p);
783 p += 4;
784 size -= 4;
785 }
786 else
787 {
788 if (size < 8)
789 return false;
790 CTime = GetUi64(p);
791 p += 8;
792 size -= 8;
793 }
794 }
795 #if 0
796 // another records are possible in future rar formats.
797 if (size != 0)
798 return false;
799 #endif
800 return true;
801 }
802
803
804 bool CInArcInfo::ParseExtra(const Byte *p, size_t size)
805 {
806 for (;;)
807 {
808 if (size == 0)
809 return true;
810 UInt64 recSize64, id;
811 PARSE_VAR_INT(p, size, recSize64)
812 if (recSize64 > size)
813 return false;
814 size_t recSize = (size_t)recSize64;
815 size -= recSize;
816 // READ_VAR_INT(p, recSize, recSize)
817 {
818 const unsigned num = ReadVarInt(p, recSize, &id);
819 if (num == 0)
820 return false;
821 p += num;
822 recSize -= num;
823 }
824 if (id == kArcExtraRecordType_Metadata)
825 {
826 Metadata_Defined = true;
827 if (!Metadata.Parse(p, recSize))
828 Metadata_Error = true;
829 }
830 else if (id == kArcExtraRecordType_Locator)
831 {
832 Locator_Defined = true;
833 if (!Locator.Parse(p, recSize))
834 Locator_Error = true;
835 }
836 else
837 UnknownExtraRecord = true;
838 p += recSize;
839 }
840 }
841
842
843
844 HRESULT CInArchive::Open(IInStream *stream, const UInt64 *searchHeaderSizeLimit, ICryptoGetTextPassword *getTextPassword,
845 CInArcInfo &info)
846 {
847 m_CryptoMode = false;
848
849 WrongPassword = false;
850 IsArc = false;
851 UnexpectedEnd = false;
852
853 Position = StreamStartPosition;
854
855 UInt64 arcStartPos = StreamStartPosition;
856 {
857 Byte marker[kMarkerSize];
858 RINOK(ReadStream_FALSE(stream, marker, kMarkerSize))
859 if (memcmp(marker, kMarker, kMarkerSize) == 0)
860 Position += kMarkerSize;
861 else
862 {
863 if (searchHeaderSizeLimit && *searchHeaderSizeLimit == 0)
864 return S_FALSE;
865 RINOK(InStream_SeekSet(stream, StreamStartPosition))
866 RINOK(FindSignatureInStream(stream, kMarker, kMarkerSize,
867 searchHeaderSizeLimit, arcStartPos))
868 arcStartPos += StreamStartPosition;
869 Position = arcStartPos + kMarkerSize;
870 RINOK(InStream_SeekSet(stream, Position))
871 }
872 }
873
874 info.StartPos = arcStartPos;
875 _stream = stream;
876
877 CHeader h;
878 RINOK(ReadBlockHeader(h))
879 info.IsEncrypted = false;
880
881 if (h.Type == NHeaderType::kArcEncrypt)
882 {
883 info.IsEncrypted = true;
884 IsArc = true;
885 if (!getTextPassword)
886 return E_NOTIMPL;
887 m_CryptoMode = true;
888 m_CryptoDecoder.Create_if_Empty();
889 RINOK(m_CryptoDecoder->SetDecoderProps(
890 Get_Buf_Data(), (unsigned)Get_Buf_RemainSize(), false, false))
891 RINOK(MySetPassword(getTextPassword, m_CryptoDecoder.ClsPtr()))
892 if (!m_CryptoDecoder->CalcKey_and_CheckPassword())
893 {
894 WrongPassword = True;
895 return S_FALSE;
896 }
897 RINOK(ReadBlockHeader(h))
898 }
899
900 if (h.Type != NHeaderType::kArc)
901 return S_FALSE;
902
903 IsArc = true;
904 info.VolNumber = 0;
905
906 if (!ReadVar(info.Flags))
907 return S_FALSE;
908
909 if (info.Flags & NArcFlags::kVolNumber)
910 if (!ReadVar(info.VolNumber))
911 return S_FALSE;
912
913 if (h.ExtraSize != Get_Buf_RemainSize())
914 return S_FALSE;
915 if (h.ExtraSize)
916 {
917 if (!info.ParseExtra(Get_Buf_Data(), h.ExtraSize))
918 info.Extra_Error = true;
919 }
920 return S_OK;
921 }
922
923
924 bool CInArchive::ReadFileHeader(const CHeader &header, CItem &item)
925 {
926 item.CommonFlags = (UInt32)header.Flags;
927 item.PackSize = header.DataSize;
928 item.UnixMTime = 0;
929 item.CRC = 0;
930
931 {
932 UInt64 flags64;
933 if (!ReadVar(flags64)) return false;
934 item.Flags = (UInt32)flags64;
935 }
936
937 if (!ReadVar(item.Size)) return false;
938
939 {
940 UInt64 attrib;
941 if (!ReadVar(attrib)) return false;
942 item.Attrib = (UInt32)attrib;
943 }
944 if (item.Has_UnixMTime())
945 {
946 if (Get_Buf_RemainSize() < 4)
947 return false;
948 item.UnixMTime = Get32(Get_Buf_Data());
949 Move_BufPos(4);
950 }
951 if (item.Has_CRC())
952 {
953 if (Get_Buf_RemainSize() < 4)
954 return false;
955 item.CRC = Get32(Get_Buf_Data());
956 Move_BufPos(4);
957 }
958 {
959 UInt64 method;
960 if (!ReadVar(method)) return false;
961 item.Method = (UInt32)method;
962 }
963
964 if (!ReadVar(item.HostOS)) return false;
965
966 {
967 UInt64 len;
968 if (!ReadVar(len)) return false;
969 if (len > Get_Buf_RemainSize())
970 return false;
971 item.Name.SetFrom_CalcLen((const char *)Get_Buf_Data(), (unsigned)len);
972 Move_BufPos((size_t)len);
973 }
974
975 item.Extra.Free();
976 const size_t extraSize = header.ExtraSize;
977 if (extraSize != 0)
978 {
979 if (Get_Buf_RemainSize() < extraSize)
980 return false;
981 item.Extra.Alloc(extraSize);
982 memcpy(item.Extra, Get_Buf_Data(), extraSize);
983 Move_BufPos(extraSize);
984 }
985
986 return Is_Buf_Finished();
987 }
988
989
990
991 struct CLinkFile
992 {
993 unsigned Index;
994 unsigned NumLinks; // the number of links to Data
995 CByteBuffer Data;
996 HRESULT Res;
997 bool crcOK;
998
999 CLinkFile(): Index(0), NumLinks(0), Res(S_OK), crcOK(true) {}
1000 };
1001
1002
1003 struct CUnpacker
1004 {
1005 CMyComPtr2<ICompressCoder, NCompress::CCopyCoder> copyCoder;
1006 CMyComPtr<ICompressCoder> LzCoders[2];
1007 bool SolidAllowed;
1008 bool NeedCrc;
1009 CFilterCoder *filterStreamSpec;
1010 CMyComPtr<ISequentialInStream> filterStream;
1011 CMyComPtr2<ICompressFilter, NCrypto::NRar5::CDecoder> cryptoDecoder;
1012 CMyComPtr<ICryptoGetTextPassword> getTextPassword;
1013 CMyComPtr2<ISequentialOutStream, COutStreamWithHash> outStream;
1014
1015 CByteBuffer _tempBuf;
1016 CLinkFile *linkFile;
1017
1018 CUnpacker(): linkFile(NULL) { SolidAllowed = false; NeedCrc = true; }
1019
1020 HRESULT Create(DECL_EXTERNAL_CODECS_LOC_VARS
1021 const CItem &item, bool isSolid, bool &wrongPassword);
1022
1023 HRESULT Code(const CItem &item, const CItem &lastItem, UInt64 packSize,
1024 ISequentialInStream *inStream, ISequentialOutStream *outStream, ICompressProgressInfo *progress,
1025 bool &isCrcOK);
1026
1027 HRESULT DecodeToBuf(DECL_EXTERNAL_CODECS_LOC_VARS
1028 const CItem &item, UInt64 packSize, ISequentialInStream *inStream, CByteBuffer &buffer);
1029 };
1030
1031
1032 static const unsigned kLzMethodMax = 5;
1033
1034 HRESULT CUnpacker::Create(DECL_EXTERNAL_CODECS_LOC_VARS
1035 const CItem &item, bool isSolid, bool &wrongPassword)
1036 {
1037 wrongPassword = false;
1038
1039 if (item.Get_AlgoVersion_RawBits() > 1)
1040 return E_NOTIMPL;
1041
1042 outStream.Create_if_Empty();
1043
1044 const unsigned method = item.Get_Method();
1045
1046 if (method == 0)
1047 copyCoder.Create_if_Empty();
1048 else
1049 {
1050 if (method > kLzMethodMax)
1051 return E_NOTIMPL;
1052 /*
1053 if (item.IsSplitBefore())
1054 return S_FALSE;
1055 */
1056 const unsigned lzIndex = item.IsService() ? 1 : 0;
1057 CMyComPtr<ICompressCoder> &lzCoder = LzCoders[lzIndex];
1058 if (!lzCoder)
1059 {
1060 const UInt32 methodID = 0x40305;
1061 RINOK(CreateCoder_Id(EXTERNAL_CODECS_LOC_VARS methodID, false, lzCoder))
1062 if (!lzCoder)
1063 return E_NOTIMPL;
1064 }
1065
1066 CMyComPtr<ICompressSetDecoderProperties2> csdp;
1067 RINOK(lzCoder.QueryInterface(IID_ICompressSetDecoderProperties2, &csdp))
1068
1069 const unsigned ver = item.Get_AlgoVersion_HuffRev();
1070 if (ver > 1)
1071 return E_NOTIMPL;
1072 const Byte props[2] =
1073 {
1074 (Byte)item.Get_DictSize_Main(),
1075 (Byte)((item.Get_DictSize_Frac() << 3) + (ver << 1) + (isSolid ? 1 : 0))
1076 };
1077 RINOK(csdp->SetDecoderProperties2(props, 2))
1078 }
1079
1080 unsigned cryptoSize = 0;
1081 const int cryptoOffset = item.FindExtra(NExtraID::kCrypto, cryptoSize);
1082
1083 if (cryptoOffset >= 0)
1084 {
1085 if (!filterStream)
1086 {
1087 filterStreamSpec = new CFilterCoder(false);
1088 filterStream = filterStreamSpec;
1089 }
1090
1091 cryptoDecoder.Create_if_Empty();
1092
1093 RINOK(cryptoDecoder->SetDecoderProps(item.Extra + (unsigned)cryptoOffset, cryptoSize, true, item.IsService()))
1094
1095 if (!getTextPassword)
1096 {
1097 wrongPassword = True;
1098 return E_NOTIMPL;
1099 }
1100
1101 RINOK(MySetPassword(getTextPassword, cryptoDecoder.ClsPtr()))
1102
1103 if (!cryptoDecoder->CalcKey_and_CheckPassword())
1104 wrongPassword = True;
1105 }
1106
1107 return S_OK;
1108 }
1109
1110
1111 HRESULT CUnpacker::Code(const CItem &item, const CItem &lastItem, UInt64 packSize,
1112 ISequentialInStream *volsInStream, ISequentialOutStream *realOutStream, ICompressProgressInfo *progress,
1113 bool &isCrcOK)
1114 {
1115 isCrcOK = true;
1116
1117 const unsigned method = item.Get_Method();
1118 if (method > kLzMethodMax)
1119 return E_NOTIMPL;
1120
1121 const bool needBuf = (linkFile && linkFile->NumLinks != 0);
1122
1123 if (needBuf && !lastItem.Is_UnknownSize())
1124 {
1125 const size_t dataSize = (size_t)lastItem.Size;
1126 if (dataSize != lastItem.Size)
1127 return E_NOTIMPL;
1128 linkFile->Data.Alloc(dataSize);
1129 }
1130
1131 bool isCryptoMode = false;
1132 ISequentialInStream *inStream;
1133
1134 if (item.IsEncrypted())
1135 {
1136 filterStreamSpec->Filter = cryptoDecoder;
1137 filterStreamSpec->SetInStream(volsInStream);
1138 filterStreamSpec->SetOutStreamSize(NULL);
1139 inStream = filterStream;
1140 isCryptoMode = true;
1141 }
1142 else
1143 inStream = volsInStream;
1144
1145 ICompressCoder *commonCoder = (method == 0) ?
1146 copyCoder.Interface() :
1147 LzCoders[item.IsService() ? 1 : 0].Interface();
1148
1149 outStream->SetStream(realOutStream);
1150 outStream->Init(lastItem, (needBuf ? (Byte *)linkFile->Data : NULL), NeedCrc);
1151
1152 HRESULT res = S_OK;
1153 if (packSize != 0 || lastItem.Is_UnknownSize() || lastItem.Size != 0)
1154 {
1155 res = commonCoder->Code(inStream, outStream, &packSize,
1156 lastItem.Is_UnknownSize() ? NULL : &lastItem.Size, progress);
1157 if (!item.IsService())
1158 SolidAllowed = true;
1159 }
1160 else
1161 {
1162 // res = res;
1163 }
1164
1165 if (isCryptoMode)
1166 filterStreamSpec->ReleaseInStream();
1167
1168 const UInt64 processedSize = outStream->GetPos();
1169 if (res == S_OK && !lastItem.Is_UnknownSize() && processedSize != lastItem.Size)
1170 res = S_FALSE;
1171
1172 // if (res == S_OK)
1173 {
1174 unsigned cryptoSize = 0;
1175 const int cryptoOffset = lastItem.FindExtra(NExtraID::kCrypto, cryptoSize);
1176 NCrypto::NRar5::CDecoder *crypto = NULL;
1177 if (cryptoOffset >= 0)
1178 {
1179 CCryptoInfo cryptoInfo;
1180 if (cryptoInfo.Parse(lastItem.Extra + (unsigned)cryptoOffset, cryptoSize))
1181 if (cryptoInfo.UseMAC())
1182 crypto = cryptoDecoder.ClsPtr();
1183 }
1184 if (NeedCrc)
1185 isCrcOK = outStream->_hash.Check(lastItem, crypto);
1186 }
1187
1188 if (linkFile)
1189 {
1190 linkFile->Res = res;
1191 linkFile->crcOK = isCrcOK;
1192 if (needBuf
1193 && !lastItem.Is_UnknownSize()
1194 && processedSize != lastItem.Size)
1195 linkFile->Data.ChangeSize_KeepData((size_t)processedSize, (size_t)processedSize);
1196 }
1197
1198 return res;
1199 }
1200
1201
1202 HRESULT CUnpacker::DecodeToBuf(DECL_EXTERNAL_CODECS_LOC_VARS
1203 const CItem &item, UInt64 packSize,
1204 ISequentialInStream *inStream,
1205 CByteBuffer &buffer)
1206 {
1207 CMyComPtr2_Create<ISequentialOutStream, CBufPtrSeqOutStream> out;
1208 _tempBuf.AllocAtLeast((size_t)item.Size);
1209 out->Init(_tempBuf, (size_t)item.Size);
1210
1211 bool wrongPassword;
1212
1213 if (item.IsSolid())
1214 return E_NOTIMPL;
1215
1216 HRESULT res = Create(EXTERNAL_CODECS_LOC_VARS item, item.IsSolid(), wrongPassword);
1217
1218 if (res == S_OK)
1219 {
1220 if (wrongPassword)
1221 return S_FALSE;
1222
1223 CMyComPtr2_Create<ISequentialInStream, CLimitedSequentialInStream> limitedStream;
1224 limitedStream->SetStream(inStream);
1225 limitedStream->Init(packSize);
1226
1227 bool crcOK = true;
1228 res = Code(item, item, packSize, limitedStream, out, NULL, crcOK);
1229 if (res == S_OK)
1230 {
1231 if (!crcOK || out->GetPos() != item.Size)
1232 res = S_FALSE;
1233 else
1234 buffer.CopyFrom(_tempBuf, (size_t)item.Size);
1235 }
1236 }
1237
1238 return res;
1239 }
1240
1241
1242 struct CTempBuf
1243 {
1244 CByteBuffer _buf;
1245 size_t _offset;
1246 bool _isOK;
1247
1248 void Clear()
1249 {
1250 _offset = 0;
1251 _isOK = true;
1252 }
1253
1254 CTempBuf() { Clear(); }
1255
1256 HRESULT Decode(DECL_EXTERNAL_CODECS_LOC_VARS
1257 const CItem &item,
1258 ISequentialInStream *inStream,
1259 CUnpacker &unpacker,
1260 CByteBuffer &destBuf);
1261 };
1262
1263
1264 HRESULT CTempBuf::Decode(DECL_EXTERNAL_CODECS_LOC_VARS
1265 const CItem &item,
1266 ISequentialInStream *inStream,
1267 CUnpacker &unpacker,
1268 CByteBuffer &destBuf)
1269 {
1270 const size_t kPackSize_Max = (1 << 24);
1271 if (item.Size > (1 << 24)
1272 || item.Size == 0
1273 || item.PackSize >= kPackSize_Max)
1274 {
1275 Clear();
1276 return S_OK;
1277 }
1278
1279 if (item.IsSplit() /* && _isOK */)
1280 {
1281 size_t packSize = (size_t)item.PackSize;
1282 if (packSize > kPackSize_Max - _offset)
1283 return S_OK;
1284 size_t newSize = _offset + packSize;
1285 if (newSize > _buf.Size())
1286 _buf.ChangeSize_KeepData(newSize, _offset);
1287
1288 Byte *data = (Byte *)_buf + _offset;
1289 RINOK(ReadStream_FALSE(inStream, data, packSize))
1290
1291 _offset += packSize;
1292
1293 if (item.IsSplitAfter())
1294 {
1295 CHash hash;
1296 hash.Init(item);
1297 hash.Update(data, packSize);
1298 _isOK = hash.Check(item, NULL); // RAR5 doesn't use HMAC for packed part
1299 }
1300 }
1301
1302 if (_isOK)
1303 {
1304 if (!item.IsSplitAfter())
1305 {
1306 if (_offset == 0)
1307 {
1308 RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_LOC_VARS
1309 item, item.PackSize, inStream, destBuf))
1310 }
1311 else
1312 {
1313 CMyComPtr2_Create<ISequentialInStream, CBufInStream> bufInStream;
1314 bufInStream->Init(_buf, _offset);
1315 RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_LOC_VARS
1316 item, _offset, bufInStream, destBuf))
1317 }
1318 }
1319 }
1320
1321 return S_OK;
1322 }
1323
1324
1325
1326 static const Byte kProps[] =
1327 {
1328 kpidPath,
1329 kpidIsDir,
1330 kpidSize,
1331 kpidPackSize,
1332 kpidMTime,
1333 kpidCTime,
1334 kpidATime,
1335 kpidAttrib,
1336 // kpidPosixAttrib, // for debug
1337
1338 kpidIsAltStream,
1339 kpidEncrypted,
1340 kpidSolid,
1341 kpidSplitBefore,
1342 kpidSplitAfter,
1343 kpidCRC,
1344 kpidHostOS,
1345 kpidMethod,
1346 kpidCharacts,
1347 kpidSymLink,
1348 kpidHardLink,
1349 kpidCopyLink,
1350
1351 kpidVolumeIndex
1352 };
1353
1354
1355 static const Byte kArcProps[] =
1356 {
1357 kpidTotalPhySize,
1358 kpidCharacts,
1359 kpidEncrypted,
1360 kpidSolid,
1361 kpidNumBlocks,
1362 kpidMethod,
1363 kpidIsVolume,
1364 kpidVolumeIndex,
1365 kpidNumVolumes,
1366 kpidName,
1367 kpidCTime,
1368 kpidComment
1369 };
1370
1371
1372 IMP_IInArchive_Props
1373 IMP_IInArchive_ArcProps
1374
1375
1376 UInt64 CHandler::GetPackSize(unsigned refIndex) const
1377 {
1378 UInt64 size = 0;
1379 unsigned index = _refs[refIndex].Item;
1380 for (;;)
1381 {
1382 const CItem &item = _items[index];
1383 size += item.PackSize;
1384 if (item.NextItem < 0)
1385 return size;
1386 index = (unsigned)item.NextItem;
1387 }
1388 }
1389
1390 static char *PrintDictSize(char *s, UInt64 w)
1391 {
1392 char c = 'K'; w >>= 10;
1393 if ((w & ((1 << 10) - 1)) == 0) { c = 'M'; w >>= 10;
1394 if ((w & ((1 << 10) - 1)) == 0) { c = 'G'; w >>= 10; }}
1395 s = ConvertUInt64ToString(w, s);
1396 *s++ = c;
1397 *s = 0;
1398 return s;
1399 }
1400
1401
1402 Z7_COM7F_IMF(CHandler::GetArchiveProperty(PROPID propID, PROPVARIANT *value))
1403 {
1404 COM_TRY_BEGIN
1405
1406 NCOM::CPropVariant prop;
1407
1408 const CInArcInfo *arcInfo = NULL;
1409 if (!_arcs.IsEmpty())
1410 arcInfo = &_arcs[0].Info;
1411
1412 switch (propID)
1413 {
1414 case kpidVolumeIndex: if (arcInfo && arcInfo->IsVolume()) prop = arcInfo->GetVolIndex(); break;
1415 case kpidSolid: if (arcInfo) prop = arcInfo->IsSolid(); break;
1416 case kpidCharacts:
1417 {
1418 AString s;
1419 if (arcInfo)
1420 {
1421 s = FlagsToString(k_ArcFlags, Z7_ARRAY_SIZE(k_ArcFlags), (UInt32)arcInfo->Flags);
1422 if (arcInfo->Extra_Error)
1423 s.Add_OptSpaced("Extra-ERROR");
1424 if (arcInfo->UnsupportedFeature)
1425 s.Add_OptSpaced("unsupported-feature");
1426 if (arcInfo->Metadata_Defined)
1427 {
1428 s.Add_OptSpaced("Metadata");
1429 if (arcInfo->Metadata_Error)
1430 s += "-ERROR";
1431 else
1432 {
1433 if (arcInfo->Metadata.Flags & NMetadataFlags::kArcName)
1434 s.Add_OptSpaced("arc-name");
1435 if (arcInfo->Metadata.Flags & NMetadataFlags::kCTime)
1436 {
1437 s.Add_OptSpaced("ctime-");
1438 s +=
1439 (arcInfo->Metadata.Flags & NMetadataFlags::kUnixTime) ?
1440 (arcInfo->Metadata.Flags & NMetadataFlags::kNanoSec) ?
1441 "1ns" : "1s" : "win";
1442 }
1443 }
1444 }
1445 if (arcInfo->Locator_Defined)
1446 {
1447 s.Add_OptSpaced("Locator");
1448 if (arcInfo->Locator_Error)
1449 s += "-ERROR";
1450 else
1451 {
1452 if (arcInfo->Locator.Is_QuickOpen())
1453 {
1454 s.Add_OptSpaced("QuickOpen:");
1455 s.Add_UInt64(arcInfo->Locator.QuickOpen);
1456 }
1457 if (arcInfo->Locator.Is_Recovery())
1458 {
1459 s += "Recovery:";
1460 s.Add_UInt64(arcInfo->Locator.Recovery);
1461 }
1462 }
1463 }
1464 if (arcInfo->UnknownExtraRecord)
1465 s.Add_OptSpaced("Unknown-Extra-Record");
1466
1467 }
1468 if (_comment_WasUsedInArc)
1469 {
1470 s.Add_OptSpaced("Comment");
1471 // s.Add_UInt32((UInt32)_comment.Size());
1472 }
1473 //
1474 if (_acls.Size() != 0)
1475 {
1476 s.Add_OptSpaced("ACL");
1477 // s.Add_UInt32(_acls.Size());
1478 }
1479 if (!s.IsEmpty())
1480 prop = s;
1481 break;
1482 }
1483 case kpidEncrypted: if (arcInfo) prop = arcInfo->IsEncrypted; break; // it's for encrypted names.
1484 case kpidIsVolume: if (arcInfo) prop = arcInfo->IsVolume(); break;
1485 case kpidNumVolumes: prop = (UInt32)_arcs.Size(); break;
1486 case kpidOffset: if (arcInfo && arcInfo->StartPos != 0) prop = arcInfo->StartPos; break;
1487
1488 case kpidTotalPhySize:
1489 {
1490 if (_arcs.Size() > 1)
1491 {
1492 UInt64 sum = 0;
1493 FOR_VECTOR (v, _arcs)
1494 sum += _arcs[v].Info.GetPhySize();
1495 prop = sum;
1496 }
1497 break;
1498 }
1499
1500 case kpidPhySize:
1501 {
1502 if (arcInfo)
1503 prop = arcInfo->GetPhySize();
1504 break;
1505 }
1506
1507 case kpidName:
1508 if (arcInfo)
1509 if (!arcInfo->Metadata_Error
1510 && !arcInfo->Metadata.ArcName.IsEmpty())
1511 {
1512 UString s;
1513 if (ConvertUTF8ToUnicode(arcInfo->Metadata.ArcName, s))
1514 prop = s;
1515 }
1516 break;
1517
1518 case kpidCTime:
1519 if (arcInfo)
1520 if (!arcInfo->Metadata_Error
1521 && (arcInfo->Metadata.Flags & NMetadataFlags::kCTime))
1522 {
1523 const UInt64 ct = arcInfo->Metadata.CTime;
1524 if (arcInfo->Metadata.Flags & NMetadataFlags::kUnixTime)
1525 {
1526 if (arcInfo->Metadata.Flags & NMetadataFlags::kNanoSec)
1527 {
1528 const UInt64 sec = ct / 1000000000;
1529 const UInt64 ns = ct % 1000000000;
1530 UInt64 wt = NTime::UnixTime64_To_FileTime64((Int64)sec);
1531 wt += ns / 100;
1532 const unsigned ns100 = (unsigned)(ns % 100);
1533 FILETIME ft;
1534 ft.dwLowDateTime = (DWORD)(UInt32)wt;
1535 ft.dwHighDateTime = (DWORD)(UInt32)(wt >> 32);
1536 prop.SetAsTimeFrom_FT_Prec_Ns100(ft, k_PropVar_TimePrec_1ns, ns100);
1537 }
1538 else
1539 {
1540 const UInt64 wt = NTime::UnixTime64_To_FileTime64((Int64)ct);
1541 prop.SetAsTimeFrom_Ft64_Prec(wt, k_PropVar_TimePrec_Unix);
1542 }
1543 }
1544 else
1545 prop.SetAsTimeFrom_Ft64_Prec(ct, k_PropVar_TimePrec_100ns);
1546 }
1547 break;
1548
1549 case kpidComment:
1550 {
1551 // if (!_arcs.IsEmpty())
1552 {
1553 // const CArc &arc = _arcs[0];
1554 const CByteBuffer &cmt = _comment;
1555 if (cmt.Size() != 0 /* && cmt.Size() < (1 << 16) */)
1556 {
1557 AString s;
1558 s.SetFrom_CalcLen((const char *)(const Byte *)cmt, (unsigned)cmt.Size());
1559 UString unicode;
1560 ConvertUTF8ToUnicode(s, unicode);
1561 prop = unicode;
1562 }
1563 }
1564 break;
1565 }
1566
1567 case kpidNumBlocks:
1568 {
1569 prop = (UInt32)_numBlocks;
1570 break;
1571 }
1572
1573 case kpidMethod:
1574 {
1575 AString s;
1576
1577 UInt64 algo = _algo_Mask;
1578 for (unsigned v = 0; algo != 0; v++, algo >>= 1)
1579 {
1580 if ((algo & 1) == 0)
1581 continue;
1582 s.Add_OptSpaced("v");
1583 s.Add_UInt32(v + 6);
1584 if (v < Z7_ARRAY_SIZE(_methodMasks))
1585 {
1586 const UInt64 dict = _dictMaxSizes[v];
1587 if (dict)
1588 {
1589 char temp[24];
1590 temp[0] = ':';
1591 PrintDictSize(temp + 1, dict);
1592 s += temp;
1593 }
1594 unsigned method = _methodMasks[v];
1595 for (unsigned m = 0; method; m++, method >>= 1)
1596 {
1597 if ((method & 1) == 0)
1598 continue;
1599 s += ":m";
1600 s.Add_UInt32(m);
1601 }
1602 }
1603 }
1604 if (_rar5comapt_mask & 2)
1605 {
1606 s += ":c";
1607 if (_rar5comapt_mask & 1)
1608 s.Add_Char('n');
1609 }
1610 prop = s;
1611 break;
1612 }
1613
1614 case kpidError:
1615 {
1616 if (/* &_missingVol || */ !_missingVolName.IsEmpty())
1617 {
1618 UString s ("Missing volume : ");
1619 s += _missingVolName;
1620 prop = s;
1621 }
1622 break;
1623 }
1624
1625 case kpidErrorFlags:
1626 {
1627 UInt32 v = _errorFlags;
1628 if (!_isArc)
1629 v |= kpv_ErrorFlags_IsNotArc;
1630 if (_error_in_ACL)
1631 v |= kpv_ErrorFlags_HeadersError;
1632 if (_split_Error)
1633 v |= kpv_ErrorFlags_HeadersError;
1634 prop = v;
1635 break;
1636 }
1637
1638 /*
1639 case kpidWarningFlags:
1640 {
1641 if (_warningFlags != 0)
1642 prop = _warningFlags;
1643 break;
1644 }
1645 */
1646
1647 case kpidExtension:
1648 if (_arcs.Size() == 1)
1649 {
1650 if (arcInfo->IsVolume())
1651 {
1652 AString s ("part");
1653 UInt32 v = (UInt32)arcInfo->GetVolIndex() + 1;
1654 if (v < 10)
1655 s.Add_Char('0');
1656 s.Add_UInt32(v);
1657 s += ".rar";
1658 prop = s;
1659 }
1660 }
1661 break;
1662
1663 case kpidIsAltStream: prop = true; break;
1664 }
1665
1666 prop.Detach(value);
1667 return S_OK;
1668
1669 COM_TRY_END
1670 }
1671
1672
1673 Z7_COM7F_IMF(CHandler::GetNumberOfItems(UInt32 *numItems))
1674 {
1675 *numItems = (UInt32)_refs.Size();
1676 return S_OK;
1677 }
1678
1679
1680 static const Byte kRawProps[] =
1681 {
1682 kpidChecksum,
1683 kpidNtSecure
1684 };
1685
1686
1687 Z7_COM7F_IMF(CHandler::GetNumRawProps(UInt32 *numProps))
1688 {
1689 *numProps = Z7_ARRAY_SIZE(kRawProps);
1690 return S_OK;
1691 }
1692
1693 Z7_COM7F_IMF(CHandler::GetRawPropInfo(UInt32 index, BSTR *name, PROPID *propID))
1694 {
1695 *propID = kRawProps[index];
1696 *name = NULL;
1697 return S_OK;
1698 }
1699
1700 Z7_COM7F_IMF(CHandler::GetParent(UInt32 index, UInt32 *parent, UInt32 *parentType))
1701 {
1702 *parentType = NParentType::kDir;
1703 *parent = (UInt32)(Int32)-1;
1704
1705 if (index >= _refs.Size())
1706 return S_OK;
1707
1708 const CRefItem &ref = _refs[index];
1709 const CItem &item = _items[ref.Item];
1710
1711 if (item.Is_STM() && ref.Parent >= 0)
1712 {
1713 *parent = (UInt32)ref.Parent;
1714 *parentType = NParentType::kAltStream;
1715 }
1716
1717 return S_OK;
1718 }
1719
1720
1721 Z7_COM7F_IMF(CHandler::GetRawProp(UInt32 index, PROPID propID, const void **data, UInt32 *dataSize, UInt32 *propType))
1722 {
1723 *data = NULL;
1724 *dataSize = 0;
1725 *propType = 0;
1726
1727 if (index >= _refs.Size())
1728 return E_INVALIDARG;
1729
1730 const CItem &item = _items[_refs[index].Item];
1731
1732 if (propID == kpidNtSecure)
1733 {
1734 if (item.ACL >= 0)
1735 {
1736 const CByteBuffer &buf = _acls[item.ACL];
1737 *dataSize = (UInt32)buf.Size();
1738 *propType = NPropDataType::kRaw;
1739 *data = (const Byte *)buf;
1740 }
1741 return S_OK;
1742 }
1743
1744 if (propID == kpidChecksum)
1745 {
1746 const int hashRecOffset = item.FindExtra_Blake();
1747 if (hashRecOffset >= 0)
1748 {
1749 *dataSize = Z7_BLAKE2S_DIGEST_SIZE;
1750 *propType = NPropDataType::kRaw;
1751 *data = item.Extra + (unsigned)hashRecOffset;
1752 }
1753 /*
1754 else if (item.Has_CRC() && item.IsEncrypted())
1755 {
1756 *dataSize = 4;
1757 *propType = NPropDataType::kRaw;
1758 *data = &item->CRC; // we must show same value for big/little endian here
1759 }
1760 */
1761 return S_OK;
1762 }
1763
1764 return S_OK;
1765 }
1766
1767
1768 static void TimeRecordToProp(const CItem &item, unsigned stampIndex, NCOM::CPropVariant &prop)
1769 {
1770 unsigned size;
1771 const int offset = item.FindExtra(NExtraID::kTime, size);
1772 if (offset < 0)
1773 return;
1774
1775 const Byte *p = item.Extra + (unsigned)offset;
1776 UInt64 flags;
1777 // PARSE_VAR_INT(p, size, flags)
1778 {
1779 const unsigned num = ReadVarInt(p, size, &flags);
1780 if (num == 0)
1781 return;
1782 p += num;
1783 size -= num;
1784 }
1785
1786 if ((flags & (NTimeRecord::NFlags::kMTime << stampIndex)) == 0)
1787 return;
1788
1789 unsigned numStamps = 0;
1790 unsigned curStamp = 0;
1791
1792 for (unsigned i = 0; i < 3; i++)
1793 if ((flags & (NTimeRecord::NFlags::kMTime << i)) != 0)
1794 {
1795 if (i == stampIndex)
1796 curStamp = numStamps;
1797 numStamps++;
1798 }
1799
1800 FILETIME ft;
1801
1802 unsigned timePrec = 0;
1803 unsigned ns100 = 0;
1804
1805 if ((flags & NTimeRecord::NFlags::kUnixTime) != 0)
1806 {
1807 curStamp *= 4;
1808 if (curStamp + 4 > size)
1809 return;
1810 p += curStamp;
1811 UInt64 val = NTime::UnixTime_To_FileTime64(Get32(p));
1812 numStamps *= 4;
1813 timePrec = k_PropVar_TimePrec_Unix;
1814 if ((flags & NTimeRecord::NFlags::kUnixNs) != 0 && numStamps * 2 <= size)
1815 {
1816 const UInt32 ns = Get32(p + numStamps) & 0x3FFFFFFF;
1817 if (ns < 1000000000)
1818 {
1819 val += ns / 100;
1820 ns100 = (unsigned)(ns % 100);
1821 timePrec = k_PropVar_TimePrec_1ns;
1822 }
1823 }
1824 ft.dwLowDateTime = (DWORD)val;
1825 ft.dwHighDateTime = (DWORD)(val >> 32);
1826 }
1827 else
1828 {
1829 curStamp *= 8;
1830 if (curStamp + 8 > size)
1831 return;
1832 p += curStamp;
1833 ft.dwLowDateTime = Get32(p);
1834 ft.dwHighDateTime = Get32(p + 4);
1835 }
1836
1837 prop.SetAsTimeFrom_FT_Prec_Ns100(ft, timePrec, ns100);
1838 }
1839
1840
1841 Z7_COM7F_IMF(CHandler::GetProperty(UInt32 index, PROPID propID, PROPVARIANT *value))
1842 {
1843 COM_TRY_BEGIN
1844
1845 NCOM::CPropVariant prop;
1846 const CRefItem &ref = _refs[index];
1847 const CItem &item = _items[ref.Item];
1848 const CItem &lastItem = _items[ref.Last];
1849
1850 switch (propID)
1851 {
1852 case kpidPath:
1853 {
1854 UString unicodeName;
1855
1856 if (item.Is_STM())
1857 {
1858 AString s;
1859 if (ref.Parent >= 0)
1860 {
1861 const CItem &mainItem = _items[_refs[ref.Parent].Item];
1862 s = mainItem.Name;
1863 }
1864
1865 AString name;
1866 item.GetAltStreamName(name);
1867 if (name[0] != ':')
1868 s.Add_Colon();
1869 s += name;
1870 ConvertUTF8ToUnicode(s, unicodeName);
1871 }
1872 else
1873 {
1874 ConvertUTF8ToUnicode(item.Name, unicodeName);
1875
1876 if (item.Version_Defined)
1877 {
1878 char temp[32];
1879 // temp[0] = ';';
1880 // ConvertUInt64ToString(item.Version, temp + 1);
1881 // unicodeName += temp;
1882 ConvertUInt64ToString(item.Version, temp);
1883 UString s2 ("[VER]" STRING_PATH_SEPARATOR);
1884 s2 += temp;
1885 s2.Add_PathSepar();
1886 unicodeName.Insert(0, s2);
1887 }
1888 }
1889
1890 NItemName::ReplaceToOsSlashes_Remove_TailSlash(unicodeName);
1891 prop = unicodeName;
1892
1893 break;
1894 }
1895
1896 case kpidIsDir: prop = item.IsDir(); break;
1897 case kpidSize: if (!lastItem.Is_UnknownSize()) prop = lastItem.Size; break;
1898 case kpidPackSize: prop = GetPackSize((unsigned)index); break;
1899
1900 case kpidMTime:
1901 {
1902 TimeRecordToProp(item, NTimeRecord::k_Index_MTime, prop);
1903 if (prop.vt == VT_EMPTY && item.Has_UnixMTime())
1904 PropVariant_SetFrom_UnixTime(prop, item.UnixMTime);
1905 if (prop.vt == VT_EMPTY && ref.Parent >= 0)
1906 {
1907 const CItem &baseItem = _items[_refs[ref.Parent].Item];
1908 TimeRecordToProp(baseItem, NTimeRecord::k_Index_MTime, prop);
1909 if (prop.vt == VT_EMPTY && baseItem.Has_UnixMTime())
1910 PropVariant_SetFrom_UnixTime(prop, baseItem.UnixMTime);
1911 }
1912 break;
1913 }
1914 case kpidCTime: TimeRecordToProp(item, NTimeRecord::k_Index_CTime, prop); break;
1915 case kpidATime: TimeRecordToProp(item, NTimeRecord::k_Index_ATime, prop); break;
1916
1917 case kpidName:
1918 {
1919 if (item.Is_STM())
1920 {
1921 AString name;
1922 item.GetAltStreamName(name);
1923 if (name[0] == ':')
1924 {
1925 name.DeleteFrontal(1);
1926 UString unicodeName;
1927 ConvertUTF8ToUnicode(name, unicodeName);
1928 prop = unicodeName;
1929 }
1930 }
1931 break;
1932 }
1933
1934 case kpidIsAltStream: prop = item.Is_STM(); break;
1935
1936 case kpidSymLink: item.Link_to_Prop(NLinkType::kUnixSymLink, prop); break;
1937 case kpidHardLink: item.Link_to_Prop(NLinkType::kHardLink, prop); break;
1938 case kpidCopyLink: item.Link_to_Prop(NLinkType::kFileCopy, prop); break;
1939
1940 case kpidAttrib: prop = item.GetWinAttrib(); break;
1941 case kpidPosixAttrib:
1942 if (item.HostOS == kHost_Unix)
1943 prop = (UInt32)item.Attrib;
1944 break;
1945
1946 case kpidEncrypted: prop = item.IsEncrypted(); break;
1947 case kpidSolid: prop = item.IsSolid(); break;
1948
1949 case kpidSplitBefore: prop = item.IsSplitBefore(); break;
1950 case kpidSplitAfter: prop = lastItem.IsSplitAfter(); break;
1951
1952 case kpidVolumeIndex:
1953 {
1954 if (item.VolIndex < _arcs.Size())
1955 {
1956 const CInArcInfo &arcInfo = _arcs[item.VolIndex].Info;
1957 if (arcInfo.IsVolume())
1958 prop = (UInt64)arcInfo.GetVolIndex();
1959 }
1960 break;
1961 }
1962
1963 case kpidCRC:
1964 {
1965 const CItem *item2 = (lastItem.IsSplitAfter() ? &item : &lastItem);
1966 // we don't want to show crc for encrypted file here,
1967 // because crc is also encrrypted.
1968 if (item2->Has_CRC() && !item2->IsEncrypted())
1969 prop = item2->CRC;
1970 break;
1971 }
1972
1973 case kpidMethod:
1974 {
1975 char temp[128];
1976 const unsigned algo = item.Get_AlgoVersion_RawBits();
1977 char *s = temp;
1978 // if (algo != 0)
1979 {
1980 *s++ = 'v';
1981 s = ConvertUInt32ToString((UInt32)algo + 6, s);
1982 if (item.Is_Rar5_Compat())
1983 *s++ = 'c';
1984 *s++ = ':';
1985 }
1986 {
1987 const unsigned m = item.Get_Method();
1988 *s++ = 'm';
1989 *s++ = (char)(m + '0');
1990 if (!item.IsDir())
1991 {
1992 *s++ = ':';
1993 const unsigned dictMain = item.Get_DictSize_Main();
1994 const unsigned frac = item.Get_DictSize_Frac();
1995 /*
1996 if (frac == 0 && algo == 0)
1997 s = ConvertUInt32ToString(dictMain + 17, s);
1998 else
1999 */
2000 s = PrintDictSize(s, (UInt64)(32 + frac) << (12 + dictMain));
2001 if (item.Is_Rar5_Compat())
2002 {
2003 *s++ = ':';
2004 *s++ = 'c';
2005 }
2006 }
2007 }
2008 unsigned cryptoSize = 0;
2009 const int cryptoOffset = item.FindExtra(NExtraID::kCrypto, cryptoSize);
2010 if (cryptoOffset >= 0)
2011 {
2012 *s++ = ' ';
2013 CCryptoInfo cryptoInfo;
2014 const bool isOK = cryptoInfo.Parse(item.Extra + (unsigned)cryptoOffset, cryptoSize);
2015 if (cryptoInfo.Algo == 0)
2016 s = MyStpCpy(s, "AES");
2017 else
2018 {
2019 s = MyStpCpy(s, "Crypto_");
2020 s = ConvertUInt64ToString(cryptoInfo.Algo, s);
2021 }
2022 if (isOK)
2023 {
2024 *s++ = ':';
2025 s = ConvertUInt32ToString(cryptoInfo.Cnt, s);
2026 *s++ = ':';
2027 s = ConvertUInt64ToString(cryptoInfo.Flags, s);
2028 }
2029 }
2030 *s = 0;
2031 prop = temp;
2032 break;
2033 }
2034
2035 case kpidCharacts:
2036 {
2037 AString s;
2038
2039 if (item.ACL >= 0)
2040 s.Add_OptSpaced("ACL");
2041
2042 const UInt32 flags = item.Flags;
2043 if (flags != 0)
2044 {
2045 const AString s2 = FlagsToString(k_FileFlags, Z7_ARRAY_SIZE(k_FileFlags), flags);
2046 if (!s2.IsEmpty())
2047 s.Add_OptSpaced(s2);
2048 }
2049
2050 item.PrintInfo(s);
2051
2052 if (!s.IsEmpty())
2053 prop = s;
2054 break;
2055 }
2056
2057
2058 case kpidHostOS:
2059 if (item.HostOS < Z7_ARRAY_SIZE(kHostOS))
2060 prop = kHostOS[(size_t)item.HostOS];
2061 else
2062 prop = (UInt64)item.HostOS;
2063 break;
2064 }
2065
2066 prop.Detach(value);
2067 return S_OK;
2068
2069 COM_TRY_END
2070 }
2071
2072
2073
2074 // ---------- Copy Links ----------
2075
2076 static int CompareItemsPaths(const CHandler &handler, unsigned p1, unsigned p2, const AString *name1)
2077 {
2078 const CItem &item1 = handler._items[handler._refs[p1].Item];
2079 const CItem &item2 = handler._items[handler._refs[p2].Item];
2080
2081 if (item1.Version_Defined)
2082 {
2083 if (!item2.Version_Defined)
2084 return -1;
2085 const int res = MyCompare(item1.Version, item2.Version);
2086 if (res != 0)
2087 return res;
2088 }
2089 else if (item2.Version_Defined)
2090 return 1;
2091
2092 if (!name1)
2093 name1 = &item1.Name;
2094 return strcmp(*name1, item2.Name);
2095 }
2096
2097 static int CompareItemsPaths2(const CHandler &handler, unsigned p1, unsigned p2, const AString *name1)
2098 {
2099 const int res = CompareItemsPaths(handler, p1, p2, name1);
2100 if (res != 0)
2101 return res;
2102 return MyCompare(p1, p2);
2103 }
2104
2105 static int CompareItemsPaths_Sort(const unsigned *p1, const unsigned *p2, void *param)
2106 {
2107 return CompareItemsPaths2(*(const CHandler *)param, *p1, *p2, NULL);
2108 }
2109
2110 static int FindLink(const CHandler &handler, const CUIntVector &sorted,
2111 const AString &s, unsigned index)
2112 {
2113 unsigned left = 0, right = sorted.Size();
2114 for (;;)
2115 {
2116 if (left == right)
2117 {
2118 if (left > 0)
2119 {
2120 const unsigned refIndex = sorted[left - 1];
2121 if (CompareItemsPaths(handler, index, refIndex, &s) == 0)
2122 return (int)refIndex;
2123 }
2124 if (right < sorted.Size())
2125 {
2126 const unsigned refIndex = sorted[right];
2127 if (CompareItemsPaths(handler, index, refIndex, &s) == 0)
2128 return (int)refIndex;
2129 }
2130 return -1;
2131 }
2132
2133 const unsigned mid = (left + right) / 2;
2134 const unsigned refIndex = sorted[mid];
2135 const int compare = CompareItemsPaths2(handler, index, refIndex, &s);
2136 if (compare == 0)
2137 return (int)refIndex;
2138 if (compare < 0)
2139 right = mid;
2140 else
2141 left = mid + 1;
2142 }
2143 }
2144
2145 void CHandler::FillLinks()
2146 {
2147 unsigned i;
2148
2149 bool need_FillLinks = false;
2150
2151 for (i = 0; i < _refs.Size(); i++)
2152 {
2153 const CItem &item = _items[_refs[i].Item];
2154 if (!item.IsDir()
2155 && !item.IsService()
2156 && item.NeedUse_as_CopyLink())
2157 need_FillLinks = true;
2158
2159 if (!item.IsSolid())
2160 _numBlocks++;
2161
2162 const unsigned algo = item.Get_AlgoVersion_RawBits();
2163 _algo_Mask |= (UInt64)1 << algo;
2164 _rar5comapt_mask |= 1u << item.Get_Rar5_CompatBit();
2165 if (!item.IsDir() && algo < Z7_ARRAY_SIZE(_methodMasks))
2166 {
2167 _methodMasks[algo] |= 1u << (item.Get_Method());
2168 UInt64 d = 32 + item.Get_DictSize_Frac();
2169 d <<= (12 + item.Get_DictSize_Main());
2170 if (_dictMaxSizes[algo] < d)
2171 _dictMaxSizes[algo] = d;
2172 }
2173 }
2174
2175 if (!need_FillLinks)
2176 return;
2177
2178 CUIntVector sorted;
2179 for (i = 0; i < _refs.Size(); i++)
2180 {
2181 const CItem &item = _items[_refs[i].Item];
2182 if (!item.IsDir() && !item.IsService())
2183 sorted.Add(i);
2184 }
2185
2186 if (sorted.IsEmpty())
2187 return;
2188
2189 sorted.Sort(CompareItemsPaths_Sort, (void *)this);
2190
2191 AString link;
2192
2193 for (i = 0; i < _refs.Size(); i++)
2194 {
2195 CRefItem &ref = _refs[i];
2196 const CItem &item = _items[ref.Item];
2197 if (item.IsDir() || item.IsService() || item.PackSize != 0)
2198 continue;
2199 CLinkInfo linkInfo;
2200 if (!item.FindExtra_Link(linkInfo) || linkInfo.Type != NLinkType::kFileCopy)
2201 continue;
2202 link.SetFrom_CalcLen((const char *)(item.Extra + linkInfo.NameOffset), linkInfo.NameLen);
2203 const int linkIndex = FindLink(*this, sorted, link, i);
2204 if (linkIndex < 0)
2205 continue;
2206 if ((unsigned)linkIndex >= i)
2207 continue; // we don't support forward links that can lead to loops
2208 const CRefItem &linkRef = _refs[linkIndex];
2209 const CItem &linkItem = _items[linkRef.Item];
2210 if (linkItem.Size == item.Size)
2211 {
2212 if (linkRef.Link >= 0)
2213 ref.Link = linkRef.Link;
2214 else if (!linkItem.NeedUse_as_CopyLink())
2215 ref.Link = linkIndex;
2216 }
2217 }
2218 }
2219
2220
2221
2222 HRESULT CHandler::Open2(IInStream *stream,
2223 const UInt64 *maxCheckStartPosition,
2224 IArchiveOpenCallback *openCallback)
2225 {
2226 CMyComPtr<IArchiveOpenVolumeCallback> openVolumeCallback;
2227 // CMyComPtr<ICryptoGetTextPassword> getTextPassword;
2228 NRar::CVolumeName seqName;
2229 CTempBuf tempBuf;
2230 CUnpacker unpacker;
2231
2232 if (openCallback)
2233 {
2234 openCallback->QueryInterface(IID_IArchiveOpenVolumeCallback, (void **)&openVolumeCallback);
2235 openCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&unpacker.getTextPassword);
2236 }
2237 // unpacker.getTextPassword = getTextPassword;
2238
2239 CInArchive arch;
2240 int prevSplitFile = -1;
2241 int prevMainFile = -1;
2242 UInt64 totalBytes = 0;
2243 UInt64 curBytes = 0;
2244 bool nextVol_is_Required = false;
2245
2246 for (;;)
2247 {
2248 CMyComPtr<IInStream> inStream;
2249
2250 if (_arcs.IsEmpty())
2251 inStream = stream;
2252 else
2253 {
2254 if (!openVolumeCallback)
2255 break;
2256 if (_arcs.Size() == 1)
2257 {
2258 UString baseName;
2259 {
2260 NCOM::CPropVariant prop;
2261 RINOK(openVolumeCallback->GetProperty(kpidName, &prop))
2262 if (prop.vt != VT_BSTR)
2263 break;
2264 baseName = prop.bstrVal;
2265 }
2266 if (!seqName.InitName(baseName))
2267 break;
2268 }
2269 const UString volName = seqName.GetNextName();
2270 const HRESULT result = openVolumeCallback->GetStream(volName, &inStream);
2271 if (result != S_OK && result != S_FALSE)
2272 return result;
2273 if (!inStream || result != S_OK)
2274 {
2275 if (nextVol_is_Required)
2276 _missingVolName = volName;
2277 break;
2278 }
2279 }
2280
2281 UInt64 endPos = 0;
2282 RINOK(InStream_GetPos_GetSize(inStream, arch.StreamStartPosition, endPos))
2283
2284 if (openCallback)
2285 {
2286 totalBytes += endPos;
2287 RINOK(openCallback->SetTotal(NULL, &totalBytes))
2288 }
2289
2290 CInArcInfo arcInfo_Open;
2291 {
2292 const HRESULT res = arch.Open(inStream, maxCheckStartPosition, unpacker.getTextPassword, arcInfo_Open);
2293 if (arch.IsArc && arch.UnexpectedEnd)
2294 _errorFlags |= kpv_ErrorFlags_UnexpectedEnd;
2295 if (_arcs.IsEmpty())
2296 _isArc = arch.IsArc;
2297 if (res != S_OK)
2298 {
2299 if (res != S_FALSE)
2300 return res;
2301 if (_arcs.IsEmpty())
2302 return res;
2303 break;
2304 }
2305 }
2306
2307 CArc &arc = _arcs.AddNew();
2308 CInArcInfo &arcInfo = arc.Info;
2309 arcInfo = arcInfo_Open;
2310 arc.Stream = inStream;
2311
2312 CItem item;
2313
2314 for (;;)
2315 {
2316 item.Clear();
2317
2318 arcInfo.EndPos = arch.Position;
2319 if (arch.Position > endPos)
2320 {
2321 _errorFlags |= kpv_ErrorFlags_UnexpectedEnd;
2322 break;
2323 }
2324 RINOK(InStream_SeekSet(inStream, arch.Position))
2325
2326 {
2327 CInArchive::CHeader h;
2328 const HRESULT res = arch.ReadBlockHeader(h);
2329 if (res != S_OK)
2330 {
2331 if (res != S_FALSE)
2332 return res;
2333 if (arch.UnexpectedEnd)
2334 {
2335 _errorFlags |= kpv_ErrorFlags_UnexpectedEnd;
2336 if (arcInfo.EndPos < arch.Position)
2337 arcInfo.EndPos = arch.Position;
2338 if (arcInfo.EndPos < endPos)
2339 arcInfo.EndPos = endPos;
2340 }
2341 else
2342 _errorFlags |= kpv_ErrorFlags_HeadersError;
2343 break;
2344 }
2345
2346 if (h.Type == NHeaderType::kEndOfArc)
2347 {
2348 arcInfo.EndPos = arch.Position;
2349 arcInfo.EndOfArchive_was_Read = true;
2350 if (!arch.ReadVar(arcInfo.EndFlags))
2351 _errorFlags |= kpv_ErrorFlags_HeadersError;
2352 if (!arch.Is_Buf_Finished() || h.ExtraSize || h.DataSize)
2353 arcInfo.UnsupportedFeature = true;
2354 if (arcInfo.IsVolume())
2355 {
2356 // for multivolume archives RAR can add ZERO bytes at the end for alignment.
2357 // We must skip these bytes to prevent phySize warning.
2358 RINOK(InStream_SeekSet(inStream, arcInfo.EndPos))
2359 bool areThereNonZeros;
2360 UInt64 numZeros;
2361 const UInt64 maxSize = 1 << 12;
2362 RINOK(ReadZeroTail(inStream, areThereNonZeros, numZeros, maxSize))
2363 if (!areThereNonZeros && numZeros != 0 && numZeros <= maxSize)
2364 arcInfo.EndPos += numZeros;
2365 }
2366 break;
2367 }
2368
2369 if (h.Type != NHeaderType::kFile &&
2370 h.Type != NHeaderType::kService)
2371 {
2372 _errorFlags |= kpv_ErrorFlags_UnsupportedFeature;
2373 break;
2374 }
2375
2376 item.RecordType = (Byte)h.Type;
2377 if (!arch.ReadFileHeader(h, item))
2378 {
2379 _errorFlags |= kpv_ErrorFlags_HeadersError;
2380 break;
2381 }
2382 item.DataPos = arch.Position;
2383 }
2384
2385 bool isOk_packSize = true;
2386 {
2387 arcInfo.EndPos = arch.Position;
2388 if (arch.Position + item.PackSize < arch.Position)
2389 {
2390 isOk_packSize = false;
2391 _errorFlags |= kpv_ErrorFlags_HeadersError;
2392 if (arcInfo.EndPos < endPos)
2393 arcInfo.EndPos = endPos;
2394 }
2395 else
2396 {
2397 arch.AddToSeekValue(item.PackSize); // Position points to next header;
2398 arcInfo.EndPos = arch.Position;
2399 }
2400 }
2401
2402 bool needAdd = true;
2403
2404 if (!_comment_WasUsedInArc
2405 && _comment.Size() == 0
2406 && item.Is_CMT())
2407 {
2408 _comment_WasUsedInArc = true;
2409 if ( item.PackSize <= kCommentSize_Max
2410 && item.PackSize == item.Size
2411 && item.PackSize != 0
2412 && item.Get_Method() == 0
2413 && !item.IsSplit())
2414 {
2415 RINOK(unpacker.DecodeToBuf(EXTERNAL_CODECS_VARS item, item.PackSize, inStream, _comment))
2416 needAdd = false;
2417 // item.RecordType = (Byte)NHeaderType::kFile; // for debug
2418 }
2419 }
2420
2421 CRefItem ref;
2422 ref.Item = _items.Size();
2423 ref.Last = ref.Item;
2424 ref.Parent = -1;
2425 ref.Link = -1;
2426
2427 if (needAdd)
2428 {
2429 if (item.IsService())
2430 {
2431 if (item.Is_STM())
2432 {
2433 if (prevMainFile >= 0)
2434 ref.Parent = prevMainFile;
2435 }
2436 else
2437 {
2438 needAdd = false;
2439 if (item.Is_ACL())
2440 {
2441 _acl_Used = true;
2442 if (item.IsEncrypted() && !arch.m_CryptoMode)
2443 _error_in_ACL = true;
2444 else if (item.IsSolid()
2445 || prevMainFile < 0
2446 || item.Size >= (1 << 24)
2447 || item.Size == 0)
2448 _error_in_ACL = true;
2449 if (prevMainFile >= 0 && item.Size < (1 << 24) && item.Size != 0)
2450 {
2451 CItem &mainItem = _items[_refs[prevMainFile].Item];
2452
2453 if (mainItem.ACL < 0)
2454 {
2455 CByteBuffer acl;
2456 const HRESULT res = tempBuf.Decode(EXTERNAL_CODECS_VARS item, inStream, unpacker, acl);
2457 if (!item.IsSplitAfter())
2458 tempBuf.Clear();
2459 if (res != S_OK)
2460 {
2461 tempBuf.Clear();
2462 if (res != S_FALSE && res != E_NOTIMPL)
2463 return res;
2464 _error_in_ACL = true;
2465 }
2466 else if (acl.Size() != 0)
2467 {
2468 if (_acls.IsEmpty() || acl != _acls.Back())
2469 _acls.Add(acl);
2470 mainItem.ACL = (int)_acls.Size() - 1;
2471 }
2472 }
2473 }
2474 }
2475 }
2476 } // item.IsService()
2477
2478 if (needAdd)
2479 {
2480 if (item.IsSplitBefore())
2481 {
2482 if (prevSplitFile >= 0)
2483 {
2484 CRefItem &ref2 = _refs[prevSplitFile];
2485 CItem &prevItem = _items[ref2.Last];
2486 if (item.IsNextForItem(prevItem))
2487 {
2488 ref2.Last = _items.Size();
2489 prevItem.NextItem = (int)ref2.Last;
2490 needAdd = false;
2491 }
2492 }
2493 else
2494 _split_Error = true;
2495 }
2496 }
2497
2498 if (needAdd)
2499 {
2500 if (item.IsSplitAfter())
2501 prevSplitFile = (int)_refs.Size();
2502 if (!item.IsService())
2503 prevMainFile = (int)_refs.Size();
2504 }
2505 }
2506
2507 {
2508 UInt64 version;
2509 if (item.FindExtra_Version(version))
2510 {
2511 item.Version_Defined = true;
2512 item.Version = version;
2513 }
2514 }
2515
2516 item.VolIndex = _arcs.Size() - 1;
2517 _items.Add(item);
2518 if (needAdd)
2519 _refs.Add(ref);
2520
2521 if (openCallback && (_items.Size() & 0xFF) == 0)
2522 {
2523 const UInt64 numFiles = _refs.Size(); // _items.Size()
2524 const UInt64 numBytes = curBytes + item.DataPos;
2525 RINOK(openCallback->SetCompleted(&numFiles, &numBytes))
2526 }
2527
2528 if (!isOk_packSize)
2529 break;
2530 }
2531
2532 curBytes += endPos;
2533
2534 nextVol_is_Required = false;
2535
2536 if (!arcInfo.IsVolume())
2537 break;
2538
2539 if (arcInfo.EndOfArchive_was_Read)
2540 {
2541 if (!arcInfo.AreMoreVolumes())
2542 break;
2543 nextVol_is_Required = true;
2544 }
2545 }
2546
2547 FillLinks();
2548 return S_OK;
2549 }
2550
2551
2552
2553 Z7_COM7F_IMF(CHandler::Open(IInStream *stream,
2554 const UInt64 *maxCheckStartPosition,
2555 IArchiveOpenCallback *openCallback))
2556 {
2557 COM_TRY_BEGIN
2558 Close();
2559 return Open2(stream, maxCheckStartPosition, openCallback);
2560 COM_TRY_END
2561 }
2562
2563 Z7_COM7F_IMF(CHandler::Close())
2564 {
2565 COM_TRY_BEGIN
2566 _missingVolName.Empty();
2567 _errorFlags = 0;
2568 // _warningFlags = 0;
2569 _isArc = false;
2570 _comment_WasUsedInArc = false;
2571 _acl_Used = false;
2572 _error_in_ACL = false;
2573 _split_Error = false;
2574 _numBlocks = 0;
2575 _rar5comapt_mask = 0;
2576 _algo_Mask = 0; // (UInt64)0u - 1;
2577 for (unsigned i = 0; i < Z7_ARRAY_SIZE(_methodMasks); i++)
2578 {
2579 _methodMasks[i] = 0;
2580 _dictMaxSizes[i] = 0;
2581 }
2582
2583 _refs.Clear();
2584 _items.Clear();
2585 _arcs.Clear();
2586 _acls.Clear();
2587 _comment.Free();
2588 return S_OK;
2589 COM_TRY_END
2590 }
2591
2592
2593 Z7_CLASS_IMP_NOQIB_1(
2594 CVolsInStream
2595 , ISequentialInStream
2596 )
2597 UInt64 _rem;
2598 ISequentialInStream *_stream;
2599 const CObjectVector<CArc> *_arcs;
2600 const CObjectVector<CItem> *_items;
2601 int _itemIndex;
2602 public:
2603 bool CrcIsOK;
2604 private:
2605 CHash _hash;
2606 public:
2607 void Init(const CObjectVector<CArc> *arcs,
2608 const CObjectVector<CItem> *items,
2609 unsigned itemIndex)
2610 {
2611 _arcs = arcs;
2612 _items = items;
2613 _itemIndex = (int)itemIndex;
2614 _stream = NULL;
2615 CrcIsOK = true;
2616 }
2617 };
2618
2619 Z7_COM7F_IMF(CVolsInStream::Read(void *data, UInt32 size, UInt32 *processedSize))
2620 {
2621 if (processedSize)
2622 *processedSize = 0;
2623 UInt32 realProcessedSize = 0;
2624
2625 while (size != 0)
2626 {
2627 if (!_stream)
2628 {
2629 if (_itemIndex < 0)
2630 break;
2631 const CItem &item = (*_items)[_itemIndex];
2632 IInStream *s = (*_arcs)[item.VolIndex].Stream;
2633 RINOK(InStream_SeekSet(s, item.GetDataPosition()))
2634 _stream = s;
2635 if (CrcIsOK && item.IsSplitAfter())
2636 _hash.Init(item);
2637 else
2638 _hash.Init_NoCalc();
2639 _rem = item.PackSize;
2640 }
2641 {
2642 UInt32 cur = size;
2643 if (cur > _rem)
2644 cur = (UInt32)_rem;
2645 const UInt32 num = cur;
2646 HRESULT res = _stream->Read(data, cur, &cur);
2647 _hash.Update(data, cur);
2648 realProcessedSize += cur;
2649 if (processedSize)
2650 *processedSize = realProcessedSize;
2651 data = (Byte *)data + cur;
2652 size -= cur;
2653 _rem -= cur;
2654 if (_rem == 0)
2655 {
2656 const CItem &item = (*_items)[_itemIndex];
2657 _itemIndex = item.NextItem;
2658 if (!_hash.Check(item, NULL)) // RAR doesn't use MAC here
2659 CrcIsOK = false;
2660 _stream = NULL;
2661 }
2662 if (res != S_OK)
2663 return res;
2664 if (realProcessedSize != 0)
2665 return S_OK;
2666 if (cur == 0 && num != 0)
2667 return S_OK;
2668 }
2669 }
2670
2671 return S_OK;
2672 }
2673
2674
2675 static int FindLinkBuf(CObjectVector<CLinkFile> &linkFiles, unsigned index)
2676 {
2677 unsigned left = 0, right = linkFiles.Size();
2678 for (;;)
2679 {
2680 if (left == right)
2681 return -1;
2682 const unsigned mid = (left + right) / 2;
2683 const unsigned linkIndex = linkFiles[mid].Index;
2684 if (index == linkIndex)
2685 return (int)mid;
2686 if (index < linkIndex)
2687 right = mid;
2688 else
2689 left = mid + 1;
2690 }
2691 }
2692
2693
2694 static inline int DecoderRes_to_OpRes(HRESULT res, bool crcOK)
2695 {
2696 if (res == E_NOTIMPL)
2697 return NExtract::NOperationResult::kUnsupportedMethod;
2698 // if (res == S_FALSE)
2699 if (res != S_OK)
2700 return NExtract::NOperationResult::kDataError;
2701 return crcOK ?
2702 NExtract::NOperationResult::kOK :
2703 NExtract::NOperationResult::kCRCError;
2704 }
2705
2706
2707 static HRESULT CopyData_with_Progress(const Byte *data, size_t size,
2708 ISequentialOutStream *outStream, ICompressProgressInfo *progress)
2709 {
2710 UInt64 pos64 = 0;
2711 while (size)
2712 {
2713 const UInt32 kStepSize = (UInt32)1 << 24;
2714 UInt32 cur = kStepSize;
2715 if (cur > size)
2716 cur = (UInt32)size;
2717 RINOK(outStream->Write(data, cur, &cur))
2718 if (cur == 0)
2719 return E_FAIL;
2720 size -= cur;
2721 data += cur;
2722 pos64 += cur;
2723 if (progress)
2724 {
2725 RINOK(progress->SetRatioInfo(&pos64, &pos64))
2726 }
2727 }
2728 return S_OK;
2729 }
2730
2731
2732 Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems,
2733 Int32 testMode, IArchiveExtractCallback *extractCallback))
2734 {
2735 COM_TRY_BEGIN
2736 const bool allFilesMode = (numItems == (UInt32)(Int32)-1);
2737 if (allFilesMode)
2738 numItems = (UInt32)_refs.Size();
2739 if (numItems == 0)
2740 return S_OK;
2741
2742 CByteArr extractStatuses(_refs.Size());
2743 memset(extractStatuses, 0, _refs.Size());
2744
2745 // we don't want to use temp buffer for big link files.
2746 const size_t k_CopyLinkFile_MaxSize = (size_t)1 << (28 + sizeof(size_t) / 2);
2747
2748 const Byte kStatus_Extract = 1 << 0;
2749 const Byte kStatus_Skip = 1 << 1;
2750 const Byte kStatus_Link = 1 << 2;
2751
2752 /*
2753 In original RAR:
2754 1) service streams are not allowed to be solid,
2755 and solid flag must be ignored for service streams.
2756 2) If RAR creates new solid block and first file in solid block is Link file,
2757 then it can clear solid flag for Link file and
2758 clear solid flag for first non-Link file after Link file.
2759 */
2760
2761 CObjectVector<CLinkFile> linkFiles;
2762
2763 {
2764 UInt64 total = 0;
2765 bool isThereUndefinedSize = false;
2766 bool thereAreLinks = false;
2767 {
2768 unsigned solidLimit = 0;
2769 for (UInt32 t = 0; t < numItems; t++)
2770 {
2771 const unsigned index = (unsigned)(allFilesMode ? t : indices[t]);
2772 const CRefItem &ref = _refs[index];
2773 const CItem &item = _items[ref.Item];
2774 const CItem &lastItem = _items[ref.Last];
2775
2776 extractStatuses[index] |= kStatus_Extract;
2777
2778 if (!lastItem.Is_UnknownSize())
2779 total += lastItem.Size;
2780 else
2781 isThereUndefinedSize = true;
2782
2783 if (ref.Link >= 0)
2784 {
2785 // 18.06 fixed: we use links for Test mode too
2786 // if (!testMode)
2787 {
2788 if ((unsigned)ref.Link < index)
2789 {
2790 const CRefItem &linkRef = _refs[(unsigned)ref.Link];
2791 const CItem &linkItem = _items[linkRef.Item];
2792 if (linkItem.IsSolid())
2793 if (testMode || linkItem.Size <= k_CopyLinkFile_MaxSize)
2794 {
2795 if (extractStatuses[(unsigned)ref.Link] == 0)
2796 {
2797 const CItem &lastLinkItem = _items[linkRef.Last];
2798 if (!lastLinkItem.Is_UnknownSize())
2799 total += lastLinkItem.Size;
2800 else
2801 isThereUndefinedSize = true;
2802 }
2803 extractStatuses[(unsigned)ref.Link] |= kStatus_Link;
2804 thereAreLinks = true;
2805 }
2806 }
2807 }
2808 continue;
2809 }
2810
2811 if (item.IsService())
2812 continue;
2813
2814 if (item.IsSolid())
2815 {
2816 unsigned j = index;
2817
2818 while (j > solidLimit)
2819 {
2820 j--;
2821 const CRefItem &ref2 = _refs[j];
2822 const CItem &item2 = _items[ref2.Item];
2823 if (!item2.IsService())
2824 {
2825 if (extractStatuses[j] == 0)
2826 {
2827 const CItem &lastItem2 = _items[ref2.Last];
2828 if (!lastItem2.Is_UnknownSize())
2829 total += lastItem2.Size;
2830 else
2831 isThereUndefinedSize = true;
2832 }
2833 extractStatuses[j] |= kStatus_Skip;
2834 if (!item2.IsSolid())
2835 break;
2836 }
2837 }
2838 }
2839
2840 solidLimit = index + 1;
2841 }
2842 }
2843
2844 if (thereAreLinks)
2845 {
2846 unsigned solidLimit = 0;
2847
2848 FOR_VECTOR (i, _refs)
2849 {
2850 if ((extractStatuses[i] & kStatus_Link) == 0)
2851 continue;
2852
2853 // We use CLinkFile for testMode too.
2854 // So we can show errors for copy files.
2855 // if (!testMode)
2856 {
2857 CLinkFile &linkFile = linkFiles.AddNew();
2858 linkFile.Index = i;
2859 }
2860
2861 const CItem &item = _items[_refs[i].Item];
2862 /*
2863 if (item.IsService())
2864 continue;
2865 */
2866
2867 if (item.IsSolid())
2868 {
2869 unsigned j = i;
2870
2871 while (j > solidLimit)
2872 {
2873 j--;
2874 const CRefItem &ref2 = _refs[j];
2875 const CItem &item2 = _items[ref2.Item];
2876 if (!item2.IsService())
2877 {
2878 if (extractStatuses[j] != 0)
2879 break;
2880 extractStatuses[j] = kStatus_Skip;
2881 {
2882 const CItem &lastItem2 = _items[ref2.Last];
2883 if (!lastItem2.Is_UnknownSize())
2884 total += lastItem2.Size;
2885 else
2886 isThereUndefinedSize = true;
2887 }
2888 if (!item2.IsSolid())
2889 break;
2890 }
2891 }
2892 }
2893
2894 solidLimit = i + 1;
2895 }
2896
2897 if (!testMode)
2898 for (UInt32 t = 0; t < numItems; t++)
2899 {
2900 const unsigned index = (unsigned)(allFilesMode ? t : indices[t]);
2901 const CRefItem &ref = _refs[index];
2902
2903 const int linkIndex = ref.Link;
2904 if (linkIndex < 0 || (unsigned)linkIndex >= index)
2905 continue;
2906 const CItem &linkItem = _items[_refs[(unsigned)linkIndex].Item];
2907 if (!linkItem.IsSolid() || linkItem.Size > k_CopyLinkFile_MaxSize)
2908 continue;
2909 const int bufIndex = FindLinkBuf(linkFiles, (unsigned)linkIndex);
2910 if (bufIndex < 0)
2911 return E_FAIL;
2912 linkFiles[bufIndex].NumLinks++;
2913 }
2914 }
2915
2916 if (total != 0 || !isThereUndefinedSize)
2917 {
2918 RINOK(extractCallback->SetTotal(total))
2919 }
2920 }
2921
2922
2923
2924 // ---------- MEMORY REQUEST ----------
2925 {
2926 UInt64 dictMaxSize = 0;
2927 for (UInt32 i = 0; i < _refs.Size(); i++)
2928 {
2929 if (extractStatuses[i] == 0)
2930 continue;
2931 const CRefItem &ref = _refs[i];
2932 const CItem &item = _items[ref.Item];
2933 /*
2934 if (!item.IsDir() && !item.IsService() && item.NeedUse_as_CopyLink())
2935 {
2936 }
2937 */
2938 const unsigned algo = item.Get_AlgoVersion_RawBits();
2939 if (!item.IsDir() && algo < Z7_ARRAY_SIZE(_methodMasks))
2940 {
2941 const UInt64 d = item.Get_DictSize64();
2942 if (dictMaxSize < d)
2943 dictMaxSize = d;
2944 }
2945 }
2946 // we use callback, if dict exceeds (1 GB), because
2947 // client code can set low limit (1 GB) for allowed memory usage.
2948 const UInt64 k_MemLimit_for_Callback = (UInt64)1 << 30;
2949 if (dictMaxSize > (_memUsage_WasSet ?
2950 _memUsage_Decompress : k_MemLimit_for_Callback))
2951 {
2952 {
2953 CMyComPtr<IArchiveRequestMemoryUseCallback> requestMem;
2954 extractCallback->QueryInterface(IID_IArchiveRequestMemoryUseCallback, (void **)&requestMem);
2955 if (!requestMem)
2956 {
2957 if (_memUsage_WasSet)
2958 return E_OUTOFMEMORY;
2959 }
2960 else
2961 {
2962 UInt64 allowedSize = _memUsage_WasSet ?
2963 _memUsage_Decompress :
2964 (UInt64)1 << 32; // 4 GB is default allowed limit for RAR7
2965
2966 const UInt32 flags = (_memUsage_WasSet ?
2967 NRequestMemoryUseFlags::k_AllowedSize_WasForced |
2968 NRequestMemoryUseFlags::k_MLimit_Exceeded :
2969 (dictMaxSize > allowedSize) ?
2970 NRequestMemoryUseFlags::k_DefaultLimit_Exceeded:
2971 0)
2972 | NRequestMemoryUseFlags::k_SkipArc_IsExpected
2973 // | NRequestMemoryUseFlags::k_NoErrorMessage // for debug
2974 ;
2975
2976 // we set "Allow" for default case, if requestMem doesn't process anything.
2977 UInt32 answerFlags =
2978 (_memUsage_WasSet && dictMaxSize > allowedSize) ?
2979 NRequestMemoryAnswerFlags::k_Limit_Exceeded
2980 | NRequestMemoryAnswerFlags::k_SkipArc
2981 : NRequestMemoryAnswerFlags::k_Allow;
2982
2983 RINOK(requestMem->RequestMemoryUse(
2984 flags,
2985 NEventIndexType::kNoIndex,
2986 // NEventIndexType::kInArcIndex, // for debug
2987 0, // index
2988 NULL, // path
2989 dictMaxSize, &allowedSize, &answerFlags))
2990 if ( (answerFlags & NRequestMemoryAnswerFlags::k_Allow) == 0
2991 || (answerFlags & NRequestMemoryAnswerFlags::k_Stop)
2992 || (answerFlags & NRequestMemoryAnswerFlags::k_SkipArc)
2993 )
2994 {
2995 return E_OUTOFMEMORY;
2996 }
2997 /*
2998 if ((answerFlags & NRequestMemoryAnswerFlags::k_AskForBigFile) == 0 &&
2999 (answerFlags & NRequestMemoryAnswerFlags::k_ReportForBigFile) == 0)
3000 {
3001 // requestMem.Release();
3002 }
3003 */
3004 }
3005 }
3006 }
3007 }
3008
3009
3010
3011 // ---------- UNPACK ----------
3012
3013 UInt64 totalUnpacked = 0;
3014 UInt64 totalPacked = 0;
3015 UInt64 curUnpackSize;
3016 UInt64 curPackSize;
3017
3018 CUnpacker unpacker;
3019 unpacker.NeedCrc = _needChecksumCheck;
3020 CMyComPtr2_Create<ISequentialInStream, CVolsInStream> volsInStream;
3021 CMyComPtr2_Create<ICompressProgressInfo, CLocalProgress> lps;
3022 lps->Init(extractCallback, false);
3023
3024 /*
3025 bool prevSolidWasSkipped = false;
3026 UInt64 solidDictSize_Skip = 0;
3027 */
3028
3029 for (unsigned i = 0;; i++,
3030 totalUnpacked += curUnpackSize,
3031 totalPacked += curPackSize)
3032 {
3033 lps->InSize = totalPacked;
3034 lps->OutSize = totalUnpacked;
3035 RINOK(lps->SetCur())
3036 {
3037 const unsigned num = _refs.Size();
3038 if (i >= num)
3039 break;
3040 for (;;)
3041 {
3042 if (extractStatuses[i] != 0)
3043 break;
3044 i++;
3045 if (i >= num)
3046 break;
3047 }
3048 if (i >= num)
3049 break;
3050 }
3051 curUnpackSize = 0;
3052 curPackSize = 0;
3053
3054 // isExtract means that we don't skip that item. So we need read data.
3055 const bool isExtract = ((extractStatuses[i] & kStatus_Extract) != 0);
3056 Int32 askMode =
3057 isExtract ? (testMode ?
3058 NExtract::NAskMode::kTest :
3059 NExtract::NAskMode::kExtract) :
3060 NExtract::NAskMode::kSkip;
3061
3062 unpacker.linkFile = NULL;
3063
3064 // if (!testMode)
3065 if ((extractStatuses[i] & kStatus_Link) != 0)
3066 {
3067 const int bufIndex = FindLinkBuf(linkFiles, i);
3068 if (bufIndex < 0)
3069 return E_FAIL;
3070 unpacker.linkFile = &linkFiles[bufIndex];
3071 }
3072
3073 const unsigned index = i;
3074 const CRefItem *ref = &_refs[index];
3075 const CItem *item = &_items[ref->Item];
3076 const CItem &lastItem = _items[ref->Last];
3077
3078 curUnpackSize = 0;
3079 if (!lastItem.Is_UnknownSize())
3080 curUnpackSize = lastItem.Size;
3081
3082 curPackSize = GetPackSize(index);
3083
3084 bool isSolid = false;
3085 if (!item->IsService())
3086 {
3087 if (item->IsSolid())
3088 isSolid = unpacker.SolidAllowed;
3089 unpacker.SolidAllowed = isSolid;
3090 }
3091
3092
3093 // ----- request mem -----
3094 /*
3095 // link files are complicated cases. (ref->Link >= 0)
3096 // link file can refer to non-solid file that can have big dictionary
3097 // link file can refer to solid files that requres buffer
3098 if (!item->IsDir() && requestMem && ref->Link < 0)
3099 {
3100 bool needSkip = false;
3101 if (isSolid)
3102 needSkip = prevSolidWasSkipped;
3103 else
3104 {
3105 // isSolid == false
3106 const unsigned algo = item->Get_AlgoVersion_RawBits();
3107 // const unsigned m = item.Get_Method();
3108 if (algo < Z7_ARRAY_SIZE(_methodMasks))
3109 {
3110 solidDictSize_Skip = item->Get_DictSize64();
3111 if (solidDictSize_Skip > allowedSize)
3112 needSkip = true;
3113 }
3114 }
3115 if (needSkip)
3116 {
3117 UInt32 answerFlags = 0;
3118 UInt64 allowedSize_File = allowedSize;
3119 RINOK(requestMem->RequestMemoryUse(
3120 NRequestMemoryUseFlags::k_Limit_Exceeded |
3121 NRequestMemoryUseFlags::k_IsReport,
3122 NEventIndexType::kInArcIndex,
3123 index,
3124 NULL, // path
3125 solidDictSize_Skip, &allowedSize_File, &answerFlags))
3126 if (!item->IsService())
3127 prevSolidWasSkipped = true;
3128 continue;
3129 }
3130 }
3131 if (!item->IsService() && item->IsDir())
3132 prevSolidWasSkipped = false;
3133 */
3134
3135 CMyComPtr<ISequentialOutStream> realOutStream;
3136 RINOK(extractCallback->GetStream((UInt32)index, &realOutStream, askMode))
3137
3138 if (item->IsDir())
3139 {
3140 RINOK(extractCallback->PrepareOperation(askMode))
3141 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kOK))
3142 continue;
3143 }
3144
3145 const int index2 = ref->Link;
3146
3147 int bufIndex = -1;
3148
3149 if (index2 >= 0)
3150 {
3151 const CRefItem &ref2 = _refs[index2];
3152 const CItem &item2 = _items[ref2.Item];
3153 const CItem &lastItem2 = _items[ref2.Last];
3154 if (!item2.IsSolid())
3155 {
3156 item = &item2;
3157 ref = &ref2;
3158 if (!lastItem2.Is_UnknownSize())
3159 curUnpackSize = lastItem2.Size;
3160 else
3161 curUnpackSize = 0;
3162 curPackSize = GetPackSize((unsigned)index2);
3163 }
3164 else
3165 {
3166 if ((unsigned)index2 < index)
3167 bufIndex = FindLinkBuf(linkFiles, (unsigned)index2);
3168 }
3169 }
3170
3171 bool needCallback = true;
3172
3173 if (!realOutStream)
3174 {
3175 if (testMode)
3176 {
3177 if (item->NeedUse_as_CopyLink_or_HardLink())
3178 {
3179 Int32 opRes = NExtract::NOperationResult::kOK;
3180 if (bufIndex >= 0)
3181 {
3182 const CLinkFile &linkFile = linkFiles[bufIndex];
3183 opRes = DecoderRes_to_OpRes(linkFile.Res, linkFile.crcOK);
3184 }
3185
3186 RINOK(extractCallback->PrepareOperation(askMode))
3187 RINOK(extractCallback->SetOperationResult(opRes))
3188 continue;
3189 }
3190 }
3191 else
3192 {
3193 if (item->IsService())
3194 continue;
3195
3196 needCallback = false;
3197
3198 if (!item->NeedUse_as_HardLink())
3199 if (index2 < 0)
3200
3201 for (unsigned n = i + 1; n < _refs.Size(); n++)
3202 {
3203 const CItem &nextItem = _items[_refs[n].Item];
3204 if (nextItem.IsService())
3205 continue;
3206 if (!nextItem.IsSolid())
3207 break;
3208 if (extractStatuses[i] != 0)
3209 {
3210 needCallback = true;
3211 break;
3212 }
3213 }
3214
3215 askMode = NExtract::NAskMode::kSkip;
3216 }
3217 }
3218
3219 if (needCallback)
3220 {
3221 RINOK(extractCallback->PrepareOperation(askMode))
3222 }
3223
3224 if (bufIndex >= 0)
3225 {
3226 CLinkFile &linkFile = linkFiles[bufIndex];
3227
3228 if (isExtract)
3229 {
3230 if (linkFile.NumLinks == 0)
3231 return E_FAIL;
3232
3233 if (needCallback)
3234 if (realOutStream)
3235 {
3236 RINOK(CopyData_with_Progress(linkFile.Data, linkFile.Data.Size(), realOutStream, lps))
3237 }
3238
3239 if (--linkFile.NumLinks == 0)
3240 linkFile.Data.Free();
3241 }
3242
3243 if (needCallback)
3244 {
3245 RINOK(extractCallback->SetOperationResult(DecoderRes_to_OpRes(linkFile.Res, linkFile.crcOK)))
3246 }
3247 continue;
3248 }
3249
3250 if (!needCallback)
3251 continue;
3252
3253 if (item->NeedUse_as_CopyLink())
3254 {
3255 const int opRes = realOutStream ?
3256 NExtract::NOperationResult::kUnsupportedMethod:
3257 NExtract::NOperationResult::kOK;
3258 realOutStream.Release();
3259 RINOK(extractCallback->SetOperationResult(opRes))
3260 continue;
3261 }
3262
3263 volsInStream->Init(&_arcs, &_items, ref->Item);
3264
3265 const UInt64 packSize = curPackSize;
3266
3267 if (item->IsEncrypted())
3268 if (!unpacker.getTextPassword)
3269 extractCallback->QueryInterface(IID_ICryptoGetTextPassword, (void **)&unpacker.getTextPassword);
3270
3271 bool wrongPassword;
3272 HRESULT result = unpacker.Create(EXTERNAL_CODECS_VARS *item, isSolid, wrongPassword);
3273
3274 if (wrongPassword)
3275 {
3276 realOutStream.Release();
3277 RINOK(extractCallback->SetOperationResult(NExtract::NOperationResult::kWrongPassword))
3278 continue;
3279 }
3280
3281 bool crcOK = true;
3282 if (result == S_OK)
3283 result = unpacker.Code(*item, _items[ref->Last], packSize, volsInStream, realOutStream, lps, crcOK);
3284 realOutStream.Release();
3285 if (!volsInStream->CrcIsOK)
3286 crcOK = false;
3287
3288 int opRes = crcOK ?
3289 NExtract::NOperationResult::kOK:
3290 NExtract::NOperationResult::kCRCError;
3291
3292 if (result != S_OK)
3293 {
3294 if (result == S_FALSE)
3295 opRes = NExtract::NOperationResult::kDataError;
3296 else if (result == E_NOTIMPL)
3297 opRes = NExtract::NOperationResult::kUnsupportedMethod;
3298 else
3299 return result;
3300 }
3301
3302 RINOK(extractCallback->SetOperationResult(opRes))
3303 }
3304
3305 {
3306 FOR_VECTOR (k, linkFiles)
3307 if (linkFiles[k].NumLinks != 0)
3308 return E_FAIL;
3309 }
3310
3311 return S_OK;
3312 COM_TRY_END
3313 }
3314
3315
3316 CHandler::CHandler()
3317 {
3318 InitDefaults();
3319 }
3320
3321 void CHandler::InitDefaults()
3322 {
3323 _needChecksumCheck = true;
3324 _memUsage_WasSet = false;
3325 _memUsage_Decompress = (UInt64)1 << 32;
3326 }
3327
3328 Z7_COM7F_IMF(CHandler::SetProperties(const wchar_t * const *names, const PROPVARIANT *values, UInt32 numProps))
3329 {
3330 InitDefaults();
3331
3332 for (UInt32 i = 0; i < numProps; i++)
3333 {
3334 UString name = names[i];
3335 name.MakeLower_Ascii();
3336 if (name.IsEmpty())
3337 return E_INVALIDARG;
3338
3339 const PROPVARIANT &prop = values[i];
3340
3341 if (name.IsPrefixedBy_Ascii_NoCase("mt"))
3342 {
3343 }
3344 else if (name.IsPrefixedBy_Ascii_NoCase("memx"))
3345 {
3346 UInt64 memAvail;
3347 if (!NWindows::NSystem::GetRamSize(memAvail))
3348 memAvail = (UInt64)(sizeof(size_t)) << 28;
3349 UInt64 v;
3350 if (!ParseSizeString(name.Ptr(4), prop, memAvail, v))
3351 return E_INVALIDARG;
3352 _memUsage_Decompress = v;
3353 _memUsage_WasSet = true;
3354 }
3355 else if (name.IsPrefixedBy_Ascii_NoCase("crc"))
3356 {
3357 name.Delete(0, 3);
3358 UInt32 crcSize = 1;
3359 RINOK(ParsePropToUInt32(name, prop, crcSize))
3360 _needChecksumCheck = (crcSize != 0);
3361 }
3362 else
3363 {
3364 return E_INVALIDARG;
3365 }
3366 }
3367 return S_OK;
3368 }
3369
3370
3371 IMPL_ISetCompressCodecsInfo
3372
3373 REGISTER_ARC_I(
3374 "Rar5", "rar r00", NULL, 0xCC,
3375 kMarker,
3376 0,
3377 NArcInfoFlags::kFindSignature,
3378 NULL)
3379
3380 }}
3381
3382
3383 Z7_CLASS_IMP_COM_2(
3384 CBlake2spHasher
3385 , IHasher
3386 , ICompressSetCoderProperties
3387 )
3388 CAlignedBuffer1 _buf;
3389 // CBlake2sp _blake;
3390 #define Z7_BLACK2S_ALIGN_OBJECT_OFFSET 0
3391 CBlake2sp *Obj() { return (CBlake2sp *)(void *)((Byte *)_buf + Z7_BLACK2S_ALIGN_OBJECT_OFFSET); }
3392 public:
3393 Byte _mtDummy[1 << 7]; // it's public to eliminate clang warning: unused private field
3394 CBlake2spHasher():
3395 _buf(sizeof(CBlake2sp) + Z7_BLACK2S_ALIGN_OBJECT_OFFSET)
3396 {
3397 Blake2sp_SetFunction(Obj(), 0);
3398 Blake2sp_InitState(Obj());
3399 }
3400 };
3401
3402 Z7_COM7F_IMF2(void, CBlake2spHasher::Init())
3403 {
3404 Blake2sp_InitState(Obj());
3405 }
3406
3407 Z7_COM7F_IMF2(void, CBlake2spHasher::Update(const void *data, UInt32 size))
3408 {
3409 #if 1
3410 Blake2sp_Update(Obj(), (const Byte *)data, (size_t)size);
3411 #else
3412 // for debug:
3413 for (;;)
3414 {
3415 if (size == 0)
3416 return;
3417 UInt32 size2 = (size * 0x85EBCA87) % size / 800;
3418 // UInt32 size2 = size / 2;
3419 if (size2 == 0)
3420 size2 = 1;
3421 Blake2sp_Update(Obj(), (const Byte *)data, size2);
3422 data = (const void *)((const Byte *)data + size2);
3423 size -= size2;
3424 }
3425 #endif
3426 }
3427
3428 Z7_COM7F_IMF2(void, CBlake2spHasher::Final(Byte *digest))
3429 {
3430 Blake2sp_Final(Obj(), digest);
3431 }
3432
3433 Z7_COM7F_IMF(CBlake2spHasher::SetCoderProperties(const PROPID *propIDs, const PROPVARIANT *coderProps, UInt32 numProps))
3434 {
3435 unsigned algo = 0;
3436 for (UInt32 i = 0; i < numProps; i++)
3437 {
3438 if (propIDs[i] == NCoderPropID::kDefaultProp)
3439 {
3440 const PROPVARIANT &prop = coderProps[i];
3441 if (prop.vt != VT_UI4)
3442 return E_INVALIDARG;
3443 /*
3444 if (prop.ulVal > Z7_BLAKE2S_ALGO_MAX)
3445 return E_NOTIMPL;
3446 */
3447 algo = (unsigned)prop.ulVal;
3448 }
3449 }
3450 if (!Blake2sp_SetFunction(Obj(), algo))
3451 return E_NOTIMPL;
3452 return S_OK;
3453 }
3454
3455 REGISTER_HASHER(CBlake2spHasher, 0x202, "BLAKE2sp", Z7_BLAKE2S_DIGEST_SIZE)
3456
3457 static struct CBlake2sp_Prepare { CBlake2sp_Prepare() { z7_Black2sp_Prepare(); } } g_Blake2sp_Prepare;
3458