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