LibArchive: Implement proper support for Tar file end markers

Previously this was handled implicitly, as our implementation of Tar
would just stop processing input as soon as it found something invalid.
However, since we now error out as soon as something is found to be
wrong, we require proper handling for zero blocks, which aren't actually
fatal.
This commit is contained in:
Tim Schumacher 2022-11-29 01:01:13 +01:00 committed by Andreas Kling
parent cb48b9bc30
commit 714f0c3dce
Notes: sideshowbarker 2024-07-17 06:35:16 +09:00
4 changed files with 33 additions and 7 deletions

View file

@ -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<u8 const*>(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;

View file

@ -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);

View file

@ -90,16 +90,30 @@ ErrorOr<void> TarInputStream::advance()
ErrorOr<void> 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 {};
}

View file

@ -35,7 +35,7 @@ class TarInputStream {
public:
static ErrorOr<NonnullOwnPtr<TarInputStream>> construct(NonnullOwnPtr<Core::Stream::Stream>);
ErrorOr<void> advance();
bool finished() const { return m_stream->is_eof(); }
bool finished() const { return m_found_end_of_archive || m_stream->is_eof(); }
ErrorOr<bool> valid() const;
TarFileHeader const& header() const { return m_header; }
TarFileStream file_contents();
@ -51,6 +51,7 @@ private:
NonnullOwnPtr<Core::Stream::Stream> m_stream;
unsigned long m_file_offset { 0 };
int m_generation { 0 };
bool m_found_end_of_archive { false };
friend class TarFileStream;
};