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