diff --git a/Userland/Libraries/LibArchive/Tar.cpp b/Userland/Libraries/LibArchive/Tar.cpp index a5758d93af..a91fc1fdff 100644 --- a/Userland/Libraries/LibArchive/Tar.cpp +++ b/Userland/Libraries/LibArchive/Tar.cpp @@ -30,6 +30,16 @@ void TarFileHeader::calculate_checksum() VERIFY(String::formatted("{:06o}", expected_checksum()).copy_characters_to_buffer(m_checksum, sizeof(m_checksum))); } +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; diff --git a/Userland/Libraries/LibArchive/Tar.h b/Userland/Libraries/LibArchive/Tar.h index 6b9ddd86a1..6600be810c 100644 --- a/Userland/Libraries/LibArchive/Tar.h +++ b/Userland/Libraries/LibArchive/Tar.h @@ -129,6 +129,7 @@ public: unsigned expected_checksum() const; void calculate_checksum(); + bool is_zero_block() const; bool content_is_like_extended_header() const; void set_filename_and_prefix(StringView filename); diff --git a/Userland/Libraries/LibArchive/TarStream.cpp b/Userland/Libraries/LibArchive/TarStream.cpp index 87928ffac8..7810d0bc52 100644 --- a/Userland/Libraries/LibArchive/TarStream.cpp +++ b/Userland/Libraries/LibArchive/TarStream.cpp @@ -90,16 +90,30 @@ ErrorOr TarInputStream::advance() ErrorOr TarInputStream::load_next_header() { - auto header_span = TRY(m_stream->read(Bytes(&m_header, sizeof(m_header)))); - if (header_span.size() != sizeof(m_header)) - return Error::from_string_literal("Failed to read the entire header"); + size_t number_of_consecutive_zero_blocks = 0; + while (true) { + auto header_span = TRY(m_stream->read(Bytes(&m_header, sizeof(m_header)))); + if (header_span.size() != sizeof(m_header)) + return Error::from_string_literal("Failed to read the entire header"); + + // 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"); - // Discard the rest of the header block. - TRY(m_stream->discard(block_size - sizeof(TarFileHeader))); - return {}; } diff --git a/Userland/Libraries/LibArchive/TarStream.h b/Userland/Libraries/LibArchive/TarStream.h index 46fd6e44ef..d779ea55fe 100644 --- a/Userland/Libraries/LibArchive/TarStream.h +++ b/Userland/Libraries/LibArchive/TarStream.h @@ -35,7 +35,7 @@ class TarInputStream { public: static ErrorOr> construct(NonnullOwnPtr); ErrorOr advance(); - bool finished() const { return m_stream->is_eof(); } + 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(); @@ -51,6 +51,7 @@ private: NonnullOwnPtr m_stream; unsigned long m_file_offset { 0 }; int m_generation { 0 }; + bool m_found_end_of_archive { false }; friend class TarFileStream; };