libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
ddt_v2.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2026 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <inttypes.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23
24#include "aaruformat.h"
25#include "erasure_internal.h"
26#include "internal.h"
27#include "log.h"
28
97int32_t process_ddt_v2(aaruformat_context *ctx, IndexEntry *entry, bool *found_user_data_ddt)
98{
99 TRACE("Entering process_ddt_v2(%p, %p, %d)", ctx, entry, *found_user_data_ddt);
100
101 int pos = 0;
102 size_t read_bytes = 0;
103 DdtHeader2 ddt_header;
104 uint8_t *cmp_data = NULL;
105 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH];
106 size_t lzma_size = 0;
107 int error_no = 0;
108 crc64_ctx *crc64_context = NULL;
109 uint64_t crc64 = 0;
110 uint8_t *buffer = NULL;
111
112 // Check if the context and image stream are valid
113 if(ctx == NULL || ctx->imageStream == NULL)
114 {
115 FATAL("Invalid context or image stream.");
116
117 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_NOT_AARUFORMAT");
119 }
120
121 // Seek to block
122 pos = aaruf_fseek(ctx->imageStream, (aaru_off_t)entry->offset, SEEK_SET);
123 if(pos < 0 || aaruf_ftell(ctx->imageStream) != (aaru_off_t)entry->offset)
124 {
125 FATAL("Could not seek to %" PRIu64 " as indicated by index entry...", entry->offset);
126
127 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
129 }
130
131 // Even if those two checks shall have been done before
132 TRACE("Reading DDT block header at position %" PRIu64, entry->offset);
133 read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
134
135 if(read_bytes != sizeof(DdtHeader2))
136 {
137 FATAL("Could not read block header at %" PRIu64 "", entry->offset);
138
139 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
141 }
142
143 ctx->image_info.ImageSize += ddt_header.cmpLength;
144
145 if(entry->dataType == kDataTypeUserData)
146 {
147 // User area sectors is blocks stored in DDT minus the negative and overflow displacement blocks
148 ctx->image_info.Sectors = ddt_header.blocks - ddt_header.negative - ddt_header.overflow;
149 // We need the header later for the shift calculations
150 ctx->user_data_ddt_header = ddt_header;
151 ctx->ddt_version = 2;
152 // Store the primary DDT table's file offset for secondary table references
153 ctx->primary_ddt_offset = entry->offset;
154
155 // Check for DDT compression
156 switch(ddt_header.compression)
157 {
158 case kCompressionLzma:
159 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
160 {
161 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
162 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
164 }
165
166 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
167
168 cmp_data = (uint8_t *)malloc(lzma_size);
169 if(cmp_data == NULL)
170 {
171 TRACE("Cannot allocate memory for DDT, continuing...");
172 break;
173 }
174
175 buffer = malloc(ddt_header.length);
176 if(buffer == NULL)
177 {
178 TRACE("Cannot allocate memory for DDT, continuing...");
179 free(cmp_data);
180 break;
181 }
182
183 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
184 if(read_bytes != LZMA_PROPERTIES_LENGTH)
185 {
186 TRACE("Could not read LZMA properties, continuing...");
187 free(cmp_data);
188 free(buffer);
189 break;
190 }
191
192 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
193 if(read_bytes != lzma_size)
194 {
195 TRACE("Could not read compressed block, continuing...");
196 free(cmp_data);
197 free(buffer);
198 break;
199 }
200
201 read_bytes = ddt_header.length;
202 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
203 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
205
206 if(error_no != 0)
207 {
208 FATAL("Got error %d from LZMA, stopping...", error_no);
209 free(cmp_data);
210 free(buffer);
211 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
213 }
214
215 if(read_bytes != ddt_header.length)
216 {
217 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
218 free(cmp_data);
219 free(buffer);
220 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
222 }
223
224 free(cmp_data);
225
226 crc64_context = aaruf_crc64_init();
227
228 if(crc64_context == NULL)
229 {
230 FATAL("Could not initialize CRC64.");
231 free(buffer);
232
233 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
235 }
236
237 aaruf_crc64_update(crc64_context, buffer, read_bytes);
238 aaruf_crc64_final(crc64_context, &crc64);
239 aaruf_crc64_free(crc64_context);
240
241 if(crc64 != ddt_header.crc64)
242 {
243 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
244 free(buffer);
245 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
247 }
248
249 ctx->user_data_ddt2 = (uint64_t *)buffer;
250
251 ctx->in_memory_ddt = true;
252 *found_user_data_ddt = true;
253
254 break;
255 case kCompressionZstd:
256 if(ddt_header.cmpLength == 0)
257 {
258 FATAL("Compressed DDT payload has zero length for zstd.");
259 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
261 }
262
263 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
264 if(cmp_data == NULL)
265 {
266 TRACE("Cannot allocate memory for DDT, continuing...");
267 break;
268 }
269
270 buffer = malloc(ddt_header.length);
271 if(buffer == NULL)
272 {
273 TRACE("Cannot allocate memory for DDT, continuing...");
274 free(cmp_data);
275 break;
276 }
277
278 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
279 if(read_bytes != ddt_header.cmpLength)
280 {
281 TRACE("Could not read compressed block, continuing...");
282 free(cmp_data);
283 free(buffer);
284 break;
285 }
286
287 read_bytes =
288 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
289
290 if(read_bytes != ddt_header.length)
291 {
292 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
293 free(cmp_data);
294 free(buffer);
295 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
297 }
298
299 free(cmp_data);
300
301 crc64_context = aaruf_crc64_init();
302
303 if(crc64_context == NULL)
304 {
305 FATAL("Could not initialize CRC64.");
306 free(buffer);
307 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
309 }
310
311 aaruf_crc64_update(crc64_context, buffer, read_bytes);
312 aaruf_crc64_final(crc64_context, &crc64);
313 aaruf_crc64_free(crc64_context);
314
315 if(crc64 != ddt_header.crc64)
316 {
317 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
318 free(buffer);
319 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
321 }
322
323 ctx->user_data_ddt2 = (uint64_t *)buffer;
324
325 ctx->in_memory_ddt = true;
326 *found_user_data_ddt = true;
327
328 break;
329 case kCompressionNone:
330 buffer = malloc(ddt_header.length);
331
332 if(buffer == NULL)
333 {
334 TRACE("Cannot allocate memory for DDT, continuing...");
335 break;
336 }
337
338 TRACE("Reading DDT of length %zu bytes", ddt_header.length);
339 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
340
341 if(read_bytes != ddt_header.length)
342 {
343 free(buffer);
344 FATAL("Could not read deduplication table, continuing...");
345 break;
346 }
347
348 crc64_context = aaruf_crc64_init();
349
350 if(crc64_context == NULL)
351 {
352 FATAL("Could not initialize CRC64.");
353 free(buffer);
354 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
356 }
357
358 aaruf_crc64_update(crc64_context, buffer, read_bytes);
359 aaruf_crc64_final(crc64_context, &crc64);
360 aaruf_crc64_free(crc64_context);
361
362 if(crc64 != ddt_header.crc64)
363 {
364 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
365 free(buffer);
366 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
368 }
369
370 ctx->user_data_ddt2 = (uint64_t *)buffer;
371
372 ctx->in_memory_ddt = true;
373 *found_user_data_ddt = true;
374
375 break;
376 default:
377 TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
378 *found_user_data_ddt = false;
379 break;
380 }
381 }
383 switch(ddt_header.compression)
384 {
385 case kCompressionLzma:
386 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
387 {
388 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
389 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
391 }
392
393 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
394
395 cmp_data = (uint8_t *)malloc(lzma_size);
396 if(cmp_data == NULL)
397 {
398 TRACE("Cannot allocate memory for DDT, continuing...");
399 break;
400 }
401
402 buffer = malloc(ddt_header.length);
403 if(buffer == NULL)
404 {
405 TRACE("Cannot allocate memory for DDT, continuing...");
406 free(cmp_data);
407 break;
408 }
409
410 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
411 if(read_bytes != LZMA_PROPERTIES_LENGTH)
412 {
413 TRACE("Could not read LZMA properties, continuing...");
414 free(cmp_data);
415 free(buffer);
416 break;
417 }
418
419 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
420 if(read_bytes != lzma_size)
421 {
422 TRACE("Could not read compressed block, continuing...");
423 free(cmp_data);
424 free(buffer);
425 break;
426 }
427
428 read_bytes = ddt_header.length;
429 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
430 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
432
433 if(error_no != 0)
434 {
435 FATAL("Got error %d from LZMA, stopping...", error_no);
436 free(cmp_data);
437 free(buffer);
438 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
440 }
441
442 if(read_bytes != ddt_header.length)
443 {
444 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
445 free(cmp_data);
446 free(buffer);
447 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
449 }
450
451 free(cmp_data);
452 cmp_data = NULL;
453
454 crc64_context = aaruf_crc64_init();
455
456 if(crc64_context == NULL)
457 {
458 FATAL("Could not initialize CRC64.");
459 free(buffer);
460 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
462 }
463
464 aaruf_crc64_update(crc64_context, buffer, read_bytes);
465 aaruf_crc64_final(crc64_context, &crc64);
466 aaruf_crc64_free(crc64_context);
467
468 if(crc64 != ddt_header.crc64)
469 {
470 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
471 free(buffer);
472 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
474 }
475
477 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
478 else if(entry->dataType == kDataTypeCdSectorSuffix)
479 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
480 else
481 free(buffer);
482
483 break;
484 case kCompressionZstd:
485 if(ddt_header.cmpLength == 0)
486 {
487 FATAL("Compressed DDT payload has zero length for zstd.");
488 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
490 }
491
492 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
493 if(cmp_data == NULL)
494 {
495 TRACE("Cannot allocate memory for DDT, continuing...");
496 break;
497 }
498
499 buffer = malloc(ddt_header.length);
500 if(buffer == NULL)
501 {
502 TRACE("Cannot allocate memory for DDT, continuing...");
503 free(cmp_data);
504 break;
505 }
506
507 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
508 if(read_bytes != ddt_header.cmpLength)
509 {
510 TRACE("Could not read compressed block, continuing...");
511 free(cmp_data);
512 free(buffer);
513 break;
514 }
515
516 read_bytes =
517 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
518
519 if(read_bytes != ddt_header.length)
520 {
521 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
522 free(cmp_data);
523 free(buffer);
524 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
526 }
527
528 free(cmp_data);
529 cmp_data = NULL;
530
531 crc64_context = aaruf_crc64_init();
532
533 if(crc64_context == NULL)
534 {
535 FATAL("Could not initialize CRC64.");
536 free(buffer);
537 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
539 }
540
541 aaruf_crc64_update(crc64_context, buffer, read_bytes);
542 aaruf_crc64_final(crc64_context, &crc64);
543 aaruf_crc64_free(crc64_context);
544
545 if(crc64 != ddt_header.crc64)
546 {
547 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
548 free(buffer);
549 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
551 }
552
554 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
555 else if(entry->dataType == kDataTypeCdSectorSuffix)
556 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
557 else
558 free(buffer);
559
560 break;
561
562 case kCompressionNone:
563 buffer = malloc(ddt_header.length);
564
565 if(buffer == NULL)
566 {
567 TRACE("Cannot allocate memory for deduplication table.");
568 break;
569 }
570
571 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
572
573 if(read_bytes != ddt_header.length)
574 {
575 free(buffer);
576 FATAL("Could not read deduplication table, continuing...");
577 break;
578 }
579
580 crc64_context = aaruf_crc64_init();
581
582 if(crc64_context == NULL)
583 {
584 FATAL("Could not initialize CRC64.");
585 free(buffer);
586 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
588 }
589
590 aaruf_crc64_update(crc64_context, buffer, read_bytes);
591 aaruf_crc64_final(crc64_context, &crc64);
592 aaruf_crc64_free(crc64_context);
593
594 if(crc64 != ddt_header.crc64)
595 {
596 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
597 free(buffer);
598 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
600 }
601
603 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
604 else if(entry->dataType == kDataTypeCdSectorSuffix)
605 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
606 else
607 free(buffer);
608
609 break;
610 default:
611 TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
612 break;
613 }
614
615 TRACE("Exiting process_ddt_v2() = AARUF_STATUS_OK");
616 return AARUF_STATUS_OK;
617}
618
663int32_t decode_ddt_entry_v2(aaruformat_context *ctx, const uint64_t sector_address, bool negative, uint64_t *offset,
664 uint64_t *block_offset, uint8_t *sector_status)
665{
666 TRACE("Entering decode_ddt_entry_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative, *offset,
667 *block_offset, *sector_status);
668 // Check if the context and image stream are valid
669 if(ctx == NULL || ctx->imageStream == NULL)
670 {
671 FATAL("Invalid context or image stream.");
672
673 TRACE("Exiting decode_ddt_entry_v2() = AARUF_ERROR_NOT_AARUFORMAT");
675 }
676
678 {
679 int32_t result = decode_ddt_multi_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status);
680
681 /* If secondary DDT load failed, attempt EC recovery */
683 {
684 /* Compute which secondary DDT failed */
685 uint64_t sa = negative ? ctx->user_data_ddt_header.negative - sector_address
686 : sector_address + ctx->user_data_ddt_header.negative;
687 int items = 1 << ctx->user_data_ddt_header.tableShift;
688 uint64_t pos = sa / items;
689 uint64_t ddt_off = ctx->user_data_ddt2[pos];
690 ddt_off *= 1 << ctx->user_data_ddt_header.blockAlignmentShift;
691
692 TRACE("Attempting EC recovery for DDT secondary at offset %" PRIu64, ddt_off);
693 uint8_t *recovered = NULL;
694 uint32_t rec_size = 0;
695 if(ec_recover_ddt_block(ctx, ddt_off, &recovered, &rec_size) == AARUF_STATUS_OK && recovered)
696 {
697 /* Parse DdtHeader2 from recovered bytes, decompress, load into cache */
698 if(rec_size >= sizeof(DdtHeader2))
699 {
700 DdtHeader2 rec_hdr;
701 memcpy(&rec_hdr, recovered, sizeof(DdtHeader2));
702
703 uint8_t *payload = recovered + sizeof(DdtHeader2);
704 uint32_t payload_size = rec_size - sizeof(DdtHeader2);
705 uint8_t *ddt_data = NULL;
706
707 if(rec_hdr.compression == kCompressionNone && payload_size >= rec_hdr.length)
708 {
709 ddt_data = (uint8_t *)malloc((size_t)rec_hdr.length);
710 if(ddt_data) memcpy(ddt_data, payload, (size_t)rec_hdr.length);
711 }
712 else if(rec_hdr.compression == kCompressionLzma && payload_size > LZMA_PROPERTIES_LENGTH)
713 {
714 ddt_data = (uint8_t *)malloc((size_t)rec_hdr.length);
715 if(ddt_data)
716 {
717 size_t out_sz = (size_t)rec_hdr.length;
718 size_t src_sz = payload_size - LZMA_PROPERTIES_LENGTH;
719 aaruf_lzma_decode_buffer(ddt_data, &out_sz, payload + LZMA_PROPERTIES_LENGTH,
720 &src_sz, payload, LZMA_PROPERTIES_LENGTH);
721 }
722 }
723 else if(rec_hdr.compression == kCompressionZstd && payload_size > 0)
724 {
725 ddt_data = (uint8_t *)malloc((size_t)rec_hdr.length);
726 if(ddt_data)
727 aaruf_zstd_decode_buffer(ddt_data, (size_t)rec_hdr.length, payload, payload_size);
728 }
729
730 if(ddt_data)
731 {
732 TRACE("EC recovery succeeded for DDT secondary, loading into cache");
733 free(ctx->cached_secondary_ddt2);
734 ctx->cached_secondary_ddt2 = (uint64_t *)ddt_data;
735 ctx->cached_ddt_offset = ddt_off;
736 /* Retry the decode */
737 result = decode_ddt_multi_level_v2(ctx, sector_address, negative,
738 offset, block_offset, sector_status);
739 }
740 }
741 free(recovered);
742 }
743 }
744
745 return result;
746 }
747
748 return decode_ddt_single_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status);
749}
750
805int32_t decode_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset,
806 uint64_t *block_offset, uint8_t *sector_status)
807{
808 TRACE("Entering decode_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
809 *offset, *block_offset, *sector_status);
810
811 uint64_t ddt_entry = 0;
812
813 // Check if the context and image stream are valid
814 if(ctx == NULL || ctx->imageStream == NULL)
815 {
816 FATAL("Invalid context or image stream.");
817
818 TRACE("Exiting decode_ddt_single_level_v2() = AARUF_ERROR_NOT_AARUFORMAT");
820 }
821
822 // Should not really be here
823 if(ctx->user_data_ddt_header.tableShift != 0)
824 {
825 FATAL("DDT table shift is not zero, but we are in single-level DDT decoding.");
826 TRACE("Exiting decode_ddt_single_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
828 }
829
830 // Calculate positive or negative sector
831 if(negative)
832 sector_address = ctx->user_data_ddt_header.negative - sector_address;
833 else
834 sector_address += ctx->user_data_ddt_header.negative;
835
836 ddt_entry = ctx->user_data_ddt2[sector_address];
837
838 if(ddt_entry == 0)
839 {
840 *sector_status = SectorStatusNotDumped;
841 *offset = 0;
842 *block_offset = 0;
843 TRACE("Exiting decode_ddt_single_level_v2(%p, %" PRIu64 ", %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
844 sector_address, *offset, *block_offset, *sector_status);
845 return AARUF_STATUS_OK;
846 }
847
848 *sector_status = ddt_entry >> 60;
849 ddt_entry &= 0xFFFFFFFFFFFFFFF;
850
851 const uint64_t offset_mask = (uint64_t)((1 << ctx->user_data_ddt_header.dataShift) - 1);
852 *offset = ddt_entry & offset_mask;
853 *block_offset =
855
856 TRACE("Exiting decode_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
857 sector_address, negative, *offset, *block_offset, *sector_status);
858 return AARUF_STATUS_OK;
859}
860
948int32_t decode_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset,
949 uint64_t *block_offset, uint8_t *sector_status)
950{
951 TRACE("Entering decode_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
952 *offset, *block_offset, *sector_status);
953
954 uint64_t ddt_entry = 0;
955 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH];
956 size_t lzma_size = 0;
957 uint8_t *cmp_data = NULL;
958 uint8_t *buffer = NULL;
959 crc64_ctx *crc64_context = NULL;
960 uint64_t crc64 = 0;
961 int items_per_ddt_entry = 0;
962 uint64_t ddt_position = 0;
963 uint64_t secondary_ddt_offset = 0;
964
965 // Check if the context and image stream are valid
966 if(ctx == NULL || ctx->imageStream == NULL)
967 {
968 FATAL("Invalid context or image stream.");
969
970 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_NOT_AARUFORMAT");
972 }
973
974 // Should not really be here
975 if(ctx->user_data_ddt_header.tableShift == 0)
976 {
977 FATAL("DDT table shift is zero, but we are in multi-level DDT decoding.");
978 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
980 }
981
982 // Calculate positive or negative sector
983 if(negative)
984 sector_address = ctx->user_data_ddt_header.negative - sector_address;
985 else
986 sector_address += ctx->user_data_ddt_header.negative;
987
988 items_per_ddt_entry = 1 << ctx->user_data_ddt_header.tableShift;
989 ddt_position = sector_address / items_per_ddt_entry;
990 secondary_ddt_offset = ctx->user_data_ddt2[ddt_position];
991
992 // Position in file of the child DDT table
993 secondary_ddt_offset *= 1 << ctx->user_data_ddt_header.blockAlignmentShift;
994
995 // Is the one we have cached the same as the one we need to read?
996 if(ctx->cached_ddt_offset != secondary_ddt_offset)
997 {
998 int32_t error_no = 0;
999 aaruf_fseek(ctx->imageStream, (aaru_off_t)secondary_ddt_offset, SEEK_SET);
1000 DdtHeader2 ddt_header;
1001 size_t read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
1002
1003 if(read_bytes != sizeof(DdtHeader2))
1004 {
1005 FATAL("Could not read block header at %" PRIu64 "", secondary_ddt_offset);
1006 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1008 }
1009
1010 if((ddt_header.identifier != DeDuplicationTableSecondary &&
1011 ddt_header.identifier != DeDuplicationTableSAlpha) ||
1012 ddt_header.type != kDataTypeUserData)
1013 {
1014 FATAL("Invalid block header at %" PRIu64 "", secondary_ddt_offset);
1015 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1017 }
1018
1019 // Check for DDT compression
1020 switch(ddt_header.compression)
1021 {
1022 case kCompressionLzma:
1023 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
1024 {
1025 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
1026 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1028 }
1029
1030 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
1031
1032 cmp_data = (uint8_t *)malloc(lzma_size);
1033 if(cmp_data == NULL)
1034 {
1035 FATAL("Cannot allocate memory for DDT, stopping...");
1036 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1038 }
1039
1040 buffer = malloc(ddt_header.length);
1041 if(buffer == NULL)
1042 {
1043 FATAL("Cannot allocate memory for DDT, stopping...");
1044 free(cmp_data);
1046 }
1047
1048 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
1049 if(read_bytes != LZMA_PROPERTIES_LENGTH)
1050 {
1051 FATAL("Could not read LZMA properties, stopping...");
1052 free(cmp_data);
1053 free(buffer);
1054 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1056 }
1057
1058 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
1059 if(read_bytes != lzma_size)
1060 {
1061 FATAL("Could not read compressed block, stopping...");
1062 free(cmp_data);
1063 free(buffer);
1064 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1066 }
1067
1068 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
1069 read_bytes = ddt_header.length;
1070 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
1072
1073 if(error_no != 0)
1074 {
1075 FATAL("Got error %d from LZMA, stopping...", error_no);
1076 free(cmp_data);
1077 free(buffer);
1078 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1080 }
1081
1082 if(read_bytes != ddt_header.length)
1083 {
1084 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
1085 free(cmp_data);
1086 free(buffer);
1087 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1089 }
1090
1091 free(cmp_data);
1092
1093 crc64_context = aaruf_crc64_init();
1094
1095 if(crc64_context == NULL)
1096 {
1097 FATAL("Could not initialize CRC64.");
1098 free(buffer);
1099 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1101 }
1102
1103 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1104 aaruf_crc64_final(crc64_context, &crc64);
1105 aaruf_crc64_free(crc64_context);
1106
1107 if(crc64 != ddt_header.crc64)
1108 {
1109 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1110 free(buffer);
1111 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1113 }
1114
1115 // Free old cached DDT before replacing it
1116 free(ctx->cached_secondary_ddt2);
1117
1118 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1119
1120 ctx->cached_ddt_offset = secondary_ddt_offset;
1121
1122 break;
1123 case kCompressionZstd:
1124 if(ddt_header.cmpLength == 0)
1125 {
1126 FATAL("Compressed DDT payload has zero length for zstd.");
1127 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1129 }
1130
1131 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
1132 if(cmp_data == NULL)
1133 {
1134 FATAL("Cannot allocate memory for DDT, stopping...");
1135 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1137 }
1138
1139 buffer = malloc(ddt_header.length);
1140 if(buffer == NULL)
1141 {
1142 FATAL("Cannot allocate memory for DDT, stopping...");
1143 free(cmp_data);
1145 }
1146
1147 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
1148 if(read_bytes != ddt_header.cmpLength)
1149 {
1150 FATAL("Could not read compressed block, stopping...");
1151 free(cmp_data);
1152 free(buffer);
1153 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1155 }
1156
1157 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
1158 read_bytes =
1159 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
1160
1161 if(read_bytes != ddt_header.length)
1162 {
1163 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
1164 free(cmp_data);
1165 free(buffer);
1166 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1168 }
1169
1170 free(cmp_data);
1171
1172 crc64_context = aaruf_crc64_init();
1173
1174 if(crc64_context == NULL)
1175 {
1176 FATAL("Could not initialize CRC64.");
1177 free(buffer);
1178 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1180 }
1181
1182 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1183 aaruf_crc64_final(crc64_context, &crc64);
1184 aaruf_crc64_free(crc64_context);
1185
1186 if(crc64 != ddt_header.crc64)
1187 {
1188 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1189 free(buffer);
1190 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1192 }
1193
1194 // Free old cached DDT before replacing it
1195 free(ctx->cached_secondary_ddt2);
1196
1197 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1198
1199 ctx->cached_ddt_offset = secondary_ddt_offset;
1200
1201 break;
1202 case kCompressionNone:
1203 buffer = malloc(ddt_header.length);
1204
1205 if(buffer == NULL)
1206 {
1207 FATAL("Cannot allocate memory for DDT, stopping...");
1208 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1210 }
1211
1212 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
1213
1214 if(read_bytes != ddt_header.length)
1215 {
1216 free(buffer);
1217 FATAL("Could not read deduplication table, stopping...");
1218 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1220 }
1221
1222 crc64_context = aaruf_crc64_init();
1223
1224 if(crc64_context == NULL)
1225 {
1226 FATAL("Could not initialize CRC64.");
1227 free(buffer);
1228 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1230 }
1231
1232 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1233 aaruf_crc64_final(crc64_context, &crc64);
1234 aaruf_crc64_free(crc64_context);
1235
1236 if(crc64 != ddt_header.crc64)
1237 {
1238 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1239 free(buffer);
1240 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1242 }
1243
1244 // Free old cached DDT before replacing it
1245 free(ctx->cached_secondary_ddt2);
1246
1247 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1248
1249 ctx->cached_ddt_offset = secondary_ddt_offset;
1250
1251 break;
1252 default:
1253 FATAL("Found unknown compression type %d, stopping...", ddt_header.compression);
1254 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1256 }
1257 }
1258
1259 ddt_entry = ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry];
1260
1261 if(ddt_entry == 0)
1262 {
1263 *sector_status = SectorStatusNotDumped;
1264 *offset = 0;
1265 *block_offset = 0;
1266
1267 TRACE("Exiting decode_ddt_multi_level_v2(%p, %" PRIu64 ", %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
1268 sector_address, *offset, *block_offset, *sector_status);
1269 return AARUF_STATUS_OK;
1270 }
1271
1272 *sector_status = ddt_entry >> 60;
1273 ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1274
1275 const uint64_t offset_mask = (uint64_t)((1 << ctx->user_data_ddt_header.dataShift) - 1);
1276 *offset = ddt_entry & offset_mask;
1277 *block_offset =
1279
1280 TRACE("Exiting decode_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
1281 sector_address, negative, *offset, *block_offset, *sector_status);
1282 return AARUF_STATUS_OK;
1283}
1284
1301bool set_ddt_entry_v2(aaruformat_context *ctx, const uint64_t sector_address, const bool negative,
1302 const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status,
1303 uint64_t *ddt_entry)
1304{
1305 TRACE("Entering set_ddt_entry_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative, offset,
1306 block_offset, sector_status);
1307
1308 // Check if the context and image stream are valid
1309 if(ctx == NULL || ctx->imageStream == NULL)
1310 {
1311 FATAL("Invalid context or image stream.");
1312 return false;
1313 }
1314
1315 if(ctx->user_data_ddt_header.tableShift > 0)
1316 return set_ddt_multi_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status, ddt_entry);
1317
1318 return set_ddt_single_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status, ddt_entry);
1319}
1320
1337bool set_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, const bool negative,
1338 const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status,
1339 uint64_t *ddt_entry)
1340{
1341 TRACE("Entering set_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
1342 offset, block_offset, sector_status);
1343
1344 // Check if the context and image stream are valid
1345 if(ctx == NULL || ctx->imageStream == NULL)
1346 {
1347 FATAL("Invalid context or image stream.");
1348 TRACE("Exiting set_ddt_single_level_v2() = false");
1349 return false;
1350 }
1351
1352 // Should not really be here
1353 if(ctx->user_data_ddt_header.tableShift != 0)
1354 {
1355 FATAL("DDT table shift is not zero, but we are in single-level DDT setting.");
1356 TRACE("Exiting set_ddt_single_level_v2() = false");
1357 return false;
1358 }
1359
1360 // Calculate positive or negative sector
1361 if(negative)
1362 sector_address = ctx->user_data_ddt_header.negative - sector_address;
1363 else
1364 sector_address += ctx->user_data_ddt_header.negative;
1365
1366 if(*ddt_entry == 0)
1367 {
1368 const uint64_t block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1369 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1370 block_index << ctx->user_data_ddt_header.dataShift;
1371
1372 // Overflow detection for DDT entry
1373 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1374 {
1375 FATAL("DDT overflow: media does not fit in big DDT");
1376 TRACE("Exiting set_ddt_single_level_v2() = false");
1377 return false;
1378 }
1379 }
1380
1381 // Sector status can be different from previous deduplicated sector
1382 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1383 *ddt_entry |= (uint64_t)sector_status << 60;
1384
1385 TRACE("Setting big single-level DDT entry %d to %ull", sector_address, (uint64_t)*ddt_entry);
1386 ctx->user_data_ddt2[sector_address] = *ddt_entry;
1387 ctx->dirty_single_level_ddt = true; // Mark single-level DDT as dirty
1388
1389 TRACE("Exiting set_ddt_single_level_v2() = true");
1390 return true;
1391}
1392
1409bool set_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset,
1410 uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
1411{
1412 TRACE("Entering set_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %" PRIu64 ", %" PRIu64 ", %d)", ctx, sector_address,
1413 negative, offset, block_offset, sector_status);
1414
1415 uint64_t items_per_ddt_entry = 0;
1416 uint64_t ddt_position = 0;
1417 uint64_t secondary_ddt_offset = 0;
1418 uint64_t block_index = 0;
1419 uint8_t *buffer = NULL;
1420 crc64_ctx *crc64_context = NULL;
1421 uint64_t crc64 = 0;
1422 DdtHeader2 ddt_header;
1423 size_t written_bytes = 0;
1424 long end_of_file = 0;
1425 bool create_new_table = false;
1426
1427 // Check if the context and image stream are valid
1428 if(ctx == NULL || ctx->imageStream == NULL)
1429 {
1430 FATAL("Invalid context or image stream.");
1431 TRACE("Exiting set_ddt_multi_level_v2() = false");
1432 return false;
1433 }
1434
1435 // Should not really be here
1436 if(ctx->user_data_ddt_header.tableShift == 0)
1437 {
1438 FATAL("DDT table shift is zero, but we are in multi-level DDT setting.");
1439 TRACE("Exiting set_ddt_multi_level_v2() = false");
1440 return false;
1441 }
1442
1443 // Calculate positive or negative sector
1444 if(negative)
1445 sector_address = ctx->user_data_ddt_header.negative - sector_address;
1446 else
1447 sector_address += ctx->user_data_ddt_header.negative;
1448
1449 // Step 1: Calculate the corresponding secondary level table
1450 items_per_ddt_entry = 1 << ctx->user_data_ddt_header.tableShift;
1451 ddt_position = sector_address / items_per_ddt_entry;
1452 secondary_ddt_offset = ctx->user_data_ddt2[ddt_position];
1453
1454 // Position in file of the child DDT table
1455 secondary_ddt_offset *= 1 << ctx->user_data_ddt_header.blockAlignmentShift;
1456
1457 // Step 2: Check if it corresponds to the currently in-memory cached secondary level table
1458 if(ctx->cached_ddt_offset == secondary_ddt_offset && secondary_ddt_offset != 0)
1459 {
1460 // Update the corresponding DDT entry directly in the cached table
1461 if(*ddt_entry == 0)
1462 {
1463 block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1464 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1465 block_index << ctx->user_data_ddt_header.dataShift;
1466
1467 // Overflow detection for DDT entry
1468 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1469 {
1470 FATAL("DDT overflow: media does not fit in big DDT");
1471 TRACE("Exiting set_ddt_multi_level_v2() = false");
1472 return false;
1473 }
1474 }
1475
1476 // Sector status can be different from previous deduplicated sector
1477 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1478 *ddt_entry |= (uint64_t)sector_status << 60;
1479
1480 TRACE("Setting small secondary DDT entry %d to %ull", sector_address % items_per_ddt_entry,
1481 (uint64_t)*ddt_entry);
1482 ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry] = *ddt_entry;
1483 ctx->dirty_secondary_ddt = true; // Mark secondary DDT as dirty
1484
1485 TRACE("Updated cached secondary DDT entry at position %" PRIu64, sector_address % items_per_ddt_entry);
1486 TRACE("Exiting set_ddt_multi_level_v2() = true");
1487 return true;
1488 }
1489
1490 // Step 2.5: Handle case where we have a cached secondary DDT that has never been written to disk
1491 // but does not contain the requested block
1492 if(ctx->cached_ddt_offset == 0 && (ctx->cached_secondary_ddt2 != NULL))
1493 {
1494 // Only write the cached table to disk if the requested block belongs to a different DDT position
1495 if(ddt_position != ctx->cached_ddt_position)
1496 {
1497 TRACE("Current secondary DDT in memory belongs to position %" PRIu64
1498 " but requested block needs position %" PRIu64,
1499 ctx->cached_ddt_position, ddt_position);
1500
1501 // Write the cached DDT to disk before proceeding with the new one
1502
1503 // Close the current data block first
1504 if(ctx->writing_buffer != NULL) aaruf_close_current_block(ctx);
1505
1506 // Get current position and seek to end of file
1507 aaruf_fseek(ctx->imageStream, 0, SEEK_END);
1508 end_of_file = aaruf_ftell(ctx->imageStream);
1509
1510 // Align to block boundary
1511 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1512 end_of_file = end_of_file + alignment_mask & ~alignment_mask;
1513 aaruf_fseek(ctx->imageStream, end_of_file, SEEK_SET);
1514
1515 // Prepare DDT header for the never-written cached table
1516 memset(&ddt_header, 0, sizeof(DdtHeader2));
1518 ddt_header.type = kDataTypeUserData;
1519 ddt_header.compression = ctx->compression_enabled
1522 ddt_header.levels = ctx->user_data_ddt_header.levels;
1523 ddt_header.tableLevel = ctx->user_data_ddt_header.tableLevel + 1;
1524 ddt_header.previousLevelOffset = ctx->primary_ddt_offset;
1525 ddt_header.negative = ctx->user_data_ddt_header.negative;
1526 ddt_header.blocks = items_per_ddt_entry;
1527 ddt_header.overflow = ctx->user_data_ddt_header.overflow;
1528 ddt_header.start = ctx->cached_ddt_position * items_per_ddt_entry; // Use cached position with table shift
1530 ddt_header.dataShift = ctx->user_data_ddt_header.dataShift;
1531 ddt_header.tableShift = 0; // Secondary tables are single level
1532 ddt_header.entries = items_per_ddt_entry;
1533
1534 // Calculate data size
1535
1536 ddt_header.length = items_per_ddt_entry * sizeof(uint64_t);
1537
1538 // Calculate CRC64 of the data
1539 crc64_context = aaruf_crc64_init();
1540 if(crc64_context == NULL)
1541 {
1542 FATAL("Could not initialize CRC64.");
1543 TRACE("Exiting set_ddt_multi_level_v2() = false");
1544 return false;
1545 }
1546
1547 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cached_secondary_ddt2, (uint32_t)ddt_header.length);
1548
1549 aaruf_crc64_final(crc64_context, &crc64);
1550 aaruf_crc64_free(crc64_context);
1551 ddt_header.crc64 = crc64;
1552
1553 uint8_t *cmp_buffer = NULL;
1554 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1555
1556 if(ddt_header.compression == kCompressionNone)
1557 {
1558
1559 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1560 ddt_header.cmpCrc64 = ddt_header.crc64;
1561 }
1562 else
1563 {
1564 cmp_buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression
1565 if(cmp_buffer == NULL)
1566 {
1567 TRACE("Failed to allocate memory for secondary DDT v2 compression");
1569 }
1570
1571 size_t dst_size;
1572
1573 if(ctx->use_zstd)
1574 {
1575 dst_size = aaruf_zstd_encode_buffer(cmp_buffer, (size_t)ddt_header.length * 2,
1576 (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length,
1577 ctx->zstd_level, ctx->num_threads);
1578 if(dst_size == 0) dst_size = ddt_header.length;
1579 }
1580 else
1581 {
1582 dst_size = (size_t)ddt_header.length * 2 * 2;
1583 size_t props_size = LZMA_PROPERTIES_LENGTH;
1584 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->cached_secondary_ddt2,
1585 ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size,
1586 4, 0, 2, 273, LZMA_THREADS(ctx));
1587 }
1588
1589 ddt_header.cmpLength = (uint32_t)dst_size;
1590
1591 if(ddt_header.cmpLength >= ddt_header.length)
1592 {
1593 ddt_header.compression = kCompressionNone;
1594 free(cmp_buffer);
1595
1596 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1597 }
1598 }
1599
1600 if(ddt_header.compression == kCompressionNone)
1601 {
1602 ddt_header.cmpLength = ddt_header.length;
1603 ddt_header.cmpCrc64 = ddt_header.crc64;
1604 }
1605 else
1606 {
1607 ddt_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, (uint32_t)ddt_header.cmpLength);
1608 if(ctx->use_zstd) ctx->has_zstd_blocks = true;
1609 }
1610
1611 if(ddt_header.compression == kCompressionLzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
1612
1613 // Write header
1614 written_bytes = fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
1615 if(written_bytes != 1)
1616 {
1617 FATAL("Could not write never-written DDT header to file.");
1618 TRACE("Exiting set_ddt_multi_level_v2() = false");
1619 return false;
1620 }
1621
1622 // Write data
1623 if(ddt_header.compression == kCompressionLzma)
1624 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1625
1626 if(fwrite(cmp_buffer, ddt_header.cmpLength, 1, ctx->imageStream) != 1)
1627 {
1628 FATAL("Could not write never-written DDT data to file.");
1629 TRACE("Exiting set_ddt_multi_level_v2() = false");
1630 return false;
1631 }
1632
1633 if(ddt_header.compression != kCompressionNone) free(cmp_buffer);
1634
1635 // Add index entry for the newly written secondary DDT
1636 IndexEntry new_ddt_entry;
1637 new_ddt_entry.blockType = DeDuplicationTableSecondary;
1638 new_ddt_entry.dataType = kDataTypeUserData;
1639 new_ddt_entry.offset = end_of_file;
1640
1641 utarray_push_back(ctx->index_entries, &new_ddt_entry);
1642 ctx->dirty_index_block = true;
1643 TRACE("Added new DDT index entry for never-written table at offset %" PRIu64, end_of_file);
1644
1645 // Update the primary level table entry to point to the new location of the secondary table
1646 uint64_t new_secondary_table_block_offset = end_of_file >> ctx->user_data_ddt_header.blockAlignmentShift;
1647
1648 ctx->user_data_ddt2[ctx->cached_ddt_position] = new_secondary_table_block_offset;
1649 ctx->dirty_primary_ddt = true; // Mark primary DDT as dirty
1650
1651 // Write the updated primary table back to its original position in the file
1652 aaru_off_t saved_pos = aaruf_ftell(ctx->imageStream);
1653 aaruf_fseek(ctx->imageStream, (aaru_off_t)(ctx->primary_ddt_offset + sizeof(DdtHeader2)), SEEK_SET);
1654
1655 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
1656
1657 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
1658
1659 if(written_bytes != 1)
1660 {
1661 FATAL("Could not flush primary DDT table to file after writing never-written secondary table.");
1662 TRACE("Exiting set_ddt_multi_level_v2() = false");
1663 return false;
1664 }
1665
1666 // Update nextBlockPosition to ensure future blocks don't overwrite the DDT
1667 uint64_t ddt_total_size = sizeof(DdtHeader2) + ddt_header.length;
1668 ctx->next_block_position = end_of_file + ddt_total_size + alignment_mask & ~alignment_mask;
1669 block_offset = ctx->next_block_position;
1670 offset = 0;
1671 TRACE("Updated nextBlockPosition after never-written DDT write to %" PRIu64, ctx->next_block_position);
1672
1673 // Free the cached table
1674
1675 free(ctx->cached_secondary_ddt2);
1676 ctx->cached_secondary_ddt2 = NULL;
1677
1678 // Reset cached values since we've written and freed the table
1679 ctx->cached_ddt_offset = 0;
1680 ctx->cached_ddt_position = 0;
1681
1682 // Restore file position
1683 aaruf_fseek(ctx->imageStream, saved_pos, SEEK_SET);
1684
1685 TRACE("Successfully wrote never-written cached secondary DDT to disk");
1686 }
1687 else
1688 // The cached DDT is actually for the requested block range, so we can use it directly
1689 TRACE("Cached DDT is for the correct block range, using it directly");
1690 // No need to write to disk, just continue with the cached table
1691 }
1692
1693 // Step 3: Write the currently in-memory cached secondary level table to the end of the file
1694 if(ctx->cached_ddt_offset != 0)
1695 {
1696 aaru_off_t current_pos = 0;
1697 // Close the current data block first
1698 if(ctx->writing_buffer != NULL) aaruf_close_current_block(ctx);
1699
1700 // Get current position and seek to end of file
1701 current_pos = aaruf_ftell(ctx->imageStream);
1702 aaruf_fseek(ctx->imageStream, 0, SEEK_END);
1703 end_of_file = aaruf_ftell(ctx->imageStream);
1704
1705 // Align to block boundary
1706 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1707 end_of_file = end_of_file + alignment_mask & ~alignment_mask;
1708 aaruf_fseek(ctx->imageStream, end_of_file, SEEK_SET);
1709
1710 // Prepare DDT header for the cached table
1711 memset(&ddt_header, 0, sizeof(DdtHeader2));
1713 ddt_header.type = kDataTypeUserData;
1714 ddt_header.compression =
1716 ddt_header.levels = ctx->user_data_ddt_header.levels;
1717 ddt_header.tableLevel = ctx->user_data_ddt_header.tableLevel + 1;
1718 ddt_header.previousLevelOffset = ctx->primary_ddt_offset; // Set to primary DDT table location
1719 ddt_header.negative = ctx->user_data_ddt_header.negative;
1720 ddt_header.blocks = items_per_ddt_entry;
1721 ddt_header.overflow = ctx->user_data_ddt_header.overflow;
1722 ddt_header.start = ddt_position * items_per_ddt_entry; // First block this DDT table references
1724 ddt_header.dataShift = ctx->user_data_ddt_header.dataShift;
1725 ddt_header.tableShift = 0; // Secondary tables are single level
1726 ddt_header.entries = items_per_ddt_entry;
1727
1728 // Calculate data size
1729
1730 ddt_header.length = items_per_ddt_entry * sizeof(uint64_t);
1731
1732 // Calculate CRC64 of the data
1733 crc64_context = aaruf_crc64_init();
1734 if(crc64_context == NULL)
1735 {
1736 FATAL("Could not initialize CRC64.");
1737 TRACE("Exiting set_ddt_multi_level_v2() = false");
1738 return false;
1739 }
1740
1741 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length);
1742
1743 aaruf_crc64_final(crc64_context, &crc64);
1744 aaruf_crc64_free(crc64_context);
1745 ddt_header.crc64 = crc64;
1746
1747 uint8_t *cmp_buffer = NULL;
1748 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1749
1750 if(ddt_header.compression == kCompressionNone)
1751 {
1752
1753 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1754 ddt_header.cmpCrc64 = ddt_header.crc64;
1755 }
1756 else
1757 {
1758 cmp_buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression
1759 if(cmp_buffer == NULL)
1760 {
1761 TRACE("Failed to allocate memory for secondary DDT v2 compression");
1763 }
1764
1765 size_t dst_size;
1766
1767 if(ctx->use_zstd)
1768 {
1769 dst_size = aaruf_zstd_encode_buffer(cmp_buffer, (size_t)ddt_header.length * 2,
1770 (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length,
1771 ctx->zstd_level, ctx->num_threads);
1772 if(dst_size == 0) dst_size = ddt_header.length;
1773 }
1774 else
1775 {
1776 dst_size = (size_t)ddt_header.length * 2 * 2;
1777 size_t props_size = LZMA_PROPERTIES_LENGTH;
1778 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->cached_secondary_ddt2,
1779 ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0,
1780 2, 273, LZMA_THREADS(ctx));
1781 }
1782
1783 ddt_header.cmpLength = (uint32_t)dst_size;
1784
1785 if(ddt_header.cmpLength >= ddt_header.length)
1786 {
1787 ddt_header.compression = kCompressionNone;
1788 free(cmp_buffer);
1789
1790 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1791 }
1792 }
1793
1794 if(ddt_header.compression == kCompressionNone)
1795 {
1796 ddt_header.cmpLength = ddt_header.length;
1797 ddt_header.cmpCrc64 = ddt_header.crc64;
1798 }
1799 else
1800 {
1801 ddt_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, (uint32_t)ddt_header.cmpLength);
1802 if(ctx->use_zstd) ctx->has_zstd_blocks = true;
1803 }
1804
1805 if(ddt_header.compression == kCompressionLzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
1806
1807 // Write header
1808 if(ddt_header.compression == kCompressionLzma)
1809 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1810
1811 written_bytes = fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
1812 if(written_bytes != 1)
1813 {
1814 FATAL("Could not write DDT header to file.");
1815 TRACE("Exiting set_ddt_multi_level_v2() = false");
1816 return false;
1817 }
1818
1819 // Write data
1820 written_bytes = fwrite(cmp_buffer, ddt_header.cmpLength, 1, ctx->imageStream);
1821
1822 if(written_bytes != 1)
1823 {
1824 FATAL("Could not write DDT data to file.");
1825 TRACE("Exiting set_ddt_multi_level_v2() = false");
1826 return false;
1827 }
1828
1829 if(ddt_header.compression != kCompressionNone) free(cmp_buffer);
1830
1831 // Update index: remove old entry and add new one for the evicted secondary DDT
1832 TRACE("Updating index for evicted secondary DDT");
1833
1834 // Remove old index entry for the cached DDT
1835 if(ctx->cached_ddt_offset != 0)
1836 {
1837 TRACE("Removing old index entry for DDT at offset %" PRIu64, ctx->cached_ddt_offset);
1838 IndexEntry *entry = NULL;
1839
1840 // Find and remove the old index entry
1841 for(unsigned int i = 0; i < utarray_len(ctx->index_entries); i++)
1842 {
1843 entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, i);
1844 if(entry && entry->offset == ctx->cached_ddt_offset &&
1846 {
1847 TRACE("Found old DDT index entry at position %u, removing", i);
1848 utarray_erase(ctx->index_entries, i, 1);
1849 break;
1850 }
1851 }
1852 }
1853
1854 // Add new index entry for the newly written secondary DDT
1855 IndexEntry new_ddt_entry;
1856 new_ddt_entry.blockType = DeDuplicationTableSecondary;
1857 new_ddt_entry.dataType = kDataTypeUserData;
1858 new_ddt_entry.offset = end_of_file;
1859
1860 utarray_push_back(ctx->index_entries, &new_ddt_entry);
1861 ctx->dirty_index_block = true;
1862 TRACE("Added new DDT index entry at offset %" PRIu64, end_of_file);
1863
1864 // Step 4: Update the primary level table entry and flush it back to file
1865 uint64_t new_secondary_table_block_offset = end_of_file >> ctx->user_data_ddt_header.blockAlignmentShift;
1866
1867 // Update the primary table entry to point to the new location of the secondary table
1868 // Use ddtPosition which was calculated from sectorAddress, not cachedDdtOffset
1869
1870 ctx->user_data_ddt2[ddt_position] = new_secondary_table_block_offset;
1871 ctx->dirty_primary_ddt = true; // Mark primary DDT as dirty
1872
1873 // Write the updated primary table back to its original position in the file
1874 aaru_off_t saved_pos = aaruf_ftell(ctx->imageStream);
1875 aaruf_fseek(ctx->imageStream, (aaru_off_t)(ctx->primary_ddt_offset + sizeof(DdtHeader2)), SEEK_SET);
1876
1877 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
1878
1879 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
1880
1881 if(written_bytes != 1)
1882 {
1883 FATAL("Could not flush primary DDT table to file.");
1884 TRACE("Exiting set_ddt_multi_level_v2() = false");
1885 return false;
1886 }
1887
1888 // Update nextBlockPosition to ensure future blocks don't overwrite the DDT
1889 uint64_t ddt_total_size = sizeof(DdtHeader2) + ddt_header.length;
1890 ctx->next_block_position = end_of_file + ddt_total_size + alignment_mask & ~alignment_mask;
1891 block_offset = ctx->next_block_position;
1892 offset = 0;
1893 TRACE("Updated nextBlockPosition after DDT write to %" PRIu64, ctx->next_block_position);
1894
1895 aaruf_fseek(ctx->imageStream, saved_pos, SEEK_SET);
1896
1897 // Free the cached table
1898
1899 free(ctx->cached_secondary_ddt2);
1900 ctx->cached_secondary_ddt2 = NULL;
1901
1902 // Restore file position
1903 aaruf_fseek(ctx->imageStream, current_pos, SEEK_SET);
1904 }
1905
1906 // Step 5: Check if the specified block already has an existing secondary level table
1907 create_new_table = ctx->cached_secondary_ddt2 == NULL;
1908
1909 if(!create_new_table && secondary_ddt_offset != 0)
1910 {
1911 // Load existing table
1912 aaruf_fseek(ctx->imageStream, (aaru_off_t)secondary_ddt_offset, SEEK_SET);
1913 size_t read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
1914
1915 if(read_bytes != sizeof(DdtHeader2) || ddt_header.identifier != DeDuplicationTable2 ||
1916 ddt_header.type != kDataTypeUserData)
1917 {
1918 FATAL("Invalid secondary DDT header at %" PRIu64, secondary_ddt_offset);
1919 TRACE("Exiting set_ddt_multi_level_v2() = false");
1920 return false;
1921 }
1922
1923 // Read the table data (assuming no compression for now)
1924 buffer = malloc(ddt_header.length);
1925 if(buffer == NULL)
1926 {
1927 FATAL("Cannot allocate memory for secondary DDT.");
1928 TRACE("Exiting set_ddt_multi_level_v2() = false");
1929 return false;
1930 }
1931
1932 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
1933 if(read_bytes != ddt_header.length)
1934 {
1935 FATAL("Could not read secondary DDT data.");
1936 free(buffer);
1937 TRACE("Exiting set_ddt_multi_level_v2() = false");
1938 return false;
1939 }
1940
1941 // Verify CRC
1942 crc64_context = aaruf_crc64_init();
1943 if(crc64_context == NULL)
1944 {
1945 FATAL("Could not initialize CRC64.");
1946 free(buffer);
1947 TRACE("Exiting set_ddt_multi_level_v2() = false");
1948 return false;
1949 }
1950
1951 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1952 aaruf_crc64_final(crc64_context, &crc64);
1953 aaruf_crc64_free(crc64_context);
1954
1955 if(crc64 != ddt_header.crc64)
1956 {
1957 FATAL("Secondary DDT CRC mismatch. Expected 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1958 free(buffer);
1959 TRACE("Exiting set_ddt_multi_level_v2() = false");
1960 return false;
1961 }
1962
1963 // Cache the loaded table
1964
1965 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1966
1967 ctx->cached_ddt_offset = secondary_ddt_offset;
1968 }
1969
1970 if(create_new_table)
1971 {
1972 // Create a new empty table
1973 size_t table_size = items_per_ddt_entry * sizeof(uint64_t);
1974
1975 buffer = calloc(1, table_size);
1976 if(buffer == NULL)
1977 {
1978 FATAL("Cannot allocate memory for new secondary DDT.");
1979 TRACE("Exiting set_ddt_multi_level_v2() = false");
1980 return false;
1981 }
1982
1983 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1984
1985 ctx->cached_ddt_offset = 0; // Will be set when written to file
1986 ctx->cached_ddt_position = ddt_position; // Track which primary DDT position this new table belongs to
1987 TRACE("Created new secondary DDT for position %" PRIu64, ddt_position);
1988 }
1989
1990 // Step 6: Update the corresponding DDT entry
1991 if(*ddt_entry == 0)
1992 {
1993 block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1994 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1995 block_index << ctx->user_data_ddt_header.dataShift;
1996
1997 // Overflow detection for DDT entry
1998 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1999 {
2000 FATAL("DDT overflow: media does not fit in big DDT");
2001 TRACE("Exiting set_ddt_multi_level_v2() = false");
2002 return false;
2003 }
2004 }
2005
2006 // Sector status can be different from previous deduplicated sector
2007 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
2008 *ddt_entry |= (uint64_t)sector_status << 60;
2009
2010 TRACE("Setting big secondary DDT entry %d to %ull", sector_address % items_per_ddt_entry, (uint64_t)*ddt_entry);
2011 ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry] = *ddt_entry;
2012 ctx->dirty_secondary_ddt = true;
2013
2014 TRACE("Updated secondary DDT entry at position %" PRIu64, sector_address % items_per_ddt_entry);
2015 TRACE("Exiting set_ddt_multi_level_v2() = true");
2016 return true;
2017}
2018
2135bool set_ddt_tape(aaruformat_context *ctx, uint64_t sector_address, const uint64_t offset, const uint64_t block_offset,
2136 const uint8_t sector_status, uint64_t *ddt_entry)
2137{
2138 TRACE("Entering set_ddt_tape(%p, %" PRIu64 ", %llu, %llu, %d)", ctx, sector_address, offset, block_offset,
2139 sector_status);
2140
2141 // Check if the context and image stream are valid
2142 if(ctx == NULL || ctx->imageStream == NULL)
2143 {
2144 FATAL("Invalid context or image stream.");
2145 TRACE("Exiting set_ddt_tape() = false");
2146 return false;
2147 }
2148
2149 // Should not really be here
2150 if(!ctx->is_tape)
2151 {
2152 FATAL("Image is not tape, wrong function called.");
2153 TRACE("Exiting set_ddt_tape() = false");
2154 return false;
2155 }
2156
2157 if(*ddt_entry == 0)
2158 {
2159 const uint64_t block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
2160 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
2161 block_index << ctx->user_data_ddt_header.dataShift;
2162 // Overflow detection for DDT entry
2163 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
2164 {
2165 FATAL("DDT overflow: media does not fit in big DDT");
2166 TRACE("Exiting set_ddt_tape() = false");
2167 return false;
2168 }
2169
2170 *ddt_entry |= (uint64_t)sector_status << 60;
2171 }
2172
2173 // Create DDT hash entry
2174 TapeDdtHashEntry *new_entry = calloc(1, sizeof(TapeDdtHashEntry));
2175 TapeDdtHashEntry *old_entry = NULL;
2176 if(new_entry == NULL)
2177 {
2178 FATAL("Cannot allocate memory for new tape DDT hash entry.");
2179 TRACE("Exiting set_ddt_tape() = false");
2180 return false;
2181 }
2182
2183 TRACE("Setting tape DDT entry %d to %u", sector_address, (uint32_t)*ddt_entry);
2184
2185 new_entry->key = sector_address;
2186 new_entry->value = *ddt_entry;
2187
2188 // Insert entry into tape DDT
2189 HASH_REPLACE(hh, ctx->tape_ddt, key, sizeof(uint64_t), new_entry, old_entry);
2190 ctx->dirty_tape_ddt = true; // Mark tape DDT as dirty
2191 if(old_entry) free(old_entry);
2192
2193 TRACE("Exiting set_ddt_tape() = true");
2194 return true;
2195}
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:86
int32_t aaruf_lzma_encode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t src_size, uint8_t *out_props, size_t *out_props_size, int32_t level, uint32_t dict_size, int32_t lc, int32_t lp, int32_t pb, int32_t fb, int32_t num_threads)
Encodes a buffer using LZMA compression.
Definition lzma.c:65
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
int32_t aaruf_lzma_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t *src_size, const uint8_t *props, size_t props_size)
Decodes an LZMA-compressed buffer.
Definition lzma.c:39
int aaruf_crc64_update(crc64_ctx *ctx, const uint8_t *data, uint32_t len)
Updates the CRC64 context with new data.
Definition crc64.c:55
size_t aaruf_zstd_encode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size, int level, int num_threads)
Encodes a buffer using Zstandard compression.
Definition zstd.c:59
void aaruf_crc64_free(crc64_ctx *ctx)
Frees a CRC64 context.
Definition crc64.c:155
size_t aaruf_zstd_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size)
Decodes a Zstandard-compressed buffer.
Definition zstd.c:34
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
crc64_ctx * aaruf_crc64_init(void)
Initializes a CRC64 context.
Definition crc64.c:32
@ DeDuplicationTableSecondary
Block containing a secondary deduplication table (v2).
Definition enums.h:168
@ DeDuplicationTable2
Block containing a deduplication table v2.
Definition enums.h:166
@ DeDuplicationTableSAlpha
Block containing a secondary deduplication table (v2) (mistake).
Definition enums.h:167
@ SectorStatusNotDumped
Sector(s) not yet acquired during image dumping.
Definition enums.h:258
@ kDataTypeCdSectorSuffix
Compact Disc sector suffix (EDC, ECC P, ECC Q).
Definition enums.h:117
@ kDataTypeUserData
User (main) data.
Definition enums.h:48
@ kDataTypeCdSectorPrefix
Compact Disc sector prefix (sync, header).
Definition enums.h:116
@ kCompressionLzma
LZMA compression.
Definition enums.h:34
@ kCompressionNone
Not compressed.
Definition enums.h:33
@ kCompressionZstd
Zstandard compression.
Definition enums.h:37
int32_t ec_recover_ddt_block(aaruformat_context *ctx, uint64_t block_offset, uint8_t **recovered_data, uint32_t *recovered_size)
Attempt to recover a DDT secondary block at a given file offset.
Definition erasure.c:918
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:82
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
#define AARUF_ERROR_CANNOT_READ_BLOCK
Generic block read failure (seek/read error).
Definition errors.h:46
#define AARUF_ERROR_INVALID_BLOCK_CRC
CRC64 mismatch indicating corruption.
Definition errors.h:57
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
#define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK
Decompression routine failed or size mismatch.
Definition errors.h:56
bool set_ddt_entry_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset, uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
Sets a DDT v2 entry for a given sector address.
Definition ddt_v2.c:1301
int32_t process_ddt_v2(aaruformat_context *ctx, IndexEntry *entry, bool *found_user_data_ddt)
Processes a DDT v2 block from the image stream.
Definition ddt_v2.c:97
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1569
int32_t decode_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset, uint64_t *block_offset, uint8_t *sector_status)
Decodes a single-level DDT v2 entry for a given sector address.
Definition ddt_v2.c:805
static int aaruf_fseek(FILE *stream, aaru_off_t offset, int origin)
Definition internal.h:46
static aaru_off_t aaruf_ftell(FILE *stream)
Definition internal.h:52
bool set_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset, uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
Sets a multi-level DDT v2 entry for a given sector address.
Definition ddt_v2.c:1409
bool set_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset, uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
Sets a single-level DDT v2 entry for a given sector address.
Definition ddt_v2.c:1337
int32_t decode_ddt_entry_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset, uint64_t *block_offset, uint8_t *sector_status)
Decodes a DDT v2 entry for a given sector address.
Definition ddt_v2.c:663
bool set_ddt_tape(aaruformat_context *ctx, uint64_t sector_address, uint64_t offset, uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
Sets a DDT entry for tape media using a hash-based lookup table.
Definition ddt_v2.c:2135
int32_t decode_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset, uint64_t *block_offset, uint8_t *sector_status)
Decodes a multi-level DDT v2 entry for a given sector address.
Definition ddt_v2.c:948
#define LZMA_THREADS(ctx)
Clamp num_threads to LZMA's valid range [1, 2].
Definition internal.h:23
int64_t aaru_off_t
Definition internal.h:42
#define FATAL(fmt,...)
Definition log.h:40
#define TRACE(fmt,...)
Definition log.h:25
Header preceding a version 2 hierarchical deduplication table.
Definition ddt.h:142
uint64_t cmpCrc64
CRC64-ECMA of compressed table payload.
Definition ddt.h:161
uint16_t type
Data classification (DataType) for sectors referenced by this table.
Definition ddt.h:144
uint64_t start
Base internal index covered by this table (used for secondary tables; currently informational).
Definition ddt.h:153
uint64_t crc64
CRC64-ECMA of uncompressed table payload.
Definition ddt.h:162
uint64_t entries
Number of entries contained in (uncompressed) table payload.
Definition ddt.h:158
uint8_t levels
Total number of hierarchy levels (root depth); > 0.
Definition ddt.h:146
uint64_t length
Uncompressed payload size in bytes.
Definition ddt.h:160
uint32_t identifier
Block identifier, must be BlockType::DeDuplicationTable2.
Definition ddt.h:143
uint8_t tableShift
2^tableShift = number of logical sectors per primary entry (multi-level only; 0 for single-level or s...
Definition ddt.h:156
uint64_t blocks
Total internal span (negative + usable + overflow) in logical sectors.
Definition ddt.h:150
uint8_t blockAlignmentShift
2^blockAlignmentShift = block alignment boundary in bytes.
Definition ddt.h:154
uint32_t overflow
Trailing dumped sectors beyond user area (overflow range), still mapped with entries.
Definition ddt.h:151
uint32_t negative
Leading negative LBA count; added to external L to build internal index.
Definition ddt.h:149
uint8_t tableLevel
Zero-based level index of this table (0 = root, increases downward).
Definition ddt.h:147
uint16_t compression
Compression algorithm for this table body (CompressionType).
Definition ddt.h:145
uint8_t dataShift
2^dataShift = sectors represented per increment in blockIndex field.
Definition ddt.h:155
uint64_t cmpLength
Compressed payload size in bytes.
Definition ddt.h:159
uint64_t previousLevelOffset
Absolute byte offset of the parent (previous) level table; 0 if root.
Definition ddt.h:148
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:937
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:938
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
uint16_t dataType
Data classification (value from DataType) or unused for untyped blocks.
Definition index.h:111
uint64_t key
Key: sector address.
Definition context.h:148
uint64_t value
Value: DDT entry.
Definition context.h:149
Master context representing an open or in‑creation Aaru image.
Definition context.h:178
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:195
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:311
bool dirty_primary_ddt
True if primary DDT table should be written during close.
Definition context.h:330
bool ec_recovery_in_progress
Recursion guard for recovery (prevents infinite loops).
Definition context.h:419
uint64_t * user_data_ddt2
DDT entries (big variant) primary/secondary current.
Definition context.h:193
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:192
uint64_t cached_ddt_offset
File offset of currently cached secondary DDT (0=none).
Definition context.h:196
bool is_tape
True if the image is a tape image.
Definition context.h:320
bool in_memory_ddt
True if primary (and possibly secondary) DDT loaded.
Definition context.h:202
bool dirty_index_block
True if index block should be written during close.
Definition context.h:354
TapeDdtHashEntry * tape_ddt
Hash table root for tape DDT entries.
Definition context.h:188
bool dirty_single_level_ddt
True if single-level DDT should be written during close.
Definition context.h:331
int ddt_version
DDT version in use (1=legacy, 2=v2 hierarchical).
Definition context.h:200
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:290
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:191
uint64_t primary_ddt_offset
File offset of the primary DDT v2 table.
Definition context.h:198
uint64_t next_block_position
Absolute file offset where next block will be written.
Definition context.h:292
bool has_zstd_blocks
True if any block was actually written with Zstandard compression.
Definition context.h:313
uint64_t * cached_secondary_ddt2
Cached secondary table (big entries) or NULL.
Definition context.h:194
int zstd_level
Zstandard compression level (writing path, default 19).
Definition context.h:314
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:182
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:262
int num_threads
Compression worker threads (1 = single-threaded, default).
Definition context.h:315
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:270
bool use_zstd
Use Zstandard instead of LZMA for data blocks.
Definition context.h:312
bool dirty_secondary_ddt
True if secondary DDT tables should be written during close.
Definition context.h:329
bool dirty_tape_ddt
True if tape DDT should be written during close.
Definition context.h:344
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:309
uint64_t cached_ddt_position
Position index of cached secondary DDT.
Definition context.h:197
bool ec_recovery_available
True if ECMB loaded and recovery is possible.
Definition context.h:418
Minimal ECMA-182 CRC64 incremental state container (running value only).
Definition crc64.h:56