* All tests pass, pack and unpack work, makeappx can unpack

* Add RelWithDebInfo option for Windows, fix bug with negative seek in x86, PR feedback
This commit is contained in:
JohnMcPMS 2019-07-10 16:38:59 -07:00 коммит произвёл GitHub
Родитель 4b7c4089ba
Коммит d4508c9b45
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
15 изменённых файлов: 650 добавлений и 388 удалений

Просмотреть файл

@ -43,6 +43,12 @@ if /I "%~2" == "--debug" (
if /I "%~2" == "-d" (
set build="Debug"
)
if /I "%~2" == "--symbols" (
set build="RelWithDebInfo"
)
if /I "%~2" == "-sym" (
set build="RelWithDebInfo"
)
if /I "%~2" == "--parser-xerces" (
set parser="-DXML_PARSER=xerces"
)

Просмотреть файл

@ -61,7 +61,7 @@ namespace MSIX {
std::uint64_t sizeRemaining = m_streamSize;
for (auto block = blocks.begin(); ((sizeRemaining != 0) && (block != blocks.end())); block++)
{
auto rangeStream = ComPtr<IStream>::Make<RangeStream>(offset, std::min(sizeRemaining, BLOCKMAP_BLOCK_SIZE), stream);
auto rangeStream = ComPtr<IStream>::Make<RangeStream>(offset, std::min(sizeRemaining, BLOCKMAP_BLOCK_SIZE), stream.Get());
auto hashStream = ComPtr<IStream>::Make<HashStream>(rangeStream, block->hash);
std::uint64_t blockSize = std::min(sizeRemaining, BLOCKMAP_BLOCK_SIZE);

Просмотреть файл

@ -22,6 +22,11 @@ namespace MSIX {
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) noexcept override;
HRESULT STDMETHODCALLTYPE Read(void* buffer, ULONG countBytes, ULONG* bytesRead) noexcept override;
HRESULT STDMETHODCALLTYPE Write(void const *buffer, ULONG countBytes, ULONG *bytesWritten) noexcept override;
// IStreamInternal
std::uint64_t GetSize() override { return m_stream.As<IStreamInternal>()->GetSize(); }
bool IsCompressed() override { return m_stream.As<IStreamInternal>()->IsCompressed(); }
std::string GetName() override { return m_stream.As<IStreamInternal>()->GetName(); }
protected:
std::vector<std::uint8_t> Deflate(int disposition);

Просмотреть файл

@ -76,7 +76,15 @@ namespace MSIX {
#endif
RaiseException(const int line, const char* const file, const char* details, C c)
{
assert(false);
#ifdef WIN32
#ifndef NDEBUG
if (IsDebuggerPresent())
#endif
#endif
{
assert(false);
}
std::ostringstream builder;
if (details) { builder << details << "\n"; }
builder << "Call failed in " << file << " on line " << line;

Просмотреть файл

@ -46,81 +46,135 @@ class FieldBase
public:
FieldBase() = default;
size_t Size() { return sizeof(T); }
std::vector<std::uint8_t> GetBytes() { return std::vector<std::uint8_t>(); }
constexpr size_t Size() const { return sizeof(T); }
void GetBytes(std::vector<std::uint8_t>& bytes) const
{
THROW_IF_PACK_NOT_ENABLED
for (size_t i = 0; i < Size(); ++i)
{
bytes.push_back(static_cast<std::uint8_t>(this->value >> (i * 8)));
}
}
FieldBase& operator=(const T& v)
{
value = v;
return *this;
}
operator T() const
{
return value;
}
T& get()
{
return value;
}
const T& get() const
{
return value;
}
T* operator &()
{
return &value;
}
protected:
T value;
};
// Simple 2, 4, and 8 byte fields
class Field2Bytes final : public FieldBase<std::uint16_t>
{
public:
std::vector<std::uint8_t> GetBytes()
{
THROW_IF_PACK_NOT_ENABLED
std::vector<std::uint8_t> result;
result.push_back(static_cast<std::uint8_t>(this->value));
result.push_back(static_cast<std::uint8_t>(this->value >> 8));
return result;
}
};
class Field4Bytes final : public FieldBase<std::uint32_t>
{
public:
std::vector<std::uint8_t> GetBytes()
{
THROW_IF_PACK_NOT_ENABLED
std::vector<std::uint8_t> result;
result.push_back(static_cast<std::uint8_t>(this->value));
result.push_back(static_cast<std::uint8_t>(this->value >> 8));
result.push_back(static_cast<std::uint8_t>(this->value >> 16));
result.push_back(static_cast<std::uint8_t>(this->value >> 24));
return result;
}
};
class Field8Bytes final : public FieldBase<std::uint64_t>
{
public:
std::vector<std::uint8_t> GetBytes()
{
THROW_IF_PACK_NOT_ENABLED
std::vector<std::uint8_t> result;
result.push_back(static_cast<std::uint8_t>(this->value));
result.push_back(static_cast<std::uint8_t>(this->value >> 8));
result.push_back(static_cast<std::uint8_t>(this->value >> 16));
result.push_back(static_cast<std::uint8_t>(this->value >> 24));
result.push_back(static_cast<std::uint8_t>(this->value >> 32));
result.push_back(static_cast<std::uint8_t>(this->value >> 40));
result.push_back(static_cast<std::uint8_t>(this->value >> 48));
result.push_back(static_cast<std::uint8_t>(this->value >> 56));
return result;
}
};
using Field2Bytes = FieldBase<std::uint16_t>;
using Field4Bytes = FieldBase<std::uint32_t>;
using Field8Bytes = FieldBase<std::uint64_t>;
// variable length field.
class FieldNBytes : public FieldBase<std::vector<std::uint8_t>>
{
public:
size_t Size() { return this->value.size(); }
std::vector<std::uint8_t> GetBytes()
size_t Size() const { return this->value.size(); }
void GetBytes(std::vector<std::uint8_t>& result) const
{
THROW_IF_PACK_NOT_ENABLED
return this->value;
result.insert(result.end(), value.begin(), value.end());
}
};
//////////////////////////////////////////////////////////////////////////////////////////////
// Base type for individual serializable/deserializable optional fields //
//////////////////////////////////////////////////////////////////////////////////////////////
template <class T>
class OptionalFieldBase
{
public:
OptionalFieldBase() = default;
constexpr size_t Size() { return (hasValue ? value.Size() : 0); }
void GetBytes(std::vector<std::uint8_t>& bytes)
{
THROW_IF_PACK_NOT_ENABLED
if (hasValue)
{
value.GetBytes(bytes);
}
}
OptionalFieldBase& operator=(const T& v)
{
hasValue = true;
value = v;
return *this;
}
operator bool() const
{
return hasValue;
}
operator T() const
{
ThrowErrorIfNot(Error::InvalidState, hasValue, "Cannot retrieve value if none is set");
return value;
}
const T& get() const
{
ThrowErrorIfNot(Error::InvalidState, hasValue, "Cannot retrieve value if none is set");
return value.get();
}
T* operator &()
{
hasValue = true;
return &value;
}
protected:
FieldBase<T> value;
bool hasValue = false;
};
// Simple 2, 4, and 8 byte fields
using OptionalField2Bytes = OptionalFieldBase<std::uint16_t>;
using OptionalField4Bytes = OptionalFieldBase<std::uint32_t>;
using OptionalField8Bytes = OptionalFieldBase<std::uint64_t>;
//////////////////////////////////////////////////////////////////////////////////////////////
// Heterogeneous collection of types that are operated on as a compile-time vector //
//////////////////////////////////////////////////////////////////////////////////////////////
template <typename... Types>
class TypeList
{
static constexpr std::size_t last_index { std::tuple_size<std::tuple<Types...>>::value };
static constexpr std::size_t last_index { sizeof...(Types) };
public:
std::tuple<Types...> fields;
std::tuple<Types...> fields;
template<std::size_t index = 0, typename FuncT, class... Args>
inline typename std::enable_if<index == last_index, void>::type for_each(FuncT, Args&&... args) { }
@ -133,6 +187,9 @@ public:
template <size_t index>
auto& Field() noexcept { return std::get<index>(fields); }
template <size_t index>
const auto& Field() const noexcept { return std::get<index>(fields); }
};
//////////////////////////////////////////////////////////////////////////////////////////////
@ -146,7 +203,8 @@ public:
{
size_t result = 0;
this->for_each([](auto& field, std::size_t index, size_t& result)
{ result += field.Size();
{
result += field.Size();
}, result);
return result;
}
@ -157,8 +215,7 @@ public:
std::vector<std::uint8_t> bytes;
this->for_each([](auto& field, std::size_t index, std::vector<std::uint8_t>& bytes)
{
auto fieldBytes = field.GetBytes();
bytes.insert(bytes.end(), fieldBytes.begin(), fieldBytes.end());
field.GetBytes(bytes);
}, bytes);
return bytes;
}

Просмотреть файл

@ -6,9 +6,7 @@
#include "Exceptions.hpp"
#include "StreamBase.hpp"
#include "ComHelper.hpp"
#include "VectorStream.hpp"
#include "MsixFeatureSelector.hpp"
#include "DeflateStream.hpp"
#include <string>
#include <map>
@ -20,41 +18,56 @@ namespace MSIX {
class RangeStream : public StreamBase
{
public:
RangeStream(std::uint64_t offset, std::uint64_t size, const ComPtr<IStream>& stream) :
RangeStream(std::uint64_t offset, std::uint64_t size, IStream* stream) :
m_offset(offset),
m_size(size),
m_stream(stream)
{
}
RangeStream(bool isCompressed) : m_offset(0), m_size(0)
// For writing/pack
// This simply keeps track of the amount of data written to the stream, as well as
// limiting any Read/Seek to the data written, rather than the entire underlying stream.
RangeStream(IStream* stream) : m_stream(stream), m_size(0)
{
THROW_IF_PACK_NOT_ENABLED
m_stream = ComPtr<IStream>::Make<VectorStream>(&m_buffer);
if (isCompressed)
{
m_stream = ComPtr<IStream>::Make<DeflateStream>(m_stream);
}
ULARGE_INTEGER pos = { 0 };
ThrowHrIfFailed(m_stream->Seek({ 0 }, Reference::CURRENT, &pos));
m_offset = pos.QuadPart;
}
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newPosition) noexcept override try
{
// Determine new range relative position
LARGE_INTEGER newPos = { 0 };
switch (origin)
{
case Reference::CURRENT:
newPos.QuadPart = m_offset + m_relativePosition + move.QuadPart;
newPos.QuadPart = m_relativePosition + move.QuadPart;
break;
case Reference::START:
newPos.QuadPart = m_offset + move.QuadPart;
newPos.QuadPart = move.QuadPart;
break;
case Reference::END:
newPos.QuadPart = m_offset + m_size + move.QuadPart;
newPos.QuadPart = m_size + move.QuadPart;
break;
}
//TODO: We need to constrain newPos so that it can't exceed the end of the stream
// Constrain newPos to range relative values
if (newPos.QuadPart < 0)
{
newPos.QuadPart = 0;
}
else if (static_cast<uint64_t>(newPos.QuadPart) > m_size)
{
newPos.QuadPart = m_size;
}
// Add in the underlying stream offset
newPos.QuadPart += m_offset;
ULARGE_INTEGER pos = { 0 };
m_stream->Seek(newPos, Reference::START, &pos);
ThrowHrIfFailed(m_stream->Seek(newPos, Reference::START, &pos));
m_relativePosition = std::min(static_cast<std::uint64_t>(pos.QuadPart - m_offset), m_size);
if (newPosition) { newPosition->QuadPart = m_relativePosition; }
return static_cast<HRESULT>(Error::OK);
@ -68,7 +81,7 @@ namespace MSIX {
ULONG amountToRead = std::min(countBytes, static_cast<ULONG>(m_size - m_relativePosition));
ULONG amountRead = 0;
ThrowHrIfFailed(m_stream->Read(buffer, amountToRead, &amountRead));
ThrowErrorIf(Error::FileRead, (amountToRead != amountRead), "Did not read as much as requesteed.");
ThrowErrorIf(Error::FileRead, (amountToRead != amountRead), "Did not read as much as requested.");
m_relativePosition += amountRead;
if (bytesRead) { *bytesRead = amountRead; }
ThrowErrorIf(Error::FileSeekOutOfRange, (m_relativePosition > m_size), "seek pointer out of bounds.");
@ -78,12 +91,15 @@ namespace MSIX {
HRESULT STDMETHODCALLTYPE Write(const void *buffer, ULONG countBytes, ULONG *bytesWritten) noexcept override try
{
THROW_IF_PACK_NOT_ENABLED
// Forward to VectorStream/DeflateStream
ThrowHrIfFailed(m_stream->Write(buffer, countBytes, bytesWritten));
m_size = static_cast<ULONG>(m_buffer.size());
m_relativePosition = m_size;
// VectorStream will validate if the written bytes are correct.
// It is expected that countBytes != bytesWritten here because this can be a compression.
LARGE_INTEGER offset = { 0 };
offset.QuadPart = m_relativePosition + m_offset;
ThrowHrIfFailed(m_stream->Seek(offset, StreamBase::START, nullptr));
ULONG amountWritten = 0;
ThrowHrIfFailed(m_stream->Write(buffer, countBytes, &amountWritten));
ThrowErrorIf(Error::FileWrite, (countBytes != amountWritten), "Did not write as much as requested.");
m_relativePosition += amountWritten;
m_size = std::max(m_size, m_relativePosition);
if (bytesWritten) { *bytesWritten = amountWritten; }
return static_cast<HRESULT>(Error::OK);
} CATCH_RETURN();
@ -94,7 +110,5 @@ namespace MSIX {
std::uint64_t m_size;
std::uint64_t m_relativePosition = 0;
ComPtr<IStream> m_stream;
// for pack, this is buffer were store the data, before writing it to the zip file
std::vector<std::uint8_t> m_buffer;
};
}

Просмотреть файл

@ -6,6 +6,7 @@
#include "AppxPackaging.hpp"
#include "Exceptions.hpp"
#include "StreamBase.hpp"
#include <utility>
@ -56,5 +57,39 @@ namespace MSIX {
ThrowHrIfFailed(stream->Write(static_cast<const void*>(toWrite.data()), static_cast<ULONG>(toWrite.size()), &written));
ThrowErrorIf(Error::FileWrite, (static_cast<ULONG>(toWrite.size()) != written), "write failed");
}
// Reverts a stream's position on destruction
struct StreamPositionReset
{
StreamPositionReset(IStream* stream) : m_stream(stream)
{
ThrowHrIfFailed(m_stream->Seek({ 0 }, StreamBase::Reference::CURRENT, &m_pos));
}
~StreamPositionReset()
{
Reset();
}
void Reset()
{
if (m_stream)
{
LARGE_INTEGER target;
target.QuadPart = static_cast<LONGLONG>(m_pos.QuadPart);
ThrowHrIfFailed(m_stream->Seek(target, StreamBase::Reference::START, nullptr));
m_stream = nullptr;
}
}
void Release()
{
m_stream = nullptr;
}
private:
ComPtr<IStream> m_stream;
ULARGE_INTEGER m_pos;
};
}
}

Просмотреть файл

@ -58,7 +58,12 @@ namespace MSIX {
ThrowErrorIf(Error::FileWrite, expected != m_offset, "Error writing to stream");
if (bytesWritten) { *bytesWritten = countBytes; }
return static_cast<HRESULT>(Error::OK);
} CATCH_RETURN();
} CATCH_RETURN();
std::uint64_t GetSize() override
{
return static_cast<std::uint64_t>(m_data->size());
}
protected:
ULONG m_offset = 0;

Просмотреть файл

@ -24,16 +24,17 @@ namespace MSIX {
bool isCompressed,
std::uint64_t offset,
std::uint64_t size,
const ComPtr<IStream>& stream // this is the actual zip file stream
) : m_isCompressed(isCompressed), RangeStream(offset, size, stream), m_name(name)
IStream* stream // this is the actual zip file stream
) : m_isCompressed(isCompressed), RangeStream(offset, size, stream), m_name(std::move(name))
{
}
// Represents an stream to be added to the zip file (pack)
ZipFileStream(
const std::string& name,
bool isCompressed
) : m_isCompressed(isCompressed), m_name(name), RangeStream(isCompressed)
std::string name,
bool isCompressed,
IStream* stream
) : m_isCompressed(isCompressed), m_name(std::move(name)), RangeStream(stream)
{
THROW_IF_PACK_NOT_ENABLED
}

Просмотреть файл

@ -16,6 +16,12 @@
namespace MSIX {
enum class ZipVersions : std::uint16_t
{
Zip32DefaultVersion = 20,
Zip64FormatExtension = 45,
};
enum class GeneralPurposeBitFlags : std::uint16_t
{
UNSUPPORTED_0 = 0x0001, // Bit 0: If set, indicates that the file is encrypted.
@ -23,7 +29,7 @@ namespace MSIX {
Deflate_MaxCompress = 0x0002, // Maximum compression (-exx/-ex), otherwise, normal compression (-en)
Deflate_FastCompress = 0x0004, // Fast (-ef), if Max+Fast then SuperFast (-es) compression
GeneralPurposeBit = 0x0008, // the field's crc-32 compressed and uncompressed sizes = 0 in the local header
DataDescriptor = 0x0008, // the field's crc-32 compressed and uncompressed sizes = 0 in the local header
// the correct values are put in the data descriptor immediately following the
// compressed data.
EnhancedDeflate = 0x0010,
@ -52,6 +58,11 @@ namespace MSIX {
return static_cast<GeneralPurposeBitFlags>(static_cast<uint16_t>(a) | static_cast<uint16_t>(b));
}
constexpr GeneralPurposeBitFlags operator ~(GeneralPurposeBitFlags a)
{
return static_cast<GeneralPurposeBitFlags>(~static_cast<uint16_t>(a));
}
enum class CompressionType : std::uint16_t
{
Store = 0,
@ -69,37 +80,61 @@ namespace MSIX {
EndOfCentralDirectory = 0x06054b50,
};
constexpr uint64_t MaxSizeToNotUseDataDescriptor = static_cast<uint64_t>(std::numeric_limits<std::uint32_t>::max() - 1);
template <typename T>
inline bool IsValueInExtendedInfo(T value) noexcept
{
return (value == std::numeric_limits<T>::max());
}
template <typename T>
inline bool IsValueInExtendedInfo(const Meta::FieldBase<T>& field) noexcept
{
return IsValueInExtendedInfo(field.get());
}
class Zip64ExtendedInformation final : public Meta::StructuredObject<
Meta::Field2Bytes, // 0 - tag for the "extra" block type 2 bytes(0x0001)
Meta::Field2Bytes, // 1 - size of this "extra" block 2 bytes
Meta::Field8Bytes, // 2 - Original uncompressed file size 8 bytes
// No point in validating these as it is actually
// possible to have a 0-byte file... Who knew.
Meta::Field8Bytes, // 3 - Compressed file size 8 bytes
// No point in validating these as it is actually
// possible to have a 0-byte file... Who knew.
Meta::Field8Bytes // 4 - Offset of local header record 8 bytes
//Meta::Field4Bytes // 5 - number of the disk on which the file starts 4 bytes -- ITS A FAAKEE!
Meta::Field2Bytes, // 0 - tag for the "extra" block type 2 bytes(0x0001)
Meta::Field2Bytes, // 1 - size of this "extra" block 2 bytes
Meta::OptionalField8Bytes, // 2 - Original uncompressed file size 8 bytes
Meta::OptionalField8Bytes, // 3 - Compressed file size 8 bytes
Meta::OptionalField8Bytes, // 4 - Offset of local header record 8 bytes
Meta::OptionalField4Bytes // 5 - number of the disk on which the file starts 4 bytes
>
{
public:
Zip64ExtendedInformation();
void SetData(std::uint64_t uncompressedSize, std::uint64_t compressedSize, std::uint64_t relativeOffset);
// The incoming values are those from the central directory record. Their value there determines
// whether we attempt to read them here.
void Read(const ComPtr<IStream>& stream, ULARGE_INTEGER start, uint32_t uncompressedSize, uint32_t compressedSize, uint32_t offset, uint16_t disk);
void Read(const ComPtr<IStream>& stream, ULARGE_INTEGER start);
std::uint64_t GetUncompressedSize() const { return Field<2>(); }
std::uint64_t GetCompressedSize() const { return Field<3>(); }
std::uint64_t GetRelativeOffsetOfLocalHeader() const { return Field<4>(); }
std::uint32_t GetDiskStartNumber() const { return Field<5>(); }
std::uint64_t GetUncompressedSize() noexcept { return Field<2>().value; }
std::uint64_t GetCompressedSize() noexcept { return Field<3>().value; }
std::uint64_t GetRelativeOffsetOfLocalHeader() noexcept { return Field<4>().value; }
void SetUncompressedSize(std::uint64_t value) noexcept { Field<2>() = value; }
void SetCompressedSize(std::uint64_t value) noexcept { Field<3>() = value; }
void SetRelativeOffsetOfLocalHeader(std::uint64_t value) noexcept { Field<4>() = value; }
bool HasAnySet() const
{
return (Field<2>() || Field<3>() || Field<4>() || Field<5>());
}
std::vector<std::uint8_t> GetBytes()
{
SetSize(static_cast<uint16_t>(Size() - NonOptionalSize));
return StructuredObject::GetBytes();
}
protected:
void SetSignature(std::uint16_t value) noexcept { Field<0>().value = value; }
void SetSize(std::uint16_t value) noexcept { Field<1>().value = value; }
void SetUncompressedSize(std::uint64_t value) noexcept { Field<2>().value = value; }
void SetCompressedSize(std::uint64_t value) noexcept { Field<3>().value = value; }
void SetRelativeOffsetOfLocalHeader(std::uint64_t value) noexcept { Field<4>().value = value; }
constexpr static size_t NonOptionalSize = 4;
void SetSignature(std::uint16_t value) noexcept { Field<0>() = value; }
void SetSize(std::uint16_t value) noexcept { Field<1>() = value; }
};
class CentralDirectoryFileHeader final : public Meta::StructuredObject<
@ -128,81 +163,125 @@ namespace MSIX {
public:
CentralDirectoryFileHeader();
void SetData(std::string& name, std::uint32_t crc, std::uint64_t compressedSize,
std::uint64_t uncompressedSize, std::uint64_t relativeOffset, std::uint16_t compressionMethod);
void SetData(const std::string& name, std::uint32_t crc, std::uint64_t compressedSize,
std::uint64_t uncompressedSize, std::uint64_t relativeOffset, std::uint16_t compressionMethod, bool forceDataDescriptor);
void Read(const ComPtr<IStream>& stream, bool isZip64);
bool IsGeneralPurposeBitSet() noexcept
GeneralPurposeBitFlags GetGeneralPurposeBitFlags() const noexcept { return static_cast<GeneralPurposeBitFlags>(Field<3>().get()); }
bool IsGeneralPurposeBitSet() const noexcept
{
return ((static_cast<GeneralPurposeBitFlags>(Field<3>().value) & GeneralPurposeBitFlags::GeneralPurposeBit) == GeneralPurposeBitFlags::GeneralPurposeBit);
return ((GetGeneralPurposeBitFlags() & GeneralPurposeBitFlags::DataDescriptor) == GeneralPurposeBitFlags::DataDescriptor);
}
CompressionType GetCompressionMethod() noexcept { return static_cast<CompressionType>(Field<4>().value); }
CompressionType GetCompressionMethod() const noexcept { return static_cast<CompressionType>(Field<4>().get()); }
std::uint64_t GetCompressedSize() noexcept
std::uint64_t GetCompressedSize() const noexcept
{
if (m_isZip64)
if (IsValueInExtendedInfo(Field<8>()))
{
return m_extendedInfo.GetCompressedSize();
}
return static_cast<std::uint64_t>(Field<8>().value);
return static_cast<std::uint64_t>(Field<8>().get());
}
std::uint64_t GetUncompressedSize() noexcept
std::uint64_t GetUncompressedSize() const noexcept
{
if (m_isZip64)
if (IsValueInExtendedInfo(Field<9>()))
{
return m_extendedInfo.GetUncompressedSize();
}
return static_cast<std::uint64_t>(Field<9>().value);
return static_cast<std::uint64_t>(Field<9>().get());
}
std::uint64_t GetRelativeOffsetOfLocalHeader() noexcept
std::uint64_t GetRelativeOffsetOfLocalHeader() const noexcept
{
if (m_isZip64)
if (IsValueInExtendedInfo(Field<16>()))
{
return m_extendedInfo.GetRelativeOffsetOfLocalHeader();
}
return static_cast<std::uint64_t>(Field<16>().value);
return static_cast<std::uint64_t>(Field<16>().get());
}
std::string GetFileName()
std::string GetFileName() const
{
auto data = Field<17>().value;
auto data = Field<17>().get();
return std::string(data.begin(), data.end());
}
protected:
void SetSignature(std::uint32_t value) noexcept { Field<0>().value = value; }
void SetVersionMadeBy(std::uint16_t value) noexcept { Field<1>().value = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<2>().value = value; }
void SetGeneralPurposeBitFlags(std::uint16_t value) noexcept { Field<3>().value = value; }
void SetCompressionMethod(std::uint16_t value) noexcept { Field<4>().value = value; }
void SetLastModFileTime(std::uint16_t value) noexcept { Field<5>().value = value; }
void SetLastModFileDate(std::uint16_t value) noexcept { Field<6>().value = value; }
void SetCrc(std::uint32_t value) noexcept { Field<7>().value = value; }
void SetCompressedSize(std::uint32_t value) noexcept { Field<8>().value = value; }
void SetUncompressedSize(std::uint32_t value) noexcept { Field<9>().value = value; }
void SetFileNameLength(std::uint16_t value) noexcept { Field<10>().value = value; }
void SetExtraFieldLength(std::uint16_t value) noexcept { Field<11>().value = value; }
void SetFileCommentLength(std::uint16_t value) noexcept { Field<12>().value = value; }
void SetDiskNumberStart(std::uint16_t value) noexcept { Field<13>().value = value; }
void SetInternalFileAttributes(std::uint16_t value) noexcept { Field<14>().value = value; }
void SetExternalFileAttributes(std::uint16_t value) noexcept { Field<15>().value = value; }
void SetRelativeOffsetOfLocalHeader(std::uint32_t value) noexcept { Field<16>().value = value; }
void SetFileName(std::string& name)
void SetSignature(std::uint32_t value) noexcept { Field<0>() = value; }
void SetVersionMadeBy(std::uint16_t value) noexcept { Field<1>() = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<2>() = value; }
void SetGeneralPurposeBitFlags(std::uint16_t value) noexcept { Field<3>() = value; }
void SetCompressionMethod(std::uint16_t value) noexcept { Field<4>() = value; }
void SetLastModFileTime(std::uint16_t value) noexcept { Field<5>() = value; }
void SetLastModFileDate(std::uint16_t value) noexcept { Field<6>() = value; }
void SetCrc(std::uint32_t value) noexcept { Field<7>() = value; }
void SetFileNameLength(std::uint16_t value) noexcept { Field<10>() = value; }
void SetExtraFieldLength(std::uint16_t value) noexcept { Field<11>() = value; }
void SetFileCommentLength(std::uint16_t value) noexcept { Field<12>() = value; }
void SetDiskNumberStart(std::uint16_t value) noexcept { Field<13>() = value; }
void SetInternalFileAttributes(std::uint16_t value) noexcept { Field<14>() = value; }
void SetExternalFileAttributes(std::uint16_t value) noexcept { Field<15>() = value; }
// Values that might appear in the extended info (minus disk, which we will never set there)
void SetCompressedSize(std::uint64_t value) noexcept
{
if (value > MaxSizeToNotUseDataDescriptor)
{
m_extendedInfo.SetCompressedSize(value);
Field<8>() = std::numeric_limits<uint32_t>::max();
}
else
{
Field<8>() = static_cast<uint32_t>(value);
}
}
void SetUncompressedSize(std::uint64_t value)noexcept
{
if (value > MaxSizeToNotUseDataDescriptor)
{
m_extendedInfo.SetUncompressedSize(value);
Field<9>() = std::numeric_limits<uint32_t>::max();
}
else
{
Field<9>() = static_cast<uint32_t>(value);
}
}
void SetRelativeOffsetOfLocalHeader(std::uint64_t value) noexcept
{
if (value > MaxSizeToNotUseDataDescriptor)
{
m_extendedInfo.SetRelativeOffsetOfLocalHeader(value);
Field<16>() = std::numeric_limits<uint32_t>::max();
}
else
{
Field<16>() = static_cast<uint32_t>(value);
}
}
void SetFileName(const std::string& name)
{
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
Field<17>().value.resize(name.size(), 0);
std::copy(name.begin(), name.end(), Field<17>().value.begin());
Field<17>().get().resize(name.size(), 0);
std::copy(name.begin(), name.end(), Field<17>().get().begin());
}
void SetExtraField(std::uint64_t compressedSize, std::uint64_t uncompressedSize, std::uint64_t relativeOffset)
void UpdateExtraField()
{
m_extendedInfo.SetData(compressedSize, uncompressedSize, relativeOffset);
SetExtraFieldLength(static_cast<std::uint16_t>(m_extendedInfo.Size()));
Field<18>().value = m_extendedInfo.GetBytes();
if (m_extendedInfo.HasAnySet())
{
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
SetExtraFieldLength(static_cast<std::uint16_t>(m_extendedInfo.Size()));
Field<18>().get() = m_extendedInfo.GetBytes();
}
}
Zip64ExtendedInformation m_extendedInfo;
@ -228,40 +307,42 @@ namespace MSIX {
public:
LocalFileHeader();
void SetData(std::string& name, bool isCompressed);
void SetData(const std::string& name, bool isCompressed);
void SetData(std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize);
void Read(const ComPtr<IStream>& stream, CentralDirectoryFileHeader& directoryEntry);
std::uint16_t GetCompressionMethod() noexcept { return Field<3>().value; }
std::uint16_t GetFileNameLength() noexcept { return Field<9>().value; }
std::string GetFileName()
GeneralPurposeBitFlags GetGeneralPurposeBitFlags() const noexcept { return static_cast<GeneralPurposeBitFlags>(Field<2>().get()); }
std::uint16_t GetCompressionMethod() const noexcept { return Field<3>(); }
std::uint16_t GetFileNameLength() const noexcept { return Field<9>(); }
std::string GetFileName() const
{
auto data = Field<11>().value;
auto data = Field<11>().get();
return std::string(data.begin(), data.end());
}
protected:
bool IsGeneralPurposeBitSet() noexcept
bool IsGeneralPurposeBitSet() const noexcept
{
return ((static_cast<GeneralPurposeBitFlags>(Field<2>().value) & GeneralPurposeBitFlags::GeneralPurposeBit) == GeneralPurposeBitFlags::GeneralPurposeBit);
return ((GetGeneralPurposeBitFlags() & GeneralPurposeBitFlags::DataDescriptor) == GeneralPurposeBitFlags::DataDescriptor);
}
void SetSignature(std::uint32_t value) noexcept { Field<0>().value = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<1>().value = value; }
void SetGeneralPurposeBitFlags(std::uint16_t value) noexcept { Field<2>().value = value; }
void SetCompressionMethod(std::uint16_t value) noexcept { Field<3>().value = value; }
void SetLastModFileTime(std::uint16_t value) noexcept { Field<4>().value = value; }
void SetLastModFileDate(std::uint16_t value) noexcept { Field<5>().value = value; }
void SetCrc(std::uint32_t value) noexcept { Field<6>().value = value; }
void SetCompressedSize(std::uint32_t value) noexcept { Field<7>().value = value; }
void SetUncompressedSize(std::uint32_t value) noexcept { Field<8>().value = value; }
void SetFileNameLength(std::uint16_t value) noexcept { Field<9>().value = value; }
void SetExtraFieldLength(std::uint16_t value) noexcept { Field<10>().value = value; }
void SetFileName(std::string& name)
void SetSignature(std::uint32_t value) noexcept { Field<0>() = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<1>() = value; }
void SetGeneralPurposeBitFlags(std::uint16_t value) noexcept { Field<2>() = value; }
void SetCompressionMethod(std::uint16_t value) noexcept { Field<3>() = value; }
void SetLastModFileTime(std::uint16_t value) noexcept { Field<4>() = value; }
void SetLastModFileDate(std::uint16_t value) noexcept { Field<5>() = value; }
void SetCrc(std::uint32_t value) noexcept { Field<6>() = value; }
void SetCompressedSize(std::uint32_t value) noexcept { Field<7>() = value; }
void SetUncompressedSize(std::uint32_t value) noexcept { Field<8>() = value; }
void SetFileNameLength(std::uint16_t value) noexcept { Field<9>() = value; }
void SetExtraFieldLength(std::uint16_t value) noexcept { Field<10>() = value; }
void SetFileName(const std::string& name)
{
SetFileNameLength(static_cast<std::uint16_t>(name.size()));
Field<11>().value.resize(name.size(), 0);
std::copy(name.begin(), name.end(), Field<11>().value.begin());
Field<11>().get().resize(name.size(), 0);
std::copy(name.begin(), name.end(), Field<11>().get().begin());
}
};
@ -287,23 +368,23 @@ namespace MSIX {
void Read(const ComPtr<IStream>& stream);
std::uint64_t GetTotalNumberOfEntries() noexcept { return Field<6>().value; }
std::uint64_t GetOffsetStartOfCD() noexcept { return Field<9>().value; }
std::uint64_t GetTotalNumberOfEntries() const noexcept { return Field<6>(); }
std::uint64_t GetOffsetStartOfCD() const noexcept { return Field<9>(); }
protected:
void SetSignature(std::uint32_t value) noexcept { Field<0>().value = value; }
void SetSizeOfZip64CDRecord(std::uint64_t value) noexcept { Field<1>().value = value; }
void SetVersionMadeBy(std::uint16_t value) noexcept { Field<2>().value = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<3>().value = value; }
void SetNumberOfThisDisk(std::uint32_t value) noexcept { Field<4>().value = value; }
void SetNumberOfTheDiskWithStartOfCD(std::uint32_t value) noexcept { Field<5>().value = value; }
void SetSignature(std::uint32_t value) noexcept { Field<0>() = value; }
void SetSizeOfZip64CDRecord(std::uint64_t value) noexcept { Field<1>() = value; }
void SetVersionMadeBy(std::uint16_t value) noexcept { Field<2>() = value; }
void SetVersionNeededToExtract(std::uint16_t value) noexcept { Field<3>() = value; }
void SetNumberOfThisDisk(std::uint32_t value) noexcept { Field<4>() = value; }
void SetNumberOfTheDiskWithStartOfCD(std::uint32_t value) noexcept { Field<5>() = value; }
void SetTotalNumberOfEntriesDisk(std::uint64_t value) noexcept
{
Field<6>().value = value;
Field<7>().value = value;
Field<6>() = value;
Field<7>() = value;
}
void SetSizeOfCD(std::uint64_t value) noexcept { Field<8>().value = value; }
void SetOffsetfStartOfCD(std::uint64_t value) noexcept { Field<9>().value = value; }
void SetSizeOfCD(std::uint64_t value) noexcept { Field<8>() = value; }
void SetOffsetfStartOfCD(std::uint64_t value) noexcept { Field<9>() = value; }
};
class Zip64EndOfCentralDirectoryLocator final : public Meta::StructuredObject<
@ -322,13 +403,13 @@ namespace MSIX {
void Read(const ComPtr<IStream>& stream);
std::uint64_t GetRelativeOffset() noexcept { return Field<2>().value; }
std::uint64_t GetRelativeOffset() const noexcept { return Field<2>(); }
protected:
void SetSignature(std::uint32_t value) noexcept { Field<0>().value = value; }
void SetNumberOfDisk(std::uint32_t value) noexcept { Field<1>().value = value; }
void SetRelativeOffset(std::uint64_t value) noexcept { Field<2>().value = value; }
void SetTotalNumberOfDisks(std::uint32_t value) noexcept { Field<3>().value = value; }
void SetSignature(std::uint32_t value) noexcept { Field<0>() = value; }
void SetNumberOfDisk(std::uint32_t value) noexcept { Field<1>() = value; }
void SetRelativeOffset(std::uint64_t value) noexcept { Field<2>() = value; }
void SetTotalNumberOfDisks(std::uint32_t value) noexcept { Field<3>() = value; }
};
class EndCentralDirectoryRecord final : public Meta::StructuredObject<
@ -352,24 +433,22 @@ namespace MSIX {
void Read(const ComPtr<IStream>& stream);
bool GetArchiveHasZip64Locator() noexcept { return m_archiveHasZip64Locator; }
bool GetIsZip64() noexcept { return m_isZip64; }
bool GetIsZip64() const noexcept { return m_isZip64; }
std::uint64_t GetNumberOfCentralDirectoryEntries() noexcept { return static_cast<std::uint64_t>(Field<3>().value); }
std::uint64_t GetStartOfCentralDirectory() noexcept { return static_cast<std::uint64_t>(Field<6>().value); }
std::uint64_t GetNumberOfCentralDirectoryEntries() noexcept { return static_cast<std::uint64_t>(Field<3>().get()); }
std::uint64_t GetStartOfCentralDirectory() noexcept { return static_cast<std::uint64_t>(Field<6>().get()); }
protected:
void SetSignature(std::uint32_t value) noexcept { Field<0>().value = value; }
void SetNumberOfDisk(std::uint16_t value) noexcept { Field<1>().value = value; }
void SetDiskStart(std::uint16_t value) noexcept { Field<2>().value = value; }
void SetTotalNumberOfEntries(std::uint16_t value) noexcept { Field<3>().value = value; }
void SetTotalEntriesInCentralDirectory(std::uint16_t value) noexcept { Field<4>().value = value; }
void SetSizeOfCentralDirectory(std::uint32_t value) noexcept { Field<5>().value = value; }
void SetOffsetOfCentralDirectory(std::uint32_t value) noexcept { Field<6>().value = value; }
void SetCommentLength(std::uint16_t value) noexcept { Field<7>().value = value; }
void SetSignature(std::uint32_t value) noexcept { Field<0>() = value; }
void SetNumberOfDisk(std::uint16_t value) noexcept { Field<1>() = value; }
void SetDiskStart(std::uint16_t value) noexcept { Field<2>() = value; }
void SetTotalNumberOfEntries(std::uint16_t value) noexcept { Field<3>() = value; }
void SetTotalEntriesInCentralDirectory(std::uint16_t value) noexcept { Field<4>() = value; }
void SetSizeOfCentralDirectory(std::uint32_t value) noexcept { Field<5>() = value; }
void SetOffsetOfCentralDirectory(std::uint32_t value) noexcept { Field<6>() = value; }
void SetCommentLength(std::uint16_t value) noexcept { Field<7>() = value; }
bool m_isZip64 = true;
bool m_archiveHasZip64Locator = true;
};
class ZipObject

Просмотреть файл

@ -12,6 +12,7 @@
#include <vector>
#include <map>
#include <memory>
#include <utility>
// {350dd671-0c40-4cd7-9a5b-27456d604bd0}
#ifndef WIN32
@ -24,12 +25,11 @@ class IZipWriter : public IUnknown
{
public:
// Writes the lfh header to the stream and return the size of the header
virtual std::uint32_t PrepareToAddFile(std::string& name, bool isCompressed) = 0;
virtual std::pair<std::uint32_t, MSIX::ComPtr<IStream>> PrepareToAddFile(const std::string& name, bool isCompressed) = 0;
// Writes the stream provided to the zip file, writes data descriptor and adds an entry
// Ends the file, rewrites the LFH or writes data descriptor and adds an entry
// to the central directories map
virtual void AddFile(MSIX::ComPtr<IStream>& fileStream, std::uint32_t crc,
std::uint64_t compressedSize, std::uint64_t uncompressedSize) = 0;
virtual void EndFile(std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize, bool forceDataDescriptor) = 0;
// Ends zip file by writing the central directory records, zip64 locator,
// zip64 end of central directory and the end of central directories.
@ -52,9 +52,8 @@ namespace MSIX {
std::string GetFileName() override { NOTIMPLEMENTED };
// IZipWriter
std::uint32_t PrepareToAddFile(std::string& name, bool isCompressed) override;
void AddFile(MSIX::ComPtr<IStream>& fileStream, std::uint32_t crc,
std::uint64_t compressedSize, std::uint64_t uncompressedSize) override;
std::pair<std::uint32_t, ComPtr<IStream>> PrepareToAddFile(const std::string& name, bool isCompressed) override;
void EndFile(std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize, bool forceDataDescriptor) override;
void Close() override;
protected:

Просмотреть файл

@ -37,11 +37,6 @@ namespace MSIX {
[Zip64EndOfCentralDirectoryLocator]
[EndCentralDirectoryRecord]
*/
enum class ZipVersions : std::uint16_t
{
Zip32DefaultVersion = 20,
Zip64FormatExtension = 45,
};
// from AppNote.txt, section 4.5.2:
enum class HeaderIDs : std::uint16_t
@ -98,31 +93,38 @@ constexpr static const GeneralPurposeBitFlags UnsupportedFlagsMask =
Zip64ExtendedInformation::Zip64ExtendedInformation()
{
SetSignature(static_cast<std::uint16_t>(HeaderIDs::Zip64ExtendedInfo));
// Should be 0x18, but do it this way if we end up field 5
SetSize(static_cast<std::uint16_t>(Size() - Field<0>().Size() - Field<1>().Size()));
SetSize(NonOptionalSize);
}
void Zip64ExtendedInformation::SetData(std::uint64_t uncompressedSize, std::uint64_t compressedSize, std::uint64_t relativeOffset)
void Zip64ExtendedInformation::Read(const ComPtr<IStream>& stream, ULARGE_INTEGER start, uint32_t uncompressedSize, uint32_t compressedSize, uint32_t offset, uint16_t disk)
{
THROW_IF_PACK_NOT_ENABLED
SetUncompressedSize(uncompressedSize);
SetCompressedSize(compressedSize);
SetRelativeOffsetOfLocalHeader(relativeOffset);
}
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(HeaderIDs::Zip64ExtendedInfo));
void Zip64ExtendedInformation::Read(const ComPtr<IStream>& stream, ULARGE_INTEGER start)
{
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<0>().value, static_cast<std::uint32_t>(HeaderIDs::Zip64ExtendedInfo));
StreamBase::Read(stream, &Field<1>());
// The incoming stream will be just the extended info, and our size should match it minus the fixed bytes
Meta::ExactValueValidation<std::uint32_t>(Field<1>(), static_cast<uint32_t>(stream.As<IStreamInternal>()->GetSize() - NonOptionalSize));
StreamBase::Read(stream, &Field<1>().value);
Meta::OnlyEitherValueValidation<std::uint32_t>(Field<1>().value, 24, 28);
StreamBase::Read(stream, &Field<2>().value);
StreamBase::Read(stream, &Field<3>().value);
if (IsValueInExtendedInfo(uncompressedSize))
{
StreamBase::Read(stream, &Field<2>());
}
StreamBase::Read(stream, &Field<4>().value);
ThrowErrorIfNot(Error::ZipBadExtendedData, Field<4>().value < start.QuadPart, "invalid relative header offset");
if (IsValueInExtendedInfo(compressedSize))
{
StreamBase::Read(stream, &Field<3>());
}
if (IsValueInExtendedInfo(offset))
{
StreamBase::Read(stream, &Field<4>());
ThrowErrorIfNot(Error::ZipBadExtendedData, Field<4>().get() < start.QuadPart, "invalid relative header offset");
}
if (IsValueInExtendedInfo(disk))
{
StreamBase::Read(stream, &Field<5>());
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
@ -132,104 +134,112 @@ CentralDirectoryFileHeader::CentralDirectoryFileHeader()
{
SetSignature(static_cast<std::uint32_t>(Signatures::CentralFileHeader));
SetVersionMadeBy(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension)); // we always do zip64
SetGeneralPurposeBitFlags(static_cast<std::uint16_t>(GeneralPurposeBitFlags::GeneralPurposeBit));
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion));
SetGeneralPurposeBitFlags(0);
SetLastModFileDate(static_cast<std::uint16_t>(MagicNumbers::FileDate)); // TODO: figure out how to convert to msdos time
SetLastModFileTime(static_cast<std::uint16_t>(MagicNumbers::FileTime));
SetCompressedSize(std::numeric_limits<std::uint32_t>::max()); // always use zip64
SetUncompressedSize(std::numeric_limits<std::uint32_t>::max()); // always use zip64
SetCompressedSize(0);
SetUncompressedSize(0);
SetExtraFieldLength(0);
SetFileCommentLength(0);
SetDiskNumberStart(0);
SetInternalFileAttributes(0);
SetExternalFileAttributes(0);
SetRelativeOffsetOfLocalHeader(std::numeric_limits<std::uint32_t>::max()); // always use zip64
SetRelativeOffsetOfLocalHeader(0);
}
void CentralDirectoryFileHeader::SetData(std::string& name, std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize,
std::uint64_t relativeOffset, std::uint16_t compressionMethod)
void CentralDirectoryFileHeader::SetData(const std::string& name, std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize,
std::uint64_t relativeOffset, std::uint16_t compressionMethod, bool forceDataDescriptor)
{
THROW_IF_PACK_NOT_ENABLED
SetCompressionMethod(compressionMethod);
SetCrc(crc);
SetFileName(name);
SetExtraField(uncompressedSize, compressedSize, relativeOffset);
// Set these values that may end up in the extra field info; then make sure to update it
SetCompressedSize(compressedSize);
SetUncompressedSize(uncompressedSize);
SetRelativeOffsetOfLocalHeader(relativeOffset);
UpdateExtraField();
if (forceDataDescriptor ||
compressedSize > MaxSizeToNotUseDataDescriptor ||
uncompressedSize > MaxSizeToNotUseDataDescriptor)
{
SetGeneralPurposeBitFlags(static_cast<std::uint16_t>(GeneralPurposeBitFlags::DataDescriptor));
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
}
}
void CentralDirectoryFileHeader::Read(const ComPtr<IStream>& stream, bool isZip64)
{
m_isZip64 = isZip64;
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<0>().value, static_cast<std::uint32_t>(Signatures::CentralFileHeader));
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(Signatures::CentralFileHeader));
StreamBase::Read(stream, &Field<1>().value);
StreamBase::Read(stream, &Field<2>().value);
StreamBase::Read(stream, &Field<1>());
StreamBase::Read(stream, &Field<2>());
StreamBase::Read(stream, &Field<3>().value);
StreamBase::Read(stream, &Field<3>());
ThrowErrorIfNot(Error::ZipCentralDirectoryHeader,
0 == (Field<3>().value & static_cast<std::uint16_t>(UnsupportedFlagsMask)),
0 == (Field<3>().get() & static_cast<std::uint16_t>(UnsupportedFlagsMask)),
"unsupported flag(s) specified");
StreamBase::Read(stream, &Field<4>().value);
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<4>().value, static_cast<std::uint16_t>(CompressionType::Deflate),
StreamBase::Read(stream, &Field<4>());
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<4>(), static_cast<std::uint16_t>(CompressionType::Deflate),
static_cast<std::uint16_t>(CompressionType::Store));
StreamBase::Read(stream, &Field<5>().value);
StreamBase::Read(stream, &Field<6>().value);
StreamBase::Read(stream, &Field<7>().value);
StreamBase::Read(stream, &Field<8>().value);
StreamBase::Read(stream, &Field<9>().value);
StreamBase::Read(stream, &Field<5>());
StreamBase::Read(stream, &Field<6>());
StreamBase::Read(stream, &Field<7>());
StreamBase::Read(stream, &Field<8>());
StreamBase::Read(stream, &Field<9>());
StreamBase::Read(stream, &Field<10>().value);
ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (Field<10>().value != 0), "unsupported file name size");
if (Field<10>().value !=0) {Field<17>().value.resize(Field<10>().value, 0); }
StreamBase::Read(stream, &Field<10>());
ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (Field<10>().get() != 0), "unsupported file name size");
if (Field<10>().get() != 0) {Field<17>().get().resize(Field<10>().get(), 0); }
StreamBase::Read(stream, &Field<11>().value);
if (Field<11>().value != 0) { Field<18>().value.resize(Field<11>().value, 0); }
StreamBase::Read(stream, &Field<11>());
if (Field<11>().get() != 0) { Field<18>().get().resize(Field<11>().get(), 0); }
StreamBase::Read(stream, &Field<12>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<12>().value, 0);
StreamBase::Read(stream, &Field<12>());
Meta::ExactValueValidation<std::uint32_t>(Field<12>(), 0);
StreamBase::Read(stream, &Field<13>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<13>().value, 0);
StreamBase::Read(stream, &Field<13>());
Meta::ExactValueValidation<std::uint32_t>(Field<13>(), 0);
StreamBase::Read(stream, &Field<14>().value);
StreamBase::Read(stream, &Field<14>());
StreamBase::Read(stream, &Field<15>().value);
StreamBase::Read(stream, &Field<16>().value);
StreamBase::Read(stream, &Field<15>());
StreamBase::Read(stream, &Field<16>());
ULARGE_INTEGER pos = {0};
ThrowHrIfFailed(stream->Seek({0}, StreamBase::Reference::CURRENT, &pos));
if (m_isZip64)
if (!m_isZip64 || !IsValueInExtendedInfo(Field<16>()))
{
ThrowErrorIf(Error::ZipCentralDirectoryHeader, (Field<16>().value != 0xFFFFFFFF), "invalid zip64 local header offset");
}
else
{
ThrowErrorIf(Error::ZipCentralDirectoryHeader, (Field<16>().value >= pos.QuadPart), "invalid relative header offset");
ThrowErrorIf(Error::ZipCentralDirectoryHeader, (Field<16>().get() >= pos.QuadPart), "invalid relative header offset");
}
if (Field<17>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<17>().value.data()), static_cast<ULONG>(Field<17>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<17>().get().data()), static_cast<ULONG>(Field<17>().Size()), nullptr));
}
if (Field<18>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<18>().value.data()), static_cast<ULONG>(Field<18>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<18>().get().data()), static_cast<ULONG>(Field<18>().Size()), nullptr));
}
// Only process for Zip64ExtendedInformation
if (Field<18>().Size() > 2 && Field<18>().value[0] == 0x01 && Field<18>().value[1] == 0x00)
if (Field<18>().Size() > 2 && Field<18>().get()[0] == 0x01 && Field<18>().get()[1] == 0x00)
{
LARGE_INTEGER zero = {0};
ThrowHrIfFailed(stream->Seek(zero, StreamBase::Reference::CURRENT, &pos));
auto vectorStream = ComPtr<IStream>::Make<VectorStream>(&Field<18>().value);
ThrowErrorIfNot(Error::ZipCentralDirectoryHeader, (Field<18>().Size() >= m_extendedInfo.Size()), "Unexpected extended info size");
m_extendedInfo.Read(vectorStream.Get(), pos);
auto vectorStream = ComPtr<IStream>::Make<VectorStream>(&Field<18>());
m_extendedInfo.Read(vectorStream.Get(), pos, Field<9>(), Field<8>(), Field<16>(), Field<13>());
}
if (Field<19>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<19>().value.data()), static_cast<ULONG>(Field<19>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<19>().get().data()), static_cast<ULONG>(Field<19>().Size()), nullptr));
}
}
@ -239,8 +249,8 @@ void CentralDirectoryFileHeader::Read(const ComPtr<IStream>& stream, bool isZip6
LocalFileHeader::LocalFileHeader()
{
SetSignature(static_cast<std::uint32_t>(Signatures::LocalFileHeader));
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension)); // always zip64
SetGeneralPurposeBitFlags(static_cast<std::uint16_t>(GeneralPurposeBitFlags::GeneralPurposeBit));
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension)); // deafult to zip64
SetGeneralPurposeBitFlags(static_cast<std::uint16_t>(GeneralPurposeBitFlags::DataDescriptor));
SetLastModFileTime(static_cast<std::uint16_t>(MagicNumbers::FileTime)); // TODO: figure out how to convert to msdos time
SetLastModFileDate(static_cast<std::uint16_t>(MagicNumbers::FileDate));
SetCrc(0);
@ -249,7 +259,7 @@ LocalFileHeader::LocalFileHeader()
SetExtraFieldLength(0);
}
void LocalFileHeader::SetData(std::string& name, bool isCompressed)
void LocalFileHeader::SetData(const std::string& name, bool isCompressed)
{
THROW_IF_PACK_NOT_ENABLED
auto compressMethod = (isCompressed) ? CompressionType::Deflate : CompressionType::Store;
@ -257,49 +267,59 @@ void LocalFileHeader::SetData(std::string& name, bool isCompressed)
SetFileName(name);
}
void LocalFileHeader::SetData(std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize)
{
THROW_IF_PACK_NOT_ENABLED
SetVersionNeededToExtract(static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion));
SetGeneralPurposeBitFlags(static_cast<std::uint16_t>(GetGeneralPurposeBitFlags() & ~GeneralPurposeBitFlags::DataDescriptor));
SetCrc(crc);
SetCompressedSize(static_cast<uint32_t>(compressedSize));
SetUncompressedSize(static_cast<uint32_t>(uncompressedSize));
}
void LocalFileHeader::Read(const ComPtr<IStream> &stream, CentralDirectoryFileHeader& directoryEntry)
{
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>( Field<0>().value, static_cast<std::uint32_t>(Signatures::LocalFileHeader));
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(Signatures::LocalFileHeader));
StreamBase::Read(stream, &Field<1>().value);
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<1>().value, static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion),
StreamBase::Read(stream, &Field<1>());
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<1>(), static_cast<std::uint16_t>(ZipVersions::Zip32DefaultVersion),
static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
StreamBase::Read(stream, &Field<2>().value);
ThrowErrorIfNot(Error::ZipLocalFileHeader, ((Field<2>().value & static_cast<std::uint16_t>(UnsupportedFlagsMask)) == 0), "unsupported flag(s) specified");
StreamBase::Read(stream, &Field<2>());
ThrowErrorIfNot(Error::ZipLocalFileHeader, ((Field<2>().get() & static_cast<std::uint16_t>(UnsupportedFlagsMask)) == 0), "unsupported flag(s) specified");
ThrowErrorIfNot(Error::ZipLocalFileHeader, (IsGeneralPurposeBitSet() == directoryEntry.IsGeneralPurposeBitSet()), "inconsistent general purpose bits specified");
StreamBase::Read(stream, &Field<3>().value);
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<3>().value, static_cast<std::uint16_t>(CompressionType::Deflate),
StreamBase::Read(stream, &Field<3>());
Meta::OnlyEitherValueValidation<std::uint16_t>(Field<3>(), static_cast<std::uint16_t>(CompressionType::Deflate),
static_cast<std::uint16_t>(CompressionType::Store));
StreamBase::Read(stream, &Field<4>().value);
StreamBase::Read(stream, &Field<5>().value);
StreamBase::Read(stream, &Field<6>().value);
ThrowErrorIfNot(Error::ZipLocalFileHeader, (!IsGeneralPurposeBitSet() || (Field<6>().value == 0)), "Invalid Zip CRC");
StreamBase::Read(stream, &Field<4>());
StreamBase::Read(stream, &Field<5>());
StreamBase::Read(stream, &Field<6>());
ThrowErrorIfNot(Error::ZipLocalFileHeader, (!IsGeneralPurposeBitSet() || (Field<6>().get() == 0)), "Invalid Zip CRC");
StreamBase::Read(stream, &Field<7>().value);
ThrowErrorIfNot(Error::ZipLocalFileHeader, (!IsGeneralPurposeBitSet() || (Field<7>().value == 0)), "Invalid Zip compressed size");
StreamBase::Read(stream, &Field<7>());
ThrowErrorIfNot(Error::ZipLocalFileHeader, (!IsGeneralPurposeBitSet() || (Field<7>().get() == 0)), "Invalid Zip compressed size");
StreamBase::Read(stream, &Field<8>().value);
StreamBase::Read(stream, &Field<8>());
StreamBase::Read(stream, &Field<9>().value);
ThrowErrorIfNot(Error::ZipLocalFileHeader, (Field<9>().value != 0), "unsupported file name size");
Field<11>().value.resize(GetFileNameLength(), 0);
StreamBase::Read(stream, &Field<9>());
ThrowErrorIfNot(Error::ZipLocalFileHeader, (Field<9>().get() != 0), "unsupported file name size");
Field<11>().get().resize(GetFileNameLength(), 0);
StreamBase::Read(stream, &Field<10>().value);
StreamBase::Read(stream, &Field<10>());
// Even if we don't validate them, we need to read the extra field
if (Field<10>().value != 0) {Field<12>().value.resize(Field<10>().value, 0); }
if (Field<10>().get() != 0) {Field<12>().get().resize(Field<10>().get(), 0); }
if (Field<11>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<11>().value.data()), static_cast<ULONG>(Field<11>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<11>().get().data()), static_cast<ULONG>(Field<11>().Size()), nullptr));
}
if (Field<12>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<12>().value.data()), static_cast<ULONG>(Field<12>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<12>().get().data()), static_cast<ULONG>(Field<12>().Size()), nullptr));
}
}
@ -326,46 +346,46 @@ void Zip64EndOfCentralDirectoryRecord::SetData(std::uint64_t numCentralDirs, std
void Zip64EndOfCentralDirectoryRecord::Read(const ComPtr<IStream>& stream)
{
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<0>().value, static_cast<std::uint32_t>(Signatures::Zip64EndOfCD));
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(Signatures::Zip64EndOfCD));
StreamBase::Read(stream, &Field<1>().value);
StreamBase::Read(stream, &Field<1>());
//4.3.14.1 The value stored into the "size of zip64 end of central
// directory record" should be the size of the remaining
// record and should not include the leading 12 bytes.
ThrowErrorIfNot(Error::Zip64EOCDRecord, (Field<1>().value == (this->Size() - 12)), "invalid size of zip64 EOCD");
ThrowErrorIfNot(Error::Zip64EOCDRecord, (Field<1>().get() == (this->Size() - 12)), "invalid size of zip64 EOCD");
StreamBase::Read(stream, &Field<2>().value);
Meta::ExactValueValidation<std::uint16_t>(Field<2>().value, static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
StreamBase::Read(stream, &Field<2>());
Meta::ExactValueValidation<std::uint16_t>(Field<2>(), static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
StreamBase::Read(stream, &Field<3>().value);
Meta::ExactValueValidation<std::uint16_t>(Field<3>().value, static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
StreamBase::Read(stream, &Field<3>());
Meta::ExactValueValidation<std::uint16_t>(Field<3>(), static_cast<std::uint16_t>(ZipVersions::Zip64FormatExtension));
StreamBase::Read(stream, &Field<4>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<4>().value, 0);
StreamBase::Read(stream, &Field<4>());
Meta::ExactValueValidation<std::uint32_t>(Field<4>(), 0);
StreamBase::Read(stream, &Field<5>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<5>().value, 0);
StreamBase::Read(stream, &Field<5>());
Meta::ExactValueValidation<std::uint32_t>(Field<5>(), 0);
StreamBase::Read(stream, &Field<6>().value);
Meta::NotValueValidation<std::uint64_t>(Field<6>().value, 0);
StreamBase::Read(stream, &Field<6>());
Meta::NotValueValidation<std::uint64_t>(Field<6>(), 0);
StreamBase::Read(stream, &Field<7>().value);
Meta::NotValueValidation<std::uint64_t>(Field<7>().value, 0);
ThrowErrorIfNot(Error::Zip64EOCDRecord, (Field<7>().value == GetTotalNumberOfEntries()), "invalid total number of entries");
StreamBase::Read(stream, &Field<7>());
Meta::NotValueValidation<std::uint64_t>(Field<7>(), 0);
ThrowErrorIfNot(Error::Zip64EOCDRecord, (Field<7>().get() == GetTotalNumberOfEntries()), "invalid total number of entries");
ULARGE_INTEGER pos = {0};
ThrowHrIfFailed(stream->Seek({0}, StreamBase::Reference::CURRENT, &pos));
StreamBase::Read(stream, &Field<8>().value);
ThrowErrorIfNot(Error::Zip64EOCDRecord, ((Field<8>().value != 0) && (Field<8>().value < pos.QuadPart)), "invalid size of central directory");
StreamBase::Read(stream, &Field<8>());
ThrowErrorIfNot(Error::Zip64EOCDRecord, ((Field<8>().get() != 0) && (Field<8>().get() < pos.QuadPart)), "invalid size of central directory");
ThrowHrIfFailed(stream->Seek({0}, StreamBase::Reference::CURRENT, &pos));
StreamBase::Read(stream, &Field<9>().value);
ThrowErrorIfNot(Error::Zip64EOCDRecord, ((Field<9>().value != 0) && (Field<9>().value < pos.QuadPart)), "invalid size of central directory");
StreamBase::Read(stream, &Field<9>());
ThrowErrorIfNot(Error::Zip64EOCDRecord, ((Field<9>().get() != 0) && (Field<9>().get() < pos.QuadPart)), "invalid size of central directory");
if (Field<10>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<10>().value.data()), static_cast<ULONG>(Field<10>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<10>().get().data()), static_cast<ULONG>(Field<10>().Size()), nullptr));
}
}
@ -386,19 +406,19 @@ void Zip64EndOfCentralDirectoryLocator::SetData(std::uint64_t zip64EndCdrOffset)
void Zip64EndOfCentralDirectoryLocator::Read(const ComPtr<IStream>& stream)
{
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<0>().value, static_cast<std::uint32_t>(Signatures::Zip64EndOfCDLocator));
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(Signatures::Zip64EndOfCDLocator));
StreamBase::Read(stream, &Field<1>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<1>().value, 0);
StreamBase::Read(stream, &Field<1>());
Meta::ExactValueValidation<std::uint32_t>(Field<1>(), 0);
ULARGE_INTEGER pos = {0};
StreamBase::Read(stream, &Field<2>().value);
StreamBase::Read(stream, &Field<2>());
ThrowHrIfFailed(stream->Seek({0}, StreamBase::Reference::CURRENT, &pos));
ThrowErrorIfNot(Error::Zip64EOCDLocator, ((Field<2>().value != 0) && (Field<2>().value < pos.QuadPart)), "Invalid relative offset");
ThrowErrorIfNot(Error::Zip64EOCDLocator, ((Field<2>().get() != 0) && (Field<2>().get() < pos.QuadPart)), "Invalid relative offset");
StreamBase::Read(stream, &Field<3>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<3>().value, 1);
StreamBase::Read(stream, &Field<3>());
Meta::ExactValueValidation<std::uint32_t>(Field<3>(), 1);
}
//////////////////////////////////////////////////////////////////////////////////////////////
@ -408,8 +428,8 @@ EndCentralDirectoryRecord::EndCentralDirectoryRecord()
{
SetSignature(static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory));
// Always use zip64
SetNumberOfDisk(std::numeric_limits<std::uint16_t>::max());
SetDiskStart(std::numeric_limits<std::uint16_t>::max());
SetNumberOfDisk(0);
SetDiskStart(0);
SetTotalNumberOfEntries(std::numeric_limits<std::uint16_t>::max());
SetTotalEntriesInCentralDirectory(std::numeric_limits<std::uint16_t>::max());
SetSizeOfCentralDirectory(std::numeric_limits<std::uint32_t>::max());
@ -419,42 +439,45 @@ EndCentralDirectoryRecord::EndCentralDirectoryRecord()
void EndCentralDirectoryRecord::Read(const ComPtr<IStream>& stream)
{
StreamBase::Read(stream, &Field<0>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<0>().value, static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory));
StreamBase::Read(stream, &Field<0>());
Meta::ExactValueValidation<std::uint32_t>(Field<0>(), static_cast<std::uint32_t>(Signatures::EndOfCentralDirectory));
StreamBase::Read(stream, &Field<1>().value);
Meta::OnlyEitherValueValidation<std::uint32_t>(Field<1>().value, 0, 0xFFFF);
StreamBase::Read(stream, &Field<1>());
Meta::OnlyEitherValueValidation<std::uint32_t>(Field<1>(), 0, 0xFFFF);
StreamBase::Read(stream, &Field<2>().value);
Meta::OnlyEitherValueValidation<std::uint32_t>(Field<2>().value, 0, 0xFFFF);
ThrowErrorIf(Error::ZipEOCDRecord, (Field<1>().value != Field<2>().value), "field missmatch");
m_isZip64 = (0xFFFF == Field<2>().value);
StreamBase::Read(stream, &Field<2>());
Meta::OnlyEitherValueValidation<std::uint32_t>(Field<2>(), 0, 0xFFFF);
ThrowErrorIf(Error::ZipEOCDRecord, (Field<1>().get() != Field<2>().get()), "field missmatch");
StreamBase::Read(stream, &Field<3>().value);
if (Field<3>().value != 0 && Field<3>().value != 0xFFFF)
{ m_archiveHasZip64Locator = false;
}
StreamBase::Read(stream, &Field<3>());
StreamBase::Read(stream, &Field<4>());
ThrowErrorIf(Error::ZipEOCDRecord, (Field<3>().get() != Field<4>().get()), "field missmatch");
StreamBase::Read(stream, &Field<4>().value);
ThrowErrorIf(Error::ZipEOCDRecord, (Field<3>().value != Field<4>().value), "field missmatch");
StreamBase::Read(stream, &Field<5>());
StreamBase::Read(stream, &Field<6>());
StreamBase::Read(stream, &Field<5>().value);
StreamBase::Read(stream, &Field<6>().value);
m_isZip64 = (
IsValueInExtendedInfo(Field<1>()) ||
IsValueInExtendedInfo(Field<2>()) ||
IsValueInExtendedInfo(Field<3>()) ||
IsValueInExtendedInfo(Field<4>()) ||
IsValueInExtendedInfo(Field<5>()) ||
IsValueInExtendedInfo(Field<6>()));
if(m_archiveHasZip64Locator)
if (m_isZip64)
{
ThrowErrorIf(Error::ZipEOCDRecord, ((Field<5>().value != 0) && (Field<5>().value != 0xFFFFFFFF)),
ThrowErrorIf(Error::ZipEOCDRecord, ((Field<5>().get() != 0) && (Field<5>().get() != 0xFFFFFFFF)),
"unsupported size of central directory");
ThrowErrorIf(Error::ZipEOCDRecord, ((Field<6>().value != 0) && (Field<6>().value != 0xFFFFFFFF)),
ThrowErrorIf(Error::ZipEOCDRecord, ((Field<6>().get() != 0) && (Field<6>().get() != 0xFFFFFFFF)),
"unsupported offset of start of central directory");
}
StreamBase::Read(stream, &Field<7>().value);
Meta::ExactValueValidation<std::uint32_t>(Field<7>().value, 0);
StreamBase::Read(stream, &Field<7>());
Meta::ExactValueValidation<std::uint32_t>(Field<7>(), 0);
if (Field<8>().Size())
{
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<8>().value.data()), static_cast<ULONG>(Field<8>().Size()), nullptr));
ThrowHrIfFailed(stream->Read(reinterpret_cast<void*>(Field<8>().get().data()), static_cast<ULONG>(Field<8>().Size()), nullptr));
}
}

