misc/libphysfs/lzma/CPP/7zip/Archive/7z/7zExtract.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

// 7zExtract.cpp

#include "StdAfx.h"

#include "7zHandler.h"
#include "7zFolderOutStream.h"
#include "7zDecode.h"
// #include "7z1Decode.h"

#include "../../../Common/ComTry.h"
#include "../../Common/StreamObjects.h"
#include "../../Common/ProgressUtils.h"
#include "../../Common/LimitedStreams.h"

namespace NArchive {
namespace N7z {

struct CExtractFolderInfo
{
  #ifdef _7Z_VOL
  int VolumeIndex;
  #endif
  CNum FileIndex;
  CNum FolderIndex;
  CBoolVector ExtractStatuses;
  UInt64 UnPackSize;
  CExtractFolderInfo(
    #ifdef _7Z_VOL
    int volumeIndex, 
    #endif
    CNum fileIndex, CNum folderIndex): 
    #ifdef _7Z_VOL
    VolumeIndex(volumeIndex),
    #endif
    FileIndex(fileIndex),
    FolderIndex(folderIndex), 
    UnPackSize(0) 
  {
    if (fileIndex != kNumNoIndex)
    {
      ExtractStatuses.Reserve(1);
      ExtractStatuses.Add(true);
    }
  };
};

STDMETHODIMP CHandler::Extract(const UInt32* indices, UInt32 numItems,
    Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec)
{
  COM_TRY_BEGIN
  bool testMode = (testModeSpec != 0);
  CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec;
  UInt64 importantTotalUnPacked = 0;

  bool allFilesMode = (numItems == UInt32(-1));
  if (allFilesMode)
    numItems = 
    #ifdef _7Z_VOL
    _refs.Size();
    #else
    _database.Files.Size();
    #endif

  if(numItems == 0)
    return S_OK;

  /*
  if(_volumes.Size() != 1)
    return E_FAIL;
  const CVolume &volume = _volumes.Front();
  const CArchiveDatabaseEx &_database = volume.Database;
  IInStream *_inStream = volume.Stream;
  */
  
  CObjectVector<CExtractFolderInfo> extractFolderInfoVector;
  for(UInt32 ii = 0; ii < numItems; ii++)
  {
    // UInt32 fileIndex = allFilesMode ? indexIndex : indices[indexIndex];
    UInt32 ref2Index = allFilesMode ? ii : indices[ii];
    // const CRef2 &ref2 = _refs[ref2Index];

    // for(UInt32 ri = 0; ri < ref2.Refs.Size(); ri++)
    {
      #ifdef _7Z_VOL
      // const CRef &ref = ref2.Refs[ri];
      const CRef &ref = _refs[ref2Index];

      int volumeIndex = ref.VolumeIndex;
      const CVolume &volume = _volumes[volumeIndex];
      const CArchiveDatabaseEx &database = volume.Database;
      UInt32 fileIndex = ref.ItemIndex;
      #else
      const CArchiveDatabaseEx &database = _database;
      UInt32 fileIndex = ref2Index;
      #endif

      CNum folderIndex = database.FileIndexToFolderIndexMap[fileIndex];
      if (folderIndex == kNumNoIndex)
      {
        extractFolderInfoVector.Add(CExtractFolderInfo(
            #ifdef _7Z_VOL
            volumeIndex, 
            #endif
            fileIndex, kNumNoIndex));
        continue;
      }
      if (extractFolderInfoVector.IsEmpty() || 
        folderIndex != extractFolderInfoVector.Back().FolderIndex 
        #ifdef _7Z_VOL
        || volumeIndex != extractFolderInfoVector.Back().VolumeIndex
        #endif
        )
      {
        extractFolderInfoVector.Add(CExtractFolderInfo(
            #ifdef _7Z_VOL
            volumeIndex, 
            #endif
            kNumNoIndex, folderIndex));
        const CFolder &folderInfo = database.Folders[folderIndex];
        UInt64 unPackSize = folderInfo.GetUnPackSize();
        importantTotalUnPacked += unPackSize;
        extractFolderInfoVector.Back().UnPackSize = unPackSize;
      }
      
      CExtractFolderInfo &efi = extractFolderInfoVector.Back();
      
      // const CFolderInfo &folderInfo = m_dam_Folders[folderIndex];
      CNum startIndex = database.FolderStartFileIndex[folderIndex];
      for (CNum index = efi.ExtractStatuses.Size();
          index <= fileIndex - startIndex; index++)
      {
        // UInt64 unPackSize = _database.Files[startIndex + index].UnPackSize;
        // Count partial_folder_size
        // efi.UnPackSize += unPackSize;
        // importantTotalUnPacked += unPackSize;
        efi.ExtractStatuses.Add(index == fileIndex - startIndex);
      }
    }
  }

  extractCallback->SetTotal(importantTotalUnPacked);

  CDecoder decoder(
    #ifdef _ST_MODE
    false
    #else
    true
    #endif
    );
  // CDecoder1 decoder;

  UInt64 currentTotalPacked = 0;
  UInt64 currentTotalUnPacked = 0;
  UInt64 totalFolderUnPacked;
  UInt64 totalFolderPacked;

  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(extractCallback, false);

  for(int i = 0; i < extractFolderInfoVector.Size(); i++, 
      currentTotalUnPacked += totalFolderUnPacked,
      currentTotalPacked += totalFolderPacked)
  {
    lps->OutSize = currentTotalUnPacked;
    lps->InSize = currentTotalPacked;
    RINOK(lps->SetCur());
    
    const CExtractFolderInfo &efi = extractFolderInfoVector[i];
    totalFolderUnPacked = efi.UnPackSize;

    totalFolderPacked = 0;

    CFolderOutStream *folderOutStream = new CFolderOutStream;
    CMyComPtr<ISequentialOutStream> outStream(folderOutStream);

    #ifdef _7Z_VOL
    const CVolume &volume = _volumes[efi.VolumeIndex];
    const CArchiveDatabaseEx &database = volume.Database;
    #else
    const CArchiveDatabaseEx &database = _database;
    #endif

    CNum startIndex;
    if (efi.FileIndex != kNumNoIndex)
      startIndex = efi.FileIndex;
    else
      startIndex = database.FolderStartFileIndex[efi.FolderIndex];


    HRESULT result = folderOutStream->Init(&database, 
        #ifdef _7Z_VOL
        volume.StartRef2Index, 
        #else
        0,
        #endif
        startIndex, 
        &efi.ExtractStatuses, extractCallback, testMode, _crcSize != 0);

    RINOK(result);

    if (efi.FileIndex != kNumNoIndex)
      continue;

    CNum folderIndex = efi.FolderIndex;
    const CFolder &folderInfo = database.Folders[folderIndex];

    totalFolderPacked = _database.GetFolderFullPackSize(folderIndex);

    CNum packStreamIndex = database.FolderStartPackStreamIndex[folderIndex];
    UInt64 folderStartPackPos = database.GetFolderStreamPos(folderIndex, 0);

    #ifndef _NO_CRYPTO
    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
    if (extractCallback)
      extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
    #endif

    try
    {
      HRESULT result = decoder.Decode(
          EXTERNAL_CODECS_VARS
          #ifdef _7Z_VOL
          volume.Stream,
          #else
          _inStream,
          #endif
          folderStartPackPos, 
          &database.PackSizes[packStreamIndex],
          folderInfo,
          outStream,
          progress
          #ifndef _NO_CRYPTO
          , getTextPassword
          #endif
          #ifdef COMPRESS_MT
          , true, _numThreads
          #endif
          );

      if (result == S_FALSE)
      {
        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
        continue;
      }
      if (result == E_NOTIMPL)
      {
        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kUnSupportedMethod));
        continue;
      }
      if (result != S_OK)
        return result;
      if (folderOutStream->WasWritingFinished() != S_OK)
      {
        RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
        continue;
      }
    }
    catch(...)
    {
      RINOK(folderOutStream->FlushCorrupted(NArchive::NExtract::NOperationResult::kDataError));
      continue;
    }
  }
  return S_OK;
  COM_TRY_END
}

}}