HRESULT UpdateVolume(
    IInStream *inStream,
    const CArchiveDatabaseEx *database,
    CObjectVector<CUpdateItem> &updateItems,
    ISequentialOutStream *seqOutStream,
    IArchiveUpdateCallback *updateCallback,
    const CUpdateOptions &options)
{
  if (updateItems.Size() != 1)
    return E_NOTIMPL;

  CMyComPtr<IArchiveUpdateCallback2> volumeCallback;
  RINOK(updateCallback->QueryInterface(IID_IArchiveUpdateCallback2, (void **)&volumeCallback));
  if (!volumeCallback)
    return E_NOTIMPL;

  CMyComPtr<ISequentialInStream> fileStream;
  HRESULT result = updateCallback->GetStream(0, &fileStream);
  if (result != S_OK && result != S_FALSE)
    return result;
  if (result == S_FALSE)
    return E_FAIL;
  
  CFileItem file;
  
  const CUpdateItem &updateItem = updateItems[0];
  if (updateItem.NewProperties)
    FromUpdateItemToFileItem(updateItem, file);
  else
    file = database->Files[updateItem.IndexInArchive];
  if (file.IsAnti || file.IsDirectory)
    return E_FAIL;

  UInt64 complexity = 0;
  file.IsStartPosDefined = true;
  file.StartPos = 0;
  for (UInt64 volumeIndex = 0; true; volumeIndex++)
  { 
    UInt64 volSize;
    RINOK(volumeCallback->GetVolumeSize(volumeIndex, &volSize));
    UInt64 pureSize = COutArchive::GetVolPureSize(volSize, file.Name.Length(), true);
    CMyComPtr<ISequentialOutStream> volumeStream;
    RINOK(volumeCallback->GetVolumeStream(volumeIndex, &volumeStream));
   
    COutArchive archive;
    RINOK(archive.Create(volumeStream, true));
    RINOK(archive.SkeepPrefixArchiveHeader());
        
    CSequentialInStreamWithCRC *inCrcStreamSpec = new CSequentialInStreamWithCRC;
    CMyComPtr<ISequentialInStream> inCrcStream = inCrcStreamSpec;
    inCrcStreamSpec->Init(fileStream);

    RINOK(WriteRange(inCrcStream, volumeStream, pureSize, updateCallback, complexity));
    file.UnPackSize = inCrcStreamSpec->GetSize();
    if (file.UnPackSize == 0)
      break;
    file.FileCRC = inCrcStreamSpec->GetCRC();
    RINOK(WriteVolumeHeader(archive, file, options));
    file.StartPos += file.UnPackSize;
    if (file.UnPackSize < pureSize)
      break;
  }
  return S_OK;
}
Exemple #2
0
HRESULT UpdateArchive(
    DECL_EXTERNAL_CODECS_LOC_VARS
    IInStream * /* inStream */, 
    UInt64 unpackSize,
    ISequentialOutStream *outStream,
    const CItem &newItem,
    const CCompressionMethodMode &compressionMethod,
    int indexInClient,
    IArchiveUpdateCallback *updateCallback)
{
  UInt64 complexity = unpackSize;

  RINOK(updateCallback->SetTotal(complexity));

  CMyComPtr<ICompressCoder> deflateEncoder;
  
  complexity = 0;
  RINOK(updateCallback->SetCompleted(&complexity));

  CMyComPtr<ISequentialInStream> fileInStream;

  RINOK(updateCallback->GetStream(indexInClient, &fileInStream));

  CSequentialInStreamWithCRC *inStreamSpec = new CSequentialInStreamWithCRC;
  CMyComPtr<ISequentialInStream> crcStream(inStreamSpec);
  inStreamSpec->SetStream(fileInStream);
  inStreamSpec->Init();

  CLocalProgress *lps = new CLocalProgress;
  CMyComPtr<ICompressProgressInfo> progress = lps;
  lps->Init(updateCallback, true);
  
  COutArchive outArchive;
  outArchive.Create(outStream);

  CItem item = newItem;
  item.CompressionMethod = NFileHeader::NCompressionMethod::kDeflate;
  item.ExtraFlags = 0;
  item.HostOS = kHostOS;

  RINOK(outArchive.WriteHeader(item));

  {
    RINOK(CreateCoder(
        EXTERNAL_CODECS_LOC_VARS
        kMethodId_Deflate, deflateEncoder, true));
    if (!deflateEncoder)
      return E_NOTIMPL;

    NWindows::NCOM::CPropVariant properties[] = 
    { 
      compressionMethod.Algo, 
      compressionMethod.NumPasses, 
      compressionMethod.NumFastBytes,
      compressionMethod.NumMatchFinderCycles
    };
    PROPID propIDs[] = 
    { 
      NCoderPropID::kAlgorithm,
      NCoderPropID::kNumPasses, 
      NCoderPropID::kNumFastBytes,
      NCoderPropID::kMatchFinderCycles
    };
    int numProps = sizeof(propIDs) / sizeof(propIDs[0]);
    if (!compressionMethod.NumMatchFinderCyclesDefined)
      numProps--;
    CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
    RINOK(deflateEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties));
    RINOK(setCoderProperties->SetCoderProperties(propIDs, properties, numProps));
  }
  RINOK(deflateEncoder->Code(crcStream, outStream, NULL, NULL, progress));

  item.FileCRC = inStreamSpec->GetCRC();
  item.UnPackSize32 = (UInt32)inStreamSpec->GetSize();
  RINOK(outArchive.WritePostHeader(item));
  return updateCallback->SetOperationResult(NArchive::NUpdate::NOperationResult::kOK);
}
HRESULT CAddCommon::Compress(
    DECL_EXTERNAL_CODECS_LOC_VARS
    ISequentialInStream *inStream, IOutStream *outStream,
    ICompressProgressInfo *progress, CCompressingResult &operationResult)
{
  CSequentialInStreamWithCRC *inSecCrcStreamSpec = 0;
  CInStreamWithCRC *inCrcStreamSpec = 0;
  CMyComPtr<ISequentialInStream> inCrcStream;
  {
    CMyComPtr<IInStream> inStream2;
    // we don't support stdin, since stream from stdin can require 64-bit size header
    RINOK(inStream->QueryInterface(IID_IInStream, (void **)&inStream2));
    if (inStream2)
    {
      inCrcStreamSpec = new CInStreamWithCRC;
      inCrcStream = inCrcStreamSpec;
      inCrcStreamSpec->SetStream(inStream2);
      inCrcStreamSpec->Init();
    }
    else
    {
      inSecCrcStreamSpec = new CSequentialInStreamWithCRC;
      inCrcStream = inSecCrcStreamSpec;
      inSecCrcStreamSpec->SetStream(inStream);
      inSecCrcStreamSpec->Init();
    }
  }

  int numTestMethods = _options.MethodSequence.Size();
  if (numTestMethods > 1 || _options.PasswordIsDefined)
  {
    if (inCrcStreamSpec == 0)
    {
      if (_options.PasswordIsDefined)
        return E_NOTIMPL;
      numTestMethods = 1;
    }
  }
  Byte method = 0;
  COutStreamReleaser outStreamReleaser;
  for (int i = 0; i < numTestMethods; i++)
  {
    if (inCrcStreamSpec != 0)
      RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL));
    RINOK(outStream->Seek(0, STREAM_SEEK_SET, NULL));
    if (_options.PasswordIsDefined)
    {
      if (!_cryptoStream)
      {
        _cryptoStreamSpec = new CFilterCoder;
        _cryptoStream = _cryptoStreamSpec;
      }
      if (_options.IsAesMode)
      {
        _cryptoStreamSpec->Filter = _aesFilter = _filterAesSpec = new NCrypto::NWzAes::CEncoder;
        _filterAesSpec->SetKeyMode(_options.AesKeyMode);
        RINOK(_filterAesSpec->CryptoSetPassword(
            (const Byte *)(const char *)_options.Password, _options.Password.Length()));
        RINOK(_filterAesSpec->WriteHeader(outStream));
      }
      else
      {
        _cryptoStreamSpec->Filter = _zipCryptoFilter = _filterSpec = new NCrypto::NZip::CEncoder;
        RINOK(_filterSpec->CryptoSetPassword(
            (const Byte *)(const char *)_options.Password, _options.Password.Length()));
        UInt32 crc = 0;
        RINOK(GetStreamCRC(inStream, crc));
        RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL));
        RINOK(_filterSpec->CryptoSetCRC(crc));
        RINOK(_filterSpec->WriteHeader(outStream));
      }
      RINOK(_cryptoStreamSpec->SetOutStream(outStream));
      outStreamReleaser.FilterCoder = _cryptoStreamSpec;
    }

    method = _options.MethodSequence[i];
    switch(method)
    {
      case NFileHeader::NCompressionMethod::kStored:
      {
        if (_copyCoderSpec == NULL)
        {
          _copyCoderSpec = new NCompress::CCopyCoder;
          _copyCoder = _copyCoderSpec;
        }
        CMyComPtr<ISequentialOutStream> outStreamNew;
        if (_options.PasswordIsDefined)
          outStreamNew = _cryptoStream;
        else
          outStreamNew = outStream;
        RINOK(_copyCoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress));
        operationResult.ExtractVersion = NFileHeader::NCompressionMethod::kStoreExtractVersion;
        break;
      }
      default:
      {
        if (!_compressEncoder)
        {
          if (method == NFileHeader::NCompressionMethod::kLZMA)
          {
            CLzmaEncoder *_lzmaEncoder = new CLzmaEncoder();
            _compressEncoder = _lzmaEncoder;
            NWindows::NCOM::CPropVariant props[] =
            {
              #ifdef COMPRESS_MT
              _options.NumThreads,
              #endif
              _options.Algo,
              _options.DicSize,
              _options.NumFastBytes,
              (BSTR)(const wchar_t *)_options.MatchFinder,
              _options.NumMatchFinderCycles
            };
            PROPID propIDs[] =
            {
              #ifdef COMPRESS_MT
              NCoderPropID::kNumThreads,
              #endif
              NCoderPropID::kAlgorithm,
              NCoderPropID::kDictionarySize,
              NCoderPropID::kNumFastBytes,
              NCoderPropID::kMatchFinder,
              NCoderPropID::kMatchFinderCycles
            };
            int numProps = sizeof(propIDs) / sizeof(propIDs[0]);
            if (!_options.NumMatchFinderCyclesDefined)
              numProps--;
            RINOK(_lzmaEncoder->SetCoderProperties(propIDs, props, numProps));
          }
          else
          {
          CMethodId methodId;
          switch(method)
          {
            case NFileHeader::NCompressionMethod::kBZip2:
              methodId = kMethodId_BZip2;
              break;
            default:
              methodId = kMethodId_ZipBase + method;
              break;
          }
          RINOK(CreateCoder(
              EXTERNAL_CODECS_LOC_VARS
              methodId, _compressEncoder, true));
          if (!_compressEncoder)
            return E_NOTIMPL;

          if (method == NFileHeader::NCompressionMethod::kDeflated ||
              method == NFileHeader::NCompressionMethod::kDeflated64)
          {
            NWindows::NCOM::CPropVariant props[] =
            {
              _options.Algo,
              _options.NumPasses,
              _options.NumFastBytes,
              _options.NumMatchFinderCycles
            };
            PROPID propIDs[] =
            {
              NCoderPropID::kAlgorithm,
              NCoderPropID::kNumPasses,
              NCoderPropID::kNumFastBytes,
              NCoderPropID::kMatchFinderCycles
            };
            int numProps = sizeof(propIDs) / sizeof(propIDs[0]);
            if (!_options.NumMatchFinderCyclesDefined)
              numProps--;
            CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
            _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties);
            if (setCoderProperties)
            {
              RINOK(setCoderProperties->SetCoderProperties(propIDs, props, numProps));
            }
          }
          else if (method == NFileHeader::NCompressionMethod::kBZip2)
          {
            NWindows::NCOM::CPropVariant props[] =
            {
              _options.DicSize,
              _options.NumPasses
              #ifdef COMPRESS_MT
              , _options.NumThreads
              #endif
            };
            PROPID propIDs[] =
            {
              NCoderPropID::kDictionarySize,
              NCoderPropID::kNumPasses
              #ifdef COMPRESS_MT
              , NCoderPropID::kNumThreads
              #endif
            };
            CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
            _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties);
            if (setCoderProperties)
            {
              RINOK(setCoderProperties->SetCoderProperties(propIDs, props, sizeof(propIDs) / sizeof(propIDs[0])));
            }
          }
          }
        }
        CMyComPtr<ISequentialOutStream> outStreamNew;
        if (_options.PasswordIsDefined)
          outStreamNew = _cryptoStream;
        else
          outStreamNew = outStream;
        RINOK(_compressEncoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress));
        operationResult.ExtractVersion = NFileHeader::NCompressionMethod::kDeflateExtractVersion;
        break;
      }
    }

    RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &operationResult.PackSize));

    if (inCrcStreamSpec != 0)
    {
      operationResult.CRC = inCrcStreamSpec->GetCRC();
      operationResult.UnpackSize = inCrcStreamSpec->GetSize();
    }
    else
    {
      operationResult.CRC = inSecCrcStreamSpec->GetCRC();
      operationResult.UnpackSize = inSecCrcStreamSpec->GetSize();
    }

    if (_options.PasswordIsDefined)
    {
      if (operationResult.PackSize < operationResult.UnpackSize +
          (_options.IsAesMode ? _filterAesSpec->GetHeaderSize() : NCrypto::NZip::kHeaderSize))
        break;
    }
    else if (operationResult.PackSize < operationResult.UnpackSize)
      break;
  }
  if (_options.IsAesMode)
  {
    RINOK(_filterAesSpec->WriteFooter(outStream));
    RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &operationResult.PackSize));
  }
  operationResult.Method = method;
  return outStream->SetSize(operationResult.PackSize);
}
HRESULT CAddCommon::Compress(ISequentialInStream *inStream, IOutStream *outStream, 
      ICompressProgressInfo *progress, CCompressingResult &operationResult)
{
  CSequentialInStreamWithCRC *inSecCrcStreamSpec = 0;
  CInStreamWithCRC *inCrcStreamSpec = 0;
  CMyComPtr<ISequentialInStream> inCrcStream;
  {
    CMyComPtr<IInStream> inStream2;
    // we don't support stdin, since stream from stdin can require 64-bit size header 
    RINOK(inStream->QueryInterface(IID_IInStream, (void **)&inStream2));
    if (inStream2)
    {
      inCrcStreamSpec = new CInStreamWithCRC;
      inCrcStream = inCrcStreamSpec;
      inCrcStreamSpec->SetStream(inStream2);
      inCrcStreamSpec->Init();
    }
    else
    {
      inSecCrcStreamSpec = new CSequentialInStreamWithCRC;
      inCrcStream = inSecCrcStreamSpec;
      inSecCrcStreamSpec->SetStream(inStream);
      inSecCrcStreamSpec->Init();
    }
  }

  int numTestMethods = _options.MethodSequence.Size();
  if (numTestMethods > 1 || _options.PasswordIsDefined)
  {
    if (inCrcStreamSpec == 0)
    {
      if (_options.PasswordIsDefined)
        return E_NOTIMPL;
      numTestMethods = 1;
    }
  }
  Byte method = 0;
  COutStreamReleaser outStreamReleaser;
  for(int i = 0; i < numTestMethods; i++)
  {
    if (inCrcStreamSpec != 0)
      RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL));
    RINOK(outStream->Seek(0, STREAM_SEEK_SET, NULL));
    if (_options.PasswordIsDefined)
    {
      if (!_cryptoStream)
      {
        _cryptoStreamSpec = new CFilterCoder;
        _cryptoStream = _cryptoStreamSpec;
      }
      if (_options.IsAesMode)
      {
        _cryptoStreamSpec->Filter = _aesFilter = _filterAesSpec = new NCrypto::NWzAES::CEncoder;
        _filterAesSpec->SetKeyMode(_options.AesKeyMode);
        RINOK(_filterAesSpec->CryptoSetPassword(
            (const Byte *)(const char *)_options.Password, _options.Password.Length()));
        RINOK(_filterAesSpec->WriteHeader(outStream));
      }
      else
      {
        _cryptoStreamSpec->Filter = _zipCryptoFilter = _filterSpec = new NCrypto::NZip::CEncoder;
        RINOK(_filterSpec->CryptoSetPassword(
            (const Byte *)(const char *)_options.Password, _options.Password.Length()));
        UInt32 crc = 0;
        RINOK(GetStreamCRC(inStream, crc));
        RINOK(inCrcStreamSpec->Seek(0, STREAM_SEEK_SET, NULL));
        RINOK(_filterSpec->CryptoSetCRC(crc));
        RINOK(_filterSpec->WriteHeader(outStream));
      }
      RINOK(_cryptoStreamSpec->SetOutStream(outStream));
      outStreamReleaser.FilterCoder = _cryptoStreamSpec;
    }

    method = _options.MethodSequence[i];
    switch(method)
    {
      case NFileHeader::NCompressionMethod::kStored:
      {
        if(_copyCoderSpec == NULL)
        {
          _copyCoderSpec = new NCompress::CCopyCoder;
          _copyCoder = _copyCoderSpec;
        }
        CMyComPtr<ISequentialOutStream> outStreamNew;
        if (_options.PasswordIsDefined)
          outStreamNew = _cryptoStream;
        else
          outStreamNew = outStream;
        RINOK(_copyCoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress));
        operationResult.ExtractVersion = NFileHeader::NCompressionMethod::kStoreExtractVersion;
        break;
      }
      default:
      {
        if(!_compressEncoder)
        {
          // RINOK(m_MatchFinder.CoCreateInstance(CLSID_CMatchFinderBT3));
          #ifndef COMPRESS_DEFLATE
          UString methodName;
          N7z::LoadMethodMap();
          #endif
          switch(method)
          {
            case NFileHeader::NCompressionMethod::kDeflated:
            {
              #ifdef COMPRESS_DEFLATE
              _compressEncoder = new NCompress::NDeflate::NEncoder::CCOMCoder;
              #else
              methodName = L"Deflate";
              #endif
              break;
            }
            case NFileHeader::NCompressionMethod::kDeflated64:
            {
              #ifdef COMPRESS_DEFLATE64
              _compressEncoder = new NCompress::NDeflate::NEncoder::CCOMCoder64;
              #else
              methodName = L"Deflate64";
              #endif
              break;
            }
            case NFileHeader::NCompressionMethod::kBZip2:
            {
              #ifdef COMPRESS_BZIP2
              _compressEncoder = new NCompress::NBZip2::CEncoder;
              #else
              methodName = L"BZip2";
              #endif
              break;
            }
          }
          #ifndef COMPRESS_DEFLATE
          N7z::CMethodInfo2 methodInfo;
          if (!N7z::GetMethodInfo(methodName, methodInfo))
            return E_NOTIMPL;
          RINOK(_compressLib.LoadAndCreateCoder(
            methodInfo.FilePath, methodInfo.Encoder, &_compressEncoder));
          #endif

          if (method == NFileHeader::NCompressionMethod::kDeflated ||
              method == NFileHeader::NCompressionMethod::kDeflated64)
          {
            NWindows::NCOM::CPropVariant properties[] = 
            {
              _options.NumPasses, 
              _options.NumFastBytes,
              _options.NumMatchFinderCycles
            };
            PROPID propIDs[] = 
            {
              NCoderPropID::kNumPasses,
              NCoderPropID::kNumFastBytes,
              NCoderPropID::kMatchFinderCycles
            };
            int numProps = sizeof(propIDs) / sizeof(propIDs[0]);
            if (!_options.NumMatchFinderCyclesDefined)
              numProps--;
            CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
            _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties);
            if (setCoderProperties)
            {
              RINOK(setCoderProperties->SetCoderProperties(propIDs, properties, numProps));
            }
          } 
          else if (method == NFileHeader::NCompressionMethod::kBZip2)
          {
            NWindows::NCOM::CPropVariant properties[] = 
            {
              _options.DicSize, 
              _options.NumPasses
              #ifdef COMPRESS_MT
              , _options.NumThreads
              #endif
            };
            PROPID propIDs[] = 
            {
              NCoderPropID::kDictionarySize,
              NCoderPropID::kNumPasses
              #ifdef COMPRESS_MT
              , NCoderPropID::kNumThreads
              #endif
            };
            CMyComPtr<ICompressSetCoderProperties> setCoderProperties;
            _compressEncoder.QueryInterface(IID_ICompressSetCoderProperties, &setCoderProperties);
            if (setCoderProperties)
            {
              RINOK(setCoderProperties->SetCoderProperties(propIDs, properties, sizeof(propIDs) / sizeof(propIDs[0])));
            }
          }
        }
        CMyComPtr<ISequentialOutStream> outStreamNew;
        if (_options.PasswordIsDefined)
          outStreamNew = _cryptoStream;
        else
          outStreamNew = outStream;
        RINOK(_compressEncoder->Code(inCrcStream, outStreamNew, NULL, NULL, progress));
        operationResult.ExtractVersion = NFileHeader::NCompressionMethod::kDeflateExtractVersion;
        break;
      }
    }

    RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &operationResult.PackSize));

    if (inCrcStreamSpec != 0)
    {
      operationResult.CRC = inCrcStreamSpec->GetCRC();
      operationResult.UnpackSize = inCrcStreamSpec->GetSize();
    }
    else
    {
      operationResult.CRC = inSecCrcStreamSpec->GetCRC();
      operationResult.UnpackSize = inSecCrcStreamSpec->GetSize();
    }

    if (_options.PasswordIsDefined)
    {
      if (operationResult.PackSize < operationResult.UnpackSize + 
          (_options.IsAesMode ? _filterAesSpec->GetHeaderSize() : NCrypto::NZip::kHeaderSize))
        break;
    }
    else if (operationResult.PackSize < operationResult.UnpackSize) 
      break;
  }
  if (_options.IsAesMode)
  {
    RINOK(_filterAesSpec->WriteFooter(outStream));
    RINOK(outStream->Seek(0, STREAM_SEEK_CUR, &operationResult.PackSize));
  }
  operationResult.Method = method;
  return outStream->SetSize(operationResult.PackSize);
}