"Fossies" - the Fresh Open Source Software Archive

Member "p7zip_16.02/CPP/7zip/Archive/Rar/Rar5Handler.cpp" (20 May 2016, 64713 Bytes) of package /linux/misc/p7zip_16.02_src_all.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "Rar5Handler.cpp": 15.14.1_src_all_vs_16.02_src_all.

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