diff options
author | Tim Schumacher <timschumi@gmx.de> | 2023-04-05 16:41:05 +0200 |
---|---|---|
committer | Brian Gianforcaro <b.gianfo@gmail.com> | 2023-04-08 15:18:59 -0700 |
commit | e6b1e1bb3397e5371e08268fd1199295ea35f819 (patch) | |
tree | 89b2d419cd214c47bd8feae3b11b6326111797c4 /Userland/Libraries/LibCompress/Xz.cpp | |
parent | 68984abc43236a197e22b8228b456dc2058e538c (diff) | |
download | serenity-e6b1e1bb3397e5371e08268fd1199295ea35f819.zip |
LibCompress: Move finishing the current XZ stream into its own function
Diffstat (limited to 'Userland/Libraries/LibCompress/Xz.cpp')
-rw-r--r-- | Userland/Libraries/LibCompress/Xz.cpp | 201 |
1 files changed, 105 insertions, 96 deletions
diff --git a/Userland/Libraries/LibCompress/Xz.cpp b/Userland/Libraries/LibCompress/Xz.cpp index b0a00c340e..744f2a2bc6 100644 --- a/Userland/Libraries/LibCompress/Xz.cpp +++ b/Userland/Libraries/LibCompress/Xz.cpp @@ -301,6 +301,109 @@ ErrorOr<void> XzDecompressor::finish_current_block() return {}; } +ErrorOr<void> XzDecompressor::finish_current_stream() +{ + // We already read the Index Indicator (one byte) to determine that this is an Index. + auto start_of_current_block = m_stream->read_bytes() - 1; + + // 4.2. Number of Records: + // "This field indicates how many Records there are in the List + // of Records field, and thus how many Blocks there are in the + // Stream. The value is stored using the encoding described in + // Section 1.2." + u64 number_of_records = TRY(m_stream->read_value<XzMultibyteInteger>()); + + if (m_processed_blocks.size() != number_of_records) + return Error::from_string_literal("Number of Records in XZ Index does not match the number of processed Blocks"); + + // 4.3. List of Records: + // "List of Records consists of as many Records as indicated by the + // Number of Records field:" + for (u64 i = 0; i < number_of_records; i++) { + // "Each Record contains information about one Block: + // + // +===============+===================+ + // | Unpadded Size | Uncompressed Size | + // +===============+===================+" + + // 4.3.1. Unpadded Size: + // "This field indicates the size of the Block excluding the Block + // Padding field. That is, Unpadded Size is the size of the Block + // Header, Compressed Data, and Check fields. Unpadded Size is + // stored using the encoding described in Section 1.2." + u64 unpadded_size = TRY(m_stream->read_value<XzMultibyteInteger>()); + + // "The value MUST never be zero; with the current structure of Blocks, the + // actual minimum value for Unpadded Size is five." + if (unpadded_size < 5) + return Error::from_string_literal("XZ index contains a record with an unpadded size of less than five"); + + // 4.3.2. Uncompressed Size: + // "This field indicates the Uncompressed Size of the respective + // Block as bytes. The value is stored using the encoding + // described in Section 1.2." + u64 uncompressed_size = TRY(m_stream->read_value<XzMultibyteInteger>()); + + // 4.3. List of Records: + // "If the decoder has decoded all the Blocks of the Stream, it + // MUST verify that the contents of the Records match the real + // Unpadded Size and Uncompressed Size of the respective Blocks." + if (m_processed_blocks[i].uncompressed_size != uncompressed_size) + return Error::from_string_literal("Uncompressed size of XZ Block does not match the Index"); + + if (m_processed_blocks[i].unpadded_size != unpadded_size) + return Error::from_string_literal("Unpadded size of XZ Block does not match the Index"); + } + + // 4.4. Index Padding: + // "This field MUST contain 0-3 null bytes to pad the Index to + // a multiple of four bytes. If any of the bytes are not null + // bytes, the decoder MUST indicate an error." + while ((m_stream->read_bytes() - start_of_current_block) % 4 != 0) { + auto padding_byte = TRY(m_stream->read_value<u8>()); + + if (padding_byte != 0) + return Error::from_string_literal("XZ index contains a non-null padding byte"); + } + + // 4.5. CRC32: + // "The CRC32 is calculated over everything in the Index field + // except the CRC32 field itself. The CRC32 is stored as an + // unsigned 32-bit little endian integer." + u32 index_crc32 = TRY(m_stream->read_value<LittleEndian<u32>>()); + + // "If the calculated value does not match the stored one, the decoder MUST indicate + // an error." + // TODO: Validation of the index CRC32 is currently unimplemented. + (void)index_crc32; + + auto size_of_index = m_stream->read_bytes() - start_of_current_block; + + // According to the specification of a stream (2.1. Stream), the index is the last element in a stream, + // followed by the stream footer (2.1.2. Stream Footer). + auto stream_footer = TRY(m_stream->read_value<XzStreamFooter>()); + + // This handles verifying the CRC32 (2.1.2.1. CRC32) and the magic bytes (2.1.2.4. Footer Magic Bytes). + TRY(stream_footer.validate()); + + // 2.1.2.2. Backward Size: + // "If the stored value does not match the real size of the Index + // field, the decoder MUST indicate an error." + if (stream_footer.backward_size() != size_of_index) + return Error::from_string_literal("XZ index size does not match the stored size in the stream footer"); + + // 2.1.2.3. Stream Flags: + // "This is a copy of the Stream Flags field from the Stream + // Header. The information stored to Stream Flags is needed + // when parsing the Stream backwards. The decoder MUST compare + // the Stream Flags fields in both Stream Header and Stream + // Footer, and indicate an error if they are not identical." + if (Bytes { &*m_stream_flags, sizeof(XzStreamFlags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) }) + return Error::from_string_literal("XZ stream header flags don't match the stream footer"); + + return {}; +} + ErrorOr<Bytes> XzDecompressor::read_some(Bytes bytes) { if (!m_stream_flags.has_value()) { @@ -324,102 +427,8 @@ ErrorOr<Bytes> XzDecompressor::read_some(Bytes bytes) auto encoded_block_header_size_or_index_indicator = TRY(m_stream->read_value<u8>()); if (encoded_block_header_size_or_index_indicator == 0x00) { - // This is an Index. - - // 4.2. Number of Records: - // "This field indicates how many Records there are in the List - // of Records field, and thus how many Blocks there are in the - // Stream. The value is stored using the encoding described in - // Section 1.2." - u64 number_of_records = TRY(m_stream->read_value<XzMultibyteInteger>()); - - if (m_processed_blocks.size() != number_of_records) - return Error::from_string_literal("Number of Records in XZ Index does not match the number of processed Blocks"); - - // 4.3. List of Records: - // "List of Records consists of as many Records as indicated by the - // Number of Records field:" - for (u64 i = 0; i < number_of_records; i++) { - // "Each Record contains information about one Block: - // - // +===============+===================+ - // | Unpadded Size | Uncompressed Size | - // +===============+===================+" - - // 4.3.1. Unpadded Size: - // "This field indicates the size of the Block excluding the Block - // Padding field. That is, Unpadded Size is the size of the Block - // Header, Compressed Data, and Check fields. Unpadded Size is - // stored using the encoding described in Section 1.2." - u64 unpadded_size = TRY(m_stream->read_value<XzMultibyteInteger>()); - - // "The value MUST never be zero; with the current structure of Blocks, the - // actual minimum value for Unpadded Size is five." - if (unpadded_size < 5) - return Error::from_string_literal("XZ index contains a record with an unpadded size of less than five"); - - // 4.3.2. Uncompressed Size: - // "This field indicates the Uncompressed Size of the respective - // Block as bytes. The value is stored using the encoding - // described in Section 1.2." - u64 uncompressed_size = TRY(m_stream->read_value<XzMultibyteInteger>()); - - // 4.3. List of Records: - // "If the decoder has decoded all the Blocks of the Stream, it - // MUST verify that the contents of the Records match the real - // Unpadded Size and Uncompressed Size of the respective Blocks." - if (m_processed_blocks[i].uncompressed_size != uncompressed_size) - return Error::from_string_literal("Uncompressed size of XZ Block does not match the Index"); - - if (m_processed_blocks[i].unpadded_size != unpadded_size) - return Error::from_string_literal("Unpadded size of XZ Block does not match the Index"); - } - - // 4.4. Index Padding: - // "This field MUST contain 0-3 null bytes to pad the Index to - // a multiple of four bytes. If any of the bytes are not null - // bytes, the decoder MUST indicate an error." - while ((m_stream->read_bytes() - start_of_current_block) % 4 != 0) { - auto padding_byte = TRY(m_stream->read_value<u8>()); - - if (padding_byte != 0) - return Error::from_string_literal("XZ index contains a non-null padding byte"); - } - - // 4.5. CRC32: - // "The CRC32 is calculated over everything in the Index field - // except the CRC32 field itself. The CRC32 is stored as an - // unsigned 32-bit little endian integer." - u32 index_crc32 = TRY(m_stream->read_value<LittleEndian<u32>>()); - - // "If the calculated value does not match the stored one, the decoder MUST indicate - // an error." - // TODO: Validation of the index CRC32 is currently unimplemented. - (void)index_crc32; - - auto size_of_index = m_stream->read_bytes() - start_of_current_block; - - // According to the specification of a stream (2.1. Stream), the index is the last element in a stream, - // followed by the stream footer (2.1.2. Stream Footer). - auto stream_footer = TRY(m_stream->read_value<XzStreamFooter>()); - - // This handles verifying the CRC32 (2.1.2.1. CRC32) and the magic bytes (2.1.2.4. Footer Magic Bytes). - TRY(stream_footer.validate()); - - // 2.1.2.2. Backward Size: - // "If the stored value does not match the real size of the Index - // field, the decoder MUST indicate an error." - if (stream_footer.backward_size() != size_of_index) - return Error::from_string_literal("XZ index size does not match the stored size in the stream footer"); - - // 2.1.2.3. Stream Flags: - // "This is a copy of the Stream Flags field from the Stream - // Header. The information stored to Stream Flags is needed - // when parsing the Stream backwards. The decoder MUST compare - // the Stream Flags fields in both Stream Header and Stream - // Footer, and indicate an error if they are not identical." - if (Bytes { &*m_stream_flags, sizeof(XzStreamFlags) } != Bytes { &stream_footer.flags, sizeof(stream_footer.flags) }) - return Error::from_string_literal("XZ stream header flags don't match the stream footer"); + // This is an Index, which is the last element before the stream footer. + TRY(finish_current_stream()); // Another XZ Stream might follow, so we just unset the current information and continue on the next read. m_stream_flags.clear(); |