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 "internal.h"
26#include "log.h"
27
96int32_t process_ddt_v2(aaruformat_context *ctx, IndexEntry *entry, bool *found_user_data_ddt)
97{
98 TRACE("Entering process_ddt_v2(%p, %p, %d)", ctx, entry, *found_user_data_ddt);
99
100 int pos = 0;
101 size_t read_bytes = 0;
102 DdtHeader2 ddt_header;
103 uint8_t *cmp_data = NULL;
104 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH];
105 size_t lzma_size = 0;
106 int error_no = 0;
107 crc64_ctx *crc64_context = NULL;
108 uint64_t crc64 = 0;
109 uint8_t *buffer = NULL;
110
111 // Check if the context and image stream are valid
112 if(ctx == NULL || ctx->imageStream == NULL)
113 {
114 FATAL("Invalid context or image stream.");
115
116 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_NOT_AARUFORMAT");
118 }
119
120 // Seek to block
121 pos = aaruf_fseek(ctx->imageStream, (aaru_off_t)entry->offset, SEEK_SET);
122 if(pos < 0 || aaruf_ftell(ctx->imageStream) != (aaru_off_t)entry->offset)
123 {
124 FATAL("Could not seek to %" PRIu64 " as indicated by index entry...", entry->offset);
125
126 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
128 }
129
130 // Even if those two checks shall have been done before
131 TRACE("Reading DDT block header at position %" PRIu64, entry->offset);
132 read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
133
134 if(read_bytes != sizeof(DdtHeader2))
135 {
136 FATAL("Could not read block header at %" PRIu64 "", entry->offset);
137
138 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
140 }
141
142 ctx->image_info.ImageSize += ddt_header.cmpLength;
143
144 if(entry->dataType == kDataTypeUserData)
145 {
146 // User area sectors is blocks stored in DDT minus the negative and overflow displacement blocks
147 ctx->image_info.Sectors = ddt_header.blocks - ddt_header.negative - ddt_header.overflow;
148 // We need the header later for the shift calculations
149 ctx->user_data_ddt_header = ddt_header;
150 ctx->ddt_version = 2;
151 // Store the primary DDT table's file offset for secondary table references
152 ctx->primary_ddt_offset = entry->offset;
153
154 // Check for DDT compression
155 switch(ddt_header.compression)
156 {
157 case kCompressionLzma:
158 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
159 {
160 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
161 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
163 }
164
165 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
166
167 cmp_data = (uint8_t *)malloc(lzma_size);
168 if(cmp_data == NULL)
169 {
170 TRACE("Cannot allocate memory for DDT, continuing...");
171 break;
172 }
173
174 buffer = malloc(ddt_header.length);
175 if(buffer == NULL)
176 {
177 TRACE("Cannot allocate memory for DDT, continuing...");
178 free(cmp_data);
179 break;
180 }
181
182 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
183 if(read_bytes != LZMA_PROPERTIES_LENGTH)
184 {
185 TRACE("Could not read LZMA properties, continuing...");
186 free(cmp_data);
187 free(buffer);
188 break;
189 }
190
191 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
192 if(read_bytes != lzma_size)
193 {
194 TRACE("Could not read compressed block, continuing...");
195 free(cmp_data);
196 free(buffer);
197 break;
198 }
199
200 read_bytes = ddt_header.length;
201 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
202 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
204
205 if(error_no != 0)
206 {
207 FATAL("Got error %d from LZMA, stopping...", error_no);
208 free(cmp_data);
209 free(buffer);
210 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
212 }
213
214 if(read_bytes != ddt_header.length)
215 {
216 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
217 free(cmp_data);
218 free(buffer);
219 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
221 }
222
223 free(cmp_data);
224
225 crc64_context = aaruf_crc64_init();
226
227 if(crc64_context == NULL)
228 {
229 FATAL("Could not initialize CRC64.");
230 free(buffer);
231
232 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
234 }
235
236 aaruf_crc64_update(crc64_context, buffer, read_bytes);
237 aaruf_crc64_final(crc64_context, &crc64);
238 aaruf_crc64_free(crc64_context);
239
240 if(crc64 != ddt_header.crc64)
241 {
242 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
243 free(buffer);
244 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
246 }
247
248 ctx->user_data_ddt2 = (uint64_t *)buffer;
249
250 ctx->in_memory_ddt = true;
251 *found_user_data_ddt = true;
252
253 break;
254 case kCompressionZstd:
255 if(ddt_header.cmpLength == 0)
256 {
257 FATAL("Compressed DDT payload has zero length for zstd.");
258 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
260 }
261
262 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
263 if(cmp_data == NULL)
264 {
265 TRACE("Cannot allocate memory for DDT, continuing...");
266 break;
267 }
268
269 buffer = malloc(ddt_header.length);
270 if(buffer == NULL)
271 {
272 TRACE("Cannot allocate memory for DDT, continuing...");
273 free(cmp_data);
274 break;
275 }
276
277 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
278 if(read_bytes != ddt_header.cmpLength)
279 {
280 TRACE("Could not read compressed block, continuing...");
281 free(cmp_data);
282 free(buffer);
283 break;
284 }
285
286 read_bytes =
287 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
288
289 if(read_bytes != ddt_header.length)
290 {
291 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
292 free(cmp_data);
293 free(buffer);
294 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
296 }
297
298 free(cmp_data);
299
300 crc64_context = aaruf_crc64_init();
301
302 if(crc64_context == NULL)
303 {
304 FATAL("Could not initialize CRC64.");
305 free(buffer);
306 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
308 }
309
310 aaruf_crc64_update(crc64_context, buffer, read_bytes);
311 aaruf_crc64_final(crc64_context, &crc64);
312 aaruf_crc64_free(crc64_context);
313
314 if(crc64 != ddt_header.crc64)
315 {
316 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
317 free(buffer);
318 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
320 }
321
322 ctx->user_data_ddt2 = (uint64_t *)buffer;
323
324 ctx->in_memory_ddt = true;
325 *found_user_data_ddt = true;
326
327 break;
328 case kCompressionNone:
329 buffer = malloc(ddt_header.length);
330
331 if(buffer == NULL)
332 {
333 TRACE("Cannot allocate memory for DDT, continuing...");
334 break;
335 }
336
337 TRACE("Reading DDT of length %zu bytes", ddt_header.length);
338 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
339
340 if(read_bytes != ddt_header.length)
341 {
342 free(buffer);
343 FATAL("Could not read deduplication table, continuing...");
344 break;
345 }
346
347 crc64_context = aaruf_crc64_init();
348
349 if(crc64_context == NULL)
350 {
351 FATAL("Could not initialize CRC64.");
352 free(buffer);
353 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
355 }
356
357 aaruf_crc64_update(crc64_context, buffer, read_bytes);
358 aaruf_crc64_final(crc64_context, &crc64);
359 aaruf_crc64_free(crc64_context);
360
361 if(crc64 != ddt_header.crc64)
362 {
363 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
364 free(buffer);
365 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
367 }
368
369 ctx->user_data_ddt2 = (uint64_t *)buffer;
370
371 ctx->in_memory_ddt = true;
372 *found_user_data_ddt = true;
373
374 break;
375 default:
376 TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
377 *found_user_data_ddt = false;
378 break;
379 }
380 }
382 switch(ddt_header.compression)
383 {
384 case kCompressionLzma:
385 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
386 {
387 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
388 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
390 }
391
392 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
393
394 cmp_data = (uint8_t *)malloc(lzma_size);
395 if(cmp_data == NULL)
396 {
397 TRACE("Cannot allocate memory for DDT, continuing...");
398 break;
399 }
400
401 buffer = malloc(ddt_header.length);
402 if(buffer == NULL)
403 {
404 TRACE("Cannot allocate memory for DDT, continuing...");
405 free(cmp_data);
406 break;
407 }
408
409 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
410 if(read_bytes != LZMA_PROPERTIES_LENGTH)
411 {
412 TRACE("Could not read LZMA properties, continuing...");
413 free(cmp_data);
414 free(buffer);
415 break;
416 }
417
418 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
419 if(read_bytes != lzma_size)
420 {
421 TRACE("Could not read compressed block, continuing...");
422 free(cmp_data);
423 free(buffer);
424 break;
425 }
426
427 read_bytes = ddt_header.length;
428 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
429 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
431
432 if(error_no != 0)
433 {
434 FATAL("Got error %d from LZMA, stopping...", error_no);
435 free(cmp_data);
436 free(buffer);
437 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
439 }
440
441 if(read_bytes != ddt_header.length)
442 {
443 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
444 free(cmp_data);
445 free(buffer);
446 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
448 }
449
450 free(cmp_data);
451 cmp_data = NULL;
452
453 crc64_context = aaruf_crc64_init();
454
455 if(crc64_context == NULL)
456 {
457 FATAL("Could not initialize CRC64.");
458 free(buffer);
459 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
461 }
462
463 aaruf_crc64_update(crc64_context, buffer, read_bytes);
464 aaruf_crc64_final(crc64_context, &crc64);
465 aaruf_crc64_free(crc64_context);
466
467 if(crc64 != ddt_header.crc64)
468 {
469 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
470 free(buffer);
471 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
473 }
474
476 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
477 else if(entry->dataType == kDataTypeCdSectorSuffix)
478 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
479 else
480 free(buffer);
481
482 break;
483 case kCompressionZstd:
484 if(ddt_header.cmpLength == 0)
485 {
486 FATAL("Compressed DDT payload has zero length for zstd.");
487 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
489 }
490
491 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
492 if(cmp_data == NULL)
493 {
494 TRACE("Cannot allocate memory for DDT, continuing...");
495 break;
496 }
497
498 buffer = malloc(ddt_header.length);
499 if(buffer == NULL)
500 {
501 TRACE("Cannot allocate memory for DDT, continuing...");
502 free(cmp_data);
503 break;
504 }
505
506 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
507 if(read_bytes != ddt_header.cmpLength)
508 {
509 TRACE("Could not read compressed block, continuing...");
510 free(cmp_data);
511 free(buffer);
512 break;
513 }
514
515 read_bytes =
516 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
517
518 if(read_bytes != ddt_header.length)
519 {
520 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
521 free(cmp_data);
522 free(buffer);
523 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
525 }
526
527 free(cmp_data);
528 cmp_data = NULL;
529
530 crc64_context = aaruf_crc64_init();
531
532 if(crc64_context == NULL)
533 {
534 FATAL("Could not initialize CRC64.");
535 free(buffer);
536 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
538 }
539
540 aaruf_crc64_update(crc64_context, buffer, read_bytes);
541 aaruf_crc64_final(crc64_context, &crc64);
542 aaruf_crc64_free(crc64_context);
543
544 if(crc64 != ddt_header.crc64)
545 {
546 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
547 free(buffer);
548 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
550 }
551
553 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
554 else if(entry->dataType == kDataTypeCdSectorSuffix)
555 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
556 else
557 free(buffer);
558
559 break;
560
561 case kCompressionNone:
562 buffer = malloc(ddt_header.length);
563
564 if(buffer == NULL)
565 {
566 TRACE("Cannot allocate memory for deduplication table.");
567 break;
568 }
569
570 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
571
572 if(read_bytes != ddt_header.length)
573 {
574 free(buffer);
575 FATAL("Could not read deduplication table, continuing...");
576 break;
577 }
578
579 crc64_context = aaruf_crc64_init();
580
581 if(crc64_context == NULL)
582 {
583 FATAL("Could not initialize CRC64.");
584 free(buffer);
585 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
587 }
588
589 aaruf_crc64_update(crc64_context, buffer, read_bytes);
590 aaruf_crc64_final(crc64_context, &crc64);
591 aaruf_crc64_free(crc64_context);
592
593 if(crc64 != ddt_header.crc64)
594 {
595 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
596 free(buffer);
597 TRACE("Exiting process_ddt_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
599 }
600
602 ctx->sector_prefix_ddt2 = (uint64_t *)buffer;
603 else if(entry->dataType == kDataTypeCdSectorSuffix)
604 ctx->sector_suffix_ddt2 = (uint64_t *)buffer;
605 else
606 free(buffer);
607
608 break;
609 default:
610 TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
611 break;
612 }
613
614 TRACE("Exiting process_ddt_v2() = AARUF_STATUS_OK");
615 return AARUF_STATUS_OK;
616}
617
662int32_t decode_ddt_entry_v2(aaruformat_context *ctx, const uint64_t sector_address, bool negative, uint64_t *offset,
663 uint64_t *block_offset, uint8_t *sector_status)
664{
665 TRACE("Entering decode_ddt_entry_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative, *offset,
666 *block_offset, *sector_status);
667 // Check if the context and image stream are valid
668 if(ctx == NULL || ctx->imageStream == NULL)
669 {
670 FATAL("Invalid context or image stream.");
671
672 TRACE("Exiting decode_ddt_entry_v2() = AARUF_ERROR_NOT_AARUFORMAT");
674 }
675
677 return decode_ddt_multi_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status);
678
679 return decode_ddt_single_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status);
680}
681
736int32_t decode_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset,
737 uint64_t *block_offset, uint8_t *sector_status)
738{
739 TRACE("Entering decode_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
740 *offset, *block_offset, *sector_status);
741
742 uint64_t ddt_entry = 0;
743
744 // Check if the context and image stream are valid
745 if(ctx == NULL || ctx->imageStream == NULL)
746 {
747 FATAL("Invalid context or image stream.");
748
749 TRACE("Exiting decode_ddt_single_level_v2() = AARUF_ERROR_NOT_AARUFORMAT");
751 }
752
753 // Should not really be here
754 if(ctx->user_data_ddt_header.tableShift != 0)
755 {
756 FATAL("DDT table shift is not zero, but we are in single-level DDT decoding.");
757 TRACE("Exiting decode_ddt_single_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
759 }
760
761 // Calculate positive or negative sector
762 if(negative)
763 sector_address = ctx->user_data_ddt_header.negative - sector_address;
764 else
765 sector_address += ctx->user_data_ddt_header.negative;
766
767 ddt_entry = ctx->user_data_ddt2[sector_address];
768
769 if(ddt_entry == 0)
770 {
771 *sector_status = SectorStatusNotDumped;
772 *offset = 0;
773 *block_offset = 0;
774 TRACE("Exiting decode_ddt_single_level_v2(%p, %" PRIu64 ", %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
775 sector_address, *offset, *block_offset, *sector_status);
776 return AARUF_STATUS_OK;
777 }
778
779 *sector_status = ddt_entry >> 60;
780 ddt_entry &= 0xFFFFFFFFFFFFFFF;
781
782 const uint64_t offset_mask = (uint64_t)((1 << ctx->user_data_ddt_header.dataShift) - 1);
783 *offset = ddt_entry & offset_mask;
784 *block_offset =
786
787 TRACE("Exiting decode_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
788 sector_address, negative, *offset, *block_offset, *sector_status);
789 return AARUF_STATUS_OK;
790}
791
879int32_t decode_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t *offset,
880 uint64_t *block_offset, uint8_t *sector_status)
881{
882 TRACE("Entering decode_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
883 *offset, *block_offset, *sector_status);
884
885 uint64_t ddt_entry = 0;
886 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH];
887 size_t lzma_size = 0;
888 uint8_t *cmp_data = NULL;
889 uint8_t *buffer = NULL;
890 crc64_ctx *crc64_context = NULL;
891 uint64_t crc64 = 0;
892 int items_per_ddt_entry = 0;
893 uint64_t ddt_position = 0;
894 uint64_t secondary_ddt_offset = 0;
895
896 // Check if the context and image stream are valid
897 if(ctx == NULL || ctx->imageStream == NULL)
898 {
899 FATAL("Invalid context or image stream.");
900
901 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_NOT_AARUFORMAT");
903 }
904
905 // Should not really be here
906 if(ctx->user_data_ddt_header.tableShift == 0)
907 {
908 FATAL("DDT table shift is zero, but we are in multi-level DDT decoding.");
909 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
911 }
912
913 // Calculate positive or negative sector
914 if(negative)
915 sector_address = ctx->user_data_ddt_header.negative - sector_address;
916 else
917 sector_address += ctx->user_data_ddt_header.negative;
918
919 items_per_ddt_entry = 1 << ctx->user_data_ddt_header.tableShift;
920 ddt_position = sector_address / items_per_ddt_entry;
921 secondary_ddt_offset = ctx->user_data_ddt2[ddt_position];
922
923 // Position in file of the child DDT table
924 secondary_ddt_offset *= 1 << ctx->user_data_ddt_header.blockAlignmentShift;
925
926 // Is the one we have cached the same as the one we need to read?
927 if(ctx->cached_ddt_offset != secondary_ddt_offset)
928 {
929 int32_t error_no = 0;
930 aaruf_fseek(ctx->imageStream, (aaru_off_t)secondary_ddt_offset, SEEK_SET);
931 DdtHeader2 ddt_header;
932 size_t read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
933
934 if(read_bytes != sizeof(DdtHeader2))
935 {
936 FATAL("Could not read block header at %" PRIu64 "", secondary_ddt_offset);
937 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
939 }
940
941 if((ddt_header.identifier != DeDuplicationTableSecondary &&
942 ddt_header.identifier != DeDuplicationTableSAlpha) ||
943 ddt_header.type != kDataTypeUserData)
944 {
945 FATAL("Invalid block header at %" PRIu64 "", secondary_ddt_offset);
946 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
948 }
949
950 // Check for DDT compression
951 switch(ddt_header.compression)
952 {
953 case kCompressionLzma:
954 if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
955 {
956 FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
957 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
959 }
960
961 lzma_size = (size_t)(ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH);
962
963 cmp_data = (uint8_t *)malloc(lzma_size);
964 if(cmp_data == NULL)
965 {
966 FATAL("Cannot allocate memory for DDT, stopping...");
967 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
969 }
970
971 buffer = malloc(ddt_header.length);
972 if(buffer == NULL)
973 {
974 FATAL("Cannot allocate memory for DDT, stopping...");
975 free(cmp_data);
977 }
978
979 read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
980 if(read_bytes != LZMA_PROPERTIES_LENGTH)
981 {
982 FATAL("Could not read LZMA properties, stopping...");
983 free(cmp_data);
984 free(buffer);
985 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
987 }
988
989 read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
990 if(read_bytes != lzma_size)
991 {
992 FATAL("Could not read compressed block, stopping...");
993 free(cmp_data);
994 free(buffer);
995 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
997 }
998
999 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
1000 read_bytes = ddt_header.length;
1001 error_no = aaruf_lzma_decode_buffer(buffer, &read_bytes, cmp_data, &lzma_size, lzma_properties,
1003
1004 if(error_no != 0)
1005 {
1006 FATAL("Got error %d from LZMA, stopping...", error_no);
1007 free(cmp_data);
1008 free(buffer);
1009 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1011 }
1012
1013 if(read_bytes != ddt_header.length)
1014 {
1015 FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
1016 free(cmp_data);
1017 free(buffer);
1018 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1020 }
1021
1022 free(cmp_data);
1023
1024 crc64_context = aaruf_crc64_init();
1025
1026 if(crc64_context == NULL)
1027 {
1028 FATAL("Could not initialize CRC64.");
1029 free(buffer);
1030 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1032 }
1033
1034 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1035 aaruf_crc64_final(crc64_context, &crc64);
1036 aaruf_crc64_free(crc64_context);
1037
1038 if(crc64 != ddt_header.crc64)
1039 {
1040 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1041 free(buffer);
1042 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1044 }
1045
1046 // Free old cached DDT before replacing it
1047 free(ctx->cached_secondary_ddt2);
1048
1049 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1050
1051 ctx->cached_ddt_offset = secondary_ddt_offset;
1052
1053 break;
1054 case kCompressionZstd:
1055 if(ddt_header.cmpLength == 0)
1056 {
1057 FATAL("Compressed DDT payload has zero length for zstd.");
1058 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1060 }
1061
1062 cmp_data = (uint8_t *)malloc(ddt_header.cmpLength);
1063 if(cmp_data == NULL)
1064 {
1065 FATAL("Cannot allocate memory for DDT, stopping...");
1066 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1068 }
1069
1070 buffer = malloc(ddt_header.length);
1071 if(buffer == NULL)
1072 {
1073 FATAL("Cannot allocate memory for DDT, stopping...");
1074 free(cmp_data);
1076 }
1077
1078 read_bytes = fread(cmp_data, 1, ddt_header.cmpLength, ctx->imageStream);
1079 if(read_bytes != ddt_header.cmpLength)
1080 {
1081 FATAL("Could not read compressed block, stopping...");
1082 free(cmp_data);
1083 free(buffer);
1084 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1086 }
1087
1088 TRACE("Decompressing block of size %zu bytes", ddt_header.length);
1089 read_bytes =
1090 aaruf_zstd_decode_buffer(buffer, ddt_header.length, cmp_data, ddt_header.cmpLength);
1091
1092 if(read_bytes != ddt_header.length)
1093 {
1094 FATAL("Error decompressing zstd DDT, expected %zu got %zu", ddt_header.length, read_bytes);
1095 free(cmp_data);
1096 free(buffer);
1097 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK");
1099 }
1100
1101 free(cmp_data);
1102
1103 crc64_context = aaruf_crc64_init();
1104
1105 if(crc64_context == NULL)
1106 {
1107 FATAL("Could not initialize CRC64.");
1108 free(buffer);
1109 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1111 }
1112
1113 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1114 aaruf_crc64_final(crc64_context, &crc64);
1115 aaruf_crc64_free(crc64_context);
1116
1117 if(crc64 != ddt_header.crc64)
1118 {
1119 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1120 free(buffer);
1121 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1123 }
1124
1125 // Free old cached DDT before replacing it
1126 free(ctx->cached_secondary_ddt2);
1127
1128 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1129
1130 ctx->cached_ddt_offset = secondary_ddt_offset;
1131
1132 break;
1133 case kCompressionNone:
1134 buffer = malloc(ddt_header.length);
1135
1136 if(buffer == NULL)
1137 {
1138 FATAL("Cannot allocate memory for DDT, stopping...");
1139 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1141 }
1142
1143 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
1144
1145 if(read_bytes != ddt_header.length)
1146 {
1147 free(buffer);
1148 FATAL("Could not read deduplication table, stopping...");
1149 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1151 }
1152
1153 crc64_context = aaruf_crc64_init();
1154
1155 if(crc64_context == NULL)
1156 {
1157 FATAL("Could not initialize CRC64.");
1158 free(buffer);
1159 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1161 }
1162
1163 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1164 aaruf_crc64_final(crc64_context, &crc64);
1165 aaruf_crc64_free(crc64_context);
1166
1167 if(crc64 != ddt_header.crc64)
1168 {
1169 FATAL("Expected DDT CRC 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1170 free(buffer);
1171 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_INVALID_BLOCK_CRC");
1173 }
1174
1175 // Free old cached DDT before replacing it
1176 free(ctx->cached_secondary_ddt2);
1177
1178 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1179
1180 ctx->cached_ddt_offset = secondary_ddt_offset;
1181
1182 break;
1183 default:
1184 FATAL("Found unknown compression type %d, stopping...", ddt_header.compression);
1185 TRACE("Exiting decode_ddt_multi_level_v2() = AARUF_ERROR_CANNOT_READ_BLOCK");
1187 }
1188 }
1189
1190 ddt_entry = ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry];
1191
1192 if(ddt_entry == 0)
1193 {
1194 *sector_status = SectorStatusNotDumped;
1195 *offset = 0;
1196 *block_offset = 0;
1197
1198 TRACE("Exiting decode_ddt_multi_level_v2(%p, %" PRIu64 ", %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
1199 sector_address, *offset, *block_offset, *sector_status);
1200 return AARUF_STATUS_OK;
1201 }
1202
1203 *sector_status = ddt_entry >> 60;
1204 ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1205
1206 const uint64_t offset_mask = (uint64_t)((1 << ctx->user_data_ddt_header.dataShift) - 1);
1207 *offset = ddt_entry & offset_mask;
1208 *block_offset =
1210
1211 TRACE("Exiting decode_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d) = AARUF_STATUS_OK", ctx,
1212 sector_address, negative, *offset, *block_offset, *sector_status);
1213 return AARUF_STATUS_OK;
1214}
1215
1232bool set_ddt_entry_v2(aaruformat_context *ctx, const uint64_t sector_address, const bool negative,
1233 const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status,
1234 uint64_t *ddt_entry)
1235{
1236 TRACE("Entering set_ddt_entry_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative, offset,
1237 block_offset, sector_status);
1238
1239 // Check if the context and image stream are valid
1240 if(ctx == NULL || ctx->imageStream == NULL)
1241 {
1242 FATAL("Invalid context or image stream.");
1243 return false;
1244 }
1245
1246 if(ctx->user_data_ddt_header.tableShift > 0)
1247 return set_ddt_multi_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status, ddt_entry);
1248
1249 return set_ddt_single_level_v2(ctx, sector_address, negative, offset, block_offset, sector_status, ddt_entry);
1250}
1251
1268bool set_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, const bool negative,
1269 const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status,
1270 uint64_t *ddt_entry)
1271{
1272 TRACE("Entering set_ddt_single_level_v2(%p, %" PRIu64 ", %d, %llu, %llu, %d)", ctx, sector_address, negative,
1273 offset, block_offset, sector_status);
1274
1275 // Check if the context and image stream are valid
1276 if(ctx == NULL || ctx->imageStream == NULL)
1277 {
1278 FATAL("Invalid context or image stream.");
1279 TRACE("Exiting set_ddt_single_level_v2() = false");
1280 return false;
1281 }
1282
1283 // Should not really be here
1284 if(ctx->user_data_ddt_header.tableShift != 0)
1285 {
1286 FATAL("DDT table shift is not zero, but we are in single-level DDT setting.");
1287 TRACE("Exiting set_ddt_single_level_v2() = false");
1288 return false;
1289 }
1290
1291 // Calculate positive or negative sector
1292 if(negative)
1293 sector_address = ctx->user_data_ddt_header.negative - sector_address;
1294 else
1295 sector_address += ctx->user_data_ddt_header.negative;
1296
1297 if(*ddt_entry == 0)
1298 {
1299 const uint64_t block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1300 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1301 block_index << ctx->user_data_ddt_header.dataShift;
1302
1303 // Overflow detection for DDT entry
1304 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1305 {
1306 FATAL("DDT overflow: media does not fit in big DDT");
1307 TRACE("Exiting set_ddt_single_level_v2() = false");
1308 return false;
1309 }
1310 }
1311
1312 // Sector status can be different from previous deduplicated sector
1313 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1314 *ddt_entry |= (uint64_t)sector_status << 60;
1315
1316 TRACE("Setting big single-level DDT entry %d to %ull", sector_address, (uint64_t)*ddt_entry);
1317 ctx->user_data_ddt2[sector_address] = *ddt_entry;
1318 ctx->dirty_single_level_ddt = true; // Mark single-level DDT as dirty
1319
1320 TRACE("Exiting set_ddt_single_level_v2() = true");
1321 return true;
1322}
1323
1340bool set_ddt_multi_level_v2(aaruformat_context *ctx, uint64_t sector_address, bool negative, uint64_t offset,
1341 uint64_t block_offset, uint8_t sector_status, uint64_t *ddt_entry)
1342{
1343 TRACE("Entering set_ddt_multi_level_v2(%p, %" PRIu64 ", %d, %" PRIu64 ", %" PRIu64 ", %d)", ctx, sector_address,
1344 negative, offset, block_offset, sector_status);
1345
1346 uint64_t items_per_ddt_entry = 0;
1347 uint64_t ddt_position = 0;
1348 uint64_t secondary_ddt_offset = 0;
1349 uint64_t block_index = 0;
1350 uint8_t *buffer = NULL;
1351 crc64_ctx *crc64_context = NULL;
1352 uint64_t crc64 = 0;
1353 DdtHeader2 ddt_header;
1354 size_t written_bytes = 0;
1355 long end_of_file = 0;
1356 bool create_new_table = false;
1357
1358 // Check if the context and image stream are valid
1359 if(ctx == NULL || ctx->imageStream == NULL)
1360 {
1361 FATAL("Invalid context or image stream.");
1362 TRACE("Exiting set_ddt_multi_level_v2() = false");
1363 return false;
1364 }
1365
1366 // Should not really be here
1367 if(ctx->user_data_ddt_header.tableShift == 0)
1368 {
1369 FATAL("DDT table shift is zero, but we are in multi-level DDT setting.");
1370 TRACE("Exiting set_ddt_multi_level_v2() = false");
1371 return false;
1372 }
1373
1374 // Calculate positive or negative sector
1375 if(negative)
1376 sector_address = ctx->user_data_ddt_header.negative - sector_address;
1377 else
1378 sector_address += ctx->user_data_ddt_header.negative;
1379
1380 // Step 1: Calculate the corresponding secondary level table
1381 items_per_ddt_entry = 1 << ctx->user_data_ddt_header.tableShift;
1382 ddt_position = sector_address / items_per_ddt_entry;
1383 secondary_ddt_offset = ctx->user_data_ddt2[ddt_position];
1384
1385 // Position in file of the child DDT table
1386 secondary_ddt_offset *= 1 << ctx->user_data_ddt_header.blockAlignmentShift;
1387
1388 // Step 2: Check if it corresponds to the currently in-memory cached secondary level table
1389 if(ctx->cached_ddt_offset == secondary_ddt_offset && secondary_ddt_offset != 0)
1390 {
1391 // Update the corresponding DDT entry directly in the cached table
1392 if(*ddt_entry == 0)
1393 {
1394 block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1395 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1396 block_index << ctx->user_data_ddt_header.dataShift;
1397
1398 // Overflow detection for DDT entry
1399 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1400 {
1401 FATAL("DDT overflow: media does not fit in big DDT");
1402 TRACE("Exiting set_ddt_multi_level_v2() = false");
1403 return false;
1404 }
1405 }
1406
1407 // Sector status can be different from previous deduplicated sector
1408 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1409 *ddt_entry |= (uint64_t)sector_status << 60;
1410
1411 TRACE("Setting small secondary DDT entry %d to %ull", sector_address % items_per_ddt_entry,
1412 (uint64_t)*ddt_entry);
1413 ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry] = *ddt_entry;
1414 ctx->dirty_secondary_ddt = true; // Mark secondary DDT as dirty
1415
1416 TRACE("Updated cached secondary DDT entry at position %" PRIu64, sector_address % items_per_ddt_entry);
1417 TRACE("Exiting set_ddt_multi_level_v2() = true");
1418 return true;
1419 }
1420
1421 // Step 2.5: Handle case where we have a cached secondary DDT that has never been written to disk
1422 // but does not contain the requested block
1423 if(ctx->cached_ddt_offset == 0 && (ctx->cached_secondary_ddt2 != NULL))
1424 {
1425 // Only write the cached table to disk if the requested block belongs to a different DDT position
1426 if(ddt_position != ctx->cached_ddt_position)
1427 {
1428 TRACE("Current secondary DDT in memory belongs to position %" PRIu64
1429 " but requested block needs position %" PRIu64,
1430 ctx->cached_ddt_position, ddt_position);
1431
1432 // Write the cached DDT to disk before proceeding with the new one
1433
1434 // Close the current data block first
1435 if(ctx->writing_buffer != NULL) aaruf_close_current_block(ctx);
1436
1437 // Get current position and seek to end of file
1438 aaruf_fseek(ctx->imageStream, 0, SEEK_END);
1439 end_of_file = aaruf_ftell(ctx->imageStream);
1440
1441 // Align to block boundary
1442 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1443 end_of_file = end_of_file + alignment_mask & ~alignment_mask;
1444 aaruf_fseek(ctx->imageStream, end_of_file, SEEK_SET);
1445
1446 // Prepare DDT header for the never-written cached table
1447 memset(&ddt_header, 0, sizeof(DdtHeader2));
1449 ddt_header.type = kDataTypeUserData;
1450 ddt_header.compression = ctx->compression_enabled
1453 ddt_header.levels = ctx->user_data_ddt_header.levels;
1454 ddt_header.tableLevel = ctx->user_data_ddt_header.tableLevel + 1;
1455 ddt_header.previousLevelOffset = ctx->primary_ddt_offset;
1456 ddt_header.negative = ctx->user_data_ddt_header.negative;
1457 ddt_header.blocks = items_per_ddt_entry;
1458 ddt_header.overflow = ctx->user_data_ddt_header.overflow;
1459 ddt_header.start = ctx->cached_ddt_position * items_per_ddt_entry; // Use cached position with table shift
1461 ddt_header.dataShift = ctx->user_data_ddt_header.dataShift;
1462 ddt_header.tableShift = 0; // Secondary tables are single level
1463 ddt_header.entries = items_per_ddt_entry;
1464
1465 // Calculate data size
1466
1467 ddt_header.length = items_per_ddt_entry * sizeof(uint64_t);
1468
1469 // Calculate CRC64 of the data
1470 crc64_context = aaruf_crc64_init();
1471 if(crc64_context == NULL)
1472 {
1473 FATAL("Could not initialize CRC64.");
1474 TRACE("Exiting set_ddt_multi_level_v2() = false");
1475 return false;
1476 }
1477
1478 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cached_secondary_ddt2, (uint32_t)ddt_header.length);
1479
1480 aaruf_crc64_final(crc64_context, &crc64);
1481 aaruf_crc64_free(crc64_context);
1482 ddt_header.crc64 = crc64;
1483
1484 uint8_t *cmp_buffer = NULL;
1485 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1486
1487 if(ddt_header.compression == kCompressionNone)
1488 {
1489
1490 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1491 ddt_header.cmpCrc64 = ddt_header.crc64;
1492 }
1493 else
1494 {
1495 cmp_buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression
1496 if(cmp_buffer == NULL)
1497 {
1498 TRACE("Failed to allocate memory for secondary DDT v2 compression");
1500 }
1501
1502 size_t dst_size;
1503
1504 if(ctx->use_zstd)
1505 {
1506 dst_size = aaruf_zstd_encode_buffer(cmp_buffer, (size_t)ddt_header.length * 2,
1507 (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length,
1508 ctx->zstd_level, ctx->num_threads);
1509 if(dst_size == 0) dst_size = ddt_header.length;
1510 }
1511 else
1512 {
1513 dst_size = (size_t)ddt_header.length * 2 * 2;
1514 size_t props_size = LZMA_PROPERTIES_LENGTH;
1515 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->cached_secondary_ddt2,
1516 ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size,
1517 4, 0, 2, 273, LZMA_THREADS(ctx));
1518 }
1519
1520 ddt_header.cmpLength = (uint32_t)dst_size;
1521
1522 if(ddt_header.cmpLength >= ddt_header.length)
1523 {
1524 ddt_header.compression = kCompressionNone;
1525 free(cmp_buffer);
1526
1527 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1528 }
1529 }
1530
1531 if(ddt_header.compression == kCompressionNone)
1532 {
1533 ddt_header.cmpLength = ddt_header.length;
1534 ddt_header.cmpCrc64 = ddt_header.crc64;
1535 }
1536 else
1537 {
1538 ddt_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, (uint32_t)ddt_header.cmpLength);
1539 if(ctx->use_zstd) ctx->has_zstd_blocks = true;
1540 }
1541
1542 if(ddt_header.compression == kCompressionLzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
1543
1544 // Write header
1545 written_bytes = fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
1546 if(written_bytes != 1)
1547 {
1548 FATAL("Could not write never-written DDT header to file.");
1549 TRACE("Exiting set_ddt_multi_level_v2() = false");
1550 return false;
1551 }
1552
1553 // Write data
1554 if(ddt_header.compression == kCompressionLzma)
1555 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1556
1557 if(fwrite(cmp_buffer, ddt_header.cmpLength, 1, ctx->imageStream) != 1)
1558 {
1559 FATAL("Could not write never-written DDT data to file.");
1560 TRACE("Exiting set_ddt_multi_level_v2() = false");
1561 return false;
1562 }
1563
1564 if(ddt_header.compression != kCompressionNone) free(cmp_buffer);
1565
1566 // Add index entry for the newly written secondary DDT
1567 IndexEntry new_ddt_entry;
1568 new_ddt_entry.blockType = DeDuplicationTableSecondary;
1569 new_ddt_entry.dataType = kDataTypeUserData;
1570 new_ddt_entry.offset = end_of_file;
1571
1572 utarray_push_back(ctx->index_entries, &new_ddt_entry);
1573 ctx->dirty_index_block = true;
1574 TRACE("Added new DDT index entry for never-written table at offset %" PRIu64, end_of_file);
1575
1576 // Update the primary level table entry to point to the new location of the secondary table
1577 uint64_t new_secondary_table_block_offset = end_of_file >> ctx->user_data_ddt_header.blockAlignmentShift;
1578
1579 ctx->user_data_ddt2[ctx->cached_ddt_position] = new_secondary_table_block_offset;
1580 ctx->dirty_primary_ddt = true; // Mark primary DDT as dirty
1581
1582 // Write the updated primary table back to its original position in the file
1583 aaru_off_t saved_pos = aaruf_ftell(ctx->imageStream);
1584 aaruf_fseek(ctx->imageStream, (aaru_off_t)(ctx->primary_ddt_offset + sizeof(DdtHeader2)), SEEK_SET);
1585
1586 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
1587
1588 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
1589
1590 if(written_bytes != 1)
1591 {
1592 FATAL("Could not flush primary DDT table to file after writing never-written secondary table.");
1593 TRACE("Exiting set_ddt_multi_level_v2() = false");
1594 return false;
1595 }
1596
1597 // Update nextBlockPosition to ensure future blocks don't overwrite the DDT
1598 uint64_t ddt_total_size = sizeof(DdtHeader2) + ddt_header.length;
1599 ctx->next_block_position = end_of_file + ddt_total_size + alignment_mask & ~alignment_mask;
1600 block_offset = ctx->next_block_position;
1601 offset = 0;
1602 TRACE("Updated nextBlockPosition after never-written DDT write to %" PRIu64, ctx->next_block_position);
1603
1604 // Free the cached table
1605
1606 free(ctx->cached_secondary_ddt2);
1607 ctx->cached_secondary_ddt2 = NULL;
1608
1609 // Reset cached values since we've written and freed the table
1610 ctx->cached_ddt_offset = 0;
1611 ctx->cached_ddt_position = 0;
1612
1613 // Restore file position
1614 aaruf_fseek(ctx->imageStream, saved_pos, SEEK_SET);
1615
1616 TRACE("Successfully wrote never-written cached secondary DDT to disk");
1617 }
1618 else
1619 // The cached DDT is actually for the requested block range, so we can use it directly
1620 TRACE("Cached DDT is for the correct block range, using it directly");
1621 // No need to write to disk, just continue with the cached table
1622 }
1623
1624 // Step 3: Write the currently in-memory cached secondary level table to the end of the file
1625 if(ctx->cached_ddt_offset != 0)
1626 {
1627 aaru_off_t current_pos = 0;
1628 // Close the current data block first
1629 if(ctx->writing_buffer != NULL) aaruf_close_current_block(ctx);
1630
1631 // Get current position and seek to end of file
1632 current_pos = aaruf_ftell(ctx->imageStream);
1633 aaruf_fseek(ctx->imageStream, 0, SEEK_END);
1634 end_of_file = aaruf_ftell(ctx->imageStream);
1635
1636 // Align to block boundary
1637 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1638 end_of_file = end_of_file + alignment_mask & ~alignment_mask;
1639 aaruf_fseek(ctx->imageStream, end_of_file, SEEK_SET);
1640
1641 // Prepare DDT header for the cached table
1642 memset(&ddt_header, 0, sizeof(DdtHeader2));
1644 ddt_header.type = kDataTypeUserData;
1645 ddt_header.compression =
1647 ddt_header.levels = ctx->user_data_ddt_header.levels;
1648 ddt_header.tableLevel = ctx->user_data_ddt_header.tableLevel + 1;
1649 ddt_header.previousLevelOffset = ctx->primary_ddt_offset; // Set to primary DDT table location
1650 ddt_header.negative = ctx->user_data_ddt_header.negative;
1651 ddt_header.blocks = items_per_ddt_entry;
1652 ddt_header.overflow = ctx->user_data_ddt_header.overflow;
1653 ddt_header.start = ddt_position * items_per_ddt_entry; // First block this DDT table references
1655 ddt_header.dataShift = ctx->user_data_ddt_header.dataShift;
1656 ddt_header.tableShift = 0; // Secondary tables are single level
1657 ddt_header.entries = items_per_ddt_entry;
1658
1659 // Calculate data size
1660
1661 ddt_header.length = items_per_ddt_entry * sizeof(uint64_t);
1662
1663 // Calculate CRC64 of the data
1664 crc64_context = aaruf_crc64_init();
1665 if(crc64_context == NULL)
1666 {
1667 FATAL("Could not initialize CRC64.");
1668 TRACE("Exiting set_ddt_multi_level_v2() = false");
1669 return false;
1670 }
1671
1672 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length);
1673
1674 aaruf_crc64_final(crc64_context, &crc64);
1675 aaruf_crc64_free(crc64_context);
1676 ddt_header.crc64 = crc64;
1677
1678 uint8_t *cmp_buffer = NULL;
1679 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1680
1681 if(ddt_header.compression == kCompressionNone)
1682 {
1683
1684 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1685 ddt_header.cmpCrc64 = ddt_header.crc64;
1686 }
1687 else
1688 {
1689 cmp_buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression
1690 if(cmp_buffer == NULL)
1691 {
1692 TRACE("Failed to allocate memory for secondary DDT v2 compression");
1694 }
1695
1696 size_t dst_size;
1697
1698 if(ctx->use_zstd)
1699 {
1700 dst_size = aaruf_zstd_encode_buffer(cmp_buffer, (size_t)ddt_header.length * 2,
1701 (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length,
1702 ctx->zstd_level, ctx->num_threads);
1703 if(dst_size == 0) dst_size = ddt_header.length;
1704 }
1705 else
1706 {
1707 dst_size = (size_t)ddt_header.length * 2 * 2;
1708 size_t props_size = LZMA_PROPERTIES_LENGTH;
1709 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->cached_secondary_ddt2,
1710 ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0,
1711 2, 273, LZMA_THREADS(ctx));
1712 }
1713
1714 ddt_header.cmpLength = (uint32_t)dst_size;
1715
1716 if(ddt_header.cmpLength >= ddt_header.length)
1717 {
1718 ddt_header.compression = kCompressionNone;
1719 free(cmp_buffer);
1720
1721 cmp_buffer = (uint8_t *)ctx->cached_secondary_ddt2;
1722 }
1723 }
1724
1725 if(ddt_header.compression == kCompressionNone)
1726 {
1727 ddt_header.cmpLength = ddt_header.length;
1728 ddt_header.cmpCrc64 = ddt_header.crc64;
1729 }
1730 else
1731 {
1732 ddt_header.cmpCrc64 = aaruf_crc64_data(cmp_buffer, (uint32_t)ddt_header.cmpLength);
1733 if(ctx->use_zstd) ctx->has_zstd_blocks = true;
1734 }
1735
1736 if(ddt_header.compression == kCompressionLzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
1737
1738 // Write header
1739 if(ddt_header.compression == kCompressionLzma)
1740 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1741
1742 written_bytes = fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
1743 if(written_bytes != 1)
1744 {
1745 FATAL("Could not write DDT header to file.");
1746 TRACE("Exiting set_ddt_multi_level_v2() = false");
1747 return false;
1748 }
1749
1750 // Write data
1751 written_bytes = fwrite(cmp_buffer, ddt_header.cmpLength, 1, ctx->imageStream);
1752
1753 if(written_bytes != 1)
1754 {
1755 FATAL("Could not write DDT data to file.");
1756 TRACE("Exiting set_ddt_multi_level_v2() = false");
1757 return false;
1758 }
1759
1760 if(ddt_header.compression != kCompressionNone) free(cmp_buffer);
1761
1762 // Update index: remove old entry and add new one for the evicted secondary DDT
1763 TRACE("Updating index for evicted secondary DDT");
1764
1765 // Remove old index entry for the cached DDT
1766 if(ctx->cached_ddt_offset != 0)
1767 {
1768 TRACE("Removing old index entry for DDT at offset %" PRIu64, ctx->cached_ddt_offset);
1769 IndexEntry *entry = NULL;
1770
1771 // Find and remove the old index entry
1772 for(unsigned int i = 0; i < utarray_len(ctx->index_entries); i++)
1773 {
1774 entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, i);
1775 if(entry && entry->offset == ctx->cached_ddt_offset &&
1777 {
1778 TRACE("Found old DDT index entry at position %u, removing", i);
1779 utarray_erase(ctx->index_entries, i, 1);
1780 break;
1781 }
1782 }
1783 }
1784
1785 // Add new index entry for the newly written secondary DDT
1786 IndexEntry new_ddt_entry;
1787 new_ddt_entry.blockType = DeDuplicationTableSecondary;
1788 new_ddt_entry.dataType = kDataTypeUserData;
1789 new_ddt_entry.offset = end_of_file;
1790
1791 utarray_push_back(ctx->index_entries, &new_ddt_entry);
1792 ctx->dirty_index_block = true;
1793 TRACE("Added new DDT index entry at offset %" PRIu64, end_of_file);
1794
1795 // Step 4: Update the primary level table entry and flush it back to file
1796 uint64_t new_secondary_table_block_offset = end_of_file >> ctx->user_data_ddt_header.blockAlignmentShift;
1797
1798 // Update the primary table entry to point to the new location of the secondary table
1799 // Use ddtPosition which was calculated from sectorAddress, not cachedDdtOffset
1800
1801 ctx->user_data_ddt2[ddt_position] = new_secondary_table_block_offset;
1802 ctx->dirty_primary_ddt = true; // Mark primary DDT as dirty
1803
1804 // Write the updated primary table back to its original position in the file
1805 aaru_off_t saved_pos = aaruf_ftell(ctx->imageStream);
1806 aaruf_fseek(ctx->imageStream, (aaru_off_t)(ctx->primary_ddt_offset + sizeof(DdtHeader2)), SEEK_SET);
1807
1808 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
1809
1810 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
1811
1812 if(written_bytes != 1)
1813 {
1814 FATAL("Could not flush primary DDT table to file.");
1815 TRACE("Exiting set_ddt_multi_level_v2() = false");
1816 return false;
1817 }
1818
1819 // Update nextBlockPosition to ensure future blocks don't overwrite the DDT
1820 uint64_t ddt_total_size = sizeof(DdtHeader2) + ddt_header.length;
1821 ctx->next_block_position = end_of_file + ddt_total_size + alignment_mask & ~alignment_mask;
1822 block_offset = ctx->next_block_position;
1823 offset = 0;
1824 TRACE("Updated nextBlockPosition after DDT write to %" PRIu64, ctx->next_block_position);
1825
1826 aaruf_fseek(ctx->imageStream, saved_pos, SEEK_SET);
1827
1828 // Free the cached table
1829
1830 free(ctx->cached_secondary_ddt2);
1831 ctx->cached_secondary_ddt2 = NULL;
1832
1833 // Restore file position
1834 aaruf_fseek(ctx->imageStream, current_pos, SEEK_SET);
1835 }
1836
1837 // Step 5: Check if the specified block already has an existing secondary level table
1838 create_new_table = ctx->cached_secondary_ddt2 == NULL;
1839
1840 if(!create_new_table && secondary_ddt_offset != 0)
1841 {
1842 // Load existing table
1843 aaruf_fseek(ctx->imageStream, (aaru_off_t)secondary_ddt_offset, SEEK_SET);
1844 size_t read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader2), ctx->imageStream);
1845
1846 if(read_bytes != sizeof(DdtHeader2) || ddt_header.identifier != DeDuplicationTable2 ||
1847 ddt_header.type != kDataTypeUserData)
1848 {
1849 FATAL("Invalid secondary DDT header at %" PRIu64, secondary_ddt_offset);
1850 TRACE("Exiting set_ddt_multi_level_v2() = false");
1851 return false;
1852 }
1853
1854 // Read the table data (assuming no compression for now)
1855 buffer = malloc(ddt_header.length);
1856 if(buffer == NULL)
1857 {
1858 FATAL("Cannot allocate memory for secondary DDT.");
1859 TRACE("Exiting set_ddt_multi_level_v2() = false");
1860 return false;
1861 }
1862
1863 read_bytes = fread(buffer, 1, ddt_header.length, ctx->imageStream);
1864 if(read_bytes != ddt_header.length)
1865 {
1866 FATAL("Could not read secondary DDT data.");
1867 free(buffer);
1868 TRACE("Exiting set_ddt_multi_level_v2() = false");
1869 return false;
1870 }
1871
1872 // Verify CRC
1873 crc64_context = aaruf_crc64_init();
1874 if(crc64_context == NULL)
1875 {
1876 FATAL("Could not initialize CRC64.");
1877 free(buffer);
1878 TRACE("Exiting set_ddt_multi_level_v2() = false");
1879 return false;
1880 }
1881
1882 aaruf_crc64_update(crc64_context, buffer, read_bytes);
1883 aaruf_crc64_final(crc64_context, &crc64);
1884 aaruf_crc64_free(crc64_context);
1885
1886 if(crc64 != ddt_header.crc64)
1887 {
1888 FATAL("Secondary DDT CRC mismatch. Expected 0x%16lX but got 0x%16lX.", ddt_header.crc64, crc64);
1889 free(buffer);
1890 TRACE("Exiting set_ddt_multi_level_v2() = false");
1891 return false;
1892 }
1893
1894 // Cache the loaded table
1895
1896 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1897
1898 ctx->cached_ddt_offset = secondary_ddt_offset;
1899 }
1900
1901 if(create_new_table)
1902 {
1903 // Create a new empty table
1904 size_t table_size = items_per_ddt_entry * sizeof(uint64_t);
1905
1906 buffer = calloc(1, table_size);
1907 if(buffer == NULL)
1908 {
1909 FATAL("Cannot allocate memory for new secondary DDT.");
1910 TRACE("Exiting set_ddt_multi_level_v2() = false");
1911 return false;
1912 }
1913
1914 ctx->cached_secondary_ddt2 = (uint64_t *)buffer;
1915
1916 ctx->cached_ddt_offset = 0; // Will be set when written to file
1917 ctx->cached_ddt_position = ddt_position; // Track which primary DDT position this new table belongs to
1918 TRACE("Created new secondary DDT for position %" PRIu64, ddt_position);
1919 }
1920
1921 // Step 6: Update the corresponding DDT entry
1922 if(*ddt_entry == 0)
1923 {
1924 block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
1925 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
1926 block_index << ctx->user_data_ddt_header.dataShift;
1927
1928 // Overflow detection for DDT entry
1929 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
1930 {
1931 FATAL("DDT overflow: media does not fit in big DDT");
1932 TRACE("Exiting set_ddt_multi_level_v2() = false");
1933 return false;
1934 }
1935 }
1936
1937 // Sector status can be different from previous deduplicated sector
1938 *ddt_entry &= 0x0FFFFFFFFFFFFFFF;
1939 *ddt_entry |= (uint64_t)sector_status << 60;
1940
1941 TRACE("Setting big secondary DDT entry %d to %ull", sector_address % items_per_ddt_entry, (uint64_t)*ddt_entry);
1942 ctx->cached_secondary_ddt2[sector_address % items_per_ddt_entry] = *ddt_entry;
1943 ctx->dirty_secondary_ddt = true;
1944
1945 TRACE("Updated secondary DDT entry at position %" PRIu64, sector_address % items_per_ddt_entry);
1946 TRACE("Exiting set_ddt_multi_level_v2() = true");
1947 return true;
1948}
1949
2066bool set_ddt_tape(aaruformat_context *ctx, uint64_t sector_address, const uint64_t offset, const uint64_t block_offset,
2067 const uint8_t sector_status, uint64_t *ddt_entry)
2068{
2069 TRACE("Entering set_ddt_tape(%p, %" PRIu64 ", %llu, %llu, %d)", ctx, sector_address, offset, block_offset,
2070 sector_status);
2071
2072 // Check if the context and image stream are valid
2073 if(ctx == NULL || ctx->imageStream == NULL)
2074 {
2075 FATAL("Invalid context or image stream.");
2076 TRACE("Exiting set_ddt_tape() = false");
2077 return false;
2078 }
2079
2080 // Should not really be here
2081 if(!ctx->is_tape)
2082 {
2083 FATAL("Image is not tape, wrong function called.");
2084 TRACE("Exiting set_ddt_tape() = false");
2085 return false;
2086 }
2087
2088 if(*ddt_entry == 0)
2089 {
2090 const uint64_t block_index = block_offset >> ctx->user_data_ddt_header.blockAlignmentShift;
2091 *ddt_entry = offset & (1ULL << ctx->user_data_ddt_header.dataShift) - 1 |
2092 block_index << ctx->user_data_ddt_header.dataShift;
2093 // Overflow detection for DDT entry
2094 if(*ddt_entry > 0xFFFFFFFFFFFFFFF)
2095 {
2096 FATAL("DDT overflow: media does not fit in big DDT");
2097 TRACE("Exiting set_ddt_tape() = false");
2098 return false;
2099 }
2100
2101 *ddt_entry |= (uint64_t)sector_status << 60;
2102 }
2103
2104 // Create DDT hash entry
2105 TapeDdtHashEntry *new_entry = calloc(1, sizeof(TapeDdtHashEntry));
2106 TapeDdtHashEntry *old_entry = NULL;
2107 if(new_entry == NULL)
2108 {
2109 FATAL("Cannot allocate memory for new tape DDT hash entry.");
2110 TRACE("Exiting set_ddt_tape() = false");
2111 return false;
2112 }
2113
2114 TRACE("Setting tape DDT entry %d to %u", sector_address, (uint32_t)*ddt_entry);
2115
2116 new_entry->key = sector_address;
2117 new_entry->value = *ddt_entry;
2118
2119 // Insert entry into tape DDT
2120 HASH_REPLACE(hh, ctx->tape_ddt, key, sizeof(uint64_t), new_entry, old_entry);
2121 ctx->dirty_tape_ddt = true; // Mark tape DDT as dirty
2122 if(old_entry) free(old_entry);
2123
2124 TRACE("Exiting set_ddt_tape() = true");
2125 return true;
2126}
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
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:96
bool set_ddt_tape(aaruformat_context *ctx, uint64_t sector_address, const uint64_t offset, const uint64_t block_offset, const 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:2066
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:736
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:1340
bool set_ddt_entry_v2(aaruformat_context *ctx, const uint64_t sector_address, const bool negative, const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status, uint64_t *ddt_entry)
Sets a DDT v2 entry for a given sector address.
Definition ddt_v2.c:1232
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:879
bool set_ddt_single_level_v2(aaruformat_context *ctx, uint64_t sector_address, const bool negative, const uint64_t offset, const uint64_t block_offset, const uint8_t sector_status, uint64_t *ddt_entry)
Sets a single-level DDT v2 entry for a given sector address.
Definition ddt_v2.c:1268
int32_t decode_ddt_entry_v2(aaruformat_context *ctx, const 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:662
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
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
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
@ 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
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:81
#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
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1536
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
#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:145
uint64_t value
Value: DDT entry.
Definition context.h:146
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:192
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:304
bool dirty_primary_ddt
True if primary DDT table should be written during close.
Definition context.h:323
uint64_t * user_data_ddt2
DDT entries (big variant) primary/secondary current.
Definition context.h:190
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:189
uint64_t cached_ddt_offset
File offset of currently cached secondary DDT (0=none).
Definition context.h:193
bool is_tape
True if the image is a tape image.
Definition context.h:313
bool in_memory_ddt
True if primary (and possibly secondary) DDT loaded.
Definition context.h:199
bool dirty_index_block
True if index block should be written during close.
Definition context.h:345
TapeDdtHashEntry * tape_ddt
Hash table root for tape DDT entries.
Definition context.h:185
bool dirty_single_level_ddt
True if single-level DDT should be written during close.
Definition context.h:324
int ddt_version
DDT version in use (1=legacy, 2=v2 hierarchical).
Definition context.h:197
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:283
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:188
uint64_t primary_ddt_offset
File offset of the primary DDT v2 table.
Definition context.h:195
uint64_t next_block_position
Absolute file offset where next block will be written.
Definition context.h:285
bool has_zstd_blocks
True if any block was actually written with Zstandard compression.
Definition context.h:306
uint64_t * cached_secondary_ddt2
Cached secondary table (big entries) or NULL.
Definition context.h:191
int zstd_level
Zstandard compression level (writing path, default 19).
Definition context.h:307
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:255
int num_threads
Compression worker threads (1 = single-threaded, default).
Definition context.h:308
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
bool use_zstd
Use Zstandard instead of LZMA for data blocks.
Definition context.h:305
bool dirty_secondary_ddt
True if secondary DDT tables should be written during close.
Definition context.h:322
bool dirty_tape_ddt
True if tape DDT should be written during close.
Definition context.h:336
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:302
uint64_t cached_ddt_position
Position index of cached secondary DDT.
Definition context.h:194
Minimal ECMA-182 CRC64 incremental state container (running value only).
Definition crc64.h:56