From 3bfd4e903934a320136276a4ea307de8a61daf23 Mon Sep 17 00:00:00 2001 From: Tucker Willenborg Date: Wed, 24 Jun 2026 18:38:41 -0400 Subject: [PATCH 1/4] Add support for decmpfs compression types 9 and 10 (stored/uncompressed) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Types 9 (xattr-inline) and 10 (resource-fork chunked) store file data verbatim rather than compressing it. Both use a 0xCC sentinel as the first byte of the payload to distinguish them from actual compressed streams; all subsequent bytes are raw plaintext. Changes: - definitions.h.in: add LIBFSAPFS_COMPRESSION_METHOD_STORED = 4 - file_entry.c: dispatch cases 9 and 10 to STORED; add type 10 to the resource-fork routing condition (even-numbered types use rsrc fork) - compression.c: add STORED decompressor that validates the 0xCC sentinel and memcpys the remaining bytes, mirroring the existing LZVN 0x06 stored-chunk path - compressed_data_handle.c: add STORED to the compression method allowlist in libfsapfs_compressed_data_handle_initialize Empirical evidence (macOS 26.5.1, no-SIP VM + dtrace): - ARM64e kernel disassembly of the type-9 READ path shows a 3-instruction sequence ending in BL uiomove64 with zero CMP #0xCC in the entire function — the kernel takes a pure memcpy path, no decompressor involved. - dtrace apfs_vnop_getxattr tracing confirms the kernel never consults com.apple.ResourceFork for type-9 reads; only com.apple.decmpfs is read. - Any first byte other than 0xCC causes the kernel to return 0 bytes (decmpfs_validate_compressed_file fails to populate the cnode). - Type 9 max payload: 3785 bytes (APFS inline xattr limit of 3804 bytes minus 2 bytes xv_flags, 16 bytes decmpfs header, 1 byte sentinel). - Type 10 confirmed functional with correct chunk table format (table_size = (N+1)*4, chunk_ends[] = absolute end offsets from rsrc start). - 0 type-10 files observed in 662K macOS 26.5.1 system files (full scan via XATTR_SHOWCOMPRESSION); type 9 has 545 files in the same scan. --- libfsapfs/libfsapfs_compressed_data_handle.c | 4 +- libfsapfs/libfsapfs_compression.c | 66 ++++++++++++++++++++ libfsapfs/libfsapfs_definitions.h.in | 1 + libfsapfs/libfsapfs_file_entry.c | 8 ++- 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/libfsapfs/libfsapfs_compressed_data_handle.c b/libfsapfs/libfsapfs_compressed_data_handle.c index 93486a07..98594c1c 100644 --- a/libfsapfs/libfsapfs_compressed_data_handle.c +++ b/libfsapfs/libfsapfs_compressed_data_handle.c @@ -82,6 +82,7 @@ int libfsapfs_compressed_data_handle_initialize( } if( ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_DEFLATE ) && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_LZVN ) + && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_STORED ) && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_UNKNOWN5 ) ) { libcerror_error_set( @@ -459,7 +460,8 @@ int libfsapfs_compressed_data_handle_get_compressed_block_offsets( compressed_descriptors_offset += 4; compressed_block_descriptor_size = 8; } - else if( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_LZVN ) + else if( ( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_LZVN ) + || ( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_STORED ) ) { segment_data_offset = 0; diff --git a/libfsapfs/libfsapfs_compression.c b/libfsapfs/libfsapfs_compression.c index aac92649..0533e022 100644 --- a/libfsapfs/libfsapfs_compression.c +++ b/libfsapfs/libfsapfs_compression.c @@ -274,6 +274,72 @@ int libfsapfs_decompress_data( #endif /* ( defined( HAVE_ZLIB ) && defined( HAVE_ZLIB_UNCOMPRESS ) ) || defined( ZLIB_DLL ) */ } } + else if( compression_method == LIBFSAPFS_COMPRESSION_METHOD_STORED ) + { + /* Types 9 and 10 store data verbatim after a 0xCC sentinel byte */ + if( ( compressed_data_size < 1 ) + || ( compressed_data[ 0 ] != 0xcc ) ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, + "%s: invalid stored data: missing 0xCC sentinel byte.", + function ); + + return( -1 ); + } + if( compressed_data_size > (size_t) SSIZE_MAX ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_ARGUMENTS, + LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM, + "%s: invalid compressed data size value exceeds maximum.", + function ); + + return( -1 ); + } + if( *uncompressed_data_size > (size_t) SSIZE_MAX ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_ARGUMENTS, + LIBCERROR_ARGUMENT_ERROR_VALUE_EXCEEDS_MAXIMUM, + "%s: invalid uncompressed data size value exceeds maximum.", + function ); + + return( -1 ); + } + if( ( compressed_data_size - 1 ) > *uncompressed_data_size ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_RUNTIME, + LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, + "%s: compressed data size value exceeds uncompressed data size.", + function ); + + return( -1 ); + } + *uncompressed_data_size = compressed_data_size - 1; + + if( memory_copy( + uncompressed_data, + &( compressed_data[ 1 ] ), + *uncompressed_data_size ) == NULL ) + { + libcerror_error_set( + error, + LIBCERROR_ERROR_DOMAIN_MEMORY, + LIBCERROR_MEMORY_ERROR_COPY_FAILED, + "%s: unable to copy stored data.", + function ); + + return( -1 ); + } + result = 1; + } #ifdef TODO /* TODO need sample data */ else if( compression_method == LIBFSAPFS_COMPRESSION_METHOD_LZFSE ) diff --git a/libfsapfs/libfsapfs_definitions.h.in b/libfsapfs/libfsapfs_definitions.h.in index 147715ee..195d3b48 100644 --- a/libfsapfs/libfsapfs_definitions.h.in +++ b/libfsapfs/libfsapfs_definitions.h.in @@ -90,6 +90,7 @@ enum LIBFSAPFS_COMPRESSION_METHODS LIBFSAPFS_COMPRESSION_METHOD_DEFLATE = 1, LIBFSAPFS_COMPRESSION_METHOD_LZFSE = 2, LIBFSAPFS_COMPRESSION_METHOD_LZVN = 3, + LIBFSAPFS_COMPRESSION_METHOD_STORED = 4, LIBFSAPFS_COMPRESSION_METHOD_UNKNOWN5 = 5 }; diff --git a/libfsapfs/libfsapfs_file_entry.c b/libfsapfs/libfsapfs_file_entry.c index 657356da..00f9fe42 100644 --- a/libfsapfs/libfsapfs_file_entry.c +++ b/libfsapfs/libfsapfs_file_entry.c @@ -4357,6 +4357,11 @@ int libfsapfs_internal_file_entry_get_data_stream( compression_method = LIBFSAPFS_COMPRESSION_METHOD_LZVN; break; + case 9: + case 10: + compression_method = LIBFSAPFS_COMPRESSION_METHOD_STORED; + break; + case 11: case 12: compression_method = LIBFSAPFS_COMPRESSION_METHOD_LZFSE; @@ -4374,7 +4379,8 @@ int libfsapfs_internal_file_entry_get_data_stream( goto on_error; } if( ( internal_file_entry->compressed_data_header->compression_method == 4 ) - || ( internal_file_entry->compressed_data_header->compression_method == 8 ) ) + || ( internal_file_entry->compressed_data_header->compression_method == 8 ) + || ( internal_file_entry->compressed_data_header->compression_method == 10 ) ) { if( libfsapfs_attributes_get_data_stream( internal_file_entry->resource_fork_attribute_values, From 65452665b135a23c0317a5c8db16e315e69ea989 Mon Sep 17 00:00:00 2001 From: Tucker Willenborg Date: Wed, 24 Jun 2026 18:38:41 -0400 Subject: [PATCH 2/4] tests: add STORED (types 9/10) test cases fsapfs_test_compression.c (libfsapfs_decompress_data): - valid 0xCC sentinel + payload, with output verified via memory_compare - bad sentinel -> -1 with error set - SSIZE_MAX and zero-size bounds cases, plus a memcpy-failure path - mirrors the existing DEFLATE and LZVN case structure fsapfs_test_compressed_data_handle.c: - get_compressed_block_offsets STORED cases for the type-9 fpmc single-block path and the type-10 chunk-table path - read_segment_data STORED cases for both paths, with decompressed bytes verified via memory_compare Verified with the compression and compressed_data_handle test programs passing on macOS / ARM64. --- tests/fsapfs_test_compressed_data_handle.c | 333 +++++++++++++++++++++ tests/fsapfs_test_compression.c | 171 +++++++++++ 2 files changed, 504 insertions(+) diff --git a/tests/fsapfs_test_compressed_data_handle.c b/tests/fsapfs_test_compressed_data_handle.c index c9e23384..867a1e4f 100644 --- a/tests/fsapfs_test_compressed_data_handle.c +++ b/tests/fsapfs_test_compressed_data_handle.c @@ -21,6 +21,7 @@ #include #include +#include #include #if defined( HAVE_STDLIB_H ) || defined( WINAPI ) @@ -43,6 +44,23 @@ uint8_t fsapfs_test_compressed_data_handle_lzvn_compressed_data1[ 35 ] = { 0xe0, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x06 }; +/* Type 9 (stored xattr): fpmc header (method=9, uncompressed_size=16) + 0xCC sentinel + 16 bytes raw */ +uint8_t fsapfs_test_compressed_data_handle_stored_compressed_data1[ 33 ] = { + 0x66, 0x70, 0x6d, 0x63, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f }; + +/* Type 10 (stored rsrc): 1-chunk LZVN-style table + 0xCC sentinel + 16 bytes raw. + * table_size=8 => (N+1)*4 for N=1 chunk; chunk_end[0]=25 (absolute offset from rsrc start). */ +uint8_t fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1[ 25 ] = { + 0x08, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f }; + +/* Expected plaintext for both stored test streams (the 0xCC sentinel and any table stripped) */ +uint8_t fsapfs_test_compressed_data_handle_stored_uncompressed_data1[ 16 ] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + #if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) /* Tests the libfsapfs_compressed_data_handle_initialize function @@ -463,6 +481,162 @@ int fsapfs_test_compressed_data_handle_get_compressed_block_offsets( "error", error ); + /* Test STORED (type 9 xattr): single fpmc block, 0xCC sentinel + raw payload + */ + { + libfdata_stream_t *stored_stream = NULL; + libfsapfs_compressed_data_handle_t *stored_compressed_data_handle = NULL; + + result = libfsapfs_data_stream_initialize_from_data( + &stored_stream, + fsapfs_test_compressed_data_handle_stored_compressed_data1, + 33, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "stored_stream", + stored_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &stored_compressed_data_handle, + stored_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "stored_compressed_data_handle", + stored_compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( + stored_compressed_data_handle, + NULL, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_free( + &stored_compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfdata_stream_free( + &stored_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + } + + /* Test STORED (type 10 rsrc): 4-byte LE chunk table (same path as LZVN), 0xCC chunks + */ + { + libfdata_stream_t *stored_rsrc_stream = NULL; + libfsapfs_compressed_data_handle_t *stored_rsrc_compressed_data_handle = NULL; + + result = libfsapfs_data_stream_initialize_from_data( + &stored_rsrc_stream, + fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1, + 25, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "stored_rsrc_stream", + stored_rsrc_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &stored_rsrc_compressed_data_handle, + stored_rsrc_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "stored_rsrc_compressed_data_handle", + stored_rsrc_compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( + stored_rsrc_compressed_data_handle, + NULL, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_free( + &stored_rsrc_compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfdata_stream_free( + &stored_rsrc_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + } + /* Test error cases */ result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( @@ -599,6 +773,165 @@ int fsapfs_test_compressed_data_handle_read_segment_data( /* Test regular cases */ + /* Test STORED: read 16 bytes of 0xCC-prefixed plaintext via the fpmc single-block path + */ + { + uint8_t stored_segment_data[ 16 ]; + + libfdata_stream_t *stored_stream = NULL; + libfsapfs_compressed_data_handle_t *stored_compressed_data_handle = NULL; + + result = libfsapfs_data_stream_initialize_from_data( + &stored_stream, + fsapfs_test_compressed_data_handle_stored_compressed_data1, + 33, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfsapfs_compressed_data_handle_initialize( + &stored_compressed_data_handle, + stored_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + read_count = libfsapfs_compressed_data_handle_read_segment_data( + stored_compressed_data_handle, + NULL, + 0, + 0, + stored_segment_data, + 16, + 0, + 0, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_SSIZE( + "read_count", + read_count, + (ssize_t) 16 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + stored_segment_data, + fsapfs_test_compressed_data_handle_stored_uncompressed_data1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = libfsapfs_compressed_data_handle_free( + &stored_compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfdata_stream_free( + &stored_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + } + /* Test STORED rsrc: read 16 bytes of 0xCC-prefixed plaintext via the chunk table path + */ + { + uint8_t stored_rsrc_segment_data[ 16 ]; + + libfdata_stream_t *stored_rsrc_stream = NULL; + libfsapfs_compressed_data_handle_t *stored_rsrc_compressed_data_handle = NULL; + + result = libfsapfs_data_stream_initialize_from_data( + &stored_rsrc_stream, + fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1, + 25, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfsapfs_compressed_data_handle_initialize( + &stored_rsrc_compressed_data_handle, + stored_rsrc_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + read_count = libfsapfs_compressed_data_handle_read_segment_data( + stored_rsrc_compressed_data_handle, + NULL, + 0, + 0, + stored_rsrc_segment_data, + 16, + 0, + 0, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_SSIZE( + "read_count", + read_count, + (ssize_t) 16 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + stored_rsrc_segment_data, + fsapfs_test_compressed_data_handle_stored_uncompressed_data1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + result = libfsapfs_compressed_data_handle_free( + &stored_rsrc_compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + result = libfdata_stream_free( + &stored_rsrc_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + } + /* Test error cases */ read_count = libfsapfs_compressed_data_handle_read_segment_data( diff --git a/tests/fsapfs_test_compression.c b/tests/fsapfs_test_compression.c index c2672016..cf328c47 100644 --- a/tests/fsapfs_test_compression.c +++ b/tests/fsapfs_test_compression.c @@ -56,6 +56,16 @@ uint8_t fsapfs_test_compression_lzvn_uncompressed_data1[ 17 ] = { uint8_t fsapfs_test_compression_uncompressed_data1[ 16 ] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; +/* Type 9/10 stored: 0xCC sentinel + 16 bytes of raw data */ +uint8_t fsapfs_test_compression_stored_data1[ 17 ] = { + 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f }; + +/* Bad sentinel — first byte is not 0xCC */ +uint8_t fsapfs_test_compression_stored_bad_sentinel[ 17 ] = { + 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f }; + #if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) /* Tests the libfsapfs_decompress_data function @@ -208,6 +218,65 @@ int fsapfs_test_decompress_data( result, 0 ); + /* Test STORED (types 9 and 10): 0xCC sentinel + verbatim data + */ + uncompressed_data_size = 16; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_data1, + 17, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_EQUAL_SIZE( + "uncompressed_data_size", + uncompressed_data_size, + (size_t) 16 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + uncompressed_data, + fsapfs_test_compression_uncompressed_data1, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + /* Bad sentinel — must return error */ + uncompressed_data_size = 16; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_bad_sentinel, + 17, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + /* Test error cases */ uncompressed_data_size = 16; @@ -481,6 +550,72 @@ int fsapfs_test_decompress_data( libcerror_error_free( &error ); + uncompressed_data_size = 16; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_data1, + (size_t) SSIZE_MAX + 1, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + uncompressed_data_size = 0; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_data1, + 17, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + + uncompressed_data_size = (size_t) SSIZE_MAX + 1; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_data1, + 17, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + #if defined( HAVE_FSAPFS_TEST_MEMORY ) && defined( OPTIMIZATION_DISABLED ) uncompressed_data_size = 16; @@ -517,6 +652,42 @@ int fsapfs_test_decompress_data( } #endif /* defined( HAVE_FSAPFS_TEST_MEMORY ) && defined( OPTIMIZATION_DISABLED ) */ +#if defined( HAVE_FSAPFS_TEST_MEMORY ) && defined( OPTIMIZATION_DISABLED ) + + uncompressed_data_size = 16; + + /* Test libfsapfs_decompress_data with memcpy failing + */ + fsapfs_test_memcpy_attempts_before_fail = 0; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_stored_data1, + 17, + LIBFSAPFS_COMPRESSION_METHOD_STORED, + uncompressed_data, + &uncompressed_data_size, + &error ); + + if( fsapfs_test_memcpy_attempts_before_fail != -1 ) + { + fsapfs_test_memcpy_attempts_before_fail = -1; + } + else + { + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + } +#endif /* defined( HAVE_FSAPFS_TEST_MEMORY ) && defined( OPTIMIZATION_DISABLED ) */ + return( 1 ); on_error: From 1d2dd79752420bbe755a11207131e527ddd69725 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Sun, 2 Aug 2026 12:55:46 +0200 Subject: [PATCH 3/4] Changes after review --- libfsapfs/libfsapfs_compressed_data_handle.c | 4 +- libfsapfs/libfsapfs_compression.c | 3 +- libfsapfs/libfsapfs_definitions.h.in | 2 +- libfsapfs/libfsapfs_file_entry.c | 2 +- tests/fsapfs_test_compressed_data_handle.c | 825 ++++++++++++------- tests/fsapfs_test_compression.c | 81 +- 6 files changed, 589 insertions(+), 328 deletions(-) diff --git a/libfsapfs/libfsapfs_compressed_data_handle.c b/libfsapfs/libfsapfs_compressed_data_handle.c index 98594c1c..8724e2a7 100644 --- a/libfsapfs/libfsapfs_compressed_data_handle.c +++ b/libfsapfs/libfsapfs_compressed_data_handle.c @@ -82,7 +82,7 @@ int libfsapfs_compressed_data_handle_initialize( } if( ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_DEFLATE ) && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_LZVN ) - && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_STORED ) + && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_RAW ) && ( compression_method != LIBFSAPFS_COMPRESSION_METHOD_UNKNOWN5 ) ) { libcerror_error_set( @@ -461,7 +461,7 @@ int libfsapfs_compressed_data_handle_get_compressed_block_offsets( compressed_block_descriptor_size = 8; } else if( ( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_LZVN ) - || ( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_STORED ) ) + || ( data_handle->compression_method == LIBFSAPFS_COMPRESSION_METHOD_RAW ) ) { segment_data_offset = 0; diff --git a/libfsapfs/libfsapfs_compression.c b/libfsapfs/libfsapfs_compression.c index 0533e022..dd7e10c0 100644 --- a/libfsapfs/libfsapfs_compression.c +++ b/libfsapfs/libfsapfs_compression.c @@ -274,9 +274,8 @@ int libfsapfs_decompress_data( #endif /* ( defined( HAVE_ZLIB ) && defined( HAVE_ZLIB_UNCOMPRESS ) ) || defined( ZLIB_DLL ) */ } } - else if( compression_method == LIBFSAPFS_COMPRESSION_METHOD_STORED ) + else if( compression_method == LIBFSAPFS_COMPRESSION_METHOD_RAW ) { - /* Types 9 and 10 store data verbatim after a 0xCC sentinel byte */ if( ( compressed_data_size < 1 ) || ( compressed_data[ 0 ] != 0xcc ) ) { diff --git a/libfsapfs/libfsapfs_definitions.h.in b/libfsapfs/libfsapfs_definitions.h.in index 195d3b48..cb21b715 100644 --- a/libfsapfs/libfsapfs_definitions.h.in +++ b/libfsapfs/libfsapfs_definitions.h.in @@ -90,7 +90,7 @@ enum LIBFSAPFS_COMPRESSION_METHODS LIBFSAPFS_COMPRESSION_METHOD_DEFLATE = 1, LIBFSAPFS_COMPRESSION_METHOD_LZFSE = 2, LIBFSAPFS_COMPRESSION_METHOD_LZVN = 3, - LIBFSAPFS_COMPRESSION_METHOD_STORED = 4, + LIBFSAPFS_COMPRESSION_METHOD_RAW = 4, LIBFSAPFS_COMPRESSION_METHOD_UNKNOWN5 = 5 }; diff --git a/libfsapfs/libfsapfs_file_entry.c b/libfsapfs/libfsapfs_file_entry.c index 00f9fe42..0983a1f6 100644 --- a/libfsapfs/libfsapfs_file_entry.c +++ b/libfsapfs/libfsapfs_file_entry.c @@ -4359,7 +4359,7 @@ int libfsapfs_internal_file_entry_get_data_stream( case 9: case 10: - compression_method = LIBFSAPFS_COMPRESSION_METHOD_STORED; + compression_method = LIBFSAPFS_COMPRESSION_METHOD_RAW; break; case 11: diff --git a/tests/fsapfs_test_compressed_data_handle.c b/tests/fsapfs_test_compressed_data_handle.c index 867a1e4f..bd2b6530 100644 --- a/tests/fsapfs_test_compressed_data_handle.c +++ b/tests/fsapfs_test_compressed_data_handle.c @@ -39,27 +39,25 @@ #include "../libfsapfs/libfsapfs_data_stream.h" #include "../libfsapfs/libfsapfs_definitions.h" +/* Single chunk (7) LZVN compresssed data + */ uint8_t fsapfs_test_compressed_data_handle_lzvn_compressed_data1[ 35 ] = { 0x66, 0x70, 0x6d, 0x63, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x06 }; -/* Type 9 (stored xattr): fpmc header (method=9, uncompressed_size=16) + 0xCC sentinel + 16 bytes raw */ -uint8_t fsapfs_test_compressed_data_handle_stored_compressed_data1[ 33 ] = { +/* Single chunk raw (9) compresssed data + */ +uint8_t fsapfs_test_compressed_data_handle_raw_chunk_compressed_data1[ 33 ] = { 0x66, 0x70, 0x6d, 0x63, 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; -/* Type 10 (stored rsrc): 1-chunk LZVN-style table + 0xCC sentinel + 16 bytes raw. - * table_size=8 => (N+1)*4 for N=1 chunk; chunk_end[0]=25 (absolute offset from rsrc start). */ -uint8_t fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1[ 25 ] = { - 0x08, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f }; - -/* Expected plaintext for both stored test streams (the 0xCC sentinel and any table stripped) */ -uint8_t fsapfs_test_compressed_data_handle_stored_uncompressed_data1[ 16 ] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; +/* Multi chunk raw (10) compresssed data + */ +uint8_t fsapfs_test_compressed_data_handle_raw_compressed_data1[ 25 ] = { + 0x08, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; #if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) @@ -481,161 +479,268 @@ int fsapfs_test_compressed_data_handle_get_compressed_block_offsets( "error", error ); - /* Test STORED (type 9 xattr): single fpmc block, 0xCC sentinel + raw payload + /* Clean up */ - { - libfdata_stream_t *stored_stream = NULL; - libfsapfs_compressed_data_handle_t *stored_compressed_data_handle = NULL; + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); - result = libfsapfs_data_stream_initialize_from_data( - &stored_stream, - fsapfs_test_compressed_data_handle_stored_compressed_data1, - 33, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); - FSAPFS_TEST_ASSERT_IS_NOT_NULL( - "stored_stream", - stored_stream ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + result = libfdata_stream_free( + &compressed_data_stream, + &error ); - result = libfsapfs_compressed_data_handle_initialize( - &stored_compressed_data_handle, - stored_stream, - 16, - LIBFSAPFS_COMPRESSION_METHOD_STORED, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); - FSAPFS_TEST_ASSERT_IS_NOT_NULL( - "stored_compressed_data_handle", - stored_compressed_data_handle ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_raw_chunk_compressed_data1, + 33, + &error ); - result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( - stored_compressed_data_handle, - NULL, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - result = libfsapfs_compressed_data_handle_free( - &stored_compressed_data_handle, - &error ); + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_RAW, + &error ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - result = libfdata_stream_free( - &stored_stream, - &error ); + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - } + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - /* Test STORED (type 10 rsrc): 4-byte LE chunk table (same path as LZVN), 0xCC chunks + /* Test regular cases */ - { - libfdata_stream_t *stored_rsrc_stream = NULL; - libfsapfs_compressed_data_handle_t *stored_rsrc_compressed_data_handle = NULL; + result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( + compressed_data_handle, + NULL, + &error ); - result = libfsapfs_data_stream_initialize_from_data( - &stored_rsrc_stream, - fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1, - 25, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_IS_NOT_NULL( - "stored_rsrc_stream", - stored_rsrc_stream ); + /* Clean up + */ + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - result = libfsapfs_compressed_data_handle_initialize( - &stored_rsrc_compressed_data_handle, - stored_rsrc_stream, - 16, - LIBFSAPFS_COMPRESSION_METHOD_STORED, - &error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_IS_NOT_NULL( - "stored_rsrc_compressed_data_handle", - stored_rsrc_compressed_data_handle ); + result = libfdata_stream_free( + &compressed_data_stream, + &error ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( - stored_rsrc_compressed_data_handle, - NULL, - &error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_raw_compressed_data1, + 25, + &error ); - result = libfsapfs_compressed_data_handle_free( - &stored_rsrc_compressed_data_handle, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); - result = libfdata_stream_free( - &stored_rsrc_stream, - &error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - } + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_RAW, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular cases + */ + result = libfsapfs_compressed_data_handle_get_compressed_block_offsets( + compressed_data_handle, + NULL, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Clean up + */ + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfdata_stream_free( + &compressed_data_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_lzvn_compressed_data1, + 35, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_LZVN, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); /* Test error cases */ @@ -721,6 +826,9 @@ int fsapfs_test_compressed_data_handle_get_compressed_block_offsets( int fsapfs_test_compressed_data_handle_read_segment_data( void ) { + uint8_t expected_segment_data2[ 16 ] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; + uint8_t segment_data[ 32 ]; libcerror_error_t *error = NULL; @@ -772,165 +880,322 @@ int fsapfs_test_compressed_data_handle_read_segment_data( /* Test regular cases */ + read_count = libfsapfs_compressed_data_handle_read_segment_data( + compressed_data_handle, + NULL, + 0, + 0, + segment_data, + 16, + 0, + 0, + &error ); - /* Test STORED: read 16 bytes of 0xCC-prefixed plaintext via the fpmc single-block path - */ - { - uint8_t stored_segment_data[ 16 ]; - - libfdata_stream_t *stored_stream = NULL; - libfsapfs_compressed_data_handle_t *stored_compressed_data_handle = NULL; - - result = libfsapfs_data_stream_initialize_from_data( - &stored_stream, - fsapfs_test_compressed_data_handle_stored_compressed_data1, - 33, - &error ); - - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - - result = libfsapfs_compressed_data_handle_initialize( - &stored_compressed_data_handle, - stored_stream, - 16, - LIBFSAPFS_COMPRESSION_METHOD_STORED, - &error ); - - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - - read_count = libfsapfs_compressed_data_handle_read_segment_data( - stored_compressed_data_handle, - NULL, - 0, - 0, - stored_segment_data, - 16, - 0, - 0, - &error ); - - FSAPFS_TEST_ASSERT_EQUAL_SSIZE( - "read_count", - read_count, - (ssize_t) 16 ); - - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); - - result = memory_compare( - stored_segment_data, - fsapfs_test_compressed_data_handle_stored_uncompressed_data1, - 16 ); - - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 0 ); - - result = libfsapfs_compressed_data_handle_free( - &stored_compressed_data_handle, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_SSIZE( + "read_count", + read_count, + (ssize_t) 16 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - result = libfdata_stream_free( - &stored_stream, - &error ); + /* TODO compare segment_data with expected_segment_data1 */ - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - } - /* Test STORED rsrc: read 16 bytes of 0xCC-prefixed plaintext via the chunk table path + /* Clean up */ - { - uint8_t stored_rsrc_segment_data[ 16 ]; + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - libfdata_stream_t *stored_rsrc_stream = NULL; - libfsapfs_compressed_data_handle_t *stored_rsrc_compressed_data_handle = NULL; + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); - result = libfsapfs_data_stream_initialize_from_data( - &stored_rsrc_stream, - fsapfs_test_compressed_data_handle_stored_rsrc_compressed_data1, - 25, - &error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + result = libfdata_stream_free( + &compressed_data_stream, + &error ); - result = libfsapfs_compressed_data_handle_initialize( - &stored_rsrc_compressed_data_handle, - stored_rsrc_stream, - 16, - LIBFSAPFS_COMPRESSION_METHOD_STORED, - &error ); + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - - read_count = libfsapfs_compressed_data_handle_read_segment_data( - stored_rsrc_compressed_data_handle, - NULL, - 0, - 0, - stored_rsrc_segment_data, - 16, - 0, - 0, - &error ); - - FSAPFS_TEST_ASSERT_EQUAL_SSIZE( - "read_count", - read_count, - (ssize_t) 16 ); - - FSAPFS_TEST_ASSERT_IS_NULL( - "error", - error ); - - result = memory_compare( - stored_rsrc_segment_data, - fsapfs_test_compressed_data_handle_stored_uncompressed_data1, - 16 ); - - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 0 ); - - result = libfsapfs_compressed_data_handle_free( - &stored_rsrc_compressed_data_handle, - &error ); + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); - result = libfdata_stream_free( - &stored_rsrc_stream, - &error ); + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_raw_chunk_compressed_data1, + 33, + &error ); - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - 1 ); - } + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_RAW, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular cases + */ + read_count = libfsapfs_compressed_data_handle_read_segment_data( + compressed_data_handle, + NULL, + 0, + 0, + segment_data, + 16, + 0, + 0, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_SSIZE( + "read_count", + read_count, + (ssize_t) 16 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + segment_data, + expected_segment_data2, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + /* Clean up + */ + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfdata_stream_free( + &compressed_data_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_raw_compressed_data1, + 25, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_RAW, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Test regular cases + */ + read_count = libfsapfs_compressed_data_handle_read_segment_data( + compressed_data_handle, + NULL, + 0, + 0, + segment_data, + 16, + 0, + 0, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_SSIZE( + "read_count", + read_count, + (ssize_t) 16 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = memory_compare( + segment_data, + expected_segment_data2, + 16 ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 0 ); + + /* Clean up + */ + result = libfsapfs_compressed_data_handle_free( + &compressed_data_handle, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfdata_stream_free( + &compressed_data_stream, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + /* Initialize test + */ + result = libfsapfs_data_stream_initialize_from_data( + &compressed_data_stream, + fsapfs_test_compressed_data_handle_lzvn_compressed_data1, + 35, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_stream", + compressed_data_stream ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); + + result = libfsapfs_compressed_data_handle_initialize( + &compressed_data_handle, + compressed_data_stream, + 16, + LIBFSAPFS_COMPRESSION_METHOD_LZVN, + &error ); + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + 1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "compressed_data_handle", + compressed_data_handle ); + + FSAPFS_TEST_ASSERT_IS_NULL( + "error", + error ); /* Test error cases */ diff --git a/tests/fsapfs_test_compression.c b/tests/fsapfs_test_compression.c index cf328c47..561ce2af 100644 --- a/tests/fsapfs_test_compression.c +++ b/tests/fsapfs_test_compression.c @@ -56,16 +56,10 @@ uint8_t fsapfs_test_compression_lzvn_uncompressed_data1[ 17 ] = { uint8_t fsapfs_test_compression_uncompressed_data1[ 16 ] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; -/* Type 9/10 stored: 0xCC sentinel + 16 bytes of raw data */ -uint8_t fsapfs_test_compression_stored_data1[ 17 ] = { +uint8_t fsapfs_test_compression_raw_data1[ 17 ] = { 0xcc, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }; -/* Bad sentinel — first byte is not 0xCC */ -uint8_t fsapfs_test_compression_stored_bad_sentinel[ 17 ] = { - 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f }; - #if defined( __GNUC__ ) && !defined( LIBFSAPFS_DLL_IMPORT ) /* Tests the libfsapfs_decompress_data function @@ -218,14 +212,12 @@ int fsapfs_test_decompress_data( result, 0 ); - /* Test STORED (types 9 and 10): 0xCC sentinel + verbatim data - */ uncompressed_data_size = 16; result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_data1, + fsapfs_test_compression_raw_data1, 17, - LIBFSAPFS_COMPRESSION_METHOD_STORED, + LIBFSAPFS_COMPRESSION_METHOD_RAW, uncompressed_data, &uncompressed_data_size, &error ); @@ -254,29 +246,6 @@ int fsapfs_test_decompress_data( result, 0 ); - /* Bad sentinel — must return error */ - uncompressed_data_size = 16; - - result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_bad_sentinel, - 17, - LIBFSAPFS_COMPRESSION_METHOD_STORED, - uncompressed_data, - &uncompressed_data_size, - &error ); - - FSAPFS_TEST_ASSERT_EQUAL_INT( - "result", - result, - -1 ); - - FSAPFS_TEST_ASSERT_IS_NOT_NULL( - "error", - error ); - - libcerror_error_free( - &error ); - /* Test error cases */ uncompressed_data_size = 16; @@ -448,6 +417,34 @@ int fsapfs_test_decompress_data( libcerror_error_free( &error ); + /* Test with unsupported raw compressed data + */ + uncompressed_data_size = 16; + + fsapfs_test_compression_raw_data1[ 0 ] = 0x00; + + result = libfsapfs_decompress_data( + fsapfs_test_compression_raw_data1, + 17, + LIBFSAPFS_COMPRESSION_METHOD_RAW, + uncompressed_data, + &uncompressed_data_size, + &error ); + + fsapfs_test_compression_raw_data1[ 0 ] = 0xcc; + + FSAPFS_TEST_ASSERT_EQUAL_INT( + "result", + result, + -1 ); + + FSAPFS_TEST_ASSERT_IS_NOT_NULL( + "error", + error ); + + libcerror_error_free( + &error ); + #if defined( HAVE_FSAPFS_TEST_MEMORY ) && defined( OPTIMIZATION_DISABLED ) uncompressed_data_size = 16; @@ -553,9 +550,9 @@ int fsapfs_test_decompress_data( uncompressed_data_size = 16; result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_data1, + fsapfs_test_compression_raw_data1, (size_t) SSIZE_MAX + 1, - LIBFSAPFS_COMPRESSION_METHOD_STORED, + LIBFSAPFS_COMPRESSION_METHOD_RAW, uncompressed_data, &uncompressed_data_size, &error ); @@ -575,9 +572,9 @@ int fsapfs_test_decompress_data( uncompressed_data_size = 0; result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_data1, + fsapfs_test_compression_raw_data1, 17, - LIBFSAPFS_COMPRESSION_METHOD_STORED, + LIBFSAPFS_COMPRESSION_METHOD_RAW, uncompressed_data, &uncompressed_data_size, &error ); @@ -597,9 +594,9 @@ int fsapfs_test_decompress_data( uncompressed_data_size = (size_t) SSIZE_MAX + 1; result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_data1, + fsapfs_test_compression_raw_data1, 17, - LIBFSAPFS_COMPRESSION_METHOD_STORED, + LIBFSAPFS_COMPRESSION_METHOD_RAW, uncompressed_data, &uncompressed_data_size, &error ); @@ -661,9 +658,9 @@ int fsapfs_test_decompress_data( fsapfs_test_memcpy_attempts_before_fail = 0; result = libfsapfs_decompress_data( - fsapfs_test_compression_stored_data1, + fsapfs_test_compression_raw_data1, 17, - LIBFSAPFS_COMPRESSION_METHOD_STORED, + LIBFSAPFS_COMPRESSION_METHOD_RAW, uncompressed_data, &uncompressed_data_size, &error ); From 6ddbefaf211f5533f7e75cff5f89439ac8846c9f Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Sun, 2 Aug 2026 13:06:49 +0200 Subject: [PATCH 4/4] Changes after review --- libfsapfs/libfsapfs_compression.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libfsapfs/libfsapfs_compression.c b/libfsapfs/libfsapfs_compression.c index dd7e10c0..49fe3960 100644 --- a/libfsapfs/libfsapfs_compression.c +++ b/libfsapfs/libfsapfs_compression.c @@ -149,7 +149,7 @@ int libfsapfs_decompress_data( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_COPY_FAILED, - "%s: unable to compressed to uncompressed data.", + "%s: unable to copy compressed to uncompressed data.", function ); return( -1 ); @@ -283,7 +283,7 @@ int libfsapfs_decompress_data( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE, - "%s: invalid stored data: missing 0xCC sentinel byte.", + "%s: invalid raw compressed data - unsupported marker byte.", function ); return( -1 ); @@ -332,7 +332,7 @@ int libfsapfs_decompress_data( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_COPY_FAILED, - "%s: unable to copy stored data.", + "%s: unable to copy compressed to uncompressed data.", function ); return( -1 ); @@ -412,7 +412,7 @@ int libfsapfs_decompress_data( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_COPY_FAILED, - "%s: unable to compressed to uncompressed data.", + "%s: unable to copy compressed to uncompressed data.", function ); return( -1 );