Skip to content

Commit

Permalink
OrcLib: Archive: add Archive7z
Browse files Browse the repository at this point in the history
  • Loading branch information
fabienfl-orc committed Nov 9, 2020
1 parent 868ca46 commit b2628c6
Show file tree
Hide file tree
Showing 20 changed files with 1,336 additions and 0 deletions.
253 changes: 253 additions & 0 deletions src/OrcLib/Archive/7z/Archive7z.cpp
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;
};
51 changes: 51 additions & 0 deletions src/OrcLib/Archive/7z/Archive7z.h
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
30 changes: 30 additions & 0 deletions src/OrcLib/Archive/7z/ArchiveOpenCallback.cpp
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;
}
40 changes: 40 additions & 0 deletions src/OrcLib/Archive/7z/ArchiveOpenCallback.h
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
Loading

0 comments on commit b2628c6

Please sign in to comment.