diff --git a/Libraries/LibArchive/CMakeLists.txt b/Libraries/LibArchive/CMakeLists.txt deleted file mode 100644 index 710e4b5733d..00000000000 --- a/Libraries/LibArchive/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -set(SOURCES - Tar.cpp - TarStream.cpp - Zip.cpp - ) - -serenity_lib(LibArchive archive) -target_link_libraries(LibArchive PRIVATE LibCompress LibCore LibCrypto) diff --git a/Libraries/LibArchive/Statistics.h b/Libraries/LibArchive/Statistics.h deleted file mode 100644 index bbd41d9dcb1..00000000000 --- a/Libraries/LibArchive/Statistics.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2023, Sam Atkins - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include - -namespace Archive { - -class Statistics { -public: - Statistics(size_t file_count, size_t directory_count, size_t total_uncompressed_bytes) - : m_file_count(file_count) - , m_directory_count(directory_count) - , m_total_uncompressed_bytes(total_uncompressed_bytes) - { - } - - size_t file_count() const { return m_file_count; } - size_t directory_count() const { return m_directory_count; } - size_t member_count() const { return file_count() + directory_count(); } - size_t total_uncompressed_bytes() const { return m_total_uncompressed_bytes; } - -private: - size_t m_file_count { 0 }; - size_t m_directory_count { 0 }; - size_t m_total_uncompressed_bytes { 0 }; -}; - -} diff --git a/Libraries/LibArchive/Tar.cpp b/Libraries/LibArchive/Tar.cpp deleted file mode 100644 index a7c2105419d..00000000000 --- a/Libraries/LibArchive/Tar.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2020, Peter Elliott - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Tar.h" - -namespace Archive { - -unsigned TarFileHeader::expected_checksum() const -{ - auto checksum = 0u; - u8 const* u8_this = reinterpret_cast(this); - u8 const* u8_m_checksum = reinterpret_cast(&m_checksum); - for (auto i = 0u; i < sizeof(TarFileHeader); ++i) { - if (u8_this + i >= u8_m_checksum && u8_this + i < u8_m_checksum + sizeof(m_checksum)) { - checksum += ' '; - } else { - checksum += u8_this[i]; - } - } - return checksum; -} - -ErrorOr TarFileHeader::calculate_checksum() -{ - memset(m_checksum, ' ', sizeof(m_checksum)); - - auto octal = TRY(String::formatted("{:06o}", expected_checksum())); - bool copy_successful = octal.bytes_as_string_view().copy_characters_to_buffer(m_checksum, sizeof(m_checksum)); - VERIFY(copy_successful); - - return {}; -} - -bool TarFileHeader::is_zero_block() const -{ - u8 const* buffer = reinterpret_cast(this); - for (size_t i = 0; i < sizeof(TarFileHeader); ++i) { - if (buffer[i] != 0) - return false; - } - return true; -} - -bool TarFileHeader::content_is_like_extended_header() const -{ - return type_flag() == TarFileType::ExtendedHeader || type_flag() == TarFileType::GlobalExtendedHeader; -} - -void TarFileHeader::set_filename_and_prefix(StringView filename) -{ - // FIXME: Add support for extended tar headers for longer filenames. - VERIFY(filename.length() <= sizeof(m_filename) + sizeof(m_prefix)); - - if (filename.length() <= sizeof(m_filename)) { - set_prefix(""sv); - set_filename(filename); - return; - } - - Optional slash = filename.find('/', filename.length() - sizeof(m_filename)); - - VERIFY(slash.has_value()); - set_prefix(filename.substring_view(0, slash.value() + 1)); - set_filename(filename.substring_view(slash.value() + 1)); -} - -} diff --git a/Libraries/LibArchive/Tar.h b/Libraries/LibArchive/Tar.h deleted file mode 100644 index 8b62ec21dcf..00000000000 --- a/Libraries/LibArchive/Tar.h +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) 2020-2022, Peter Elliott - * Copyright (c) 2021, Idan Horowitz - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -// glibc before 2.28 defines these from sys/types.h, but we don't want -// TarFileHeader::major() and TarFileHeader::minor() to use those macros -#ifdef minor -# undef minor -#endif - -#ifdef major -# undef major -#endif - -namespace Archive { - -enum class TarFileType : char { - NormalFile = '0', - AlternateNormalFile = '\0', - HardLink = '1', - SymLink = '2', - CharacterSpecialFile = '3', - BlockSpecialFile = '4', - Directory = '5', - FIFO = '6', - ContiguousFile = '7', - GlobalExtendedHeader = 'g', - ExtendedHeader = 'x', - - // GNU extensions - LongName = 'L', -}; - -constexpr size_t block_size = 512; -constexpr StringView gnu_magic = "ustar "sv; // gnu format magic -constexpr StringView gnu_version = " "sv; // gnu format version -constexpr StringView ustar_magic = "ustar"sv; // ustar format magic -constexpr StringView ustar_version = "00"sv; // ustar format version -constexpr StringView posix1_tar_magic = ""sv; // POSIX.1-1988 format magic -constexpr StringView posix1_tar_version = ""sv; // POSIX.1-1988 format version - -template -static ErrorOr get_field_as_integral(char const (&field)[N]) -{ - size_t value = 0; - for (size_t i = 0; i < N; ++i) { - if (field[i] == 0 || field[i] == ' ') - break; - - if (field[i] < '0' || field[i] > '7') - return Error::from_string_literal("Passed a non-octal value"); - value *= 8; - value += field[i] - '0'; - } - return value; -} - -template -static StringView get_field_as_string_view(char const (&field)[N]) -{ - return { field, min(__builtin_strlen(field), N) }; -} - -template -static void set_field(char (&field)[N], TSource&& source) -{ - VERIFY(N >= source.length()); - memcpy(field, StringView(source).characters_without_null_termination(), source.length()); - if (N > source.length()) - field[source.length()] = 0; -} - -template -static ErrorOr set_octal_field(char (&field)[N], TSource&& source) -{ - auto octal = TRY(String::formatted("{:o}", forward(source))); - set_field(field, octal.bytes_as_string_view()); - return {}; -} - -class [[gnu::packed]] TarFileHeader { -public: - StringView filename() const { return get_field_as_string_view(m_filename); } - ErrorOr mode() const { return TRY(get_field_as_integral(m_mode)); } - ErrorOr uid() const { return TRY(get_field_as_integral(m_uid)); } - ErrorOr gid() const { return TRY(get_field_as_integral(m_gid)); } - // FIXME: support 2001-star size encoding - ErrorOr size() const { return TRY(get_field_as_integral(m_size)); } - ErrorOr timestamp() const { return TRY(get_field_as_integral(m_timestamp)); } - ErrorOr checksum() const { return TRY(get_field_as_integral(m_checksum)); } - TarFileType type_flag() const { return TarFileType(m_type_flag); } - StringView link_name() const { return { m_link_name, strlen(m_link_name) }; } - StringView magic() const { return get_field_as_string_view(m_magic); } - StringView version() const { return get_field_as_string_view(m_version); } - StringView owner_name() const { return get_field_as_string_view(m_owner_name); } - StringView group_name() const { return get_field_as_string_view(m_group_name); } - ErrorOr major() const { return TRY(get_field_as_integral(m_major)); } - ErrorOr minor() const { return TRY(get_field_as_integral(m_minor)); } - // FIXME: support ustar filename prefix - StringView prefix() const { return get_field_as_string_view(m_prefix); } - - void set_filename(StringView filename) { set_field(m_filename, filename); } - ErrorOr set_mode(mode_t mode) { return set_octal_field(m_mode, mode); } - ErrorOr set_uid(uid_t uid) { return set_octal_field(m_uid, uid); } - ErrorOr set_gid(gid_t gid) { return set_octal_field(m_gid, gid); } - ErrorOr set_size(size_t size) { return set_octal_field(m_size, size); } - ErrorOr set_timestamp(time_t timestamp) { return set_octal_field(m_timestamp, timestamp); } - void set_type_flag(TarFileType type) { m_type_flag = to_underlying(type); } - void set_link_name(StringView link_name) { set_field(m_link_name, link_name); } - // magic doesn't necessarily include a null byte - void set_magic(StringView magic) { set_field(m_magic, magic); } - // version doesn't necessarily include a null byte - void set_version(StringView version) { set_field(m_version, version); } - void set_owner_name(StringView owner_name) { set_field(m_owner_name, owner_name); } - void set_group_name(StringView group_name) { set_field(m_group_name, group_name); } - ErrorOr set_major(int major) { return set_octal_field(m_major, major); } - ErrorOr set_minor(int minor) { return set_octal_field(m_minor, minor); } - void set_prefix(StringView prefix) { set_field(m_prefix, prefix); } - - unsigned expected_checksum() const; - ErrorOr calculate_checksum(); - - bool is_zero_block() const; - bool content_is_like_extended_header() const; - - void set_filename_and_prefix(StringView filename); - -private: - char m_filename[100] { 0 }; - char m_mode[8] { 0 }; - char m_uid[8] { 0 }; - char m_gid[8] { 0 }; - char m_size[12] { 0 }; - char m_timestamp[12] { 0 }; - char m_checksum[8] { 0 }; // an uninitialized header's checksum is filled with spaces - char m_type_flag { 0 }; - char m_link_name[100] { 0 }; - char m_magic[6] { 0 }; - char m_version[2] { 0 }; - char m_owner_name[32] { 0 }; - char m_group_name[32] { 0 }; - char m_major[8] { 0 }; - char m_minor[8] { 0 }; - char m_prefix[155] { 0 }; // zero out the prefix for archiving -}; - -} - -template<> -struct AK::Traits : public AK::DefaultTraits { - static constexpr bool is_trivially_serializable() { return true; } -}; diff --git a/Libraries/LibArchive/TarStream.cpp b/Libraries/LibArchive/TarStream.cpp deleted file mode 100644 index dc4cf8ef26a..00000000000 --- a/Libraries/LibArchive/TarStream.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2020, Peter Elliott - * Copyright (c) 2021, Idan Horowitz - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -namespace Archive { -TarFileStream::TarFileStream(TarInputStream& tar_stream) - : m_tar_stream(tar_stream) - , m_generation(tar_stream.m_generation) -{ -} - -ErrorOr TarFileStream::read_some(Bytes bytes) -{ - // Verify that the stream has not advanced. - VERIFY(m_tar_stream.m_generation == m_generation); - - auto header_size = TRY(m_tar_stream.header().size()); - - auto to_read = min(bytes.size(), header_size - m_tar_stream.m_file_offset); - - auto slice = TRY(m_tar_stream.m_stream->read_some(bytes.trim(to_read))); - m_tar_stream.m_file_offset += slice.size(); - - return slice; -} - -bool TarFileStream::is_eof() const -{ - // Verify that the stream has not advanced. - VERIFY(m_tar_stream.m_generation == m_generation); - - auto header_size_or_error = m_tar_stream.header().size(); - if (header_size_or_error.is_error()) - return true; - auto header_size = header_size_or_error.release_value(); - - return m_tar_stream.m_stream->is_eof() - || m_tar_stream.m_file_offset >= header_size; -} - -ErrorOr TarFileStream::write_some(ReadonlyBytes) -{ - return Error::from_errno(EBADF); -} - -ErrorOr> TarInputStream::construct(NonnullOwnPtr stream) -{ - auto tar_stream = TRY(adopt_nonnull_own_or_enomem(new (nothrow) TarInputStream(move(stream)))); - - TRY(tar_stream->load_next_header()); - - return tar_stream; -} - -TarInputStream::TarInputStream(NonnullOwnPtr stream) - : m_stream(move(stream)) -{ -} - -static constexpr unsigned long block_ceiling(unsigned long offset) -{ - return block_size * (1 + ((offset - 1) / block_size)); -} - -ErrorOr TarInputStream::advance() -{ - if (finished()) - return Error::from_string_literal("Attempted to advance a finished stream"); - - m_generation++; - - // Discard the pending bytes of the current entry. - auto file_size = TRY(m_header.size()); - TRY(m_stream->discard(block_ceiling(file_size) - m_file_offset)); - m_file_offset = 0; - - TRY(load_next_header()); - - return {}; -} - -ErrorOr TarInputStream::load_next_header() -{ - size_t number_of_consecutive_zero_blocks = 0; - while (true) { - m_header = TRY(m_stream->read_value()); - - // Discard the rest of the header block. - TRY(m_stream->discard(block_size - sizeof(TarFileHeader))); - - if (!header().is_zero_block()) - break; - - number_of_consecutive_zero_blocks++; - - // Two zero blocks in a row marks the end of the archive. - if (number_of_consecutive_zero_blocks >= 2) { - m_found_end_of_archive = true; - return {}; - } - } - - if (!TRY(valid())) - return Error::from_string_literal("Header has an invalid magic or checksum"); - - return {}; -} - -ErrorOr TarInputStream::valid() const -{ - auto const header_magic = header().magic(); - auto const header_version = header().version(); - - if (!((header_magic == gnu_magic && header_version == gnu_version) - || (header_magic == ustar_magic && header_version == ustar_version) - || (header_magic == posix1_tar_magic && header_version == posix1_tar_version))) - return false; - - // POSIX.1-1988 tar does not have magic numbers, so we also need to verify the header checksum. - return TRY(header().checksum()) == header().expected_checksum(); -} - -TarFileStream TarInputStream::file_contents() -{ - VERIFY(!finished()); - return TarFileStream(*this); -} - -TarOutputStream::TarOutputStream(MaybeOwned stream) - : m_stream(move(stream)) -{ -} - -ErrorOr TarOutputStream::add_directory(StringView path, mode_t mode) -{ - VERIFY(!m_finished); - TarFileHeader header {}; - TRY(header.set_size(0)); - header.set_filename_and_prefix(TRY(String::formatted("{}/", path))); // Old tar implementations assume directory names end with a / - header.set_type_flag(TarFileType::Directory); - TRY(header.set_mode(mode)); - header.set_magic(gnu_magic); - header.set_version(gnu_version); - TRY(header.calculate_checksum()); - TRY(m_stream->write_until_depleted(Bytes { &header, sizeof(header) })); - u8 padding[block_size] = { 0 }; - TRY(m_stream->write_until_depleted(Bytes { &padding, block_size - sizeof(header) })); - return {}; -} - -ErrorOr TarOutputStream::add_file(StringView path, mode_t mode, ReadonlyBytes bytes) -{ - VERIFY(!m_finished); - TarFileHeader header {}; - TRY(header.set_size(bytes.size())); - header.set_filename_and_prefix(path); - header.set_type_flag(TarFileType::NormalFile); - TRY(header.set_mode(mode)); - header.set_magic(gnu_magic); - header.set_version(gnu_version); - TRY(header.calculate_checksum()); - TRY(m_stream->write_until_depleted(ReadonlyBytes { &header, sizeof(header) })); - constexpr Array padding { 0 }; - TRY(m_stream->write_until_depleted(ReadonlyBytes { &padding, block_size - sizeof(header) })); - size_t n_written = 0; - while (n_written < bytes.size()) { - n_written += MUST(m_stream->write_some(bytes.slice(n_written, min(bytes.size() - n_written, block_size)))); - } - TRY(m_stream->write_until_depleted(ReadonlyBytes { &padding, block_size - (n_written % block_size) })); - return {}; -} - -ErrorOr TarOutputStream::add_link(StringView path, mode_t mode, StringView link_name) -{ - VERIFY(!m_finished); - TarFileHeader header {}; - TRY(header.set_size(0)); - header.set_filename_and_prefix(path); - header.set_type_flag(TarFileType::SymLink); - TRY(header.set_mode(mode)); - header.set_magic(gnu_magic); - header.set_version(gnu_version); - header.set_link_name(link_name); - TRY(header.calculate_checksum()); - TRY(m_stream->write_until_depleted(Bytes { &header, sizeof(header) })); - u8 padding[block_size] = { 0 }; - TRY(m_stream->write_until_depleted(Bytes { &padding, block_size - sizeof(header) })); - return {}; -} - -ErrorOr TarOutputStream::finish() -{ - VERIFY(!m_finished); - constexpr Array padding { 0 }; - // 2 empty records that are used to signify the end of the archive. - TRY(m_stream->write_until_depleted(ReadonlyBytes { &padding, block_size })); - TRY(m_stream->write_until_depleted(ReadonlyBytes { &padding, block_size })); - m_finished = true; - return {}; -} - -} diff --git a/Libraries/LibArchive/TarStream.h b/Libraries/LibArchive/TarStream.h deleted file mode 100644 index 2ff3b8c5dd0..00000000000 --- a/Libraries/LibArchive/TarStream.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2020, Peter Elliott - * Copyright (c) 2021, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Archive { - -class TarInputStream; - -class TarFileStream : public Stream { -public: - virtual ErrorOr read_some(Bytes) override; - virtual ErrorOr write_some(ReadonlyBytes) override; - virtual bool is_eof() const override; - virtual bool is_open() const override { return true; } - virtual void close() override {}; - -private: - TarFileStream(TarInputStream& stream); - TarInputStream& m_tar_stream; - int m_generation; - - friend class TarInputStream; -}; - -class TarInputStream { -public: - static ErrorOr> construct(NonnullOwnPtr); - ErrorOr advance(); - bool finished() const { return m_found_end_of_archive || m_stream->is_eof(); } - ErrorOr valid() const; - TarFileHeader const& header() const { return m_header; } - TarFileStream file_contents(); - - template F> - ErrorOr for_each_extended_header(F func); - -private: - TarInputStream(NonnullOwnPtr); - ErrorOr load_next_header(); - - TarFileHeader m_header; - NonnullOwnPtr m_stream; - unsigned long m_file_offset { 0 }; - int m_generation { 0 }; - bool m_found_end_of_archive { false }; - - friend class TarFileStream; -}; - -class TarOutputStream { -public: - TarOutputStream(MaybeOwned); - ErrorOr add_file(StringView path, mode_t, ReadonlyBytes); - ErrorOr add_link(StringView path, mode_t, StringView); - ErrorOr add_directory(StringView path, mode_t); - ErrorOr finish(); - -private: - MaybeOwned m_stream; - bool m_finished { false }; - - friend class TarFileStream; -}; - -template F> -inline ErrorOr TarInputStream::for_each_extended_header(F func) -{ - VERIFY(header().content_is_like_extended_header()); - - Archive::TarFileStream file_stream = file_contents(); - - auto header_size = TRY(header().size()); - ByteBuffer file_contents_buffer = TRY(ByteBuffer::create_zeroed(header_size)); - TRY(file_stream.read_until_filled(file_contents_buffer)); - - StringView file_contents { file_contents_buffer }; - - while (!file_contents.is_empty()) { - // Split off the length (until the first space). - Optional length_end_index = file_contents.find(' '); - if (!length_end_index.has_value()) - return Error::from_string_literal("Malformed extended header: No length found."); - Optional length = file_contents.substring_view(0, length_end_index.value()).to_number(); - if (!length.has_value()) - return Error::from_string_literal("Malformed extended header: Could not parse length."); - - if (length_end_index.value() >= length.value()) - return Error::from_string_literal("Malformed extended header: Header length too short."); - - unsigned int remaining_length = length.value(); - - remaining_length -= length_end_index.value() + 1; - file_contents = file_contents.substring_view(length_end_index.value() + 1); - - if (file_contents.length() < remaining_length - 1) - return Error::from_string_literal("Malformed extended header: Header length too large."); - - // Extract the header. - StringView header = file_contents.substring_view(0, remaining_length - 1); - file_contents = file_contents.substring_view(remaining_length - 1); - - // Ensure that the header ends at the expected location. - if (file_contents.length() < 1 || !file_contents.starts_with('\n')) - return Error::from_string_literal("Malformed extended header: Header does not end at expected location."); - file_contents = file_contents.substring_view(1); - - // Find the delimiting '='. - Optional header_delimiter_index = header.find('='); - if (!header_delimiter_index.has_value()) - return Error::from_string_literal("Malformed extended header: Header does not have a delimiter."); - StringView key = header.substring_view(0, header_delimiter_index.value()); - StringView value = header.substring_view(header_delimiter_index.value() + 1); - - func(key, value); - } - - return {}; -} - -} diff --git a/Libraries/LibArchive/Zip.cpp b/Libraries/LibArchive/Zip.cpp deleted file mode 100644 index e58d734becd..00000000000 --- a/Libraries/LibArchive/Zip.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -namespace Archive { - -bool Zip::find_end_of_central_directory_offset(ReadonlyBytes buffer, size_t& offset) -{ - for (size_t backwards_offset = 0; backwards_offset <= UINT16_MAX; backwards_offset++) // the file may have a trailing comment of an arbitrary 16 bit length - { - if (buffer.size() < (sizeof(EndOfCentralDirectory) - sizeof(u8*)) + backwards_offset) - return false; - - 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; - } - } - return false; -} - -Optional Zip::try_create(ReadonlyBytes buffer) -{ - size_t end_of_central_directory_offset; - if (!find_end_of_central_directory_offset(buffer, end_of_central_directory_offset)) - return {}; - - EndOfCentralDirectory end_of_central_directory {}; - if (!end_of_central_directory.read(buffer.slice(end_of_central_directory_offset))) - return {}; - - if (end_of_central_directory.disk_number != 0 || end_of_central_directory.central_directory_start_disk != 0 || end_of_central_directory.disk_records_count != end_of_central_directory.total_records_count) - return {}; // TODO: support multi-volume zip archives - - size_t member_offset = end_of_central_directory.central_directory_offset; - for (size_t i = 0; i < end_of_central_directory.total_records_count; i++) { - CentralDirectoryRecord central_directory_record {}; - if (member_offset > buffer.size()) - return {}; - if (!central_directory_record.read(buffer.slice(member_offset))) - return {}; - if (central_directory_record.general_purpose_flags.encrypted) - return {}; // TODO: support encrypted zip members - if (central_directory_record.general_purpose_flags.data_descriptor) - return {}; // TODO: support zip data descriptors - if (central_directory_record.compression_method != ZipCompressionMethod::Store && central_directory_record.compression_method != ZipCompressionMethod::Deflate) - return {}; // TODO: support obsolete zip compression methods - if (central_directory_record.compression_method == ZipCompressionMethod::Store && central_directory_record.uncompressed_size != central_directory_record.compressed_size) - return {}; - if (central_directory_record.start_disk != 0) - return {}; // TODO: support multi-volume zip archives - if (memchr(central_directory_record.name, 0, central_directory_record.name_length) != nullptr) - return {}; - LocalFileHeader local_file_header {}; - if (central_directory_record.local_file_header_offset > buffer.size()) - return {}; - if (!local_file_header.read(buffer.slice(central_directory_record.local_file_header_offset))) - return {}; - if (buffer.size() - (local_file_header.compressed_data - buffer.data()) < central_directory_record.compressed_size) - return {}; - member_offset += central_directory_record.size(); - } - - return Zip { - end_of_central_directory.total_records_count, - end_of_central_directory.central_directory_offset, - buffer, - }; -} - -ErrorOr Zip::for_each_member(Function(ZipMember const&)> callback) const -{ - size_t member_offset = m_members_start_offset; - for (size_t i = 0; i < m_member_count; i++) { - CentralDirectoryRecord central_directory_record {}; - VERIFY(central_directory_record.read(m_input_data.slice(member_offset))); - LocalFileHeader local_file_header {}; - VERIFY(local_file_header.read(m_input_data.slice(central_directory_record.local_file_header_offset))); - - ZipMember member; - member.name = TRY(String::from_utf8({ central_directory_record.name, central_directory_record.name_length })); - member.compressed_data = { local_file_header.compressed_data, central_directory_record.compressed_size }; - member.compression_method = central_directory_record.compression_method; - member.uncompressed_size = central_directory_record.uncompressed_size; - member.crc32 = central_directory_record.crc32; - member.modification_time = central_directory_record.modification_time; - member.modification_date = central_directory_record.modification_date; - member.is_directory = central_directory_record.external_attributes & zip_directory_external_attribute || member.name.bytes_as_string_view().ends_with('/'); // FIXME: better directory detection - - if (TRY(callback(member)) == IterationDecision::Break) - return false; - - member_offset += central_directory_record.size(); - } - return true; -} - -ErrorOr Zip::calculate_statistics() const -{ - size_t file_count = 0; - size_t directory_count = 0; - size_t uncompressed_bytes = 0; - - TRY(for_each_member([&](auto zip_member) -> ErrorOr { - if (zip_member.is_directory) - directory_count++; - else - file_count++; - uncompressed_bytes += zip_member.uncompressed_size; - return IterationDecision::Continue; - })); - - return Statistics(file_count, directory_count, uncompressed_bytes); -} - -ZipOutputStream::ZipOutputStream(NonnullOwnPtr stream) - : m_stream(move(stream)) -{ -} - -static u16 minimum_version_needed(ZipCompressionMethod method) -{ - // Deflate was added in PKZip 2.0 - return method == ZipCompressionMethod::Deflate ? 20 : 10; -} - -ErrorOr ZipOutputStream::add_member(ZipMember const& member) -{ - VERIFY(!m_finished); - VERIFY(member.name.bytes_as_string_view().length() <= UINT16_MAX); - VERIFY(member.compressed_data.size() <= UINT32_MAX); - TRY(m_members.try_append(member)); - - LocalFileHeader local_file_header { - .minimum_version = minimum_version_needed(member.compression_method), - .general_purpose_flags = { .flags = 0 }, - .compression_method = static_cast(member.compression_method), - .modification_time = member.modification_time, - .modification_date = member.modification_date, - .crc32 = member.crc32, - .compressed_size = static_cast(member.compressed_data.size()), - .uncompressed_size = member.uncompressed_size, - .name_length = static_cast(member.name.bytes_as_string_view().length()), - .extra_data_length = 0, - .name = reinterpret_cast(member.name.bytes_as_string_view().characters_without_null_termination()), - .extra_data = nullptr, - .compressed_data = member.compressed_data.data(), - }; - return local_file_header.write(*m_stream); -} - -ErrorOr ZipOutputStream::add_member_from_stream(StringView path, Stream& stream, Optional const& modification_time) -{ - auto buffer = TRY(stream.read_until_eof()); - - Archive::ZipMember member {}; - member.name = TRY(String::from_utf8(path)); - - if (modification_time.has_value()) { - member.modification_date = to_packed_dos_date(modification_time->year(), modification_time->month(), modification_time->day()); - member.modification_time = to_packed_dos_time(modification_time->hour(), modification_time->minute(), modification_time->second()); - } - - auto deflate_buffer = Compress::DeflateCompressor::compress_all(buffer); - auto compression_ratio = 1.f; - auto compressed_size = buffer.size(); - - if (!deflate_buffer.is_error() && deflate_buffer.value().size() < buffer.size()) { - member.compressed_data = deflate_buffer.value().bytes(); - member.compression_method = Archive::ZipCompressionMethod::Deflate; - - compression_ratio = static_cast(deflate_buffer.value().size()) / static_cast(buffer.size()); - compressed_size = member.compressed_data.size(); - } else { - member.compressed_data = buffer.bytes(); - member.compression_method = Archive::ZipCompressionMethod::Store; - } - - member.uncompressed_size = buffer.size(); - - Crypto::Checksum::CRC32 checksum { buffer.bytes() }; - member.crc32 = checksum.digest(); - member.is_directory = false; - - TRY(add_member(member)); - - return MemberInformation { compression_ratio, compressed_size }; -} - -ErrorOr ZipOutputStream::add_directory(StringView name, Optional const& modification_time) -{ - Archive::ZipMember member {}; - member.name = TRY(String::from_utf8(name)); - member.compressed_data = {}; - member.compression_method = Archive::ZipCompressionMethod::Store; - member.uncompressed_size = 0; - member.crc32 = 0; - member.is_directory = true; - - if (modification_time.has_value()) { - member.modification_date = to_packed_dos_date(modification_time->year(), modification_time->month(), modification_time->day()); - member.modification_time = to_packed_dos_time(modification_time->hour(), modification_time->minute(), modification_time->second()); - } - - return add_member(member); -} - -ErrorOr ZipOutputStream::finish() -{ - VERIFY(!m_finished); - m_finished = true; - - auto file_header_offset = 0u; - auto central_directory_size = 0u; - for (ZipMember const& member : m_members) { - auto zip_version = minimum_version_needed(member.compression_method); - CentralDirectoryRecord central_directory_record { - .made_by_version = zip_version, - .minimum_version = zip_version, - .general_purpose_flags = { .flags = 0 }, - .compression_method = member.compression_method, - .modification_time = member.modification_time, - .modification_date = member.modification_date, - .crc32 = member.crc32, - .compressed_size = static_cast(member.compressed_data.size()), - .uncompressed_size = member.uncompressed_size, - .name_length = static_cast(member.name.bytes_as_string_view().length()), - .extra_data_length = 0, - .comment_length = 0, - .start_disk = 0, - .internal_attributes = 0, - .external_attributes = member.is_directory ? zip_directory_external_attribute : 0, - .local_file_header_offset = file_header_offset, // FIXME: we assume the wrapped output stream was never written to before us - .name = reinterpret_cast(member.name.bytes_as_string_view().characters_without_null_termination()), - .extra_data = nullptr, - .comment = nullptr, - }; - file_header_offset += sizeof(LocalFileHeader::signature) + (sizeof(LocalFileHeader) - (sizeof(u8*) * 3)) + member.name.bytes_as_string_view().length() + member.compressed_data.size(); - TRY(central_directory_record.write(*m_stream)); - central_directory_size += central_directory_record.size(); - } - - EndOfCentralDirectory end_of_central_directory { - .disk_number = 0, - .central_directory_start_disk = 0, - .disk_records_count = static_cast(m_members.size()), - .total_records_count = static_cast(m_members.size()), - .central_directory_size = central_directory_size, - .central_directory_offset = file_header_offset, - .comment_length = 0, - .comment = nullptr, - }; - return end_of_central_directory.write(*m_stream); -} - -} diff --git a/Libraries/LibArchive/Zip.h b/Libraries/LibArchive/Zip.h deleted file mode 100644 index cb9dbf9c3a8..00000000000 --- a/Libraries/LibArchive/Zip.h +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright (c) 2021, Idan Horowitz - * Copyright (c) 2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Archive { - -template -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 auto signature_length = 4; - -struct [[gnu::packed]] EndOfCentralDirectory { - static constexpr Array signature = { 0x50, 0x4b, 0x05, 0x06 }; // 'PK\x05\x06' - - u16 disk_number; - u16 central_directory_start_disk; - u16 disk_records_count; - u16 total_records_count; - u32 central_directory_size; - u32 central_directory_offset; - u16 comment_length; - u8 const* comment; - - bool read(ReadonlyBytes buffer) - { - constexpr auto fields_size = sizeof(EndOfCentralDirectory) - (sizeof(u8*) * 1); - if (!read_helper(buffer, this)) - return false; - if (buffer.size() < signature.size() + fields_size + comment_length) - return false; - comment = buffer.data() + signature.size() + fields_size; - return true; - } - - ErrorOr write(Stream& stream) const - { - auto write_value = [&stream](auto value) { - return stream.write_until_depleted({ &value, sizeof(value) }); - }; - - TRY(stream.write_until_depleted(signature)); - TRY(write_value(disk_number)); - TRY(write_value(central_directory_start_disk)); - TRY(write_value(disk_records_count)); - TRY(write_value(total_records_count)); - TRY(write_value(central_directory_size)); - TRY(write_value(central_directory_offset)); - TRY(write_value(comment_length)); - if (comment_length > 0) - TRY(stream.write_until_depleted({ comment, comment_length })); - return {}; - } -}; - -enum class ZipCompressionMethod : u16 { - Store = 0, - Shrink = 1, - Reduce1 = 2, - Reduce2 = 3, - Reduce3 = 4, - Reduce4 = 5, - Implode = 6, - Reserved = 7, - Deflate = 8 -}; - -union ZipGeneralPurposeFlags { - u16 flags; - struct { - u16 encrypted : 1; - u16 compression_options : 2; - u16 data_descriptor : 1; - u16 enhanced_deflation : 1; - u16 compressed_patched_data : 1; - u16 strong_encryption : 1; - u16 : 4; - u16 language_encoding : 1; - u16 : 1; - u16 masked_data_values : 1; - u16 : 2; - }; -}; -static_assert(sizeof(ZipGeneralPurposeFlags) == sizeof(u16)); - -struct [[gnu::packed]] CentralDirectoryRecord { - static constexpr Array signature = { 0x50, 0x4b, 0x01, 0x02 }; // 'PK\x01\x02' - - u16 made_by_version; - u16 minimum_version; - ZipGeneralPurposeFlags general_purpose_flags; - ZipCompressionMethod compression_method; - DOSPackedTime modification_time; - DOSPackedDate modification_date; - u32 crc32; - u32 compressed_size; - u32 uncompressed_size; - u16 name_length; - u16 extra_data_length; - u16 comment_length; - u16 start_disk; - u16 internal_attributes; - u32 external_attributes; - u32 local_file_header_offset; - u8 const* name; - u8 const* extra_data; - u8 const* comment; - - bool read(ReadonlyBytes buffer) - { - constexpr auto fields_size = sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3); - if (!read_helper(buffer, this)) - return false; - if (buffer.size() < size()) - return false; - name = buffer.data() + signature.size() + fields_size; - extra_data = name + name_length; - comment = extra_data + extra_data_length; - return true; - } - - ErrorOr write(Stream& stream) const - { - auto write_value = [&stream](auto value) { - return stream.write_until_depleted({ &value, sizeof(value) }); - }; - - TRY(stream.write_until_depleted(signature)); - TRY(write_value(made_by_version)); - TRY(write_value(minimum_version)); - TRY(write_value(general_purpose_flags.flags)); - TRY(write_value(compression_method)); - TRY(write_value(modification_time)); - TRY(write_value(modification_date)); - TRY(write_value(crc32)); - TRY(write_value(compressed_size)); - TRY(write_value(uncompressed_size)); - TRY(write_value(name_length)); - TRY(write_value(extra_data_length)); - TRY(write_value(comment_length)); - TRY(write_value(start_disk)); - TRY(write_value(internal_attributes)); - TRY(write_value(external_attributes)); - TRY(write_value(local_file_header_offset)); - if (name_length > 0) - TRY(stream.write_until_depleted({ name, name_length })); - if (extra_data_length > 0) - TRY(stream.write_until_depleted({ extra_data, extra_data_length })); - if (comment_length > 0) - TRY(stream.write_until_depleted({ comment, comment_length })); - return {}; - } - - [[nodiscard]] size_t size() const - { - return signature.size() + (sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3)) + name_length + extra_data_length + comment_length; - } -}; -static constexpr u32 zip_directory_external_attribute = 1 << 4; - -struct [[gnu::packed]] LocalFileHeader { - static constexpr Array signature = { 0x50, 0x4b, 0x03, 0x04 }; // 'PK\x03\x04' - - u16 minimum_version; - ZipGeneralPurposeFlags general_purpose_flags; - u16 compression_method; - DOSPackedTime modification_time; - DOSPackedDate modification_date; - u32 crc32; - u32 compressed_size; - u32 uncompressed_size; - u16 name_length; - u16 extra_data_length; - u8 const* name; - u8 const* extra_data; - u8 const* compressed_data; - - bool read(ReadonlyBytes buffer) - { - constexpr auto fields_size = sizeof(LocalFileHeader) - (sizeof(u8*) * 3); - if (!read_helper(buffer, this)) - return false; - if (buffer.size() < signature.size() + fields_size + name_length + extra_data_length + compressed_size) - return false; - name = buffer.data() + signature.size() + fields_size; - extra_data = name + name_length; - compressed_data = extra_data + extra_data_length; - return true; - } - - ErrorOr write(Stream& stream) const - { - auto write_value = [&stream](auto value) { - return stream.write_until_depleted({ &value, sizeof(value) }); - }; - - TRY(stream.write_until_depleted(signature)); - TRY(write_value(minimum_version)); - TRY(write_value(general_purpose_flags.flags)); - TRY(write_value(compression_method)); - TRY(write_value(modification_time)); - TRY(write_value(modification_date)); - TRY(write_value(crc32)); - TRY(write_value(compressed_size)); - TRY(write_value(uncompressed_size)); - TRY(write_value(name_length)); - TRY(write_value(extra_data_length)); - if (name_length > 0) - TRY(stream.write_until_depleted({ name, name_length })); - if (extra_data_length > 0) - TRY(stream.write_until_depleted({ extra_data, extra_data_length })); - if (compressed_size > 0) - TRY(stream.write_until_depleted({ compressed_data, compressed_size })); - return {}; - } -}; - -struct ZipMember { - String name; - ReadonlyBytes compressed_data; // TODO: maybe the decompression/compression should be handled by LibArchive instead of the user? - ZipCompressionMethod compression_method; - u32 uncompressed_size; - u32 crc32; - bool is_directory; - DOSPackedTime modification_time; - DOSPackedDate modification_date; -}; - -class Zip { -public: - static Optional try_create(ReadonlyBytes buffer); - ErrorOr for_each_member(Function(ZipMember const&)>) const; - ErrorOr calculate_statistics() const; - -private: - static bool find_end_of_central_directory_offset(ReadonlyBytes, size_t& offset); - - Zip(u16 member_count, size_t members_start_offset, ReadonlyBytes input_data) - : m_member_count { member_count } - , m_members_start_offset { members_start_offset } - , m_input_data { input_data } - { - } - u16 m_member_count { 0 }; - size_t m_members_start_offset { 0 }; - ReadonlyBytes m_input_data; -}; - -class ZipOutputStream { -public: - struct MemberInformation { - float compression_ratio; - size_t compressed_size; - }; - - ZipOutputStream(NonnullOwnPtr); - - ErrorOr add_member(ZipMember const&); - ErrorOr add_member_from_stream(StringView, Stream&, Optional const& = {}); - - // NOTE: This does not add any of the files within the directory, - // it just adds an entry for it. - ErrorOr add_directory(StringView, Optional const& = {}); - - ErrorOr finish(); - -private: - NonnullOwnPtr m_stream; - Vector m_members; - - bool m_finished { false }; -}; - -} diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index 45d748f19c0..91022d1f073 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -378,7 +378,6 @@ endif() # Lagom Libraries set(lagom_standard_libraries - Archive Compress Crypto Diff diff --git a/Meta/Lagom/Fuzzers/FuzzTar.cpp b/Meta/Lagom/Fuzzers/FuzzTar.cpp deleted file mode 100644 index 33b9c6a2bba..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzTar.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2022, Idan Horowitz - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - - auto input_stream_or_error = try_make(ReadonlyBytes { data, size }); - - if (input_stream_or_error.is_error()) - return 0; - - auto tar_stream_or_error = Archive::TarInputStream::construct(input_stream_or_error.release_value()); - - if (tar_stream_or_error.is_error()) - return 0; - - auto tar_stream = tar_stream_or_error.release_value(); - - while (!tar_stream->finished()) { - auto const& header = tar_stream->header(); - - if (!header.content_is_like_extended_header()) { - if (tar_stream->advance().is_error()) - return 0; - else - continue; - } - - switch (header.type_flag()) { - case Archive::TarFileType::GlobalExtendedHeader: - case Archive::TarFileType::ExtendedHeader: { - auto result = tar_stream->for_each_extended_header([&](StringView, StringView) {}); - if (result.is_error()) - return 0; - break; - } - default: - return 0; - } - - if (tar_stream->advance().is_error()) - return 0; - } - - return 0; -} diff --git a/Meta/Lagom/Fuzzers/FuzzZip.cpp b/Meta/Lagom/Fuzzers/FuzzZip.cpp deleted file mode 100644 index 182699cac6c..00000000000 --- a/Meta/Lagom/Fuzzers/FuzzZip.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) -{ - AK::set_debug_enabled(false); - auto zip_file = Archive::Zip::try_create({ data, size }); - if (!zip_file.has_value()) - return 0; - - (void)zip_file->for_each_member([](auto&) { - return IterationDecision::Continue; - }); - - return 0; -} diff --git a/Meta/Lagom/Fuzzers/fuzzers.cmake b/Meta/Lagom/Fuzzers/fuzzers.cmake index 9d959b1d8f2..03a24b9eea4 100644 --- a/Meta/Lagom/Fuzzers/fuzzers.cmake +++ b/Meta/Lagom/Fuzzers/fuzzers.cmake @@ -29,7 +29,6 @@ set(FUZZER_TARGETS SHA256 SHA384 SHA512 - Tar TextDecoder TIFFLoader TinyVGLoader @@ -39,7 +38,6 @@ set(FUZZER_TARGETS WOFF WOFF2 XML - Zip ZlibDecompression ) @@ -62,7 +60,7 @@ set(FUZZER_DEPENDENCIES_ICCProfile LibGfx) set(FUZZER_DEPENDENCIES_ICOLoader LibGfx) set(FUZZER_DEPENDENCIES_JPEGLoader LibGfx) set(FUZZER_DEPENDENCIES_Js LibJS LibGC) -set(FUZZER_DEPENDENCIES_LzmaDecompression LibArchive LibCompress) +set(FUZZER_DEPENDENCIES_LzmaDecompression LibCompress) set(FUZZER_DEPENDENCIES_LzmaRoundtrip LibCompress) set(FUZZER_DEPENDENCIES_MatroskaReader LibMedia) set(FUZZER_DEPENDENCIES_MD5 LibCrypto) @@ -77,7 +75,6 @@ set(FUZZER_DEPENDENCIES_SHA1 LibCrypto) set(FUZZER_DEPENDENCIES_SHA256 LibCrypto) set(FUZZER_DEPENDENCIES_SHA384 LibCrypto) set(FUZZER_DEPENDENCIES_SHA512 LibCrypto) -set(FUZZER_DEPENDENCIES_Tar LibArchive) set(FUZZER_DEPENDENCIES_TextDecoder LibTextCodec) set(FUZZER_DEPENDENCIES_TIFFLoader LibGfx) set(FUZZER_DEPENDENCIES_TTF LibGfx) @@ -88,5 +85,4 @@ set(FUZZER_DEPENDENCIES_WebPLoader LibGfx) set(FUZZER_DEPENDENCIES_WOFF LibGfx) set(FUZZER_DEPENDENCIES_WOFF2 LibGfx) set(FUZZER_DEPENDENCIES_XML LibXML) -set(FUZZER_DEPENDENCIES_Zip LibArchive) set(FUZZER_DEPENDENCIES_ZlibDecompression LibCompress) diff --git a/README.md b/README.md index 282f4715641..e9107519420 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ At the moment, many core library support components are inherited from SerenityO - LibCrypto/LibTLS: Cryptography primitives and Transport Layer Security - LibHTTP: HTTP/1.1 client - LibGfx: 2D Graphics Library, Image Decoding and Rendering -- LibArchive: Archive file format support - LibUnicode: Unicode and locale support - LibMedia: Audio and video playback - LibCore: Event loop, OS abstraction layer diff --git a/UI/Android/CMakeLists.txt b/UI/Android/CMakeLists.txt index 115228792ce..2807a19a3e3 100644 --- a/UI/Android/CMakeLists.txt +++ b/UI/Android/CMakeLists.txt @@ -7,7 +7,7 @@ add_library(ladybird SHARED src/main/cpp/TimerExecutorService.cpp src/main/cpp/JNIHelpers.cpp ) -target_link_libraries(ladybird PRIVATE LibArchive jnigraphics android) +target_link_libraries(ladybird PRIVATE jnigraphics android) include(../cmake/AndroidExtras.cmake) create_ladybird_bundle(ladybird)