diff --git a/src/ddt/ddt_v2.c b/src/ddt/ddt_v2.c index 73874aa..02eaedd 100644 --- a/src/ddt/ddt_v2.c +++ b/src/ddt/ddt_v2.c @@ -1305,11 +1305,11 @@ bool set_ddt_multi_level_v2(aaruformatContext *ctx, uint64_t sector_address, boo // Prepare DDT header for the never-written cached table memset(&ddt_header, 0, sizeof(DdtHeader2)); - ddt_header.identifier = DeDuplicationTable2; - ddt_header.type = UserData; - ddt_header.compression = None; // Use no compression for simplicity - ddt_header.levels = ctx->userDataDdtHeader.levels; - ddt_header.tableLevel = ctx->userDataDdtHeader.tableLevel + 1; + ddt_header.identifier = DeDuplicationTable2; + ddt_header.type = UserData; + ddt_header.compression = ctx->compression_enabled ? Lzma : None; // Use no compression for simplicity + ddt_header.levels = ctx->userDataDdtHeader.levels; + ddt_header.tableLevel = ctx->userDataDdtHeader.tableLevel + 1; ddt_header.previousLevelOffset = ctx->primaryDdtOffset; ddt_header.negative = ctx->userDataDdtHeader.negative; ddt_header.blocks = items_per_ddt_entry; @@ -1327,8 +1327,6 @@ bool set_ddt_multi_level_v2(aaruformatContext *ctx, uint64_t sector_address, boo else ddt_header.length = items_per_ddt_entry * sizeof(uint32_t); - ddt_header.cmpLength = ddt_header.length; - // Calculate CRC64 of the data crc64_context = aaruf_crc64_init(); if(crc64_context == NULL) @@ -1339,13 +1337,63 @@ bool set_ddt_multi_level_v2(aaruformatContext *ctx, uint64_t sector_address, boo } if(ctx->userDataDdtHeader.sizeType == SmallDdtSizeType) - aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cachedSecondaryDdtSmall, ddt_header.length); + aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cachedSecondaryDdtSmall, (uint32_t)ddt_header.length); else - aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cachedSecondaryDdtBig, ddt_header.length); + aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cachedSecondaryDdtBig, (uint32_t)ddt_header.length); aaruf_crc64_final(crc64_context, &crc64); - ddt_header.crc64 = crc64; - ddt_header.cmpCrc64 = crc64; + ddt_header.crc64 = crc64; + + uint8_t *cmp_buffer = NULL; + uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0}; + + if(ddt_header.compression == None) + { + if(ctx->userDataDdtHeader.sizeType == SmallDdtSizeType) + cmp_buffer = (uint8_t *)ctx->cachedSecondaryDdtSmall; + else + cmp_buffer = (uint8_t *)ctx->cachedSecondaryDdtBig; + ddt_header.cmpCrc64 = ddt_header.crc64; + } + else + { + cmp_buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression + if(cmp_buffer == NULL) + { + TRACE("Failed to allocate memory for secondary DDT v2 compression"); + return AARUF_ERROR_NOT_ENOUGH_MEMORY; + } + + size_t dst_size = (size_t)ddt_header.length * 2 * 2; + size_t props_size = LZMA_PROPERTIES_LENGTH; + aaruf_lzma_encode_buffer( + cmp_buffer, &dst_size, + ctx->userDataDdtHeader.sizeType == SmallDdtSizeType ? (uint8_t *)ctx->cachedSecondaryDdtSmall + : (uint8_t *)ctx->cachedSecondaryDdtBig, + ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8); + + ddt_header.cmpLength = (uint32_t)dst_size; + + if(ddt_header.cmpLength >= ddt_header.length) + { + ddt_header.compression = None; + free(cmp_buffer); + if(ctx->userDataDdtHeader.sizeType == SmallDdtSizeType) + cmp_buffer = (uint8_t *)ctx->cachedSecondaryDdtSmall; + else + cmp_buffer = (uint8_t *)ctx->cachedSecondaryDdtBig; + } + } + + if(ddt_header.compression == None) + { + ddt_header.cmpLength = ddt_header.length; + ddt_header.cmpCrc64 = ddt_header.crc64; + } + else + ddt_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, (uint32_t)ddt_header.cmpLength); + + if(ddt_header.compression == Lzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH; // Write header written_bytes = fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream); @@ -1357,18 +1405,17 @@ bool set_ddt_multi_level_v2(aaruformatContext *ctx, uint64_t sector_address, boo } // Write data - if(ctx->userDataDdtHeader.sizeType == SmallDdtSizeType) - written_bytes = fwrite(ctx->cachedSecondaryDdtSmall, ddt_header.length, 1, ctx->imageStream); - else - written_bytes = fwrite(ctx->cachedSecondaryDdtBig, ddt_header.length, 1, ctx->imageStream); + if(ddt_header.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream); - if(written_bytes != 1) + if(fwrite(cmp_buffer, ddt_header.cmpLength, 1, ctx->imageStream) != 1) { FATAL("Could not write never-written DDT data to file."); TRACE("Exiting set_ddt_multi_level_v2() = false"); return false; } + if(ddt_header.compression == Lzma) free(cmp_buffer); + // Add index entry for the newly written secondary DDT IndexEntry new_ddt_entry; new_ddt_entry.blockType = DeDuplicationTable2;