Просмотреть файл

@ -8,7 +8,6 @@
#include "MsixErrors.hpp"
#include "Exceptions.hpp"
#include "ContentType.hpp"
#include "ZipFileStream.hpp"
#include "Encoding.hpp"
#include "ZipObjectWriter.hpp"
#include "AppxManifestObject.hpp"
@ -185,15 +184,15 @@ namespace MSIX {
{
opcFileName = name;
}
auto lfhSize = m_zipWriter->PrepareToAddFile(opcFileName, toCompress);
auto fileInfo = m_zipWriter->PrepareToAddFile(opcFileName, toCompress);
// Add file to block map
if (addToBlockMap)
{
m_blockMapWriter.AddFile(name, uncompressedSize, lfhSize);
m_blockMapWriter.AddFile(name, uncompressedSize, fileInfo.first);
}
auto zipFileStream = ComPtr<IStream>::Make<ZipFileStream>(opcFileName, toCompress);
auto& zipFileStream = fileInfo.second;
std::uint64_t bytesToRead = uncompressedSize;
std::uint32_t crc = 0;
@ -238,8 +237,8 @@ namespace MSIX {
}
// This could be the compressed or uncompressed size
auto streamSize = zipFileStream.As<IStreamInternal>()->GetSize();
m_zipWriter->AddFile(zipFileStream, crc, streamSize, uncompressedSize);
auto streamSize = zipFileStream.As<IStreamInternal>()->GetSize();
m_zipWriter->EndFile(crc, streamSize, uncompressedSize, true);
}
bool AppxPackageWriter::IsFootPrintFile(std::string normalized)

