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