misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zDecode.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12213 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// 7zDecode.cpp

#include "StdAfx.h"

#include "7zDecode.h"

#include "../../IPassword.h"
#include "../../Common/LockedStream.h"
#include "../../Common/StreamObjects.h"
#include "../../Common/ProgressUtils.h"
#include "../../Common/LimitedStreams.h"
#include "../../Common/CreateCoder.h"
#include "../../Common/FilterCoder.h"

namespace NArchive {
namespace N7z {

static void ConvertFolderItemInfoToBindInfo(const CFolder &folder,
    CBindInfoEx &bindInfo)
{
  bindInfo.Clear();
  int i;
  for (i = 0; i < folder.BindPairs.Size(); i++)
  {
    NCoderMixer::CBindPair bindPair;
    bindPair.InIndex = (UInt32)folder.BindPairs[i].InIndex;
    bindPair.OutIndex = (UInt32)folder.BindPairs[i].OutIndex;
    bindInfo.BindPairs.Add(bindPair);
  }
  UInt32 outStreamIndex = 0;
  for (i = 0; i < folder.Coders.Size(); i++)
  {
    NCoderMixer::CCoderStreamsInfo coderStreamsInfo;
    const CCoderInfo &coderInfo = folder.Coders[i];
    coderStreamsInfo.NumInStreams = (UInt32)coderInfo.NumInStreams;
    coderStreamsInfo.NumOutStreams = (UInt32)coderInfo.NumOutStreams;
    bindInfo.Coders.Add(coderStreamsInfo);
    bindInfo.CoderMethodIDs.Add(coderInfo.MethodID);
    for (UInt32 j = 0; j < coderStreamsInfo.NumOutStreams; j++, outStreamIndex++)
      if (folder.FindBindPairForOutStream(outStreamIndex) < 0)
        bindInfo.OutStreams.Add(outStreamIndex);
  }
  for (i = 0; i < folder.PackStreams.Size(); i++)
    bindInfo.InStreams.Add((UInt32)folder.PackStreams[i]);
}

static bool AreCodersEqual(const NCoderMixer::CCoderStreamsInfo &a1, 
    const NCoderMixer::CCoderStreamsInfo &a2)
{
  return (a1.NumInStreams == a2.NumInStreams) &&
    (a1.NumOutStreams == a2.NumOutStreams);
}

static bool AreBindPairsEqual(const NCoderMixer::CBindPair &a1, const NCoderMixer::CBindPair &a2)
{
  return (a1.InIndex == a2.InIndex) &&
    (a1.OutIndex == a2.OutIndex);
}

static bool AreBindInfoExEqual(const CBindInfoEx &a1, const CBindInfoEx &a2)
{
  if (a1.Coders.Size() != a2.Coders.Size())
    return false;
  int i;
  for (i = 0; i < a1.Coders.Size(); i++)
    if (!AreCodersEqual(a1.Coders[i], a2.Coders[i]))
      return false;
  if (a1.BindPairs.Size() != a2.BindPairs.Size())
    return false;
  for (i = 0; i < a1.BindPairs.Size(); i++)
    if (!AreBindPairsEqual(a1.BindPairs[i], a2.BindPairs[i]))
      return false;
  for (i = 0; i < a1.CoderMethodIDs.Size(); i++)
    if (a1.CoderMethodIDs[i] != a2.CoderMethodIDs[i])
      return false;
  if (a1.InStreams.Size() != a2.InStreams.Size())
    return false;
  if (a1.OutStreams.Size() != a2.OutStreams.Size())
    return false;
  return true;
}

CDecoder::CDecoder(bool multiThread)
{
  #ifndef _ST_MODE
  multiThread = true;
  #endif
  _multiThread = multiThread;
  _bindInfoExPrevIsDefined = false;
}

HRESULT CDecoder::Decode(
    DECL_EXTERNAL_CODECS_LOC_VARS
    IInStream *inStream,
    UInt64 startPos,
    const UInt64 *packSizes,
    const CFolder &folderInfo, 
    ISequentialOutStream *outStream,
    ICompressProgressInfo *compressProgress
    #ifndef _NO_CRYPTO
    , ICryptoGetTextPassword *getTextPassword
    #endif
    #ifdef COMPRESS_MT
    , bool mtMode, UInt32 numThreads
    #endif
    )
{
  CObjectVector< CMyComPtr<ISequentialInStream> > inStreams;
  
  CLockedInStream lockedInStream;
  lockedInStream.Init(inStream);
  
  for (int j = 0; j < folderInfo.PackStreams.Size(); j++)
  {
    CLockedSequentialInStreamImp *lockedStreamImpSpec = new 
        CLockedSequentialInStreamImp;
    CMyComPtr<ISequentialInStream> lockedStreamImp = lockedStreamImpSpec;
    lockedStreamImpSpec->Init(&lockedInStream, startPos);
    startPos += packSizes[j];
    
    CLimitedSequentialInStream *streamSpec = new 
        CLimitedSequentialInStream;
    CMyComPtr<ISequentialInStream> inStream = streamSpec;
    streamSpec->SetStream(lockedStreamImp);
    streamSpec->Init(packSizes[j]);
    inStreams.Add(inStream);
  }
  
  int numCoders = folderInfo.Coders.Size();
  
  CBindInfoEx bindInfo;
  ConvertFolderItemInfoToBindInfo(folderInfo, bindInfo);
  bool createNewCoders;
  if (!_bindInfoExPrevIsDefined)
    createNewCoders = true;
  else
    createNewCoders = !AreBindInfoExEqual(bindInfo, _bindInfoExPrev);
  if (createNewCoders)
  {
    int i;
    _decoders.Clear();
    // _decoders2.Clear();
    
    _mixerCoder.Release();

    if (_multiThread)
    {
      _mixerCoderMTSpec = new NCoderMixer::CCoderMixer2MT;
      _mixerCoder = _mixerCoderMTSpec;
      _mixerCoderCommon = _mixerCoderMTSpec;
    }
    else
    {
      #ifdef _ST_MODE
      _mixerCoderSTSpec = new NCoderMixer::CCoderMixer2ST;
      _mixerCoder = _mixerCoderSTSpec;
      _mixerCoderCommon = _mixerCoderSTSpec;
      #endif
    }
    RINOK(_mixerCoderCommon->SetBindInfo(bindInfo));
    
    for (i = 0; i < numCoders; i++)
    {
      const CCoderInfo &coderInfo = folderInfo.Coders[i];

  
      CMyComPtr<ICompressCoder> decoder;
      CMyComPtr<ICompressCoder2> decoder2;
      RINOK(CreateCoder(
          EXTERNAL_CODECS_LOC_VARS
          coderInfo.MethodID, decoder, decoder2, false));
      CMyComPtr<IUnknown> decoderUnknown;
      if (coderInfo.IsSimpleCoder())
      {
        if (decoder == 0)
          return E_NOTIMPL;

        decoderUnknown = (IUnknown *)decoder;
        
        if (_multiThread)
          _mixerCoderMTSpec->AddCoder(decoder);
        #ifdef _ST_MODE
        else
          _mixerCoderSTSpec->AddCoder(decoder, false);
        #endif
      }
      else
      {
        if (decoder2 == 0)
          return E_NOTIMPL;
        decoderUnknown = (IUnknown *)decoder2;
        if (_multiThread)
          _mixerCoderMTSpec->AddCoder2(decoder2);
        #ifdef _ST_MODE
        else
          _mixerCoderSTSpec->AddCoder2(decoder2, false);
        #endif
      }
      _decoders.Add(decoderUnknown);
      #ifdef EXTERNAL_CODECS
      CMyComPtr<ISetCompressCodecsInfo> setCompressCodecsInfo;
      decoderUnknown.QueryInterface(IID_ISetCompressCodecsInfo, (void **)&setCompressCodecsInfo);
      if (setCompressCodecsInfo)
      {
        RINOK(setCompressCodecsInfo->SetCompressCodecsInfo(codecsInfo));
      }
      #endif
    }
    _bindInfoExPrev = bindInfo;
    _bindInfoExPrevIsDefined = true;
  }
  int i;
  _mixerCoderCommon->ReInit();
  
  UInt32 packStreamIndex = 0, unPackStreamIndex = 0;
  UInt32 coderIndex = 0;
  // UInt32 coder2Index = 0;
  
  for (i = 0; i < numCoders; i++)
  {
    const CCoderInfo &coderInfo = folderInfo.Coders[i];
    CMyComPtr<IUnknown> &decoder = _decoders[coderIndex];
    
    {
      CMyComPtr<ICompressSetDecoderProperties2> setDecoderProperties;
      decoder.QueryInterface(IID_ICompressSetDecoderProperties2, &setDecoderProperties);
      if (setDecoderProperties)
      {
        const CByteBuffer &properties = coderInfo.Properties;
        size_t size = properties.GetCapacity();
        if (size > 0xFFFFFFFF)
          return E_NOTIMPL;
        if (size > 0)
        {
          RINOK(setDecoderProperties->SetDecoderProperties2((const Byte *)properties, (UInt32)size));
        }
      }
    }

    #ifdef COMPRESS_MT
    if (mtMode)
    {
      CMyComPtr<ICompressSetCoderMt> setCoderMt;
      decoder.QueryInterface(IID_ICompressSetCoderMt, &setCoderMt);
      if (setCoderMt)
      {
        RINOK(setCoderMt->SetNumberOfThreads(numThreads));
      }
    }
    #endif

    #ifndef _NO_CRYPTO
    {
      CMyComPtr<ICryptoSetPassword> cryptoSetPassword;
      decoder.QueryInterface(IID_ICryptoSetPassword, &cryptoSetPassword);
      if (cryptoSetPassword)
      {
        if (getTextPassword == 0)
          return E_FAIL;
        CMyComBSTR password;
        RINOK(getTextPassword->CryptoGetTextPassword(&password));
        CByteBuffer buffer;
        UString unicodePassword(password);
        const UInt32 sizeInBytes = unicodePassword.Length() * 2;
        buffer.SetCapacity(sizeInBytes);
        for (int i = 0; i < unicodePassword.Length(); i++)
        {
          wchar_t c = unicodePassword[i];
          ((Byte *)buffer)[i * 2] = (Byte)c;
          ((Byte *)buffer)[i * 2 + 1] = (Byte)(c >> 8);
        }
        RINOK(cryptoSetPassword->CryptoSetPassword(
          (const Byte *)buffer, sizeInBytes));
      }
    }
    #endif

    coderIndex++;
    
    UInt32 numInStreams = (UInt32)coderInfo.NumInStreams;
    UInt32 numOutStreams = (UInt32)coderInfo.NumOutStreams;
    CRecordVector<const UInt64 *> packSizesPointers;
    CRecordVector<const UInt64 *> unPackSizesPointers;
    packSizesPointers.Reserve(numInStreams);
    unPackSizesPointers.Reserve(numOutStreams);
    UInt32 j;
    for (j = 0; j < numOutStreams; j++, unPackStreamIndex++)
      unPackSizesPointers.Add(&folderInfo.UnPackSizes[unPackStreamIndex]);
    
    for (j = 0; j < numInStreams; j++, packStreamIndex++)
    {
      int bindPairIndex = folderInfo.FindBindPairForInStream(packStreamIndex);
      if (bindPairIndex >= 0)
        packSizesPointers.Add(
        &folderInfo.UnPackSizes[(UInt32)folderInfo.BindPairs[bindPairIndex].OutIndex]);
      else
      {
        int index = folderInfo.FindPackStreamArrayIndex(packStreamIndex);
        if (index < 0)
          return E_FAIL;
        packSizesPointers.Add(&packSizes[index]);
      }
    }
    
    _mixerCoderCommon->SetCoderInfo(i, 
        &packSizesPointers.Front(), 
        &unPackSizesPointers.Front());
  }
  UInt32 mainCoder, temp;
  bindInfo.FindOutStream(bindInfo.OutStreams[0], mainCoder, temp);

  if (_multiThread)
    _mixerCoderMTSpec->SetProgressCoderIndex(mainCoder);
  /*
  else
    _mixerCoderSTSpec->SetProgressCoderIndex(mainCoder);;
  */
  
  if (numCoders == 0)
    return 0;
  CRecordVector<ISequentialInStream *> inStreamPointers;
  inStreamPointers.Reserve(inStreams.Size());
  for (i = 0; i < inStreams.Size(); i++)
    inStreamPointers.Add(inStreams[i]);
  ISequentialOutStream *outStreamPointer = outStream;
  return _mixerCoder->Code(&inStreamPointers.Front(), NULL, 
    inStreams.Size(), &outStreamPointer, NULL, 1, compressProgress);
}

}}