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