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