tor-browser

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

7zExtract.cpp (10540B)


      1 // 7zExtract.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include "../../../../C/7zCrc.h"
      6 
      7 #include "../../../Common/ComTry.h"
      8 
      9 #include "../../Common/ProgressUtils.h"
     10 
     11 #include "7zDecode.h"
     12 #include "7zHandler.h"
     13 
     14 // EXTERN_g_ExternalCodecs
     15 
     16 namespace NArchive {
     17 namespace N7z {
     18 
     19 class CFolderOutStream:
     20  public ISequentialOutStream,
     21  public CMyUnknownImp
     22 {
     23  CMyComPtr<ISequentialOutStream> _stream;
     24 public:
     25  bool TestMode;
     26  bool CheckCrc;
     27 private:
     28  bool _fileIsOpen;
     29  bool _calcCrc;
     30  UInt32 _crc;
     31  UInt64 _rem;
     32 
     33  const UInt32 *_indexes;
     34  unsigned _numFiles;
     35  unsigned _fileIndex;
     36 
     37  HRESULT OpenFile(bool isCorrupted = false);
     38  HRESULT CloseFile_and_SetResult(Int32 res);
     39  HRESULT CloseFile();
     40  HRESULT ProcessEmptyFiles();
     41 
     42 public:
     43  MY_UNKNOWN_IMP1(ISequentialOutStream)
     44 
     45  const CDbEx *_db;
     46  CMyComPtr<IArchiveExtractCallback> ExtractCallback;
     47 
     48  bool ExtraWriteWasCut;
     49 
     50  CFolderOutStream():
     51      TestMode(false),
     52      CheckCrc(true)
     53      {}
     54 
     55  STDMETHOD(Write)(const void *data, UInt32 size, UInt32 *processedSize);
     56 
     57  HRESULT Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles);
     58  HRESULT FlushCorrupted(Int32 callbackOperationResult);
     59 
     60  bool WasWritingFinished() const { return _numFiles == 0; }
     61 };
     62 
     63 
     64 HRESULT CFolderOutStream::Init(unsigned startIndex, const UInt32 *indexes, unsigned numFiles)
     65 {
     66  _fileIndex = startIndex;
     67  _indexes = indexes;
     68  _numFiles = numFiles;
     69  
     70  _fileIsOpen = false;
     71  ExtraWriteWasCut = false;
     72  
     73  return ProcessEmptyFiles();
     74 }
     75 
     76 HRESULT CFolderOutStream::OpenFile(bool isCorrupted)
     77 {
     78  const CFileItem &fi = _db->Files[_fileIndex];
     79  UInt32 nextFileIndex = (_indexes ? *_indexes : _fileIndex);
     80  Int32 askMode = (_fileIndex == nextFileIndex) ?
     81        (TestMode ?
     82        NExtract::NAskMode::kTest :
     83        NExtract::NAskMode::kExtract) :
     84      NExtract::NAskMode::kSkip;
     85 
     86  if (isCorrupted
     87      && askMode == NExtract::NAskMode::kExtract
     88      && !_db->IsItemAnti(_fileIndex)
     89      && !fi.IsDir)
     90    askMode = NExtract::NAskMode::kTest;
     91  
     92  CMyComPtr<ISequentialOutStream> realOutStream;
     93  RINOK(ExtractCallback->GetStream(_fileIndex, &realOutStream, askMode));
     94  
     95  _stream = realOutStream;
     96  _crc = CRC_INIT_VAL;
     97  _calcCrc = (CheckCrc && fi.CrcDefined && !fi.IsDir);
     98 
     99  _fileIsOpen = true;
    100  _rem = fi.Size;
    101  
    102  if (askMode == NExtract::NAskMode::kExtract
    103      && !realOutStream
    104      && !_db->IsItemAnti(_fileIndex)
    105      && !fi.IsDir)
    106    askMode = NExtract::NAskMode::kSkip;
    107  return ExtractCallback->PrepareOperation(askMode);
    108 }
    109 
    110 HRESULT CFolderOutStream::CloseFile_and_SetResult(Int32 res)
    111 {
    112  _stream.Release();
    113  _fileIsOpen = false;
    114  
    115  if (!_indexes)
    116    _numFiles--;
    117  else if (*_indexes == _fileIndex)
    118  {
    119    _indexes++;
    120    _numFiles--;
    121  }
    122 
    123  _fileIndex++;
    124  return ExtractCallback->SetOperationResult(res);
    125 }
    126 
    127 HRESULT CFolderOutStream::CloseFile()
    128 {
    129  const CFileItem &fi = _db->Files[_fileIndex];
    130  return CloseFile_and_SetResult((!_calcCrc || fi.Crc == CRC_GET_DIGEST(_crc)) ?
    131      NExtract::NOperationResult::kOK :
    132      NExtract::NOperationResult::kCRCError);
    133 }
    134 
    135 HRESULT CFolderOutStream::ProcessEmptyFiles()
    136 {
    137  while (_numFiles != 0 && _db->Files[_fileIndex].Size == 0)
    138  {
    139    RINOK(OpenFile());
    140    RINOK(CloseFile());
    141  }
    142  return S_OK;
    143 }
    144 
    145 STDMETHODIMP CFolderOutStream::Write(const void *data, UInt32 size, UInt32 *processedSize)
    146 {
    147  if (processedSize)
    148    *processedSize = 0;
    149  
    150  while (size != 0)
    151  {
    152    if (_fileIsOpen)
    153    {
    154      UInt32 cur = (size < _rem ? size : (UInt32)_rem);
    155      if (_calcCrc)
    156      {
    157        const UInt32 k_Step = (UInt32)1 << 20;
    158        if (cur > k_Step)
    159          cur = k_Step;
    160      }
    161      HRESULT result = S_OK;
    162      if (_stream)
    163        result = _stream->Write(data, cur, &cur);
    164      if (_calcCrc)
    165        _crc = CrcUpdate(_crc, data, cur);
    166      if (processedSize)
    167        *processedSize += cur;
    168      data = (const Byte *)data + cur;
    169      size -= cur;
    170      _rem -= cur;
    171      if (_rem == 0)
    172      {
    173        RINOK(CloseFile());
    174        RINOK(ProcessEmptyFiles());
    175      }
    176      RINOK(result);
    177      if (cur == 0)
    178        break;
    179      continue;
    180    }
    181  
    182    RINOK(ProcessEmptyFiles());
    183    if (_numFiles == 0)
    184    {
    185      // we support partial extracting
    186      /*
    187      if (processedSize)
    188        *processedSize += size;
    189      break;
    190      */
    191      ExtraWriteWasCut = true;
    192      // return S_FALSE;
    193      return k_My_HRESULT_WritingWasCut;
    194    }
    195    RINOK(OpenFile());
    196  }
    197  
    198  return S_OK;
    199 }
    200 
    201 HRESULT CFolderOutStream::FlushCorrupted(Int32 callbackOperationResult)
    202 {
    203  while (_numFiles != 0)
    204  {
    205    if (_fileIsOpen)
    206    {
    207      RINOK(CloseFile_and_SetResult(callbackOperationResult));
    208    }
    209    else
    210    {
    211      RINOK(OpenFile(true));
    212    }
    213  }
    214  return S_OK;
    215 }
    216 
    217 STDMETHODIMP CHandler::Extract(const UInt32 *indices, UInt32 numItems,
    218    Int32 testModeSpec, IArchiveExtractCallback *extractCallbackSpec)
    219 {
    220  COM_TRY_BEGIN
    221  
    222  CMyComPtr<IArchiveExtractCallback> extractCallback = extractCallbackSpec;
    223  
    224  UInt64 importantTotalUnpacked = 0;
    225 
    226  // numItems = (UInt32)(Int32)-1;
    227 
    228  bool allFilesMode = (numItems == (UInt32)(Int32)-1);
    229  if (allFilesMode)
    230    numItems = _db.Files.Size();
    231 
    232  if (numItems == 0)
    233    return S_OK;
    234 
    235  {
    236    CNum prevFolder = kNumNoIndex;
    237    UInt32 nextFile = 0;
    238    
    239    UInt32 i;
    240    
    241    for (i = 0; i < numItems; i++)
    242    {
    243      UInt32 fileIndex = allFilesMode ? i : indices[i];
    244      CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
    245      if (folderIndex == kNumNoIndex)
    246        continue;
    247      if (folderIndex != prevFolder || fileIndex < nextFile)
    248        nextFile = _db.FolderStartFileIndex[folderIndex];
    249      for (CNum index = nextFile; index <= fileIndex; index++)
    250        importantTotalUnpacked += _db.Files[index].Size;
    251      nextFile = fileIndex + 1;
    252      prevFolder = folderIndex;
    253    }
    254  }
    255 
    256  RINOK(extractCallback->SetTotal(importantTotalUnpacked));
    257 
    258  CLocalProgress *lps = new CLocalProgress;
    259  CMyComPtr<ICompressProgressInfo> progress = lps;
    260  lps->Init(extractCallback, false);
    261 
    262  CDecoder decoder(
    263    #if !defined(USE_MIXER_MT)
    264      false
    265    #elif !defined(USE_MIXER_ST)
    266      true
    267    #elif !defined(__7Z_SET_PROPERTIES)
    268      #ifdef _7ZIP_ST
    269        false
    270      #else
    271        true
    272      #endif
    273    #else
    274      _useMultiThreadMixer
    275    #endif
    276    );
    277 
    278  UInt64 curPacked, curUnpacked;
    279 
    280  CMyComPtr<IArchiveExtractCallbackMessage> callbackMessage;
    281  extractCallback.QueryInterface(IID_IArchiveExtractCallbackMessage, &callbackMessage);
    282 
    283  CFolderOutStream *folderOutStream = new CFolderOutStream;
    284  CMyComPtr<ISequentialOutStream> outStream(folderOutStream);
    285 
    286  folderOutStream->_db = &_db;
    287  folderOutStream->ExtractCallback = extractCallback;
    288  folderOutStream->TestMode = (testModeSpec != 0);
    289  folderOutStream->CheckCrc = (_crcSize != 0);
    290 
    291  for (UInt32 i = 0;; lps->OutSize += curUnpacked, lps->InSize += curPacked)
    292  {
    293    RINOK(lps->SetCur());
    294 
    295    if (i >= numItems)
    296      break;
    297 
    298    curUnpacked = 0;
    299    curPacked = 0;
    300 
    301    UInt32 fileIndex = allFilesMode ? i : indices[i];
    302    CNum folderIndex = _db.FileIndexToFolderIndexMap[fileIndex];
    303 
    304    UInt32 numSolidFiles = 1;
    305 
    306    if (folderIndex != kNumNoIndex)
    307    {
    308      curPacked = _db.GetFolderFullPackSize(folderIndex);
    309      UInt32 nextFile = fileIndex + 1;
    310      fileIndex = _db.FolderStartFileIndex[folderIndex];
    311      UInt32 k;
    312 
    313      for (k = i + 1; k < numItems; k++)
    314      {
    315        UInt32 fileIndex2 = allFilesMode ? k : indices[k];
    316        if (_db.FileIndexToFolderIndexMap[fileIndex2] != folderIndex
    317            || fileIndex2 < nextFile)
    318          break;
    319        nextFile = fileIndex2 + 1;
    320      }
    321      
    322      numSolidFiles = k - i;
    323      
    324      for (k = fileIndex; k < nextFile; k++)
    325        curUnpacked += _db.Files[k].Size;
    326    }
    327 
    328    {
    329      HRESULT result = folderOutStream->Init(fileIndex,
    330          allFilesMode ? NULL : indices + i,
    331          numSolidFiles);
    332 
    333      i += numSolidFiles;
    334 
    335      RINOK(result);
    336    }
    337 
    338    // to test solid block with zero unpacked size we disable that code
    339    if (folderOutStream->WasWritingFinished())
    340      continue;
    341 
    342    #ifndef _NO_CRYPTO
    343    CMyComPtr<ICryptoGetTextPassword> getTextPassword;
    344    if (extractCallback)
    345      extractCallback.QueryInterface(IID_ICryptoGetTextPassword, &getTextPassword);
    346    #endif
    347 
    348    try
    349    {
    350      #ifndef _NO_CRYPTO
    351        bool isEncrypted = false;
    352        bool passwordIsDefined = false;
    353        UString password;
    354      #endif
    355 
    356 
    357      bool dataAfterEnd_Error = false;
    358 
    359      HRESULT result = decoder.Decode(
    360          EXTERNAL_CODECS_VARS
    361          _inStream,
    362          _db.ArcInfo.DataStartPosition,
    363          _db, folderIndex,
    364          &curUnpacked,
    365 
    366          outStream,
    367          progress,
    368          NULL // *inStreamMainRes
    369          , dataAfterEnd_Error
    370          
    371          _7Z_DECODER_CRYPRO_VARS
    372          #if !defined(_7ZIP_ST)
    373            , true, _numThreads, _memUsage
    374          #endif
    375          );
    376 
    377      if (result == S_FALSE || result == E_NOTIMPL || dataAfterEnd_Error)
    378      {
    379        bool wasFinished = folderOutStream->WasWritingFinished();
    380 
    381        int resOp = NExtract::NOperationResult::kDataError;
    382        
    383        if (result != S_FALSE)
    384        {
    385          if (result == E_NOTIMPL)
    386            resOp = NExtract::NOperationResult::kUnsupportedMethod;
    387          else if (wasFinished && dataAfterEnd_Error)
    388            resOp = NExtract::NOperationResult::kDataAfterEnd;
    389        }
    390 
    391        RINOK(folderOutStream->FlushCorrupted(resOp));
    392 
    393        if (wasFinished)
    394        {
    395          // we don't show error, if it's after required files
    396          if (/* !folderOutStream->ExtraWriteWasCut && */ callbackMessage)
    397          {
    398            RINOK(callbackMessage->ReportExtractResult(NEventIndexType::kBlockIndex, folderIndex, resOp));
    399          }
    400        }
    401        continue;
    402      }
    403      
    404      if (result != S_OK)
    405        return result;
    406 
    407      RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
    408      continue;
    409    }
    410    catch(...)
    411    {
    412      RINOK(folderOutStream->FlushCorrupted(NExtract::NOperationResult::kDataError));
    413      // continue;
    414      return E_FAIL;
    415    }
    416  }
    417 
    418  return S_OK;
    419 
    420  COM_TRY_END
    421 }
    422 
    423 }}