• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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