Просмотреть файл

@ -7,6 +7,9 @@
#include "ZipObject.hpp"
#include "MsixErrors.hpp"
#include "Exceptions.hpp"
#include "ZipFileStream.hpp"
#include "DeflateStream.hpp"
#include "StreamHelper.hpp"
namespace MSIX {
@ -22,10 +25,10 @@ namespace MSIX {
public:
DataDescriptor(std::uint32_t crc, std::uint64_t compressSize, std::uint64_t uncompressSize)
{
Field<0>().value = static_cast<std::uint32_t>(Signatures::DataDescriptor);
Field<1>().value = crc;
Field<2>().value = compressSize;
Field<3>().value = uncompressSize;
Field<0>() = static_cast<std::uint32_t>(Signatures::DataDescriptor);
Field<1>() = crc;
Field<2>() = compressSize;
Field<3>() = uncompressSize;
}
};
@ -37,7 +40,7 @@ namespace MSIX {
ZipObjectWriter::ZipObjectWriter(const ComPtr<IStorageObject>& storageObject) : ZipObject(storageObject)
{
// The storage object provided should had already initialize all the data.
ThrowErrorIfNot(Error::Zip64EOCDLocator, m_endCentralDirectoryRecord.GetArchiveHasZip64Locator(),
ThrowErrorIfNot(Error::Zip64EOCDRecord, m_endCentralDirectoryRecord.GetIsZip64(),
"Editing non zip64 packages not supported");
// Move the stream at the start of central directory record so we can start overwritting.
@ -61,38 +64,64 @@ namespace MSIX {
}
// IZipWriter
std::uint32_t ZipObjectWriter::PrepareToAddFile(std::string& name, bool isCompressed)
std::pair<std::uint32_t, ComPtr<IStream>> ZipObjectWriter::PrepareToAddFile(const std::string& name, bool isCompressed)
{
ThrowErrorIf(Error::InvalidState, m_state != ZipObjectWriter::State::ReadyForLfhOrClose, "Invalid zip writer state");
// Get position were the lfh is going to be written
ULARGE_INTEGER pos = {0};
ThrowHrIfFailed(m_stream->Seek({0}, StreamBase::Reference::CURRENT, &pos));
// write lfh
// Write lfh
LocalFileHeader lfh;
lfh.SetData(name, isCompressed);
lfh.WriteTo(m_stream);
m_lastLFH = std::make_pair(static_cast<std::uint64_t>(pos.QuadPart), std::move(lfh));
m_state = ZipObjectWriter::State::ReadyForFile;
return static_cast<std::uint32_t>(m_lastLFH.second.Size());
ComPtr<IStream> zipStream = ComPtr<IStream>::Make<ZipFileStream>(name, isCompressed, m_stream.Get());
if (isCompressed)
{
zipStream = ComPtr<IStream>::Make<DeflateStream>(zipStream);
}
return std::make_pair(static_cast<std::uint32_t>(m_lastLFH.second.Size()), std::move(zipStream));
}
void ZipObjectWriter::AddFile(MSIX::ComPtr<IStream>& fileStream, std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize)
void ZipObjectWriter::EndFile(std::uint32_t crc, std::uint64_t compressedSize, std::uint64_t uncompressedSize, bool forceDataDescriptor)
{
ThrowErrorIf(Error::InvalidState, m_state != ZipObjectWriter::State::ReadyForFile, "Invalid zip writer state");
// Write file stream
LARGE_INTEGER start = { 0 };
ThrowHrIfFailed(fileStream->Seek(start, StreamBase::Reference::START, nullptr));
ULARGE_INTEGER bytesCount = { 0 };
bytesCount.QuadPart = std::numeric_limits<std::uint64_t>::max();
ThrowHrIfFailed(fileStream->CopyTo(m_stream.Get(), bytesCount, nullptr, nullptr));
// Create and write data descriptor
DataDescriptor descriptor = DataDescriptor(crc, compressedSize, uncompressedSize);
descriptor.WriteTo(m_stream);
if (forceDataDescriptor ||
compressedSize > MaxSizeToNotUseDataDescriptor ||
uncompressedSize > MaxSizeToNotUseDataDescriptor)
{
// Create and write data descriptor
DataDescriptor descriptor = DataDescriptor(crc, compressedSize, uncompressedSize);
descriptor.WriteTo(m_stream);
}
else
{
// The sizes can fit in the LFH, rewrite it with the new data
Helper::StreamPositionReset resetAfterLFHWrite{ m_stream.Get() };
LARGE_INTEGER lfhLocation;
lfhLocation.QuadPart = static_cast<LONGLONG>(m_lastLFH.first);
ThrowHrIfFailed(m_stream->Seek(lfhLocation, StreamBase::Reference::START, nullptr));
// We cannot change the size of the LFH, ensure that we don't accidentally
size_t currentSize = m_lastLFH.second.Size();
m_lastLFH.second.SetData(crc, compressedSize, uncompressedSize);
ThrowErrorIf(Error::Unexpected, currentSize != m_lastLFH.second.Size(), "Cannot change the LFH size when updating it");
m_lastLFH.second.WriteTo(m_stream);
}
// Create and add cdh to map
CentralDirectoryFileHeader cdh;
auto name = m_lastLFH.second.GetFileName();
cdh.SetData(name, crc, compressedSize, uncompressedSize, m_lastLFH.first, m_lastLFH.second.GetCompressionMethod());
m_centralDirectories.insert(std::make_pair(name, std::move(cdh)));
cdh.SetData(m_lastLFH.second.GetFileName(), crc, compressedSize, uncompressedSize, m_lastLFH.first, m_lastLFH.second.GetCompressionMethod(), forceDataDescriptor);
m_centralDirectories.insert(std::make_pair(m_lastLFH.second.GetFileName(), std::move(cdh)));
m_state = ZipObjectWriter::State::ReadyForLfhOrClose;
}

Просмотреть файл

@ -15,21 +15,23 @@ namespace MSIX {
ZipObjectReader::ZipObjectReader(const ComPtr<IStream>& stream) : ZipObject(stream)
{
LARGE_INTEGER pos = {0};
pos.QuadPart = -1 * m_endCentralDirectoryRecord.Size();
pos.QuadPart = m_endCentralDirectoryRecord.Size();
pos.QuadPart *= -1;
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::END, nullptr));
m_endCentralDirectoryRecord.Read(m_stream.Get());
// find where the zip central directory exists.
std::uint64_t offsetStartOfCD = 0;
std::uint64_t totalNumberOfEntries = 0;
if (!m_endCentralDirectoryRecord.GetArchiveHasZip64Locator())
if (!m_endCentralDirectoryRecord.GetIsZip64())
{
offsetStartOfCD = m_endCentralDirectoryRecord.GetStartOfCentralDirectory();
totalNumberOfEntries = m_endCentralDirectoryRecord.GetNumberOfCentralDirectoryEntries();
}
else
{ // Make sure that we have a zip64 end of central directory locator
pos.QuadPart = -1*(m_endCentralDirectoryRecord.Size() + m_zip64Locator.Size());
pos.QuadPart = m_endCentralDirectoryRecord.Size() + m_zip64Locator.Size();
pos.QuadPart *= -1;
ThrowHrIfFailed(m_stream->Seek(pos, StreamBase::Reference::END, nullptr));
m_zip64Locator.Read(m_stream.Get());
@ -52,7 +54,7 @@ namespace MSIX {
m_centralDirectories.insert(std::make_pair(centralFileHeader.GetFileName(), std::move(centralFileHeader)));
}
if (m_endCentralDirectoryRecord.GetArchiveHasZip64Locator())
if (m_endCentralDirectoryRecord.GetIsZip64())
{ // We should have no data between the end of the last central directory header and the start of the EoCD
ULARGE_INTEGER uPos = {0};
ThrowHrIfFailed(m_stream->Seek({0}, StreamBase::Reference::CURRENT, &uPos));
@ -94,7 +96,7 @@ namespace MSIX {
centralFileHeader->second.GetCompressionMethod() == CompressionType::Deflate,
centralFileHeader->second.GetRelativeOffsetOfLocalHeader() + lfh.Size(),
centralFileHeader->second.GetCompressedSize(),
m_stream
m_stream.Get()
);
if (centralFileHeader->second.GetCompressionMethod() == CompressionType::Deflate)