LibArchive: Refactor zip header handling

The directory headers have some common code for reading.
This commit is contained in:
Lenny Maiorani 2022-02-16 13:33:15 -07:00 committed by Idan Horowitz
parent 1511e9a5a9
commit 484c0edafc
Notes: sideshowbarker 2024-07-17 18:33:55 +09:00
2 changed files with 42 additions and 32 deletions

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -15,8 +16,9 @@ bool Zip::find_end_of_central_directory_offset(ReadonlyBytes buffer, size_t& off
if (buffer.size() < (sizeof(EndOfCentralDirectory) - sizeof(u8*)) + backwards_offset)
return false;
auto signature_offset = (buffer.size() - (sizeof(EndOfCentralDirectory) - sizeof(u8*)) - backwards_offset);
if (memcmp(buffer.data() + signature_offset, end_of_central_directory_signature, sizeof(end_of_central_directory_signature)) == 0) {
auto const signature_offset = (buffer.size() - (sizeof(EndOfCentralDirectory) - sizeof(u8*)) - backwards_offset);
if (auto signature = ReadonlyBytes { buffer.data() + signature_offset, EndOfCentralDirectory::signature.size() };
signature == EndOfCentralDirectory::signature) {
offset = signature_offset;
return true;
}
@ -156,7 +158,7 @@ void ZipOutputStream::finish()
central_directory_record.internal_attributes = 0;
central_directory_record.external_attributes = member.is_directory ? zip_directory_external_attribute : 0;
central_directory_record.local_file_header_offset = file_header_offset; // FIXME: we assume the wrapped output stream was never written to before us
file_header_offset += sizeof(local_file_header_signature) + (sizeof(LocalFileHeader) - (sizeof(u8*) * 3)) + member.name.length() + member.compressed_data.size();
file_header_offset += sizeof(LocalFileHeader::signature) + (sizeof(LocalFileHeader) - (sizeof(u8*) * 3)) + member.name.length() + member.compressed_data.size();
central_directory_record.name = (const u8*)(member.name.characters());
central_directory_record.extra_data = nullptr;
central_directory_record.comment = nullptr;

View file

@ -1,14 +1,15 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2022, the SerenityOS developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/Function.h>
#include <AK/IterationDecision.h>
#include <AK/Span.h>
#include <AK/Stream.h>
#include <AK/String.h>
#include <AK/Vector.h>
@ -16,10 +17,24 @@
namespace Archive {
template<size_t fields_size, class T>
static bool read_helper(ReadonlyBytes buffer, T* self)
{
if (buffer.size() < T::signature.size() + fields_size)
return false;
if (buffer.slice(0, T::signature.size()) != T::signature)
return false;
memcpy(self, buffer.data() + T::signature.size(), fields_size);
return true;
}
// NOTE: Due to the format of zip files compression is streamed and decompression is random access.
static constexpr u8 end_of_central_directory_signature[] = { 0x50, 0x4b, 0x05, 0x06 }; // 'PK\x05\x06'
static constexpr auto signature_length = 4;
struct [[gnu::packed]] EndOfCentralDirectory {
static constexpr Array<u8, signature_length> signature = { 0x50, 0x4b, 0x05, 0x06 }; // 'PK\x05\x06'
u16 disk_number;
u16 central_directory_start_disk;
u16 disk_records_count;
@ -31,21 +46,18 @@ struct [[gnu::packed]] EndOfCentralDirectory {
bool read(ReadonlyBytes buffer)
{
auto fields_size = sizeof(EndOfCentralDirectory) - sizeof(u8*);
if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size)
constexpr auto fields_size = sizeof(EndOfCentralDirectory) - (sizeof(u8*) * 1);
if (!read_helper<fields_size>(buffer, this))
return false;
if (memcmp(buffer.data(), end_of_central_directory_signature, sizeof(end_of_central_directory_signature)) != 0)
if (buffer.size() < signature.size() + fields_size + comment_length)
return false;
memcpy(reinterpret_cast<void*>(&disk_number), buffer.data() + sizeof(end_of_central_directory_signature), fields_size);
if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + comment_length)
return false;
comment = buffer.data() + sizeof(end_of_central_directory_signature) + fields_size;
comment = buffer.data() + signature.size() + fields_size;
return true;
}
void write(OutputStream& stream) const
{
stream.write_or_error({ end_of_central_directory_signature, sizeof(end_of_central_directory_signature) });
stream.write_or_error(signature);
stream << disk_number;
stream << central_directory_start_disk;
stream << disk_records_count;
@ -58,8 +70,9 @@ struct [[gnu::packed]] EndOfCentralDirectory {
}
};
static constexpr u8 central_directory_record_signature[] = { 0x50, 0x4b, 0x01, 0x02 }; // 'PK\x01\x02'
struct [[gnu::packed]] CentralDirectoryRecord {
static constexpr Array<u8, signature_length> signature = { 0x50, 0x4b, 0x01, 0x02 }; // 'PK\x01\x02'
u16 made_by_version;
u16 minimum_version;
u16 general_purpose_flags;
@ -82,15 +95,12 @@ struct [[gnu::packed]] CentralDirectoryRecord {
bool read(ReadonlyBytes buffer)
{
auto fields_size = sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3);
if (buffer.size() < sizeof(central_directory_record_signature) + fields_size)
constexpr auto fields_size = sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3);
if (!read_helper<fields_size>(buffer, this))
return false;
if (memcmp(buffer.data(), central_directory_record_signature, sizeof(central_directory_record_signature)) != 0)
if (buffer.size() < size())
return false;
memcpy(reinterpret_cast<void*>(&made_by_version), buffer.data() + sizeof(central_directory_record_signature), fields_size);
if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + comment_length + name_length + extra_data_length)
return false;
name = buffer.data() + sizeof(central_directory_record_signature) + fields_size;
name = buffer.data() + signature.size() + fields_size;
extra_data = name + name_length;
comment = extra_data + extra_data_length;
return true;
@ -98,7 +108,7 @@ struct [[gnu::packed]] CentralDirectoryRecord {
void write(OutputStream& stream) const
{
stream.write_or_error({ central_directory_record_signature, sizeof(central_directory_record_signature) });
stream.write_or_error(signature);
stream << made_by_version;
stream << minimum_version;
stream << general_purpose_flags;
@ -125,13 +135,14 @@ struct [[gnu::packed]] CentralDirectoryRecord {
[[nodiscard]] size_t size() const
{
return sizeof(central_directory_record_signature) + (sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3)) + name_length + extra_data_length + comment_length;
return signature.size() + (sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3)) + name_length + extra_data_length + comment_length;
}
};
static constexpr u32 zip_directory_external_attribute = 1 << 4;
static constexpr u8 local_file_header_signature[] = { 0x50, 0x4b, 0x03, 0x04 }; // 'PK\x03\x04'
struct [[gnu::packed]] LocalFileHeader {
static constexpr Array<u8, signature_length> signature = { 0x50, 0x4b, 0x03, 0x04 }; // 'PK\x03\x04'
u16 minimum_version;
u16 general_purpose_flags;
u16 compression_method;
@ -148,15 +159,12 @@ struct [[gnu::packed]] LocalFileHeader {
bool read(ReadonlyBytes buffer)
{
auto fields_size = sizeof(LocalFileHeader) - (sizeof(u8*) * 3);
if (buffer.size() < sizeof(local_file_header_signature) + fields_size)
constexpr auto fields_size = sizeof(LocalFileHeader) - (sizeof(u8*) * 3);
if (!read_helper<fields_size>(buffer, this))
return false;
if (memcmp(buffer.data(), local_file_header_signature, sizeof(local_file_header_signature)) != 0)
if (buffer.size() < signature.size() + fields_size + name_length + extra_data_length + compressed_size)
return false;
memcpy(reinterpret_cast<void*>(&minimum_version), buffer.data() + sizeof(local_file_header_signature), fields_size);
if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + name_length + extra_data_length + compressed_size)
return false;
name = buffer.data() + sizeof(local_file_header_signature) + fields_size;
name = buffer.data() + signature.size() + fields_size;
extra_data = name + name_length;
compressed_data = extra_data + extra_data_length;
return true;
@ -164,7 +172,7 @@ struct [[gnu::packed]] LocalFileHeader {
void write(OutputStream& stream) const
{
stream.write_or_error({ local_file_header_signature, sizeof(local_file_header_signature) });
stream.write_or_error(signature);
stream << minimum_version;
stream << general_purpose_flags;
stream << compression_method;