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