-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
868ca46
commit b2628c6
Showing
20 changed files
with
1,336 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,253 @@ | ||
// | ||
// SPDX-License-Identifier: LGPL-2.1-or-later | ||
// | ||
// Copyright © 2020 ANSSI. All Rights Reserved. | ||
// | ||
// Author(s): fabienfl (ANSSI) | ||
// | ||
|
||
#include "stdafx.h" | ||
|
||
#include "Archive/7z/Archive7z.h" | ||
|
||
#include <Windows.h> | ||
|
||
#include <7zip/extras.h> | ||
|
||
#include "Archive/Item.h" | ||
#include "Archive/7z/InStreamAdapter.h" | ||
#include "Archive/7z/OutStreamAdapter.h" | ||
#include "Archive/7z/ArchiveOpenCallback.h" | ||
#include "Archive/7z/ArchiveUpdateCallback.h" | ||
#include "ByteStream.h" | ||
|
||
using namespace Orc::Archive; | ||
using namespace Orc; | ||
|
||
namespace { | ||
|
||
#ifdef _7ZIP_STATIC | ||
// Static linking requires calling specifics functions. | ||
// Call '::Lib7z::Instance()' to initialize the singleton | ||
class Lib7z | ||
{ | ||
Lib7z::Lib7z(const Lib7z&) = delete; | ||
Lib7z& Lib7z::operator=(const Lib7z&) = delete; | ||
|
||
Lib7z::Lib7z() | ||
{ | ||
::lib7zCrcTableInit(); | ||
NArchive::N7z::Register(); | ||
NArchive::NZip::Register(); | ||
NCompress::RegisterCodecCopy(); | ||
NCompress::NBcj::RegisterCodecBCJ(); | ||
NCompress::NBcj2::RegisterCodecBCJ2(); | ||
NCompress::NLzma::RegisterCodecLZMA(); | ||
NCompress::NLzma2::RegisterCodecLZMA2(); | ||
NCompress::NDeflate::RegisterCodecDeflate(); | ||
NCompress::NDeflate::RegisterCodecDeflate64(); | ||
|
||
NCrypto::N7z::RegisterCodec7zAES(); | ||
NCrypto::RegisterCodecAES256CBC(); | ||
} | ||
|
||
public: | ||
static Lib7z& Instance() | ||
{ | ||
static Lib7z lib; | ||
return lib; | ||
} | ||
}; | ||
|
||
#endif // _7ZIP_STATIC | ||
|
||
GUID ToGuid(Archive::Format format) | ||
{ | ||
switch (format) | ||
{ | ||
case Archive::Format::k7z: | ||
return CLSID_CFormat7z; | ||
default: | ||
return CLSID_CFormat7z; | ||
} | ||
} | ||
|
||
enum class Lib7zCompressionLevel | ||
{ | ||
None = 0, | ||
Fastest = 1, | ||
Fast = 3, | ||
Normal = 5, | ||
Maximum = 7, | ||
Ultra = 9 | ||
}; | ||
|
||
Lib7zCompressionLevel ToLib7zLevel(CompressionLevel level) | ||
{ | ||
switch (level) | ||
{ | ||
case CompressionLevel::kNone: | ||
return Lib7zCompressionLevel::None; | ||
case CompressionLevel::kFastest: | ||
return Lib7zCompressionLevel::Fastest; | ||
case CompressionLevel::kFast: | ||
return Lib7zCompressionLevel::Fast; | ||
case CompressionLevel::kNormal: | ||
return Lib7zCompressionLevel::Normal; | ||
case CompressionLevel::kMaximum: | ||
return Lib7zCompressionLevel::Maximum; | ||
case CompressionLevel::kUltra: | ||
return Lib7zCompressionLevel::Ultra; | ||
case CompressionLevel::kDefault: | ||
return Lib7zCompressionLevel::Normal; | ||
default: | ||
return Lib7zCompressionLevel::Normal; | ||
} | ||
} | ||
|
||
void SetCompressionLevel(const CComPtr<IOutArchive>& archiver, CompressionLevel level, std::error_code& ec) | ||
{ | ||
CMyComPtr<ISetProperties> setProperties; | ||
HRESULT hr = archiver->QueryInterface(IID_ISetProperties, (void**)&setProperties); | ||
if (setProperties == nullptr) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: spdlog::warn("Failed retrieve IID_ISetProperties: {}", ec.message()); | ||
return; | ||
} | ||
|
||
const uint8_t numProps = 1; | ||
const wchar_t* names[numProps] = {L"x"}; | ||
NWindows::NCOM::CPropVariant values[numProps] = {static_cast<UINT32>(::ToLib7zLevel(level))}; | ||
|
||
hr = setProperties->SetProperties(names, values, numProps); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: spdlog::warn("Failed to set property: {}", ec.message()); | ||
return; | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
Archive7z::Archive7z(Format format, Archive::CompressionLevel level, std::wstring password) | ||
: m_format(format) | ||
, m_compressionLevel(level) | ||
, m_password(std::move(password)) | ||
{ | ||
#ifdef _7ZIP_STATIC | ||
::Lib7z::Instance(); | ||
#endif // _7ZIP_STATIC | ||
} | ||
|
||
void Archive7z::Add(std::unique_ptr<Item> item) | ||
{ | ||
m_items.emplace_back(std::move(item)); | ||
} | ||
|
||
void Archive7z::Add(Items items) | ||
{ | ||
std::move(std::begin(items), std::end(items), std::back_inserter(m_items)); | ||
} | ||
|
||
void Archive7z::Compress( | ||
const std::shared_ptr<ByteStream>& outputArchive, | ||
const std::shared_ptr<ByteStream>& inputArchive, | ||
std::error_code& ec) | ||
{ | ||
if (m_items.empty() && inputArchive == nullptr) | ||
{ | ||
ec = std::make_error_code(std::errc::invalid_argument); | ||
return; | ||
} | ||
|
||
CComPtr<IOutArchive> archiver; | ||
const auto formatGuid = ToGuid(m_format); | ||
HRESULT hr = ::CreateObject(&formatGuid, &IID_IOutArchive, reinterpret_cast<void**>(&archiver)); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: spdlog::debug("Failed to get IID_IOutArchive: {}", ec.message()); | ||
return; | ||
} | ||
|
||
::SetCompressionLevel(archiver, m_compressionLevel, ec); | ||
if (ec) | ||
{ | ||
// TODO: spdlog::debug("Failed to update compression level"); | ||
return; | ||
} | ||
|
||
UInt32 numberOfArchivedItems = 0; | ||
if (inputArchive) | ||
{ | ||
HRESULT hr = inputArchive->CanRead(); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: log::Error(_L_, hr, L"Temp archive stream cannot be read\r\n"); | ||
return; | ||
} | ||
|
||
hr = inputArchive->SetFilePointer(0LL, FILE_BEGIN, NULL); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: log::Error(_L_, hr, L"Failed to rewind Temp stream\r\n"); | ||
return; | ||
} | ||
|
||
CComQIPtr<IInArchive, &IID_IInArchive> archiverIn(archiver); | ||
CComPtr<InStreamAdapter> inStream = new InStreamAdapter(inputArchive); | ||
CComPtr<ArchiveOpenCallback> archiveOpenCallback(new ArchiveOpenCallback()); | ||
hr = archiverIn->Open(inStream, nullptr, archiveOpenCallback); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: log::Error(_L_, hr, L"Failed to open archive stream\r\n"); | ||
return; | ||
} | ||
|
||
hr = archiverIn->GetNumberOfItems(&numberOfArchivedItems); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
return; | ||
} | ||
} | ||
|
||
const auto numberOfNewItems = m_items.size(); | ||
|
||
CComPtr<ArchiveUpdateCallback> archiveUpdateCallback( | ||
new ArchiveUpdateCallback(std::move(m_items), m_password, numberOfArchivedItems)); | ||
|
||
m_items.clear(); | ||
|
||
CComQIPtr<ISequentialOutStream, &IID_ISequentialOutStream> outStream(new OutStreamAdapter(outputArchive)); | ||
hr = archiver->UpdateItems(outStream, numberOfNewItems + numberOfArchivedItems, archiveUpdateCallback); | ||
if (FAILED(hr)) | ||
{ | ||
ec.assign(hr, std::system_category()); | ||
// TODO: spdlog::debug("Failed to compress: {}", ec.message()); | ||
return; | ||
} | ||
|
||
// 'ec' will be set to an error if one of the intermediate item compression has failed | ||
ec = archiveUpdateCallback->GetCallbackError(); | ||
} | ||
|
||
void Archive7z::Compress(const std::shared_ptr<ByteStream>& output, std::error_code& ec) | ||
{ | ||
Compress(output, {}, ec); | ||
} | ||
|
||
void Archive7z::SetCompressionLevel(Archive::CompressionLevel level, std::error_code& ec) | ||
{ | ||
m_compressionLevel = level; | ||
} | ||
|
||
const IArchive::Items& Archive7z::AddedItems() const | ||
{ | ||
return m_items; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// | ||
// SPDX-License-Identifier: LGPL-2.1-or-later | ||
// | ||
// Copyright © 2020 ANSSI. All Rights Reserved. | ||
// | ||
// Author(s): fabienfl (ANSSI) | ||
// | ||
|
||
#pragma once | ||
|
||
#include "Archive/IArchive.h" | ||
|
||
#include "Archive/CompressionLevel.h" | ||
|
||
namespace Orc { | ||
|
||
namespace Archive { | ||
|
||
class Archive7z final : public IArchive | ||
{ | ||
public: | ||
Archive7z(Format format, CompressionLevel level, std::wstring password); | ||
|
||
void Add(std::unique_ptr<Item> item) override; | ||
|
||
void Add(Items items) override; | ||
|
||
void Compress(const std::shared_ptr<ByteStream>& output, std::error_code& ec) override; | ||
|
||
void Compress( | ||
const std::shared_ptr<ByteStream>& outputArchive, | ||
const std::shared_ptr<ByteStream>& inputArchive, | ||
std::error_code& ec) override; | ||
|
||
// List of added items waiting to be processed by the Compress method | ||
const Items& AddedItems() const override; | ||
|
||
Archive::CompressionLevel CompressionLevel() const { return m_compressionLevel; } | ||
|
||
void SetCompressionLevel(Archive::CompressionLevel level, std::error_code& ec); | ||
|
||
private: | ||
const Format m_format; | ||
Archive::CompressionLevel m_compressionLevel; | ||
const std::wstring m_password; | ||
Items m_items; | ||
}; | ||
|
||
} // namespace Archive | ||
|
||
} // namespace Orc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// | ||
// SPDX-License-Identifier: LGPL-2.1-or-later | ||
// | ||
// Copyright © 2020 ANSSI. All Rights Reserved. | ||
// | ||
// Author(s): fabienfl | ||
// | ||
|
||
#include "stdafx.h" | ||
|
||
#include <7zip/7zip.h> | ||
|
||
#include "ArchiveOpenCallback.h" | ||
|
||
using namespace Orc::Archive; | ||
|
||
STDMETHODIMP ArchiveOpenCallback::SetTotal(const UInt64*, const UInt64*) | ||
{ | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP ArchiveOpenCallback::SetCompleted(const UInt64*, const UInt64*) | ||
{ | ||
return S_OK; | ||
} | ||
|
||
STDMETHODIMP ArchiveOpenCallback::CryptoGetTextPassword(BSTR*) | ||
{ | ||
return E_ABORT; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// | ||
// SPDX-License-Identifier: LGPL-2.1-or-later | ||
// | ||
// Copyright © 2020 ANSSI. All Rights Reserved. | ||
// | ||
// Author(s): fabienfl | ||
// | ||
|
||
#pragma once | ||
|
||
#include <system_error> | ||
|
||
#include <7zip/extras.h> | ||
|
||
namespace Orc { | ||
|
||
namespace Archive { | ||
|
||
class LogFileWriter; | ||
|
||
class ArchiveOpenCallback | ||
: public IArchiveOpenCallback | ||
, public ICryptoGetTextPassword | ||
, public CMyUnknownImp | ||
{ | ||
public: | ||
MY_UNKNOWN_IMP2(IArchiveOpenCallback, ICryptoGetTextPassword) | ||
|
||
public: | ||
// IArchiveOpenCallback | ||
STDMETHOD(SetTotal)(const UInt64* files, const UInt64* bytes); | ||
STDMETHOD(SetCompleted)(const UInt64* files, const UInt64* bytes); | ||
|
||
// ICryptoGetTextPassword | ||
STDMETHOD(CryptoGetTextPassword)(BSTR* password); | ||
}; | ||
|
||
} // namespace Archive | ||
|
||
} // namespace Orc |
Oops, something went wrong.