tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

7zDecode.cpp (15490B)


      1 // 7zDecode.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../Common/LimitedStreams.h"
      6 #include "../../Common/ProgressUtils.h"
      7 #include "../../Common/StreamObjects.h"
      8 
      9 #include "7zDecode.h"
     10 
     11 namespace NArchive {
     12 namespace N7z {
     13 
     14 class CDecProgress:
     15  public ICompressProgressInfo,
     16  public CMyUnknownImp
     17 {
     18  CMyComPtr<ICompressProgressInfo> _progress;
     19 public:
     20  CDecProgress(ICompressProgressInfo *progress): _progress(progress) {}
     21  
     22  MY_UNKNOWN_IMP1(ICompressProgressInfo)
     23  STDMETHOD(SetRatioInfo)(const UInt64 *inSize, const UInt64 *outSize);
     24 };
     25 
     26 STDMETHODIMP CDecProgress::SetRatioInfo(const UInt64 * /* inSize */, const UInt64 *outSize)
     27 {
     28  return _progress->SetRatioInfo(NULL, outSize);
     29 }
     30 
     31 static void Convert_FolderInfo_to_BindInfo(const CFolderEx &folder, CBindInfoEx &bi)
     32 {
     33  bi.Clear();
     34  
     35  bi.Bonds.ClearAndSetSize(folder.Bonds.Size());
     36  unsigned i;
     37  for (i = 0; i < folder.Bonds.Size(); i++)
     38  {
     39    NCoderMixer2::CBond &bond = bi.Bonds[i];
     40    const N7z::CBond &folderBond = folder.Bonds[i];
     41    bond.PackIndex = folderBond.PackIndex;
     42    bond.UnpackIndex = folderBond.UnpackIndex;
     43  }
     44 
     45  bi.Coders.ClearAndSetSize(folder.Coders.Size());
     46  bi.CoderMethodIDs.ClearAndSetSize(folder.Coders.Size());
     47  for (i = 0; i < folder.Coders.Size(); i++)
     48  {
     49    const CCoderInfo &coderInfo = folder.Coders[i];
     50    bi.Coders[i].NumStreams = coderInfo.NumStreams;
     51    bi.CoderMethodIDs[i] = coderInfo.MethodID;
     52  }
     53  
     54  /*
     55  if (!bi.SetUnpackCoder())
     56    throw 1112;
     57  */
     58  bi.UnpackCoder = folder.UnpackCoder;
     59  bi.PackStreams.ClearAndSetSize(folder.PackStreams.Size());
     60  for (i = 0; i < folder.PackStreams.Size(); i++)
     61    bi.PackStreams[i] = folder.PackStreams[i];
     62 }
     63 
     64 static inline bool AreCodersEqual(
     65    const NCoderMixer2::CCoderStreamsInfo &a1,
     66    const NCoderMixer2::CCoderStreamsInfo &a2)
     67 {
     68  return (a1.NumStreams == a2.NumStreams);
     69 }
     70 
     71 static inline bool AreBondsEqual(
     72    const NCoderMixer2::CBond &a1,
     73    const NCoderMixer2::CBond &a2)
     74 {
     75  return
     76    (a1.PackIndex == a2.PackIndex) &&
     77    (a1.UnpackIndex == a2.UnpackIndex);
     78 }
     79 
     80 static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
     81 {
     82  if (a1.Coders.Size() != a2.Coders.Size())
     83    return false;
     84  unsigned i;
     85  for (i = 0; i < a1.Coders.Size(); i++)
     86    if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
     87      return false;
     88  
     89  if (a1.Bonds.Size() != a2.Bonds.Size())
     90    return false;
     91  for (i = 0; i < a1.Bonds.Size(); i++)
     92    if (!AreBondsEqual(a1.Bonds[i], a2.Bonds[i]))
     93      return false;
     94  
     95  for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
     96    if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
     97      return false;
     98  
     99  if (a1.PackStreams.Size() != a2.PackStreams.Size())
    100    return false;
    101  for (i = 0; i < a1.PackStreams.Size(); i++)
    102    if (a1.PackStreams[i] != a2.PackStreams[i])
    103      return false;
    104 
    105  /*
    106  if (a1.UnpackCoder != a2.UnpackCoder)
    107    return false;
    108  */
    109  return true;
    110 }
    111 
    112 CDecoder::CDecoder(bool useMixerMT):
    113    _bindInfoPrev_Defined(false),
    114    _useMixerMT(useMixerMT)
    115 {}
    116 
    117 
    118 struct CLockedInStream:
    119  public IUnknown,
    120  public CMyUnknownImp
    121 {
    122  CMyComPtr<IInStream> Stream;
    123  UInt64 Pos;
    124 
    125  MY_UNKNOWN_IMP
    126 
    127  #ifdef USE_MIXER_MT
    128  NWindows::NSynchronization::CCriticalSection CriticalSection;
    129  #endif
    130 };
    131 
    132 
    133 #ifdef USE_MIXER_MT
    134 
    135 class CLockedSequentialInStreamMT:
    136  public ISequentialInStream,
    137  public CMyUnknownImp
    138 {
    139  CLockedInStream *_glob;
    140  UInt64 _pos;
    141  CMyComPtr<IUnknown> _globRef;
    142 public:
    143  void Init(CLockedInStream *lockedInStream, UInt64 startPos)
    144  {
    145    _globRef = lockedInStream;
    146    _glob = lockedInStream;
    147    _pos = startPos;
    148  }
    149 
    150  MY_UNKNOWN_IMP1(ISequentialInStream)
    151 
    152  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
    153 };
    154 
    155 STDMETHODIMP CLockedSequentialInStreamMT::Read(void *data, UInt32 size, UInt32 *processedSize)
    156 {
    157  NWindows::NSynchronization::CCriticalSectionLock lock(_glob->CriticalSection);
    158 
    159  if (_pos != _glob->Pos)
    160  {
    161    RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL));
    162    _glob->Pos = _pos;
    163  }
    164 
    165  UInt32 realProcessedSize = 0;
    166  HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
    167  _pos += realProcessedSize;
    168  _glob->Pos = _pos;
    169  if (processedSize)
    170    *processedSize = realProcessedSize;
    171  return res;
    172 }
    173 
    174 #endif
    175 
    176 
    177 #ifdef USE_MIXER_ST
    178 
    179 class CLockedSequentialInStreamST:
    180  public ISequentialInStream,
    181  public CMyUnknownImp
    182 {
    183  CLockedInStream *_glob;
    184  UInt64 _pos;
    185  CMyComPtr<IUnknown> _globRef;
    186 public:
    187  void Init(CLockedInStream *lockedInStream, UInt64 startPos)
    188  {
    189    _globRef = lockedInStream;
    190    _glob = lockedInStream;
    191    _pos = startPos;
    192  }
    193 
    194  MY_UNKNOWN_IMP1(ISequentialInStream)
    195 
    196  STDMETHOD(Read)(void *data, UInt32 size, UInt32 *processedSize);
    197 };
    198 
    199 STDMETHODIMP CLockedSequentialInStreamST::Read(void *data, UInt32 size, UInt32 *processedSize)
    200 {
    201  if (_pos != _glob->Pos)
    202  {
    203    RINOK(_glob->Stream->Seek(_pos, STREAM_SEEK_SET, NULL));
    204    _glob->Pos = _pos;
    205  }
    206 
    207  UInt32 realProcessedSize = 0;
    208  HRESULT res = _glob->Stream->Read(data, size, &realProcessedSize);
    209  _pos += realProcessedSize;
    210  _glob->Pos = _pos;
    211  if (processedSize)
    212    *processedSize = realProcessedSize;
    213  return res;
    214 }
    215 
    216 #endif
    217 
    218 
    219 
    220 HRESULT CDecoder::Decode(
    221    DECL_EXTERNAL_CODECS_LOC_VARS
    222    IInStream *inStream,
    223    UInt64 startPos,
    224    const CFolders &folders, unsigned folderIndex,
    225    const UInt64 *unpackSize
    226 
    227    , ISequentialOutStream *outStream
    228    , ICompressProgressInfo *compressProgress
    229    
    230    , ISequentialInStream **
    231        #ifdef USE_MIXER_ST
    232        inStreamMainRes
    233        #endif
    234 
    235    , bool &dataAfterEnd_Error
    236    
    237    _7Z_DECODER_CRYPRO_VARS_DECL
    238 
    239    #if !defined(_7ZIP_ST)
    240    , bool mtMode, UInt32 numThreads, UInt64 memUsage
    241    #endif
    242    )
    243 {
    244  dataAfterEnd_Error = false;
    245 
    246  const UInt64 *packPositions = &folders.PackPositions[folders.FoStartPackStreamIndex[folderIndex]];
    247  CFolderEx folderInfo;
    248  folders.ParseFolderEx(folderIndex, folderInfo);
    249 
    250  if (!folderInfo.IsDecodingSupported())
    251    return E_NOTIMPL;
    252 
    253  CBindInfoEx bindInfo;
    254  Convert_FolderInfo_to_BindInfo(folderInfo, bindInfo);
    255  if (!bindInfo.CalcMapsAndCheck())
    256    return E_NOTIMPL;
    257  
    258  UInt64 folderUnpackSize = folders.GetFolderUnpackSize(folderIndex);
    259  bool fullUnpack = true;
    260  if (unpackSize)
    261  {
    262    if (*unpackSize > folderUnpackSize)
    263      return E_FAIL;
    264    fullUnpack = (*unpackSize == folderUnpackSize);
    265  }
    266 
    267  /*
    268  We don't need to init isEncrypted and passwordIsDefined
    269  We must upgrade them only
    270  
    271  #ifndef _NO_CRYPTO
    272  isEncrypted = false;
    273  passwordIsDefined = false;
    274  #endif
    275  */
    276  
    277  if (!_bindInfoPrev_Defined || !AreBindInfoExEqual(bindInfo, _bindInfoPrev))
    278  {
    279    _mixerRef.Release();
    280 
    281    #ifdef USE_MIXER_MT
    282    #ifdef USE_MIXER_ST
    283    if (_useMixerMT)
    284    #endif
    285    {
    286      _mixerMT = new NCoderMixer2::CMixerMT(false);
    287      _mixerRef = _mixerMT;
    288      _mixer = _mixerMT;
    289    }
    290    #ifdef USE_MIXER_ST
    291    else
    292    #endif
    293    #endif
    294    {
    295      #ifdef USE_MIXER_ST
    296      _mixerST = new NCoderMixer2::CMixerST(false);
    297      _mixerRef = _mixerST;
    298      _mixer = _mixerST;
    299      #endif
    300    }
    301    
    302    RINOK(_mixer->SetBindInfo(bindInfo));
    303    
    304    FOR_VECTOR(i, folderInfo.Coders)
    305    {
    306      const CCoderInfo &coderInfo = folderInfo.Coders[i];
    307 
    308      #ifndef _SFX
    309      // we don't support RAR codecs here
    310      if ((coderInfo.MethodID >> 8) == 0x403)
    311        return E_NOTIMPL;
    312      #endif
    313  
    314      CCreatedCoder cod;
    315      RINOK(CreateCoder_Id(
    316          EXTERNAL_CODECS_LOC_VARS
    317          coderInfo.MethodID, false, cod));
    318    
    319      if (coderInfo.IsSimpleCoder())
    320      {
    321        if (!cod.Coder)
    322          return E_NOTIMPL;
    323        // CMethodId m = coderInfo.MethodID;
    324        // isFilter = (IsFilterMethod(m) || m == k_AES);
    325      }
    326      else
    327      {
    328        if (!cod.Coder2 || cod.NumStreams != coderInfo.NumStreams)
    329          return E_NOTIMPL;
    330      }
    331      _mixer->AddCoder(cod);
    332      
    333      // now there is no codec that uses another external codec
    334      /*
    335      #ifdef EXTERNAL_CODECS
    336      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
    337      decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
    338      if (setCompressCodecsInfo)
    339      {
    340        // we must use g_ExternalCodecs also
    341        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(__externalCodecs->GetCodecs));
    342      }
    343      #endif
    344      */
    345    }
    346    
    347    _bindInfoPrev = bindInfo;
    348    _bindInfoPrev_Defined = true;
    349  }
    350 
    351  _mixer->ReInit();
    352  
    353  UInt32 packStreamIndex = 0;
    354  UInt32 unpackStreamIndexStart = folders.FoToCoderUnpackSizes[folderIndex];
    355 
    356  unsigned i;
    357 
    358  bool mt_wasUsed = false;
    359 
    360  for (i = 0; i < folderInfo.Coders.Size(); i++)
    361  {
    362    const CCoderInfo &coderInfo = folderInfo.Coders[i];
    363    IUnknown *decoder = _mixer->GetCoder(i).GetUnknown();
    364 
    365    #if !defined(_7ZIP_ST)
    366    if (!mt_wasUsed)
    367    {
    368      if (mtMode)
    369      {
    370        CMyComPtr<ICompressSetCoderMt> setCoderMt;
    371        decoder->QueryInterface(IID_ICompressSetCoderMt, (void **)&setCoderMt);
    372        if (setCoderMt)
    373        {
    374          mt_wasUsed = true;
    375          RINOK(setCoderMt->SetNumberOfThreads(numThreads));
    376        }
    377      }
    378      // if (memUsage != 0)
    379      {
    380        CMyComPtr<ICompressSetMemLimit> setMemLimit;
    381        decoder->QueryInterface(IID_ICompressSetMemLimit, (void **)&setMemLimit);
    382        if (setMemLimit)
    383        {
    384          mt_wasUsed = true;
    385          RINOK(setMemLimit->SetMemLimit(memUsage));
    386        }
    387      }
    388    }
    389    #endif
    390 
    391    {
    392      CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
    393      decoder->QueryInterface(IID_ICompressSetDecoderProperties2, (void **)&setDecoderProperties);
    394      if (setDecoderProperties)
    395      {
    396        const CByteBuffer &props = coderInfo.Props;
    397        size_t size = props.Size();
    398        if (size > 0xFFFFFFFF)
    399          return E_NOTIMPL;
    400        HRESULT res = setDecoderProperties->SetDecoderProperties2((const Byte *)props, (UInt32)size);
    401        if (res == E_INVALIDARG)
    402          res = E_NOTIMPL;
    403        RINOK(res);
    404      }
    405    }
    406 
    407    #ifndef _NO_CRYPTO
    408    {
    409      CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
    410      decoder->QueryInterface(IID_ICryptoSetPassword, (void **)&cryptoSetPassword);
    411      if (cryptoSetPassword)
    412      {
    413        isEncrypted = true;
    414        if (!getTextPassword)
    415          return E_NOTIMPL;
    416        CMyComBSTR passwordBSTR;
    417        RINOK(getTextPassword->CryptoGetTextPassword(&passwordBSTR));
    418        passwordIsDefined = true;
    419        password.Empty();
    420        size_t len = 0;
    421        if (passwordBSTR)
    422        {
    423          password = passwordBSTR;
    424          len = password.Len();
    425        }
    426        CByteBuffer buffer(len * 2);
    427        for (size_t k = 0; k < len; k++)
    428        {
    429          wchar_t c = passwordBSTR[k];
    430          ((Byte *)buffer)[k * 2] = (Byte)c;
    431          ((Byte *)buffer)[k * 2 + 1] = (Byte)(c >> 8);
    432        }
    433        RINOK(cryptoSetPassword->CryptoSetPassword((const Byte *)buffer, (UInt32)buffer.Size()));
    434      }
    435    }
    436    #endif
    437 
    438    bool finishMode = false;
    439    {
    440      CMyComPtr<ICompressSetFinishMode> setFinishMode;
    441      decoder->QueryInterface(IID_ICompressSetFinishMode, (void **)&setFinishMode);
    442      if (setFinishMode)
    443      {
    444        finishMode = fullUnpack;
    445        RINOK(setFinishMode->SetFinishMode(BoolToInt(finishMode)));
    446      }
    447    }
    448    
    449    UInt32 numStreams = (UInt32)coderInfo.NumStreams;
    450    
    451    CObjArray<UInt64> packSizes(numStreams);
    452    CObjArray<const UInt64 *> packSizesPointers(numStreams);
    453       
    454    for (UInt32 j = 0; j < numStreams; j++, packStreamIndex++)
    455    {
    456      int bond = folderInfo.FindBond_for_PackStream(packStreamIndex);
    457      
    458      if (bond >= 0)
    459        packSizesPointers[j] = &folders.CoderUnpackSizes[unpackStreamIndexStart + folderInfo.Bonds[(unsigned)bond].UnpackIndex];
    460      else
    461      {
    462        int index = folderInfo.Find_in_PackStreams(packStreamIndex);
    463        if (index < 0)
    464          return E_NOTIMPL;
    465        packSizes[j] = packPositions[(unsigned)index + 1] - packPositions[(unsigned)index];
    466        packSizesPointers[j] = &packSizes[j];
    467      }
    468    }
    469 
    470    const UInt64 *unpackSizesPointer =
    471        (unpackSize && i == bindInfo.UnpackCoder) ?
    472            unpackSize :
    473            &folders.CoderUnpackSizes[unpackStreamIndexStart + i];
    474    
    475    _mixer->SetCoderInfo(i, unpackSizesPointer, packSizesPointers, finishMode);
    476  }
    477 
    478  if (outStream)
    479  {
    480    _mixer->SelectMainCoder(!fullUnpack);
    481  }
    482 
    483  CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
    484  
    485  CLockedInStream *lockedInStreamSpec = new CLockedInStream;
    486  CMyComPtr<IUnknown> lockedInStream = lockedInStreamSpec;
    487 
    488  bool needMtLock = false;
    489 
    490  if (folderInfo.PackStreams.Size() > 1)
    491  {
    492    // lockedInStream.Pos = (UInt64)(Int64)-1;
    493    // RINOK(inStream->Seek(0, STREAM_SEEK_CUR, &lockedInStream.Pos));
    494    RINOK(inStream->Seek(startPos + packPositions[0], STREAM_SEEK_SET, &lockedInStreamSpec->Pos));
    495    lockedInStreamSpec->Stream = inStream;
    496 
    497    #ifdef USE_MIXER_ST
    498    if (_mixer->IsThere_ExternalCoder_in_PackTree(_mixer->MainCoderIndex))
    499    #endif
    500      needMtLock = true;
    501  }
    502 
    503  for (unsigned j = 0; j < folderInfo.PackStreams.Size(); j++)
    504  {
    505    CMyComPtr<ISequentialInStream> packStream;
    506    UInt64 packPos = startPos + packPositions[j];
    507 
    508    if (folderInfo.PackStreams.Size() == 1)
    509    {
    510      RINOK(inStream->Seek(packPos, STREAM_SEEK_SET, NULL));
    511      packStream = inStream;
    512    }
    513    else
    514    {
    515      #ifdef USE_MIXER_MT
    516      #ifdef USE_MIXER_ST
    517      if (_useMixerMT || needMtLock)
    518      #endif
    519      {
    520        CLockedSequentialInStreamMT *lockedStreamImpSpec = new CLockedSequentialInStreamMT;
    521        packStream = lockedStreamImpSpec;
    522        lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
    523      }
    524      #ifdef USE_MIXER_ST
    525      else
    526      #endif
    527      #endif
    528      {
    529        #ifdef USE_MIXER_ST
    530        CLockedSequentialInStreamST *lockedStreamImpSpec = new CLockedSequentialInStreamST;
    531        packStream = lockedStreamImpSpec;
    532        lockedStreamImpSpec->Init(lockedInStreamSpec, packPos);
    533        #endif
    534      }
    535    }
    536 
    537    CLimitedSequentialInStream *streamSpec = new CLimitedSequentialInStream;
    538    inStreams.AddNew() = streamSpec;
    539    streamSpec->SetStream(packStream);
    540    streamSpec->Init(packPositions[j + 1] - packPositions[j]);
    541  }
    542  
    543  unsigned num = inStreams.Size();
    544  CObjArray<ISequentialInStream *> inStreamPointers(num);
    545  for (i = 0; i < num; i++)
    546    inStreamPointers[i] = inStreams[i];
    547 
    548  if (outStream)
    549  {
    550    CMyComPtr<ICompressProgressInfo> progress2;
    551    if (compressProgress && !_mixer->Is_PackSize_Correct_for_Coder(_mixer->MainCoderIndex))
    552      progress2 = new CDecProgress(compressProgress);
    553 
    554    ISequentialOutStream *outStreamPointer = outStream;
    555    return _mixer->Code(inStreamPointers, &outStreamPointer,
    556        progress2 ? (ICompressProgressInfo *)progress2 : compressProgress,
    557        dataAfterEnd_Error);
    558  }
    559  
    560  #ifdef USE_MIXER_ST
    561    return _mixerST->GetMainUnpackStream(inStreamPointers, inStreamMainRes);
    562  #else
    563    return E_FAIL;
    564  #endif
    565 }
    566 
    567 }}