libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
close.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2025 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 */
36
37#include <errno.h>
38#include <stdio.h>
39#include <stdlib.h>
40
41#ifdef __linux__
42#include <sys/mman.h>
43#endif
44
45#include <aaruformat.h>
46
47#include "internal.h"
48#include "log.h"
49
78{
79 // Write cached secondary table to file end and update primary table entry with its position
80 // Check if we have a cached table that needs to be written (either it has an offset or exists in memory)
81 bool has_cached_secondary_ddt =
82 ctx->user_data_ddt_header.tableShift > 0 && (ctx->cached_ddt_offset != 0 || ctx->cached_secondary_ddt2 != NULL);
83
84 if(!has_cached_secondary_ddt) return AARUF_STATUS_OK;
85
86 TRACE("Writing cached secondary DDT table to file");
87
88 fseek(ctx->imageStream, 0, SEEK_END);
89 long end_of_file = ftell(ctx->imageStream);
90
91 // Align the position according to block alignment shift
92 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
93 if(end_of_file & alignment_mask)
94 {
95 // Calculate the next aligned position
96 uint64_t aligned_position = end_of_file + alignment_mask & ~alignment_mask;
97
98 // Seek to the aligned position and pad with zeros if necessary
99 fseek(ctx->imageStream, aligned_position, SEEK_SET);
100 end_of_file = aligned_position;
101
102 TRACE("Aligned DDT write position from %ld to %" PRIu64 " (alignment shift: %d)",
103 ftell(ctx->imageStream) - (aligned_position - end_of_file), aligned_position,
105 }
106
107 // Prepare DDT header for the cached table
108 DdtHeader2 ddt_header = {0};
110 ddt_header.type = UserData;
111 ddt_header.compression = ctx->compression_enabled ? Lzma : None;
112 ddt_header.levels = ctx->user_data_ddt_header.levels;
113 ddt_header.tableLevel = ctx->user_data_ddt_header.tableLevel + 1;
114 ddt_header.previousLevelOffset = ctx->primary_ddt_offset;
115 ddt_header.negative = ctx->user_data_ddt_header.negative;
116 ddt_header.overflow = ctx->user_data_ddt_header.overflow;
118 ddt_header.dataShift = ctx->user_data_ddt_header.dataShift;
119 ddt_header.tableShift = 0; // Secondary tables are single level
120
121 uint64_t items_per_ddt_entry = 1 << ctx->user_data_ddt_header.tableShift;
122 ddt_header.blocks = items_per_ddt_entry;
123 ddt_header.entries = items_per_ddt_entry;
124 ddt_header.start = ctx->cached_ddt_position * items_per_ddt_entry;
125
126 // Calculate data size
127 ddt_header.length = items_per_ddt_entry * sizeof(uint64_t);
128
129 // Calculate CRC64 of the data
130 crc64_ctx *crc64_context = aaruf_crc64_init();
131 if(crc64_context != NULL)
132 {
133 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->cached_secondary_ddt2, (uint32_t)ddt_header.length);
134
135 uint64_t crc64;
136 aaruf_crc64_final(crc64_context, &crc64);
137 ddt_header.crc64 = crc64;
138 }
139
140 uint8_t *buffer = NULL;
141 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
142
143 if(ddt_header.compression == None)
144 {
145 buffer = (uint8_t *)ctx->cached_secondary_ddt2;
146 ddt_header.cmpCrc64 = ddt_header.crc64;
147 }
148 else
149 {
150 buffer = malloc((size_t)ddt_header.length * 2); // Allocate double size for compression
151 if(buffer == NULL)
152 {
153 TRACE("Failed to allocate memory for secondary DDT v2 compression");
155 }
156
157 size_t dst_size = (size_t)ddt_header.length * 2 * 2;
158 size_t props_size = LZMA_PROPERTIES_LENGTH;
159 aaruf_lzma_encode_buffer(buffer, &dst_size,
160
161 (uint8_t *)ctx->cached_secondary_ddt2, ddt_header.length, lzma_properties, &props_size,
162 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
163
164 ddt_header.cmpLength = (uint32_t)dst_size;
165
166 if(ddt_header.cmpLength >= ddt_header.length)
167 {
168 ddt_header.compression = None;
169 free(buffer);
170 buffer = (uint8_t *)ctx->cached_secondary_ddt2;
171 }
172 }
173
174 if(ddt_header.compression == None)
175 {
176 ddt_header.cmpLength = ddt_header.length;
177 ddt_header.cmpCrc64 = ddt_header.crc64;
178 }
179 else
180 ddt_header.cmpCrc64 = aaruf_crc64_data(buffer, ddt_header.cmpLength);
181
182 if(ddt_header.compression == Lzma) ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
183
184 // Write header
185 if(fwrite(&ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
186 {
187 if(ddt_header.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
188
189 // Write data
190 if(fwrite(buffer, ddt_header.cmpLength, 1, ctx->imageStream) == 1)
191 {
192 // Update primary table entry to point to new location
193 const uint64_t new_secondary_table_block_offset =
194 end_of_file >> ctx->user_data_ddt_header.blockAlignmentShift;
195
196 ctx->user_data_ddt2[ctx->cached_ddt_position] = (uint64_t)new_secondary_table_block_offset;
197
198 // Update index: remove old entry for cached DDT and add new one
199 TRACE("Updating index for cached secondary DDT");
200
201 // Remove old index entry for the cached DDT
202 if(ctx->cached_ddt_offset != 0)
203 {
204 TRACE("Removing old index entry for DDT at offset %" PRIu64, ctx->cached_ddt_offset);
205 const IndexEntry *entry = NULL;
206
207 // Find and remove the old index entry
208 for(unsigned int k = 0; k < utarray_len(ctx->index_entries); k++)
209 {
210 entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
211 if(entry && entry->offset == ctx->cached_ddt_offset &&
213 {
214 TRACE("Found old DDT index entry at position %u, removing", k);
215 utarray_erase(ctx->index_entries, k, 1);
216 break;
217 }
218 }
219 }
220
221 // Add new index entry for the newly written secondary DDT
222 IndexEntry new_ddt_entry;
224 new_ddt_entry.dataType = UserData;
225 new_ddt_entry.offset = end_of_file;
226
227 utarray_push_back(ctx->index_entries, &new_ddt_entry);
228 ctx->dirty_index_block = true;
229 TRACE("Added new DDT index entry at offset %" PRIu64, end_of_file);
230
231 // Write the updated primary table back to its original position in the file
232 long saved_pos = ftell(ctx->imageStream);
233 fseek(ctx->imageStream, ctx->primary_ddt_offset + sizeof(DdtHeader2), SEEK_SET);
234
235 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
236
237 size_t primary_written_bytes = 0;
238 primary_written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
239
240 if(primary_written_bytes != 1)
241 {
242 TRACE("Could not flush primary DDT table to file.");
244 }
245
246 fseek(ctx->imageStream, saved_pos, SEEK_SET);
247 }
248 else
249 TRACE("Failed to write cached secondary DDT data");
250 }
251 else
252 TRACE("Failed to write cached secondary DDT header");
253
254 // Free the cached table
255 free(ctx->cached_secondary_ddt2);
256 ctx->cached_secondary_ddt2 = NULL;
257 ctx->cached_ddt_offset = 0;
258
259 // Set position
260 fseek(ctx->imageStream, 0, SEEK_END);
261
262 if(ddt_header.compression == Lzma) free(buffer);
263
264 return AARUF_STATUS_OK;
265}
266
285{
286 // Write the cached primary DDT table back to its position in the file
287 if(ctx->user_data_ddt_header.tableShift <= 0 || ctx->user_data_ddt2 == NULL) return AARUF_STATUS_OK;
288
289 TRACE("Writing cached primary DDT table back to file");
290
291 // Calculate CRC64 of the primary DDT table data first
292 crc64_ctx *crc64_context = aaruf_crc64_init();
293 if(crc64_context != NULL)
294 {
295 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
296
297 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->user_data_ddt2, primary_table_size);
298
299 uint64_t crc64;
300 aaruf_crc64_final(crc64_context, &crc64);
301
302 // Properly populate all header fields for multi-level DDT primary table
306 // levels, tableLevel, previousLevelOffset, negative, overflow, blockAlignmentShift,
307 // dataShift, tableShift, sizeType, entries, blocks, start are already set during creation
308 ctx->user_data_ddt_header.crc64 = crc64;
309 ctx->user_data_ddt_header.cmpCrc64 = crc64;
310 ctx->user_data_ddt_header.length = primary_table_size;
311 ctx->user_data_ddt_header.cmpLength = primary_table_size;
312
313 TRACE("Calculated CRC64 for primary DDT: 0x%16lX", crc64);
314 }
315
316 // First write the DDT header
317 fseek(ctx->imageStream, ctx->primary_ddt_offset, SEEK_SET);
318
319 size_t headerWritten = fwrite(&ctx->user_data_ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
320 if(headerWritten != 1)
321 {
322 TRACE("Failed to write primary DDT header to file");
324 }
325
326 // Then write the table data (position is already after the header)
327 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
328
329 // Write the primary table data
330 size_t written_bytes = 0;
331 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
332
333 if(written_bytes == 1)
334 {
335 TRACE("Successfully wrote primary DDT header and table to file (%" PRIu64 " entries, %zu bytes)",
336 ctx->user_data_ddt_header.entries, primary_table_size);
337
338 // Remove any previously existing index entries of the same type before adding
339 TRACE("Removing any previously existing primary DDT index entries");
340 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
341 {
342 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
343 if(entry && entry->blockType == DeDuplicationTable2 && entry->dataType == UserData)
344 {
345 TRACE("Found existing primary DDT index entry at position %d, removing", k);
346 utarray_erase(ctx->index_entries, k, 1);
347 }
348 }
349
350 // Add primary DDT to index
351 TRACE("Adding primary DDT to index");
352 IndexEntry primary_ddt_entry;
353 primary_ddt_entry.blockType = DeDuplicationTable2;
354 primary_ddt_entry.dataType = UserData;
355 primary_ddt_entry.offset = ctx->primary_ddt_offset;
356
357 utarray_push_back(ctx->index_entries, &primary_ddt_entry);
358 ctx->dirty_index_block = true;
359 TRACE("Added primary DDT index entry at offset %" PRIu64, ctx->primary_ddt_offset);
360 }
361 else
362 TRACE("Failed to write primary DDT table to file");
363
364 return AARUF_STATUS_OK;
365}
366
384{
385 // Write the single level DDT table block aligned just after the header
386 if(ctx->user_data_ddt_header.tableShift != 0 || ctx->user_data_ddt2 == NULL) return AARUF_STATUS_OK;
387
388 TRACE("Writing single-level DDT table to file");
389
390 // Calculate CRC64 of the primary DDT table data
391 const size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
392
393 // Properly populate all header fields
397 ctx->user_data_ddt_header.levels = 1; // Single level
398 ctx->user_data_ddt_header.tableLevel = 0; // Top level
399 ctx->user_data_ddt_header.previousLevelOffset = 0; // No previous level for single-level DDT
400 // negative and overflow are already set during creation
401 // blockAlignmentShift, dataShift, tableShift, sizeType, entries, blocks, start are already set
402 ctx->user_data_ddt_header.length = primary_table_size;
403 ctx->user_data_ddt_header.cmpLength = primary_table_size;
404
405 ctx->user_data_ddt_header.crc64 = aaruf_crc64_data((uint8_t *)ctx->user_data_ddt2, primary_table_size);
406
407 TRACE("Calculated CRC64 for single-level DDT: 0x%16lX", ctx->user_data_ddt_header.crc64);
408
409 uint8_t *cmp_buffer = NULL;
410 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
411
413 {
414
415 cmp_buffer = (uint8_t *)ctx->user_data_ddt2;
417 }
418 else
419 {
420 cmp_buffer = malloc((size_t)ctx->user_data_ddt_header.length * 2); // Allocate double size for compression
421 if(cmp_buffer == NULL)
422 {
423 TRACE("Failed to allocate memory for secondary DDT v2 compression");
425 }
426
427 size_t dst_size = (size_t)ctx->user_data_ddt_header.length * 2 * 2;
428 size_t props_size = LZMA_PROPERTIES_LENGTH;
429 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->user_data_ddt2,
430 ctx->user_data_ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size,
431 4, 0, 2, 273, 8);
432
433 ctx->user_data_ddt_header.cmpLength = (uint32_t)dst_size;
434
436 {
438 free(cmp_buffer);
439
440 cmp_buffer = (uint8_t *)ctx->user_data_ddt2;
441 }
442 }
443
445 {
448 }
449 else
451 aaruf_crc64_data(cmp_buffer, (uint32_t)ctx->user_data_ddt_header.cmpLength);
452
454
455 // Write the DDT header first
456 fseek(ctx->imageStream, 0, SEEK_END);
457 long ddt_position = ftell(ctx->imageStream);
458 // Align index position to block boundary if needed
459 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
460 if(ddt_position & alignment_mask)
461 {
462 const uint64_t aligned_position = ddt_position + alignment_mask & ~alignment_mask;
463 fseek(ctx->imageStream, aligned_position, SEEK_SET);
464 ddt_position = aligned_position;
465 }
466
467 const size_t header_written = fwrite(&ctx->user_data_ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
468 if(header_written != 1)
469 {
470 TRACE("Failed to write single-level DDT header to file");
472 }
473
474 // Write the primary table data
475 size_t written_bytes = 0;
477 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
478
479 written_bytes = fwrite(cmp_buffer, ctx->user_data_ddt_header.cmpLength, 1, ctx->imageStream);
480
481 if(written_bytes == 1)
482 {
483 TRACE("Successfully wrote single-level DDT header and table to file (%" PRIu64
484 " entries, %zu bytes, %zu compressed bytes)",
486
487 // Remove any previously existing index entries of the same type before adding
488 TRACE("Removing any previously existing single-level DDT index entries");
489 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
490 {
491 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
492 if(entry && entry->blockType == DeDuplicationTable2 && entry->dataType == UserData)
493 {
494 TRACE("Found existing single-level DDT index entry at position %d, removing", k);
495 utarray_erase(ctx->index_entries, k, 1);
496 }
497 }
498
499 // Add single-level DDT to index
500 TRACE("Adding single-level DDT to index");
501 IndexEntry single_ddt_entry;
502 single_ddt_entry.blockType = DeDuplicationTable2;
503 single_ddt_entry.dataType = UserData;
504 single_ddt_entry.offset = ddt_position;
505
506 utarray_push_back(ctx->index_entries, &single_ddt_entry);
507 ctx->dirty_index_block = true;
508 TRACE("Added single-level DDT index entry at offset %" PRIu64, ddt_position);
509 }
510 else
511 TRACE("Failed to write single-level DDT table data to file");
512
513 return AARUF_STATUS_OK;
514}
515
624{
625 if(!ctx->is_tape) return AARUF_STATUS_INVALID_CONTEXT;
626
627 // Traverse the tape DDT uthash and find the biggest key
628 uint64_t max_key = 0;
629 TapeDdtHashEntry *entry, *tmp;
630 HASH_ITER(hh, ctx->tape_ddt, entry, tmp)
631 if(entry->key > max_key) max_key = entry->key;
632
633 // Initialize context user data DDT header
637 ctx->user_data_ddt_header.levels = 1; // Single level
638 ctx->user_data_ddt_header.tableLevel = 0; // Top level
639 ctx->user_data_ddt_header.previousLevelOffset = 0; // No previous level for single-level DDT
642 ctx->user_data_ddt_header.tableShift = 0; // Single level
643 ctx->user_data_ddt_header.entries = max_key + 1;
644 ctx->user_data_ddt_header.blocks = max_key + 1;
646 ctx->user_data_ddt_header.length = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
648
649 // Initialize memory for user data DDT
650 ctx->user_data_ddt2 = calloc(ctx->user_data_ddt_header.entries, sizeof(uint64_t));
651 if(ctx->user_data_ddt2 == NULL)
652 {
653 TRACE("Failed to allocate memory for tape DDT table");
655 }
656
657 // Populate user data DDT from tape DDT uthash
658 HASH_ITER(hh, ctx->tape_ddt, entry, tmp)
659 if(entry->key < ctx->user_data_ddt_header.blocks) ctx->user_data_ddt2[entry->key] = entry->value;
660
661 // Do not repeat code
662 return write_single_level_ddt(ctx);
663}
664
682{
683 uint64_t alignment_mask;
684 uint64_t aligned_position;
685
686 // Finalize pending checksums
687 if(ctx->calculating_md5)
688 {
689 ctx->checksums.hasMd5 = true;
691 }
692 if(ctx->calculating_sha1)
693 {
694 ctx->checksums.hasSha1 = true;
696 }
697 if(ctx->calculating_sha256)
698 {
699 ctx->checksums.hasSha256 = true;
701 }
702 if(ctx->calculating_spamsum)
703 {
704 ctx->checksums.hasSpamSum = true;
705 ctx->checksums.spamsum = calloc(1, FUZZY_MAX_RESULT);
708 }
709 if(ctx->calculating_blake3)
710 {
711 ctx->checksums.hasBlake3 = true;
712 blake3_hasher_finalize(ctx->blake3_context, ctx->checksums.blake3, BLAKE3_OUT_LEN);
713 free(ctx->blake3_context);
714 }
715
716 // Write the checksums block
717 bool has_checksums = ctx->checksums.hasMd5 || ctx->checksums.hasSha1 || ctx->checksums.hasSha256 ||
719
720 if(!has_checksums) return;
721
722 ChecksumHeader checksum_header = {0};
723 checksum_header.identifier = ChecksumBlock;
724
725 fseek(ctx->imageStream, 0, SEEK_END);
726 long checksum_position = ftell(ctx->imageStream);
727 // Align index position to block boundary if needed
728 alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
729 if(checksum_position & alignment_mask)
730 {
731 aligned_position = checksum_position + alignment_mask & ~alignment_mask;
732 fseek(ctx->imageStream, aligned_position, SEEK_SET);
733 checksum_position = aligned_position;
734 }
735
736 // Skip checksum_header
737 fseek(ctx->imageStream, sizeof(checksum_header), SEEK_CUR);
738
739 if(ctx->checksums.hasMd5)
740 {
741 TRACE("Writing MD5 checksum entry");
742 ChecksumEntry md5_entry = {0};
743 md5_entry.length = MD5_DIGEST_LENGTH;
744 md5_entry.type = Md5;
745 fwrite(&md5_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
746 fwrite(&ctx->checksums.md5, MD5_DIGEST_LENGTH, 1, ctx->imageStream);
747 checksum_header.length += sizeof(ChecksumEntry) + MD5_DIGEST_LENGTH;
748 checksum_header.entries++;
749 }
750
751 if(ctx->checksums.hasSha1)
752 {
753 TRACE("Writing SHA1 checksum entry");
754 ChecksumEntry sha1_entry = {0};
755 sha1_entry.length = SHA1_DIGEST_LENGTH;
756 sha1_entry.type = Sha1;
757 fwrite(&sha1_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
758 fwrite(&ctx->checksums.sha1, SHA1_DIGEST_LENGTH, 1, ctx->imageStream);
759 checksum_header.length += sizeof(ChecksumEntry) + SHA1_DIGEST_LENGTH;
760 checksum_header.entries++;
761 }
762
763 if(ctx->checksums.hasSha256)
764 {
765 TRACE("Writing SHA256 checksum entry");
766 ChecksumEntry sha256_entry = {0};
767 sha256_entry.length = SHA256_DIGEST_LENGTH;
768 sha256_entry.type = Sha256;
769 fwrite(&sha256_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
770 fwrite(&ctx->checksums.sha256, SHA256_DIGEST_LENGTH, 1, ctx->imageStream);
771 checksum_header.length += sizeof(ChecksumEntry) + SHA256_DIGEST_LENGTH;
772 checksum_header.entries++;
773 }
774
775 if(ctx->checksums.hasSpamSum)
776 {
777 TRACE("Writing SpamSum checksum entry");
778 ChecksumEntry spamsum_entry = {0};
779 spamsum_entry.length = strlen((const char *)ctx->checksums.spamsum);
780 spamsum_entry.type = SpamSum;
781 fwrite(&spamsum_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
782 fwrite(ctx->checksums.spamsum, spamsum_entry.length, 1, ctx->imageStream);
783 checksum_header.length += sizeof(ChecksumEntry) + spamsum_entry.length;
784 checksum_header.entries++;
785 }
786
787 if(ctx->checksums.hasBlake3)
788 {
789 TRACE("Writing BLAKE3 checksum entry");
790 ChecksumEntry blake3_entry = {0};
791 blake3_entry.length = BLAKE3_OUT_LEN;
792 blake3_entry.type = Blake3;
793 fwrite(&blake3_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
794 fwrite(&ctx->checksums.blake3, BLAKE3_OUT_LEN, 1, ctx->imageStream);
795 checksum_header.length += sizeof(ChecksumEntry) + BLAKE3_OUT_LEN;
796 checksum_header.entries++;
798 }
799
800 fseek(ctx->imageStream, checksum_position, SEEK_SET);
801 TRACE("Writing checksum header");
802 fwrite(&checksum_header, sizeof(ChecksumHeader), 1, ctx->imageStream);
803
804 // Remove any previously existing index entries of the same type before adding
805 TRACE("Removing any previously existing checksum block index entries");
806 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
807 {
808 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
809 if(entry && entry->blockType == ChecksumBlock && entry->dataType == 0)
810 {
811 TRACE("Found existing checksum block index entry at position %d, removing", k);
812 utarray_erase(ctx->index_entries, k, 1);
813 }
814 }
815
816 // Add checksum block to index
817 TRACE("Adding checksum block to index");
818 IndexEntry checksum_index_entry;
819 checksum_index_entry.blockType = ChecksumBlock;
820 checksum_index_entry.dataType = 0;
821 checksum_index_entry.offset = checksum_position;
822
823 utarray_push_back(ctx->index_entries, &checksum_index_entry);
824 ctx->dirty_index_block = true;
825 TRACE("Added checksum block index entry at offset %" PRIu64, checksum_position);
826}
827
839{
840 // Write tracks block
841 if(ctx->tracks_header.entries <= 0 || ctx->track_entries == NULL) return;
842
843 fseek(ctx->imageStream, 0, SEEK_END);
844 long tracks_position = ftell(ctx->imageStream);
845 // Align index position to block boundary if needed
846 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
847 if(tracks_position & alignment_mask)
848 {
849 uint64_t aligned_position = tracks_position + alignment_mask & ~alignment_mask;
850 fseek(ctx->imageStream, aligned_position, SEEK_SET);
851 tracks_position = aligned_position;
852 }
853
854 TRACE("Writing tracks block at position %ld", tracks_position);
855 // Write header
856 if(fwrite(&ctx->tracks_header, sizeof(TracksHeader), 1, ctx->imageStream) == 1)
857 {
858 // Write entries
859 size_t written_entries =
860 fwrite(ctx->track_entries, sizeof(TrackEntry), ctx->tracks_header.entries, ctx->imageStream);
861
862 if(written_entries == ctx->tracks_header.entries)
863 {
864 TRACE("Successfully wrote tracks block with %u entries", ctx->tracks_header.entries);
865
866 // Remove any previously existing index entries of the same type before adding
867 TRACE("Removing any previously existing tracks block index entries");
868 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
869 {
870 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
871 if(entry && entry->blockType == TracksBlock && entry->dataType == 0)
872 {
873 TRACE("Found existing tracks block index entry at position %d, removing", k);
874 utarray_erase(ctx->index_entries, k, 1);
875 }
876 }
877
878 // Add tracks block to index
879 TRACE("Adding tracks block to index");
880
881 IndexEntry tracks_index_entry;
882 tracks_index_entry.blockType = TracksBlock;
883 tracks_index_entry.dataType = 0;
884 tracks_index_entry.offset = tracks_position;
885 utarray_push_back(ctx->index_entries, &tracks_index_entry);
886 ctx->dirty_index_block = true;
887 TRACE("Added tracks block index entry at offset %" PRIu64, tracks_position);
888 }
889 }
890}
891
905{
906 // Write MODE 2 subheader data block
907 if(ctx->mode2_subheaders == NULL) return;
908
909 fseek(ctx->imageStream, 0, SEEK_END);
910 long mode2_subheaders_position = ftell(ctx->imageStream);
911 // Align index position to block boundary if needed
912 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
913 if(mode2_subheaders_position & alignment_mask)
914 {
915 uint64_t aligned_position = mode2_subheaders_position + alignment_mask & ~alignment_mask;
916 fseek(ctx->imageStream, aligned_position, SEEK_SET);
917 mode2_subheaders_position = aligned_position;
918 }
919
920 TRACE("Writing MODE 2 subheaders block at position %ld", mode2_subheaders_position);
921 BlockHeader subheaders_block = {0};
922 subheaders_block.identifier = DataBlock;
923 subheaders_block.type = CompactDiscMode2Subheader;
924 subheaders_block.compression = ctx->compression_enabled ? Lzma : None;
925 subheaders_block.length =
927 8;
928
929 // Calculate CRC64
930 subheaders_block.crc64 = aaruf_crc64_data(ctx->mode2_subheaders, subheaders_block.length);
931
932 uint8_t *buffer = NULL;
933 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
934
935 if(subheaders_block.compression == None)
936 {
937 buffer = ctx->mode2_subheaders;
938 subheaders_block.cmpCrc64 = subheaders_block.crc64;
939 }
940 else
941 {
942 buffer = malloc((size_t)subheaders_block.length * 2); // Allocate double size for compression
943 if(buffer == NULL)
944 {
945 TRACE("Failed to allocate memory for MODE 2 subheaders compression");
946 return;
947 }
948
949 size_t dst_size = (size_t)subheaders_block.length * 2 * 2;
950 size_t props_size = LZMA_PROPERTIES_LENGTH;
951 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->mode2_subheaders, subheaders_block.length, lzma_properties,
952 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
953
954 subheaders_block.cmpLength = (uint32_t)dst_size;
955
956 if(subheaders_block.cmpLength >= subheaders_block.length)
957 {
958 subheaders_block.compression = None;
959 free(buffer);
960 buffer = ctx->mode2_subheaders;
961 }
962 }
963
964 if(subheaders_block.compression == None)
965 {
966 subheaders_block.cmpLength = subheaders_block.length;
967 subheaders_block.cmpCrc64 = subheaders_block.crc64;
968 }
969 else
970 subheaders_block.cmpCrc64 = aaruf_crc64_data(buffer, subheaders_block.cmpLength);
971
972 const size_t length_to_write = subheaders_block.cmpLength;
973 if(subheaders_block.compression == Lzma) subheaders_block.cmpLength += LZMA_PROPERTIES_LENGTH;
974
975 // Write header
976 if(fwrite(&subheaders_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
977 {
978 if(subheaders_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
979
980 // Write data
981 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
982 if(written_bytes == 1)
983 {
984 TRACE("Successfully wrote MODE 2 subheaders block (%" PRIu64 " bytes)", subheaders_block.cmpLength);
985
986 // Remove any previously existing index entries of the same type before adding
987 TRACE("Removing any previously existing MODE 2 subheaders block index entries");
988 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
989 {
990 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
991 if(entry && entry->blockType == DataBlock && entry->dataType == CompactDiscMode2Subheader)
992 {
993 TRACE("Found existing MODE 2 subheaders block index entry at position %d, removing", k);
994 utarray_erase(ctx->index_entries, k, 1);
995 }
996 }
997
998 // Add MODE 2 subheaders block to index
999 TRACE("Adding MODE 2 subheaders block to index");
1000 IndexEntry mode2_subheaders_index_entry;
1001 mode2_subheaders_index_entry.blockType = DataBlock;
1002 mode2_subheaders_index_entry.dataType = CompactDiscMode2Subheader;
1003 mode2_subheaders_index_entry.offset = mode2_subheaders_position;
1004 utarray_push_back(ctx->index_entries, &mode2_subheaders_index_entry);
1005 ctx->dirty_index_block = true;
1006 TRACE("Added MODE 2 subheaders block index entry at offset %" PRIu64, mode2_subheaders_position);
1007 }
1008 }
1009
1010 if(subheaders_block.compression == Lzma) free(buffer);
1011}
1012
1036{
1037 if(ctx->sector_prefix == NULL) return;
1038
1039 fseek(ctx->imageStream, 0, SEEK_END);
1040 long prefix_position = ftell(ctx->imageStream);
1041 // Align index position to block boundary if needed
1042 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1043 if(prefix_position & alignment_mask)
1044 {
1045 uint64_t aligned_position = prefix_position + alignment_mask & ~alignment_mask;
1046 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1047 prefix_position = aligned_position;
1048 }
1049
1050 TRACE("Writing sector prefix block at position %ld", prefix_position);
1051 BlockHeader prefix_block = {0};
1052 prefix_block.identifier = DataBlock;
1053 prefix_block.type = CdSectorPrefix;
1054 prefix_block.compression = ctx->compression_enabled ? Lzma : None;
1055 prefix_block.length = (uint32_t)ctx->sector_prefix_offset;
1056
1057 // Calculate CRC64
1058 prefix_block.crc64 = aaruf_crc64_data(ctx->sector_prefix, prefix_block.length);
1059
1060 uint8_t *buffer = NULL;
1061 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1062
1063 if(prefix_block.compression == None)
1064 {
1065 buffer = ctx->sector_prefix;
1066 prefix_block.cmpCrc64 = prefix_block.crc64;
1067 }
1068 else
1069 {
1070 buffer = malloc((size_t)prefix_block.length * 2); // Allocate double size for compression
1071 if(buffer == NULL)
1072 {
1073 TRACE("Failed to allocate memory for CD sector prefix compression");
1074 return;
1075 }
1076
1077 size_t dst_size = (size_t)prefix_block.length * 2 * 2;
1078 size_t props_size = LZMA_PROPERTIES_LENGTH;
1079 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_prefix, prefix_block.length, lzma_properties,
1080 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1081
1082 prefix_block.cmpLength = (uint32_t)dst_size;
1083
1084 if(prefix_block.cmpLength >= prefix_block.length)
1085 {
1086 prefix_block.compression = None;
1087 free(buffer);
1088 buffer = ctx->sector_prefix;
1089 }
1090 }
1091
1092 if(prefix_block.compression == None)
1093 {
1094 prefix_block.cmpLength = prefix_block.length;
1095 prefix_block.cmpCrc64 = prefix_block.crc64;
1096 }
1097 else
1098 prefix_block.cmpCrc64 = aaruf_crc64_data(buffer, prefix_block.cmpLength);
1099
1100 const size_t length_to_write = prefix_block.cmpLength;
1101 if(prefix_block.compression == Lzma) prefix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1102
1103 // Write header
1104 if(fwrite(&prefix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1105 {
1106 if(prefix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1107
1108 // Write data
1109 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1110 if(written_bytes == 1)
1111 {
1112 TRACE("Successfully wrote CD sector prefix block (%" PRIu64 " bytes)", prefix_block.cmpLength);
1113
1114 // Remove any previously existing index entries of the same type before adding
1115 TRACE("Removing any previously existing CD sector prefix block index entries");
1116 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
1117 {
1118 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
1119 if(entry && entry->blockType == DataBlock && entry->dataType == CdSectorPrefix)
1120 {
1121 TRACE("Found existing CD sector prefix block index entry at position %d, removing", k);
1122 utarray_erase(ctx->index_entries, k, 1);
1123 }
1124 }
1125
1126 // Add prefix block to index
1127 TRACE("Adding CD sector prefix block to index");
1128 IndexEntry prefix_index_entry;
1129 prefix_index_entry.blockType = DataBlock;
1130 prefix_index_entry.dataType = CdSectorPrefix;
1131 prefix_index_entry.offset = prefix_position;
1132 utarray_push_back(ctx->index_entries, &prefix_index_entry);
1133 ctx->dirty_index_block = true;
1134 TRACE("Added CD sector prefix block index entry at offset %" PRIu64, prefix_position);
1135 }
1136 }
1137
1138 if(prefix_block.compression == Lzma) free(buffer);
1139}
1140
1173{
1174 if(ctx->sector_suffix == NULL) return;
1175
1176 fseek(ctx->imageStream, 0, SEEK_END);
1177 long suffix_position = ftell(ctx->imageStream);
1178 // Align index position to block boundary if needed
1179 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1180 if(suffix_position & alignment_mask)
1181 {
1182 const uint64_t aligned_position = suffix_position + alignment_mask & ~alignment_mask;
1183 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1184 suffix_position = aligned_position;
1185 }
1186
1187 TRACE("Writing sector suffix block at position %ld", suffix_position);
1188 BlockHeader suffix_block = {0};
1189 suffix_block.identifier = DataBlock;
1190 suffix_block.type = CdSectorSuffix;
1191 suffix_block.compression = ctx->compression_enabled ? Lzma : None;
1192 suffix_block.length = (uint32_t)ctx->sector_suffix_offset;
1193
1194 // Calculate CRC64
1195 suffix_block.crc64 = aaruf_crc64_data(ctx->sector_suffix, suffix_block.length);
1196
1197 uint8_t *buffer = NULL;
1198 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1199
1200 if(suffix_block.compression == None)
1201 {
1202 buffer = ctx->sector_suffix;
1203 suffix_block.cmpCrc64 = suffix_block.crc64;
1204 }
1205 else
1206 {
1207 buffer = malloc((size_t)suffix_block.length * 2); // Allocate double size for compression
1208 if(buffer == NULL)
1209 {
1210 TRACE("Failed to allocate memory for CD sector suffix compression");
1211 return;
1212 }
1213
1214 size_t dst_size = (size_t)suffix_block.length * 2 * 2;
1215 size_t props_size = LZMA_PROPERTIES_LENGTH;
1216 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_suffix, suffix_block.length, lzma_properties,
1217 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1218
1219 suffix_block.cmpLength = (uint32_t)dst_size;
1220
1221 if(suffix_block.cmpLength >= suffix_block.length)
1222 {
1223 suffix_block.compression = None;
1224 free(buffer);
1225 buffer = ctx->sector_suffix;
1226 }
1227 }
1228
1229 if(suffix_block.compression == None)
1230 {
1231 suffix_block.cmpLength = suffix_block.length;
1232 suffix_block.cmpCrc64 = suffix_block.crc64;
1233 }
1234 else
1235 suffix_block.cmpCrc64 = aaruf_crc64_data(buffer, suffix_block.cmpLength);
1236
1237 const size_t length_to_write = suffix_block.cmpLength;
1238 if(suffix_block.compression == Lzma) suffix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1239
1240 // Write header
1241 if(fwrite(&suffix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1242 {
1243 if(suffix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1244
1245 // Write data
1246 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1247 if(written_bytes == 1)
1248 {
1249 TRACE("Successfully wrote CD sector suffix block (%" PRIu64 " bytes)", suffix_block.cmpLength);
1250
1251 // Remove any previously existing index entries of the same type before adding
1252 TRACE("Removing any previously existing CD sector suffix block index entries");
1253 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
1254 {
1255 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
1256 if(entry && entry->blockType == DataBlock && entry->dataType == CdSectorSuffix)
1257 {
1258 TRACE("Found existing CD sector suffix block index entry at position %d, removing", k);
1259 utarray_erase(ctx->index_entries, k, 1);
1260 }
1261 }
1262
1263 // Add suffix block to index
1264 TRACE("Adding CD sector suffix block to index");
1265 IndexEntry suffix_index_entry;
1266 suffix_index_entry.blockType = DataBlock;
1267 suffix_index_entry.dataType = CdSectorSuffix;
1268 suffix_index_entry.offset = suffix_position;
1269 utarray_push_back(ctx->index_entries, &suffix_index_entry);
1270 ctx->dirty_index_block = true;
1271 TRACE("Added CD sector suffix block index entry at offset %" PRIu64, suffix_position);
1272 }
1273 }
1274
1275 if(suffix_block.compression == Lzma) free(buffer);
1276}
1277
1306{
1307 if(ctx->sector_prefix_ddt2 == NULL) return;
1308
1309 fseek(ctx->imageStream, 0, SEEK_END);
1310 long prefix_ddt_position = ftell(ctx->imageStream);
1311 // Align index position to block boundary if needed
1312 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1313 if(prefix_ddt_position & alignment_mask)
1314 {
1315 const uint64_t aligned_position = prefix_ddt_position + alignment_mask & ~alignment_mask;
1316 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1317 prefix_ddt_position = aligned_position;
1318 }
1319
1320 TRACE("Writing sector prefix DDT v2 at position %ld", prefix_ddt_position);
1321 DdtHeader2 ddt_header2 = {0};
1322 ddt_header2.identifier = DeDuplicationTable2;
1323 ddt_header2.type = CdSectorPrefix;
1324 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1325 ddt_header2.levels = 1;
1326 ddt_header2.tableLevel = 0;
1327 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1328 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1330 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1331 ddt_header2.tableShift = 0; // Single-level DDT
1332 ddt_header2.entries =
1334 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1335 ddt_header2.start = 0;
1336 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1337 // Calculate CRC64
1338 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_prefix_ddt2, (uint32_t)ddt_header2.length);
1339
1340 uint8_t *buffer = NULL;
1341 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1342
1343 if(ddt_header2.compression == None)
1344 {
1345 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1346 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1347 }
1348 else
1349 {
1350 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1351 if(buffer == NULL)
1352 {
1353 TRACE("Failed to allocate memory for sector prefix DDT v2 compression");
1354 return;
1355 }
1356
1357 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1358 size_t props_size = LZMA_PROPERTIES_LENGTH;
1359 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_prefix_ddt2, ddt_header2.length,
1360 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1361
1362 ddt_header2.cmpLength = (uint32_t)dst_size;
1363
1364 if(ddt_header2.cmpLength >= ddt_header2.length)
1365 {
1366 ddt_header2.compression = None;
1367 free(buffer);
1368 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1369 }
1370 }
1371
1372 if(ddt_header2.compression == None)
1373 {
1374 ddt_header2.cmpLength = ddt_header2.length;
1375 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1376 }
1377 else
1378 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1379
1380 const size_t length_to_write = ddt_header2.cmpLength;
1381 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1382
1383 // Write header
1384 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1385 {
1386 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1387
1388 // Write data
1389 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1390 if(written_bytes == 1)
1391 {
1392 TRACE("Successfully wrote sector prefix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1393
1394 // Remove any previously existing index entries of the same type before adding
1395 TRACE("Removing any previously existing sector prefix DDT v2 index entries");
1396 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
1397 {
1398 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
1399 if(entry && entry->blockType == DeDuplicationTable2 && entry->dataType == CdSectorPrefix)
1400 {
1401 TRACE("Found existing sector prefix DDT v2 index entry at position %d, removing", k);
1402 utarray_erase(ctx->index_entries, k, 1);
1403 }
1404 }
1405
1406 // Add prefix block to index
1407 TRACE("Adding sector prefix DDT v2 to index");
1408 IndexEntry prefix_ddt_index_entry;
1409 prefix_ddt_index_entry.blockType = DeDuplicationTable2;
1410 prefix_ddt_index_entry.dataType = CdSectorPrefix;
1411 prefix_ddt_index_entry.offset = prefix_ddt_position;
1412 utarray_push_back(ctx->index_entries, &prefix_ddt_index_entry);
1413 ctx->dirty_index_block = true;
1414 TRACE("Added sector prefix DDT v2 index entry at offset %" PRIu64, prefix_ddt_position);
1415 }
1416 }
1417
1418 if(ddt_header2.compression == Lzma) free(buffer);
1419}
1420
1465{
1466 if(ctx->sector_suffix_ddt2 == NULL) return;
1467
1468 fseek(ctx->imageStream, 0, SEEK_END);
1469 long suffix_ddt_position = ftell(ctx->imageStream);
1470 // Align index position to block boundary if needed
1471 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1472 if(suffix_ddt_position & alignment_mask)
1473 {
1474 const uint64_t aligned_position = suffix_ddt_position + alignment_mask & ~alignment_mask;
1475 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1476 suffix_ddt_position = aligned_position;
1477 }
1478
1479 TRACE("Writing sector suffix DDT v2 at position %ld", suffix_ddt_position);
1480 DdtHeader2 ddt_header2 = {0};
1481 ddt_header2.identifier = DeDuplicationTable2;
1482 ddt_header2.type = CdSectorSuffix;
1483 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1484 ddt_header2.levels = 1;
1485 ddt_header2.tableLevel = 0;
1486 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1487 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1489 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1490 ddt_header2.tableShift = 0; // Single-level DDT
1491 ddt_header2.entries =
1493 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1494 ddt_header2.start = 0;
1495 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1496 // Calculate CRC64
1497 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_suffix_ddt2, (uint32_t)ddt_header2.length);
1498
1499 uint8_t *buffer = NULL;
1500 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1501
1502 if(ddt_header2.compression == None)
1503 {
1504 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1505 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1506 }
1507 else
1508 {
1509 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1510 if(buffer == NULL)
1511 {
1512 TRACE("Failed to allocate memory for sector suffix DDT v2 compression");
1513 return;
1514 }
1515
1516 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1517 size_t props_size = LZMA_PROPERTIES_LENGTH;
1518 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_suffix_ddt2, ddt_header2.length,
1519 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1520
1521 ddt_header2.cmpLength = (uint32_t)dst_size;
1522
1523 if(ddt_header2.cmpLength >= ddt_header2.length)
1524 {
1525 ddt_header2.compression = None;
1526 free(buffer);
1527 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1528 }
1529 }
1530
1531 if(ddt_header2.compression == None)
1532 {
1533 ddt_header2.cmpLength = ddt_header2.length;
1534 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1535 }
1536 else
1537 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1538
1539 const size_t length_to_write = ddt_header2.cmpLength;
1540 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1541
1542 // Write header
1543 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1544 {
1545 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1546
1547 // Write data
1548 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1549 if(written_bytes == 1)
1550 {
1551 TRACE("Successfully wrote sector suffix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1552
1553 // Remove any previously existing index entries of the same type before adding
1554 TRACE("Removing any previously existing sector suffix DDT v2 block index entries");
1555 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
1556 {
1557 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
1558 if(entry && entry->blockType == DeDuplicationTable2 && entry->dataType == CdSectorSuffix)
1559 {
1560 TRACE("Found existing sector suffix DDT v2 index entry at position %d, removing", k);
1561 utarray_erase(ctx->index_entries, k, 1);
1562 }
1563 }
1564
1565 // Add suffix block to index
1566 TRACE("Adding sector suffix DDT v2 to index");
1567 IndexEntry suffix_ddt_index_entry;
1568 suffix_ddt_index_entry.blockType = DeDuplicationTable2;
1569 suffix_ddt_index_entry.dataType = CdSectorSuffix;
1570 suffix_ddt_index_entry.offset = suffix_ddt_position;
1571 utarray_push_back(ctx->index_entries, &suffix_ddt_index_entry);
1572 ctx->dirty_index_block = true;
1573 TRACE("Added sector suffix DDT v2 index entry at offset %" PRIu64, suffix_ddt_position);
1574 }
1575 }
1576
1577 if(ddt_header2.compression == Lzma) free(buffer);
1578}
1579
1638{
1639 if(ctx->sector_subchannel == NULL) return;
1640
1641 fseek(ctx->imageStream, 0, SEEK_END);
1642 long block_position = ftell(ctx->imageStream);
1643 // Align index position to block boundary if needed
1644 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1645 if(block_position & alignment_mask)
1646 {
1647 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
1648 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1649 block_position = aligned_position;
1650 }
1651
1652 TRACE("Writing sector subchannel block at position %ld", block_position);
1653 BlockHeader subchannel_block = {0};
1654 subchannel_block.identifier = DataBlock;
1655 subchannel_block.compression = None;
1656
1657 uint8_t *buffer = ctx->sector_subchannel;
1658 bool owns_buffer = false;
1659 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1660
1662 {
1663 subchannel_block.type = CdSectorSubchannel;
1664 subchannel_block.length = (uint32_t)(ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1666 96;
1667 subchannel_block.cmpLength = subchannel_block.length;
1668
1669 if(ctx->compression_enabled)
1670 {
1671 uint8_t *cst_buffer = malloc(subchannel_block.length);
1672
1673 if(cst_buffer == NULL)
1674 {
1675 TRACE("Failed to allocate memory for Claunia Subchannel Transform output");
1676 return;
1677 }
1678
1679 uint8_t *dst_buffer = malloc(subchannel_block.length);
1680
1681 if(dst_buffer == NULL)
1682 {
1683 TRACE("Failed to allocate memory for LZMA output");
1684 free(cst_buffer);
1685 return;
1686 }
1687
1688 aaruf_cst_transform(ctx->sector_subchannel, cst_buffer, subchannel_block.length);
1689 size_t dst_size = subchannel_block.length;
1690 size_t props_size = LZMA_PROPERTIES_LENGTH;
1691
1692 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, cst_buffer, subchannel_block.length, lzma_properties,
1693 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1694
1695 free(cst_buffer);
1696
1697 if(dst_size < subchannel_block.length)
1698 {
1700 subchannel_block.cmpLength = (uint32_t)dst_size;
1701 buffer = dst_buffer;
1702 owns_buffer = true;
1703 }
1704 else
1705 {
1706 subchannel_block.compression = None;
1707 free(dst_buffer);
1708 subchannel_block.cmpLength = subchannel_block.length;
1709 }
1710 }
1711 }
1712 else if(ctx->image_info.MetadataMediaType == BlockMedia)
1713 {
1714 switch(ctx->image_info.MediaType)
1715 {
1716 case AppleProfile:
1717 case AppleFileWare:
1718 subchannel_block.type = AppleProfileTag;
1719 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 20;
1720 break;
1721 case AppleSonyDS:
1722 case AppleSonySS:
1723 subchannel_block.type = AppleSonyTag;
1724 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 12;
1725 break;
1726 case PriamDataTower:
1727 subchannel_block.type = PriamDataTowerTag;
1728 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 24;
1729 break;
1730 default:
1731 TRACE("Incorrect media type, not writing sector subchannel block");
1732 return; // Incorrect media type
1733 }
1734 subchannel_block.cmpLength = subchannel_block.length;
1735 subchannel_block.compression = Lzma;
1736
1737 uint8_t *dst_buffer = malloc(subchannel_block.length);
1738
1739 if(dst_buffer == NULL)
1740 {
1741 TRACE("Failed to allocate memory for LZMA output");
1742 return;
1743 }
1744
1745 size_t dst_size = subchannel_block.length;
1746 size_t props_size = LZMA_PROPERTIES_LENGTH;
1747
1748 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, ctx->sector_subchannel, subchannel_block.length,
1749 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1750
1751 if(dst_size < subchannel_block.length)
1752 {
1753 subchannel_block.cmpLength = (uint32_t)dst_size;
1754 buffer = dst_buffer;
1755 owns_buffer = true;
1756 }
1757 else
1758 {
1759 subchannel_block.compression = None;
1760 free(dst_buffer);
1761 subchannel_block.cmpLength = subchannel_block.length;
1762 }
1763 }
1764 else
1765 {
1766 TRACE("Incorrect media type, not writing sector subchannel block");
1767 return; // Incorrect media type
1768 }
1769
1770 // Calculate CRC64 for raw subchannel data and compressed payload when present
1771 subchannel_block.crc64 = aaruf_crc64_data(ctx->sector_subchannel, subchannel_block.length);
1772 if(subchannel_block.compression == None)
1773 subchannel_block.cmpCrc64 = subchannel_block.crc64;
1774 else
1775 subchannel_block.cmpCrc64 = aaruf_crc64_data(buffer, subchannel_block.cmpLength);
1776
1777 const size_t length_to_write = subchannel_block.cmpLength;
1778 if(subchannel_block.compression != None) subchannel_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1779
1780 // Write header
1781 if(fwrite(&subchannel_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1782 {
1783 if(subchannel_block.compression != None) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1784
1785 // Write data
1786 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1787 if(written_bytes == 1)
1788 {
1789 TRACE("Successfully wrote sector subchannel block (%" PRIu64 " bytes)", subchannel_block.cmpLength);
1790
1791 // Remove any previously existing index entries of the same type before adding
1792 TRACE("Removing any previously existing subchannel block index entries");
1793 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
1794 {
1795 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
1796 if(entry && entry->blockType == DataBlock && entry->dataType == subchannel_block.type)
1797 {
1798 TRACE("Found existing subchannel block index entry at position %d, removing", k);
1799 utarray_erase(ctx->index_entries, k, 1);
1800 }
1801 }
1802
1803 // Add subchannel block to index
1804 TRACE("Adding sector subchannel block to index");
1805 IndexEntry subchannel_index_entry;
1806 subchannel_index_entry.blockType = DataBlock;
1807 subchannel_index_entry.dataType = subchannel_block.type;
1808 subchannel_index_entry.offset = block_position;
1809 utarray_push_back(ctx->index_entries, &subchannel_index_entry);
1810 ctx->dirty_index_block = true;
1811 TRACE("Added sector subchannel block index entry at offset %" PRIu64, block_position);
1812 }
1813 }
1814
1815 if(owns_buffer) free(buffer);
1816}
1817
1955{
1956 if(ctx->sector_id == NULL || ctx->sector_ied == NULL || ctx->sector_cpr_mai == NULL || ctx->sector_edc == NULL)
1957 return;
1958
1959 uint64_t total_sectors =
1961
1962 // Write DVD sector ID block
1963 fseek(ctx->imageStream, 0, SEEK_END);
1964 long id_position = ftell(ctx->imageStream);
1965 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1966 if(id_position & alignment_mask)
1967 {
1968 const uint64_t aligned_position = id_position + alignment_mask & ~alignment_mask;
1969 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1970 id_position = aligned_position;
1971 }
1972 TRACE("Writing DVD sector ID block at position %ld", id_position);
1973 BlockHeader id_block = {0};
1974 id_block.identifier = DataBlock;
1975 id_block.type = DvdSectorId;
1976 id_block.compression = ctx->compression_enabled ? Lzma : None;
1977 id_block.length = (uint32_t)total_sectors * 4;
1978
1979 // Calculate CRC64
1980 id_block.crc64 = aaruf_crc64_data(ctx->sector_id, id_block.length);
1981
1982 uint8_t *buffer = NULL;
1983 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1984
1985 if(id_block.compression == None)
1986 {
1987 buffer = ctx->sector_id;
1988 id_block.cmpCrc64 = id_block.crc64;
1989 }
1990 else
1991 {
1992 buffer = malloc((size_t)id_block.length * 2); // Allocate double size for compression
1993 if(buffer == NULL)
1994 {
1995 TRACE("Failed to allocate memory for DVD sector ID compression");
1996 return;
1997 }
1998
1999 size_t dst_size = (size_t)id_block.length * 2 * 2;
2000 size_t props_size = LZMA_PROPERTIES_LENGTH;
2001 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_id, id_block.length, lzma_properties, &props_size, 9,
2002 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2003
2004 id_block.cmpLength = (uint32_t)dst_size;
2005
2006 if(id_block.cmpLength >= id_block.length)
2007 {
2008 id_block.compression = None;
2009 free(buffer);
2010 buffer = ctx->sector_id;
2011 }
2012 }
2013
2014 if(id_block.compression == None)
2015 {
2016 id_block.cmpLength = id_block.length;
2017 id_block.cmpCrc64 = id_block.crc64;
2018 }
2019 else
2020 id_block.cmpCrc64 = aaruf_crc64_data(buffer, id_block.cmpLength);
2021
2022 size_t length_to_write = id_block.cmpLength;
2023 if(id_block.compression == Lzma) id_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2024
2025 // Write header
2026 if(fwrite(&id_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2027 {
2028 if(id_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2029
2030 // Write data
2031 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2032 if(written_bytes == 1)
2033 {
2034 TRACE("Successfully wrote DVD sector ID block (%" PRIu64 " bytes)", id_block.cmpLength);
2035
2036 // Remove any previously existing index entries of the same type before adding
2037 TRACE("Removing any previously existing DVD sector ID block index entries");
2038 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2039 {
2040 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2041 if(entry && entry->blockType == DataBlock && entry->dataType == DvdSectorId)
2042 {
2043 TRACE("Found existing DVD sector ID block index entry at position %d, removing", k);
2044 utarray_erase(ctx->index_entries, k, 1);
2045 }
2046 }
2047
2048 // Add ID block to index
2049 TRACE("Adding DVD sector ID block to index");
2050 IndexEntry id_index_entry;
2051 id_index_entry.blockType = DataBlock;
2052 id_index_entry.dataType = DvdSectorId;
2053 id_index_entry.offset = id_position;
2054 utarray_push_back(ctx->index_entries, &id_index_entry);
2055 ctx->dirty_index_block = true;
2056 TRACE("Added DVD sector ID block index entry at offset %" PRIu64, id_position);
2057 }
2058 }
2059
2060 if(id_block.compression == Lzma) free(buffer);
2061
2062 // Write DVD sector IED block
2063 fseek(ctx->imageStream, 0, SEEK_END);
2064 long ied_position = ftell(ctx->imageStream);
2065 if(ied_position & alignment_mask)
2066 {
2067 const uint64_t aligned_position = ied_position + alignment_mask & ~alignment_mask;
2068 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2069 ied_position = aligned_position;
2070 }
2071 TRACE("Writing DVD sector IED block at position %ld", ied_position);
2072 BlockHeader ied_block = {0};
2073 ied_block.identifier = DataBlock;
2074 ied_block.type = DvdSectorIed;
2075 ied_block.compression = ctx->compression_enabled ? Lzma : None;
2076 ied_block.length = (uint32_t)total_sectors * 2;
2077 // Calculate CRC64
2078 ied_block.crc64 = aaruf_crc64_data(ctx->sector_ied, ied_block.length);
2079
2080 buffer = NULL;
2081
2082 if(ied_block.compression == None)
2083 {
2084 buffer = ctx->sector_ied;
2085 ied_block.cmpCrc64 = ied_block.crc64;
2086 }
2087 else
2088 {
2089 buffer = malloc((size_t)ied_block.length * 2); // Allocate double size for compression
2090 if(buffer == NULL)
2091 {
2092 TRACE("Failed to allocate memory for DVD sector IED compression");
2093 return;
2094 }
2095
2096 size_t dst_size = (size_t)ied_block.length * 2 * 2;
2097 size_t props_size = LZMA_PROPERTIES_LENGTH;
2098 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_ied, ied_block.length, lzma_properties, &props_size, 9,
2099 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2100
2101 ied_block.cmpLength = (uint32_t)dst_size;
2102
2103 if(ied_block.cmpLength >= ied_block.length)
2104 {
2105 ied_block.compression = None;
2106 free(buffer);
2107 buffer = ctx->sector_ied;
2108 }
2109 }
2110
2111 if(ied_block.compression == None)
2112 {
2113 ied_block.cmpLength = ied_block.length;
2114 ied_block.cmpCrc64 = ied_block.crc64;
2115 }
2116 else
2117 ied_block.cmpCrc64 = aaruf_crc64_data(buffer, ied_block.cmpLength);
2118
2119 length_to_write = ied_block.cmpLength;
2120 if(ied_block.compression == Lzma) ied_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2121
2122 // Write header
2123 if(fwrite(&ied_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2124 {
2125 if(ied_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2126
2127 // Write data
2128 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2129 if(written_bytes == 1)
2130 {
2131 TRACE("Successfully wrote DVD sector IED block (%" PRIu64 " bytes)", ied_block.cmpLength);
2132
2133 // Remove any previously existing index entries of the same type before adding
2134 TRACE("Removing any previously existing DVD sector IED block index entries");
2135 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2136 {
2137 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2138 if(entry && entry->blockType == DataBlock && entry->dataType == DvdSectorIed)
2139 {
2140 TRACE("Found existing DVD sector IED block index entry at position %d, removing", k);
2141 utarray_erase(ctx->index_entries, k, 1);
2142 }
2143 }
2144
2145 // Add IED block to index
2146 TRACE("Adding DVD sector IED block to index");
2147 IndexEntry ied_index_entry;
2148 ied_index_entry.blockType = DataBlock;
2149 ied_index_entry.dataType = DvdSectorIed;
2150 ied_index_entry.offset = ied_position;
2151 utarray_push_back(ctx->index_entries, &ied_index_entry);
2152 ctx->dirty_index_block = true;
2153 TRACE("Added DVD sector IED block index entry at offset %" PRIu64, ied_position);
2154 }
2155 }
2156
2157 if(ied_block.compression == Lzma) free(buffer);
2158
2159 // Write DVD sector CPR/MAI block
2160 fseek(ctx->imageStream, 0, SEEK_END);
2161 long cpr_mai_position = ftell(ctx->imageStream);
2162 if(cpr_mai_position & alignment_mask)
2163 {
2164 const uint64_t aligned_position = cpr_mai_position + alignment_mask & ~alignment_mask;
2165 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2166 cpr_mai_position = aligned_position;
2167 }
2168 TRACE("Writing DVD sector CPR/MAI block at position %ld", cpr_mai_position);
2169 BlockHeader cpr_mai_block = {0};
2170 cpr_mai_block.identifier = DataBlock;
2171 cpr_mai_block.type = DvdSectorCprMai;
2172 cpr_mai_block.compression = ctx->compression_enabled ? Lzma : None;
2173 cpr_mai_block.length = (uint32_t)total_sectors * 6;
2174 // Calculate CRC64
2175 cpr_mai_block.crc64 = aaruf_crc64_data(ctx->sector_cpr_mai, cpr_mai_block.length);
2176
2177 buffer = NULL;
2178
2179 if(cpr_mai_block.compression == None)
2180 {
2181 buffer = ctx->sector_cpr_mai;
2182 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2183 }
2184 else
2185 {
2186 buffer = malloc((size_t)cpr_mai_block.length * 2); // Allocate double size for compression
2187 if(buffer == NULL)
2188 {
2189 TRACE("Failed to allocate memory for DVD sector CPR/MAI compression");
2190 return;
2191 }
2192
2193 size_t dst_size = (size_t)cpr_mai_block.length * 2 * 2;
2194 size_t props_size = LZMA_PROPERTIES_LENGTH;
2195 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_cpr_mai, cpr_mai_block.length, lzma_properties,
2196 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2197
2198 cpr_mai_block.cmpLength = (uint32_t)dst_size;
2199
2200 if(cpr_mai_block.cmpLength >= cpr_mai_block.length)
2201 {
2202 cpr_mai_block.compression = None;
2203 free(buffer);
2204 buffer = ctx->sector_cpr_mai;
2205 }
2206 }
2207
2208 if(cpr_mai_block.compression == None)
2209 {
2210 cpr_mai_block.cmpLength = cpr_mai_block.length;
2211 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2212 }
2213 else
2214 cpr_mai_block.cmpCrc64 = aaruf_crc64_data(buffer, cpr_mai_block.cmpLength);
2215
2216 length_to_write = cpr_mai_block.cmpLength;
2217 if(cpr_mai_block.compression == Lzma) cpr_mai_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2218
2219 // Write header
2220 if(fwrite(&cpr_mai_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2221 {
2222 if(cpr_mai_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2223
2224 // Write data
2225 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2226 if(written_bytes == 1)
2227 {
2228 TRACE("Successfully wrote DVD sector CPR/MAI block (%" PRIu64 " bytes)", cpr_mai_block.cmpLength);
2229
2230 // Remove any previously existing index entries of the same type before adding
2231 TRACE("Removing any previously existing DVD sector CPR/MAI block index entries");
2232 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2233 {
2234 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2235 if(entry && entry->blockType == DataBlock && entry->dataType == DvdSectorCprMai)
2236 {
2237 TRACE("Found existing DVD sector CPR/MAI block index entry at position %d, removing", k);
2238 utarray_erase(ctx->index_entries, k, 1);
2239 }
2240 }
2241
2242 // Add CPR/MAI block to index
2243 TRACE("Adding DVD sector CPR/MAI block to index");
2244 IndexEntry cpr_mai_index_entry;
2245 cpr_mai_index_entry.blockType = DataBlock;
2246 cpr_mai_index_entry.dataType = DvdSectorCprMai;
2247 cpr_mai_index_entry.offset = cpr_mai_position;
2248 utarray_push_back(ctx->index_entries, &cpr_mai_index_entry);
2249 ctx->dirty_index_block = true;
2250 TRACE("Added DVD sector CPR/MAI block index entry at offset %" PRIu64, cpr_mai_position);
2251 }
2252 }
2253
2254 if(cpr_mai_block.compression == Lzma) free(buffer);
2255
2256 // Write DVD sector EDC block
2257 fseek(ctx->imageStream, 0, SEEK_END);
2258 long edc_position = ftell(ctx->imageStream);
2259 if(edc_position & alignment_mask)
2260 {
2261 const uint64_t aligned_position = edc_position + alignment_mask & ~alignment_mask;
2262 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2263 edc_position = aligned_position;
2264 }
2265 TRACE("Writing DVD sector EDC block at position %ld", edc_position);
2266 BlockHeader edc_block = {0};
2267 edc_block.identifier = DataBlock;
2268 edc_block.type = DvdSectorEdc;
2269 edc_block.compression = ctx->compression_enabled ? Lzma : None;
2270 edc_block.length = (uint32_t)total_sectors * 4;
2271 // Calculate CRC64
2272 edc_block.crc64 = aaruf_crc64_data(ctx->sector_edc, edc_block.length);
2273
2274 buffer = NULL;
2275
2276 if(edc_block.compression == None)
2277 {
2278 buffer = ctx->sector_edc;
2279 edc_block.cmpCrc64 = edc_block.crc64;
2280 }
2281 else
2282 {
2283 buffer = malloc((size_t)edc_block.length * 2); // Allocate double size for compression
2284 if(buffer == NULL)
2285 {
2286 TRACE("Failed to allocate memory for DVD sector EDC compression");
2287 return;
2288 }
2289
2290 size_t dst_size = (size_t)edc_block.length * 2 * 2;
2291 size_t props_size = LZMA_PROPERTIES_LENGTH;
2292 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_edc, edc_block.length, lzma_properties, &props_size, 9,
2293 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2294
2295 edc_block.cmpLength = (uint32_t)dst_size;
2296
2297 if(edc_block.cmpLength >= edc_block.length)
2298 {
2299 edc_block.compression = None;
2300 free(buffer);
2301 buffer = ctx->sector_edc;
2302 }
2303 }
2304
2305 if(edc_block.compression == None)
2306 {
2307 edc_block.cmpLength = edc_block.length;
2308 edc_block.cmpCrc64 = edc_block.crc64;
2309 }
2310 else
2311 edc_block.cmpCrc64 = aaruf_crc64_data(buffer, edc_block.cmpLength);
2312
2313 length_to_write = edc_block.cmpLength;
2314 if(edc_block.compression == Lzma) edc_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2315
2316 // Write header
2317 if(fwrite(&edc_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2318 {
2319 if(edc_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2320
2321 // Write data
2322 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2323 if(written_bytes == 1)
2324 {
2325 TRACE("Successfully wrote DVD sector EDC block (%" PRIu64 " bytes)", edc_block.cmpLength);
2326
2327 // Remove any previously existing index entries of the same type before adding
2328 TRACE("Removing any previously existing DVD sector EDC block index entries");
2329 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2330 {
2331 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2332 if(entry && entry->blockType == DataBlock && entry->dataType == DvdSectorEdc)
2333 {
2334 TRACE("Found existing DVD sector EDC block index entry at position %d, removing", k);
2335 utarray_erase(ctx->index_entries, k, 1);
2336 }
2337 }
2338
2339 // Add EDC block to index
2340 TRACE("Adding DVD sector EDC block to index");
2341 IndexEntry edc_index_entry;
2342 edc_index_entry.blockType = DataBlock;
2343 edc_index_entry.dataType = DvdSectorEdc;
2344 edc_index_entry.offset = edc_position;
2345 utarray_push_back(ctx->index_entries, &edc_index_entry);
2346 ctx->dirty_index_block = true;
2347 TRACE("Added DVD sector EDC block index entry at offset %" PRIu64, edc_position);
2348 }
2349 }
2350
2351 if(edc_block.compression == Lzma) free(buffer);
2352}
2353
2452{
2453 if(ctx->sector_decrypted_title_key == NULL) return;
2454
2455 fseek(ctx->imageStream, 0, SEEK_END);
2456 long block_position = ftell(ctx->imageStream);
2457 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2458 if(block_position & alignment_mask)
2459 {
2460 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2461 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2462 block_position = aligned_position;
2463 }
2464 TRACE("Writing DVD decrypted title key block at position %ld", block_position);
2465 BlockHeader decrypted_title_key_block = {0};
2466 decrypted_title_key_block.identifier = DataBlock;
2467 decrypted_title_key_block.type = DvdSectorTitleKeyDecrypted;
2468 decrypted_title_key_block.compression = ctx->compression_enabled ? Lzma : None;
2469 decrypted_title_key_block.length =
2471 5;
2472 // Calculate CRC64
2473 decrypted_title_key_block.crc64 =
2474 aaruf_crc64_data(ctx->sector_decrypted_title_key, decrypted_title_key_block.length);
2475
2476 uint8_t *buffer = NULL;
2477 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2478
2479 if(decrypted_title_key_block.compression == None)
2480 {
2481 buffer = ctx->sector_decrypted_title_key;
2482 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2483 }
2484 else
2485 {
2486 buffer = malloc((size_t)decrypted_title_key_block.length * 2); // Allocate double size for compression
2487 if(buffer == NULL)
2488 {
2489 TRACE("Failed to allocate memory for DVD decrypted title key compression");
2490 return;
2491 }
2492
2493 size_t dst_size = (size_t)decrypted_title_key_block.length * 2 * 2;
2494 size_t props_size = LZMA_PROPERTIES_LENGTH;
2495 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_decrypted_title_key, decrypted_title_key_block.length,
2496 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2497
2498 decrypted_title_key_block.cmpLength = (uint32_t)dst_size;
2499
2500 if(decrypted_title_key_block.cmpLength >= decrypted_title_key_block.length)
2501 {
2502 decrypted_title_key_block.compression = None;
2503 free(buffer);
2504 buffer = ctx->sector_decrypted_title_key;
2505 }
2506 }
2507
2508 if(decrypted_title_key_block.compression == None)
2509 {
2510 decrypted_title_key_block.cmpLength = decrypted_title_key_block.length;
2511 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2512 }
2513 else
2514 decrypted_title_key_block.cmpCrc64 = aaruf_crc64_data(buffer, decrypted_title_key_block.cmpLength);
2515
2516 const size_t length_to_write = decrypted_title_key_block.cmpLength;
2517 if(decrypted_title_key_block.compression == Lzma) decrypted_title_key_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2518
2519 // Write header
2520 if(fwrite(&decrypted_title_key_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2521 {
2522 if(decrypted_title_key_block.compression == Lzma)
2523 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2524
2525 // Write data
2526 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2527 if(written_bytes == 1)
2528 {
2529 TRACE("Successfully wrote DVD decrypted title key block (%" PRIu64 " bytes)",
2530 decrypted_title_key_block.cmpLength);
2531
2532 // Remove any previously existing index entries of the same type before adding
2533 TRACE("Removing any previously existing DVD decrypted title key block index entries");
2534 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2535 {
2536 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2537 if(entry && entry->blockType == DataBlock && entry->dataType == DvdSectorTitleKeyDecrypted)
2538 {
2539 TRACE("Found existing DVD decrypted title key block index entry at position %d, removing", k);
2540 utarray_erase(ctx->index_entries, k, 1);
2541 }
2542 }
2543
2544 // Add decrypted title key block to index
2545 TRACE("Adding DVD decrypted title key block to index");
2546 IndexEntry decrypted_title_key_index_entry;
2547 decrypted_title_key_index_entry.blockType = DataBlock;
2548 decrypted_title_key_index_entry.dataType = DvdSectorTitleKeyDecrypted;
2549 decrypted_title_key_index_entry.offset = block_position;
2550 utarray_push_back(ctx->index_entries, &decrypted_title_key_index_entry);
2551 ctx->dirty_index_block = true;
2552 TRACE("Added DVD decrypted title key block index entry at offset %" PRIu64, block_position);
2553 }
2554 }
2555
2556 if(decrypted_title_key_block.compression == Lzma) free(buffer);
2557}
2558
2631{
2632 if(ctx->mediaTags == NULL) return;
2633
2634 mediaTagEntry *media_tag = NULL;
2635 mediaTagEntry *tmp_media_tag = NULL;
2636
2637 HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
2638 {
2639 fseek(ctx->imageStream, 0, SEEK_END);
2640 long tag_position = ftell(ctx->imageStream);
2641 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2642 if(tag_position & alignment_mask)
2643 {
2644 const uint64_t aligned_position = tag_position + alignment_mask & ~alignment_mask;
2645 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2646 tag_position = aligned_position;
2647 }
2648
2649 TRACE("Writing media tag block type %d at position %ld", aaruf_get_datatype_for_media_tag_type(media_tag->type),
2650 tag_position);
2651 BlockHeader tag_block = {0};
2652 tag_block.identifier = DataBlock;
2653 tag_block.type = (uint16_t)aaruf_get_datatype_for_media_tag_type(media_tag->type);
2654 tag_block.compression = ctx->compression_enabled ? Lzma : None;
2655 tag_block.length = media_tag->length;
2656
2657 // Calculate CRC64
2658 tag_block.crc64 = aaruf_crc64_data(media_tag->data, tag_block.length);
2659
2660 uint8_t *buffer = NULL;
2661 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2662
2663 if(tag_block.compression == None)
2664 {
2665 buffer = media_tag->data;
2666 tag_block.cmpCrc64 = tag_block.crc64;
2667 }
2668 else
2669 {
2670 buffer = malloc((size_t)tag_block.length * 2); // Allocate double size for compression
2671 if(buffer == NULL)
2672 {
2673 TRACE("Failed to allocate memory for media tag compression");
2674 return;
2675 }
2676
2677 size_t dst_size = (size_t)tag_block.length * 2 * 2;
2678 size_t props_size = LZMA_PROPERTIES_LENGTH;
2679 aaruf_lzma_encode_buffer(buffer, &dst_size, media_tag->data, tag_block.length, lzma_properties, &props_size,
2680 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2681
2682 tag_block.cmpLength = (uint32_t)dst_size;
2683
2684 if(tag_block.cmpLength >= tag_block.length)
2685 {
2686 tag_block.compression = None;
2687 free(buffer);
2688 buffer = media_tag->data;
2689 }
2690 }
2691
2692 if(tag_block.compression == None)
2693 {
2694 tag_block.cmpLength = tag_block.length;
2695 tag_block.cmpCrc64 = tag_block.crc64;
2696 }
2697 else
2698 tag_block.cmpCrc64 = aaruf_crc64_data(buffer, tag_block.cmpLength);
2699
2700 const size_t length_to_write = tag_block.cmpLength;
2701 if(tag_block.compression == Lzma) tag_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2702
2703 // Write header
2704 if(fwrite(&tag_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2705 {
2706 if(tag_block.compression == Lzma)
2707 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream); // Write data
2708
2709 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2710 if(written_bytes == 1)
2711 {
2712 TRACE("Successfully wrote media tag block type %d (%" PRIu64 " bytes)", tag_block.type,
2713 tag_block.cmpLength);
2714
2715 // Remove any previously existing index entries of the same type before adding
2716 TRACE("Removing any previously existing media tag type %d block index entries", tag_block.type);
2717 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2718 {
2719 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2720 if(entry && entry->blockType == DataBlock && entry->dataType == tag_block.type)
2721 {
2722 TRACE("Found existing media tag type %d block index entry at position %d, removing",
2723 tag_block.type, k);
2724 utarray_erase(ctx->index_entries, k, 1);
2725 }
2726 }
2727
2728 // Add media tag block to index
2729 TRACE("Adding media tag type %d block to index", tag_block.type);
2730 IndexEntry tag_index_entry;
2731 tag_index_entry.blockType = DataBlock;
2732 tag_index_entry.dataType = tag_block.type;
2733 tag_index_entry.offset = tag_position;
2734 utarray_push_back(ctx->index_entries, &tag_index_entry);
2735 ctx->dirty_index_block = true;
2736 TRACE("Added media tag block type %d index entry at offset %" PRIu64, tag_block.type, tag_position);
2737 }
2738 }
2739
2740 if(tag_block.compression == Lzma) free(buffer);
2741 }
2742}
2743
2907{
2908 if(ctx->tape_files == NULL) return;
2909
2910 // Iterate the uthash and count how many entries do we have
2911 const tapeFileHashEntry *tape_file = NULL;
2912 const tapeFileHashEntry *tmp_tape_file = NULL;
2913 size_t tape_file_count = 0;
2914 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file) tape_file_count++;
2915
2916 // Create a memory buffer to copy all the file entries
2917 const size_t buffer_size = tape_file_count * sizeof(TapeFileEntry);
2918 TapeFileEntry *buffer = malloc(buffer_size);
2919 if(buffer == NULL)
2920 {
2921 TRACE("Failed to allocate memory for tape file entries");
2922 return;
2923 }
2924 memset(buffer, 0, buffer_size);
2925 size_t index = 0;
2926 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file)
2927 {
2928 if(index >= tape_file_count) break;
2929 memcpy(&buffer[index], &tape_file->fileEntry, sizeof(TapeFileEntry));
2930 index++;
2931 }
2932
2933 // Create the tape file block in memory
2934 TapeFileHeader tape_file_block = {0};
2935 tape_file_block.identifier = TapeFileBlock;
2936 tape_file_block.length = (uint32_t)buffer_size;
2937 tape_file_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_file_block.length);
2938
2939 // Write tape file block to file, block aligned
2940 fseek(ctx->imageStream, 0, SEEK_END);
2941 long block_position = ftell(ctx->imageStream);
2942 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2943 if(block_position & alignment_mask)
2944 {
2945 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2946 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2947 block_position = aligned_position;
2948 }
2949 TRACE("Writing tape file block at position %ld", block_position);
2950 if(fwrite(&tape_file_block, sizeof(TapeFileHeader), 1, ctx->imageStream) == 1)
2951 {
2952 const size_t written_bytes = fwrite(buffer, tape_file_block.length, 1, ctx->imageStream);
2953 if(written_bytes == 1)
2954 {
2955 TRACE("Successfully wrote tape file block (%" PRIu64 " bytes)", tape_file_block.length);
2956
2957 // Remove any previously existing index entries of the same type before adding
2958 TRACE("Removing any previously existing tape file block index entries");
2959 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
2960 {
2961 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
2962 if(entry && entry->blockType == TapeFileBlock && entry->dataType == 0)
2963 {
2964 TRACE("Found existing tape file block index entry at position %d, removing", k);
2965 utarray_erase(ctx->index_entries, k, 1);
2966 }
2967 }
2968
2969 // Add tape file block to index
2970 TRACE("Adding tape file block to index");
2971 IndexEntry index_entry;
2972 index_entry.blockType = TapeFileBlock;
2973 index_entry.dataType = 0;
2974 index_entry.offset = block_position;
2975 utarray_push_back(ctx->index_entries, &index_entry);
2976 ctx->dirty_index_block = true;
2977 TRACE("Added tape file block index entry at offset %" PRIu64, block_position);
2978 }
2979 }
2980
2981 free(buffer);
2982}
2983
3153{
3154 if(ctx->tape_partitions == NULL) return;
3155
3156 // Iterate the uthash and count how many entries do we have
3157 const TapePartitionHashEntry *tape_partition = NULL;
3158 const TapePartitionHashEntry *tmp_tape_partition = NULL;
3159 size_t tape_partition_count = 0;
3160 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition) tape_partition_count++;
3161
3162 // Create a memory buffer to copy all the partition entries
3163 const size_t buffer_size = tape_partition_count * sizeof(TapePartitionEntry);
3164 TapePartitionEntry *buffer = malloc(buffer_size);
3165 if(buffer == NULL)
3166 {
3167 TRACE("Failed to allocate memory for tape partition entries");
3168 return;
3169 }
3170 memset(buffer, 0, buffer_size);
3171 size_t index = 0;
3172 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition)
3173 {
3174 if(index >= tape_partition_count) break;
3175 memcpy(&buffer[index], &tape_partition->partitionEntry, sizeof(TapePartitionEntry));
3176 index++;
3177 }
3178
3179 // Create the tape partition block in memory
3180 TapePartitionHeader tape_partition_block = {0};
3181 tape_partition_block.identifier = TapePartitionBlock;
3182 tape_partition_block.length = (uint32_t)buffer_size;
3183 tape_partition_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_partition_block.length);
3184
3185 // Write tape partition block to partition, block aligned
3186 fseek(ctx->imageStream, 0, SEEK_END);
3187 long block_position = ftell(ctx->imageStream);
3188 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3189 if(block_position & alignment_mask)
3190 {
3191 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3192 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3193 block_position = aligned_position;
3194 }
3195 TRACE("Writing tape partition block at position %ld", block_position);
3196 if(fwrite(&tape_partition_block, sizeof(TapePartitionHeader), 1, ctx->imageStream) == 1)
3197 {
3198 const size_t written_bytes = fwrite(buffer, tape_partition_block.length, 1, ctx->imageStream);
3199 if(written_bytes == 1)
3200 {
3201 TRACE("Successfully wrote tape partition block (%" PRIu64 " bytes)", tape_partition_block.length);
3202
3203 // Remove any previously existing index entries of the same type before adding
3204 TRACE("Removing any previously existing tape partition block index entries");
3205 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
3206 {
3207 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
3208 if(entry && entry->blockType == TapePartitionBlock && entry->dataType == 0)
3209 {
3210 TRACE("Found existing tape partition block index entry at position %d, removing", k);
3211 utarray_erase(ctx->index_entries, k, 1);
3212 }
3213 }
3214
3215 // Add tape partition block to index
3216 TRACE("Adding tape partition block to index");
3217 IndexEntry index_entry;
3218 index_entry.blockType = TapePartitionBlock;
3219 index_entry.dataType = 0;
3220 index_entry.offset = block_position;
3221 utarray_push_back(ctx->index_entries, &index_entry);
3222 ctx->dirty_index_block = true;
3223 TRACE("Added tape partition block index entry at offset %" PRIu64, block_position);
3224 }
3225 }
3226
3227 free(buffer);
3228}
3229
3292{
3293 if(ctx->geometry_block.identifier != GeometryBlock) return;
3294
3295 fseek(ctx->imageStream, 0, SEEK_END);
3296 long block_position = ftell(ctx->imageStream);
3297 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3298 if(block_position & alignment_mask)
3299 {
3300 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3301 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3302 block_position = aligned_position;
3303 }
3304
3305 TRACE("Writing geometry block at position %ld", block_position);
3306
3307 // Write header
3308 if(fwrite(&ctx->geometry_block, sizeof(GeometryBlockHeader), 1, ctx->imageStream) == 1)
3309 {
3310 TRACE("Successfully wrote geometry block");
3311
3312 // Remove any previously existing index entries of the same type before adding
3313 TRACE("Removing any previously existing geometry block index entries");
3314 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
3315 {
3316 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
3317 if(entry && entry->blockType == GeometryBlock && entry->dataType == 0)
3318 {
3319 TRACE("Found existing geometry block index entry at position %d, removing", k);
3320 utarray_erase(ctx->index_entries, k, 1);
3321 }
3322 }
3323
3324 // Add geometry block to index
3325 TRACE("Adding geometry block to index");
3326 IndexEntry index_entry;
3327 index_entry.blockType = GeometryBlock;
3328 index_entry.dataType = 0;
3329 index_entry.offset = block_position;
3330 utarray_push_back(ctx->index_entries, &index_entry);
3331 ctx->dirty_index_block = true;
3332 TRACE("Added geometry block index entry at offset %" PRIu64, block_position);
3333 }
3334}
3335
3441{
3443 ctx->metadata_block_header.lastMediaSequence == 0 && ctx->creator == NULL && ctx->comments == NULL &&
3444 ctx->media_title == NULL && ctx->media_manufacturer == NULL && ctx->media_model == NULL &&
3445 ctx->media_serial_number == NULL && ctx->media_barcode == NULL && ctx->media_part_number == NULL &&
3446 ctx->drive_manufacturer == NULL && ctx->drive_model == NULL && ctx->drive_serial_number == NULL &&
3447 ctx->drive_firmware_revision == NULL)
3448 return;
3449
3458
3460
3461 int pos = sizeof(MetadataBlockHeader);
3462
3463 uint8_t *buffer = calloc(1, ctx->metadata_block_header.blockSize);
3464 if(buffer == NULL) return;
3465
3466 if(ctx->creator != NULL && ctx->metadata_block_header.creatorLength > 0)
3467 {
3468 memcpy(buffer + pos, ctx->creator, ctx->metadata_block_header.creatorLength);
3471 }
3472
3473 if(ctx->comments != NULL && ctx->metadata_block_header.commentsLength > 0)
3474 {
3475 memcpy(buffer + pos, ctx->comments, ctx->metadata_block_header.commentsLength);
3478 }
3479
3480 if(ctx->media_title != NULL && ctx->metadata_block_header.mediaTitleLength > 0)
3481 {
3482 memcpy(buffer + pos, ctx->media_title, ctx->metadata_block_header.mediaTitleLength);
3485 }
3486
3488 {
3489 memcpy(buffer + pos, ctx->media_manufacturer, ctx->metadata_block_header.mediaManufacturerLength);
3492 }
3493
3494 if(ctx->media_model != NULL && ctx->metadata_block_header.mediaModelLength > 0)
3495 {
3496 memcpy(buffer + pos, ctx->media_model, ctx->metadata_block_header.mediaModelLength);
3499 }
3500
3502 {
3503 memcpy(buffer + pos, ctx->media_serial_number, ctx->metadata_block_header.mediaSerialNumberLength);
3506 }
3507
3508 if(ctx->media_barcode != NULL && ctx->metadata_block_header.mediaBarcodeLength > 0)
3509 {
3510 memcpy(buffer + pos, ctx->media_barcode, ctx->metadata_block_header.mediaBarcodeLength);
3513 }
3514
3516 {
3517 memcpy(buffer + pos, ctx->media_part_number, ctx->metadata_block_header.mediaPartNumberLength);
3520 }
3521
3523 {
3524 memcpy(buffer + pos, ctx->drive_manufacturer, ctx->metadata_block_header.driveManufacturerLength);
3527 }
3528
3529 if(ctx->drive_model != NULL && ctx->metadata_block_header.driveModelLength > 0)
3530 {
3531 memcpy(buffer + pos, ctx->drive_model, ctx->metadata_block_header.driveModelLength);
3534 }
3535
3537 {
3538 memcpy(buffer + pos, ctx->drive_serial_number, ctx->metadata_block_header.driveSerialNumberLength);
3541 }
3542
3544 {
3547 }
3548
3549 fseek(ctx->imageStream, 0, SEEK_END);
3550 long block_position = ftell(ctx->imageStream);
3551 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3552 if(block_position & alignment_mask)
3553 {
3554 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3555 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3556 block_position = aligned_position;
3557 }
3558
3559 memcpy(buffer, &ctx->metadata_block_header, sizeof(MetadataBlockHeader));
3560
3561 TRACE("Writing metadata block at position %ld", block_position);
3562
3563 if(fwrite(buffer, ctx->metadata_block_header.blockSize, 1, ctx->imageStream) == 1)
3564 {
3565 TRACE("Successfully wrote metadata block");
3566
3567 // Remove any previously existing index entries of the same type before adding
3568 TRACE("Removing any previously existing metadata block index entries");
3569 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
3570 {
3571 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
3572 if(entry && entry->blockType == MetadataBlock && entry->dataType == 0)
3573 {
3574 TRACE("Found existing metadata block index entry at position %d, removing", k);
3575 utarray_erase(ctx->index_entries, k, 1);
3576 }
3577 }
3578
3579 // Add metadata block to index
3580 TRACE("Adding metadata block to index");
3581 IndexEntry index_entry;
3582 index_entry.blockType = MetadataBlock;
3583 index_entry.dataType = 0;
3584 index_entry.offset = block_position;
3585 utarray_push_back(ctx->index_entries, &index_entry);
3586 ctx->dirty_index_block = true;
3587 TRACE("Added metadata block index entry at offset %" PRIu64, block_position);
3588 }
3589
3590 free(buffer);
3591}
3592
3739{
3740
3741 if(ctx->dump_hardware_entries_with_data == NULL || ctx->dump_hardware_header.entries == 0 ||
3743 return;
3744
3745 const size_t required_length = sizeof(DumpHardwareHeader) + ctx->dump_hardware_header.length;
3746
3747 uint8_t *buffer = calloc(1, required_length);
3748
3749 if(buffer == NULL) return;
3750
3751 // Start to iterate and copy the data
3752 size_t offset = sizeof(DumpHardwareHeader);
3753 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
3754 {
3755 size_t entry_size = sizeof(DumpHardwareEntry) +
3765
3766 if(offset + entry_size > required_length)
3767 {
3768 FATAL("Calculated size exceeds provided buffer length");
3769 free(buffer);
3770 return;
3771 }
3772
3773 memcpy(buffer + offset, &ctx->dump_hardware_entries_with_data[i].entry, sizeof(DumpHardwareEntry));
3774 offset += sizeof(DumpHardwareEntry);
3777 {
3778 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].manufacturer,
3781 }
3783 ctx->dump_hardware_entries_with_data[i].model != NULL)
3784 {
3785 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].model,
3788 }
3791 {
3792 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].revision,
3795 }
3798 {
3799 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].firmware,
3802 }
3805 {
3806 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].serial,
3809 }
3812 {
3813 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareName,
3816 }
3819 {
3820 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareVersion,
3823 }
3826 {
3827 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem,
3830 }
3833 {
3834 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].extents,
3836 offset += ctx->dump_hardware_entries_with_data[i].entry.extents * sizeof(DumpExtent);
3837 }
3838 }
3839
3840 // Calculate CRC64
3843
3844 // Copy header
3845 memcpy(buffer, &ctx->dump_hardware_header, sizeof(DumpHardwareHeader));
3846
3847 fseek(ctx->imageStream, 0, SEEK_END);
3848 long block_position = ftell(ctx->imageStream);
3849 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3850 if(block_position & alignment_mask)
3851 {
3852 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3853 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3854 block_position = aligned_position;
3855 }
3856 TRACE("Writing dump hardware block at position %ld", block_position);
3857 if(fwrite(buffer, required_length, 1, ctx->imageStream) == 1)
3858 {
3859 TRACE("Successfully wrote dump hardware block");
3860
3861 // Remove any previously existing index entries of the same type before adding
3862 TRACE("Removing any previously existing dump hardware block index entries");
3863 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
3864 {
3865 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
3866 if(entry && entry->blockType == DumpHardwareBlock && entry->dataType == 0)
3867 {
3868 TRACE("Found existing dump hardware block index entry at position %d, removing", k);
3869 utarray_erase(ctx->index_entries, k, 1);
3870 }
3871 }
3872
3873 // Add dump hardware block to index
3874 TRACE("Adding dump hardware block to index");
3875 IndexEntry index_entry;
3876 index_entry.blockType = DumpHardwareBlock;
3877 index_entry.dataType = 0;
3878 index_entry.offset = block_position;
3879 utarray_push_back(ctx->index_entries, &index_entry);
3880 ctx->dirty_index_block = true;
3881 TRACE("Added dump hardware block index entry at offset %" PRIu64, block_position);
3882 }
3883
3884 free(buffer);
3885}
3886
3982{
3983 if(ctx->cicm_block == NULL || ctx->cicm_block_header.length == 0 || ctx->cicm_block_header.identifier != CicmBlock)
3984 return;
3985
3986 fseek(ctx->imageStream, 0, SEEK_END);
3987 long block_position = ftell(ctx->imageStream);
3988 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3989
3990 if(block_position & alignment_mask)
3991 {
3992 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3993 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3994 block_position = aligned_position;
3995 }
3996
3997 TRACE("Writing CICM XML block at position %ld", block_position);
3998 if(fwrite(&ctx->cicm_block_header, sizeof(CicmMetadataBlock), 1, ctx->imageStream) == 1 &&
3999 fwrite(ctx->cicm_block, ctx->cicm_block_header.length, 1, ctx->imageStream) == 1)
4000 {
4001 TRACE("Successfully wrote CICM XML block");
4002
4003 // Remove any previously existing index entries of the same type before adding
4004 TRACE("Removing any previously existing CICM XML block index entries");
4005 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
4006 {
4007 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
4008 if(entry && entry->blockType == CicmBlock && entry->dataType == 0)
4009 {
4010 TRACE("Found existing CICM XML block index entry at position %d, removing", k);
4011 utarray_erase(ctx->index_entries, k, 1);
4012 }
4013 }
4014
4015 // Add CICM block to index
4016 TRACE("Adding CICM XML block to index");
4017 IndexEntry index_entry;
4018 index_entry.blockType = CicmBlock;
4019 index_entry.dataType = 0;
4020 index_entry.offset = block_position;
4021 utarray_push_back(ctx->index_entries, &index_entry);
4022 ctx->dirty_index_block = true;
4023 TRACE("Added CICM XML block index entry at offset %" PRIu64, block_position);
4024 }
4025}
4026
4132{
4133 if(ctx->json_block == NULL || ctx->json_block_header.length == 0 ||
4135 return;
4136
4137 fseek(ctx->imageStream, 0, SEEK_END);
4138 long block_position = ftell(ctx->imageStream);
4139 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
4140
4141 if(block_position & alignment_mask)
4142 {
4143 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
4144 fseek(ctx->imageStream, aligned_position, SEEK_SET);
4145 block_position = aligned_position;
4146 }
4147
4148 TRACE("Writing Aaru metadata JSON block at position %ld", block_position);
4149 if(fwrite(&ctx->json_block_header, sizeof(AaruMetadataJsonBlockHeader), 1, ctx->imageStream) == 1 &&
4150 fwrite(ctx->json_block, ctx->json_block_header.length, 1, ctx->imageStream) == 1)
4151 {
4152 TRACE("Successfully wrote Aaru metadata JSON block");
4153
4154 // Remove any previously existing index entries of the same type before adding
4155 TRACE("Removing any previously existing Aaru metadata JSON block index entries");
4156 for(int k = utarray_len(ctx->index_entries) - 1; k >= 0; k--)
4157 {
4158 const IndexEntry *entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, k);
4159 if(entry && entry->blockType == AaruMetadataJsonBlock && entry->dataType == 0)
4160 {
4161 TRACE("Found existing Aaru metadata JSON block index entry at position %d, removing", k);
4162 utarray_erase(ctx->index_entries, k, 1);
4163 }
4164 }
4165
4166 // Add Aaru metadata JSON block to index
4167 TRACE("Adding Aaru metadata JSON block to index");
4168 IndexEntry index_entry;
4169 index_entry.blockType = AaruMetadataJsonBlock;
4170 index_entry.dataType = 0;
4171 index_entry.offset = block_position;
4172 utarray_push_back(ctx->index_entries, &index_entry);
4173 ctx->dirty_index_block = true;
4174 TRACE("Added Aaru metadata JSON block index entry at offset %" PRIu64, block_position);
4175 }
4176}
4177
4195{
4196 // Write the complete index at the end of the file
4197 TRACE("Writing index at the end of the file");
4198 fseek(ctx->imageStream, 0, SEEK_END);
4199 long index_position = ftell(ctx->imageStream);
4200
4201 // Align index position to block boundary if needed
4202 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
4203 if(index_position & alignment_mask)
4204 {
4205 uint64_t aligned_position = index_position + alignment_mask & ~alignment_mask;
4206 fseek(ctx->imageStream, aligned_position, SEEK_SET);
4207 index_position = aligned_position;
4208 TRACE("Aligned index position to %" PRIu64, aligned_position);
4209 }
4210
4211 // Prepare index header
4212 IndexHeader3 index_header;
4213 index_header.identifier = IndexBlock3;
4214 index_header.entries = utarray_len(ctx->index_entries);
4215 index_header.previous = 0; // No previous index for now
4216
4217 TRACE("Writing index with %" PRIu64 " entries at position %ld", index_header.entries, index_position);
4218
4219 // Calculate CRC64 of index entries
4220 crc64_ctx *index_crc64_context = aaruf_crc64_init();
4221 if(index_crc64_context != NULL && index_header.entries > 0)
4222 {
4223 size_t index_data_size = index_header.entries * sizeof(IndexEntry);
4224 aaruf_crc64_update(index_crc64_context, utarray_front(ctx->index_entries), index_data_size);
4225 aaruf_crc64_final(index_crc64_context, &index_header.crc64);
4226 TRACE("Calculated index CRC64: 0x%16lX", index_header.crc64);
4227 }
4228 else
4229 index_header.crc64 = 0;
4230
4231 // Write index header
4232 if(fwrite(&index_header, sizeof(IndexHeader3), 1, ctx->imageStream) == 1)
4233 {
4234 TRACE("Successfully wrote index header");
4235
4236 // Write index entries
4237 if(index_header.entries > 0)
4238 {
4239 size_t entries_written = 0;
4240 IndexEntry *entry = NULL;
4241
4242 for(entry = (IndexEntry *)utarray_front(ctx->index_entries); entry != NULL;
4243 entry = (IndexEntry *)utarray_next(ctx->index_entries, entry))
4244 if(fwrite(entry, sizeof(IndexEntry), 1, ctx->imageStream) == 1)
4245 {
4246 entries_written++;
4247 TRACE("Wrote index entry: blockType=0x%08X dataType=%u offset=%" PRIu64, entry->blockType,
4248 entry->dataType, entry->offset);
4249 }
4250 else
4251 {
4252 TRACE("Failed to write index entry %zu", entries_written);
4253 break;
4254 }
4255
4256 if(entries_written == index_header.entries)
4257 {
4258 TRACE("Successfully wrote all %zu index entries", entries_written);
4259
4260 // Update header with index offset and rewrite it
4261 ctx->header.indexOffset = index_position;
4262 TRACE("Updating header with index offset: %" PRIu64, ctx->header.indexOffset);
4263
4264 // Seek back to beginning and rewrite header
4265 fseek(ctx->imageStream, 0, SEEK_SET);
4266 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) == 1)
4267 TRACE("Successfully updated header with index offset");
4268 else
4269 {
4270 TRACE("Failed to update header with index offset");
4272 }
4273 }
4274 else
4275 {
4276 TRACE("Failed to write all index entries (wrote %zu of %" PRIu64 ")", entries_written,
4277 index_header.entries);
4279 }
4280 }
4281 }
4282 else
4283 {
4284 TRACE("Failed to write index header");
4286 }
4287
4288 return AARUF_STATUS_OK;
4289}
4290
4328{
4329 TRACE("Entering aaruf_close(%p)", context);
4330
4331 mediaTagEntry *media_tag = NULL;
4332 mediaTagEntry *tmp_media_tag = NULL;
4333
4334 if(context == NULL)
4335 {
4336 FATAL("Invalid context");
4337 errno = EINVAL;
4338 return -1;
4339 }
4340
4341 aaruformat_context *ctx = context;
4342
4343 // Not a libaaruformat context
4344 if(ctx->magic != AARU_MAGIC)
4345 {
4346 FATAL("Invalid context");
4347 errno = EINVAL;
4348 return -1;
4349 }
4350
4351 if(ctx->is_writing)
4352 {
4353 TRACE("File is writing");
4354
4355 TRACE("Seeking to start of image");
4356 // Write the header at the beginning of the file
4357 fseek(ctx->imageStream, 0, SEEK_SET);
4358
4359 TRACE("Writing header at position 0");
4360 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) != 1)
4361 {
4362 fclose(ctx->imageStream);
4363 ctx->imageStream = NULL;
4365 return -1;
4366 }
4367
4368 // Close current block first
4369 TRACE("Closing current block if any");
4370 if(ctx->writing_buffer != NULL)
4371 {
4372 int error = aaruf_close_current_block(ctx);
4373
4374 if(error != AARUF_STATUS_OK) return error;
4375 }
4376
4377 int32_t res;
4378 if(ctx->is_tape)
4379 {
4380 // Write tape DDT
4381 if(ctx->dirty_tape_ddt)
4382 {
4383 res = write_tape_ddt(ctx);
4384 if(res != AARUF_STATUS_OK) return res;
4385 }
4386 }
4387 else
4388 {
4389 // Write cached secondary DDT table if any
4390 if(ctx->dirty_secondary_ddt)
4391 {
4392 res = write_cached_secondary_ddt(ctx);
4393 if(res != AARUF_STATUS_OK) return res;
4394 }
4395
4396 // Write primary DDT table (multi-level) if applicable
4397 if(ctx->dirty_primary_ddt)
4398 {
4399 res = write_primary_ddt(ctx);
4400 if(res != AARUF_STATUS_OK) return res;
4401 }
4402
4403 // Write single-level DDT table if applicable
4404 if(ctx->dirty_single_level_ddt)
4405 {
4406 res = write_single_level_ddt(ctx);
4407 if(res != AARUF_STATUS_OK) return res;
4408 }
4409 }
4410
4411 // Finalize checksums and write checksum block
4413
4414 // Write tracks block
4416
4417 // Write MODE 2 subheader data block
4419
4420 // Write CD sector prefix data block
4422
4423 // Write sector prefix DDT (statuses + optional indexes)
4425
4426 // Write CD sector suffix data block (EDC/ECC captures)
4428
4429 // Write sector prefix DDT (EDC/ECC captures)
4431
4432 // Write sector subchannel data block
4434
4435 // Write DVD long sector data blocks
4437
4438 // Write DVD decrypted title keys
4440
4441 // Write media tags data blocks
4442 if(ctx->dirty_media_tags) write_media_tags(ctx);
4443
4444 // Write tape files
4446
4447 // Write tape partitions
4449
4450 // Write geometry block if any
4452
4453 // Write metadata block
4455
4456 // Write dump hardware block if any
4458
4459 // Write CICM XML block if any
4460 if(ctx->dirty_cicm_block) write_cicm_block(ctx);
4461
4462 // Write Aaru metadata JSON block if any
4464
4465 // Write the complete index at the end of the file
4466 if(ctx->dirty_index_block)
4467 {
4468 res = write_index_block(ctx);
4469 if(res != AARUF_STATUS_OK) return res;
4470 }
4471
4472 if(ctx->deduplicate && ctx->sector_hash_map != NULL)
4473 {
4474 TRACE("Clearing sector hash map");
4475 // Clear sector hash map
4477 ctx->sector_hash_map = NULL;
4478 }
4479 }
4480
4481 TRACE("Freeing memory pointers");
4482 // This may do nothing if imageStream is NULL, but as the behaviour is undefined, better sure than sorry
4483 if(ctx->imageStream != NULL)
4484 {
4485 fclose(ctx->imageStream);
4486 ctx->imageStream = NULL;
4487 }
4488
4489 // Free index entries array
4490 if(ctx->index_entries != NULL)
4491 {
4492 utarray_free(ctx->index_entries);
4493 ctx->index_entries = NULL;
4494 }
4495
4496 free(ctx->sector_prefix);
4497 ctx->sector_prefix = NULL;
4498 free(ctx->sector_prefix_corrected);
4499 ctx->sector_prefix_corrected = NULL;
4500 free(ctx->sector_suffix);
4501 ctx->sector_suffix = NULL;
4502 free(ctx->sector_suffix_corrected);
4503 ctx->sector_suffix_corrected = NULL;
4504 free(ctx->sector_subchannel);
4505 ctx->sector_subchannel = NULL;
4506 free(ctx->mode2_subheaders);
4507 ctx->mode2_subheaders = NULL;
4508
4509 TRACE("Freeing media tags");
4510 if(ctx->mediaTags != NULL) HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
4511 {
4512 HASH_DEL(ctx->mediaTags, media_tag);
4513 free(media_tag->data);
4514 free(media_tag);
4515 }
4516
4517#ifdef __linux__ // TODO: Implement
4518 TRACE("Unmapping user data DDT if it is not in memory");
4519 if(!ctx->in_memory_ddt)
4520 {
4521 munmap(ctx->user_data_ddt, ctx->mapped_memory_ddt_size);
4522 ctx->user_data_ddt = NULL;
4523 }
4524#endif
4525
4526 free(ctx->sector_prefix_ddt2);
4527 ctx->sector_prefix_ddt2 = NULL;
4528 free(ctx->sector_prefix_ddt);
4529 ctx->sector_prefix_ddt = NULL;
4530 free(ctx->sector_suffix_ddt2);
4531 ctx->sector_suffix_ddt2 = NULL;
4532 free(ctx->sector_suffix_ddt);
4533 ctx->sector_suffix_ddt = NULL;
4534
4535 free(ctx->metadata_block);
4536 ctx->metadata_block = NULL;
4537 free(ctx->track_entries);
4538 ctx->track_entries = NULL;
4539 free(ctx->cicm_block);
4540 ctx->cicm_block = NULL;
4541
4542 if(ctx->dump_hardware_entries_with_data != NULL)
4543 {
4544 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
4545 {
4564 }
4566 }
4567
4568 free(ctx->readableSectorTags);
4569 ctx->readableSectorTags = NULL;
4570
4571 free(ctx->ecc_cd_context);
4572 ctx->ecc_cd_context = NULL;
4573
4574 free(ctx->checksums.spamsum);
4575 ctx->checksums.spamsum = NULL;
4576
4577 free(ctx->sector_id);
4578 free(ctx->sector_ied);
4579 free(ctx->sector_cpr_mai);
4580 free(ctx->sector_edc);
4581
4582 // TODO: Free caches
4583
4584 free(context);
4585
4586 TRACE("Exiting aaruf_close() = 0");
4587 return 0;
4588}
void write_dvd_long_sector_blocks(aaruformat_context *ctx)
Serialize DVD long sector auxiliary data blocks to the image file.
Definition close.c:1954
static int32_t write_primary_ddt(aaruformat_context *ctx)
Write (flush) the multi-level primary DDT table header and data back to its file offset.
Definition close.c:284
static void write_cicm_block(aaruformat_context *ctx)
Serialize the CICM XML metadata block to the image file.
Definition close.c:3981
static void write_sector_subchannel(aaruformat_context *ctx)
Serialize the per-sector subchannel or tag data block.
Definition close.c:1637
static int32_t write_index_block(aaruformat_context *ctx)
Serialize the accumulated index entries at the end of the image and back-patch the header.
Definition close.c:4194
static void write_media_tags(aaruformat_context *ctx)
Serialize all accumulated media tags to the image file.
Definition close.c:2630
static void write_dvd_title_key_decrypted_block(aaruformat_context *ctx)
Serialize the DVD decrypted title key data block to the image file.
Definition close.c:2451
int aaruf_close(void *context)
Close an Aaru image context, flushing pending data structures and releasing resources.
Definition close.c:4327
static int32_t write_single_level_ddt(aaruformat_context *ctx)
Serialize a single-level DDT (tableShift == 0) directly after its header.
Definition close.c:383
static void write_tape_file_block(aaruformat_context *ctx)
Serialize the tape file metadata block to the image file.
Definition close.c:2906
static void write_dumphw_block(aaruformat_context *ctx)
Serialize the dump hardware block containing acquisition environment information.
Definition close.c:3738
static void write_checksum_block(aaruformat_context *ctx)
Finalize any active checksum calculations and append a checksum block.
Definition close.c:681
static void write_sector_suffix(aaruformat_context *ctx)
Serialize the optional CD sector suffix block (EDC/ECC region capture).
Definition close.c:1172
static void write_sector_suffix_ddt(aaruformat_context *ctx)
Serialize the per-sector CD suffix status / index DeDuplication Table (DDT v2, suffix variant).
Definition close.c:1464
static void write_tracks_block(aaruformat_context *ctx)
Serialize the tracks metadata block and add it to the index.
Definition close.c:838
static void write_aaru_json_block(aaruformat_context *ctx)
Serialize the Aaru metadata JSON block to the image file.
Definition close.c:4131
static void write_geometry_block(aaruformat_context *ctx)
Serialize the geometry metadata block to the image file.
Definition close.c:3291
static void write_mode2_subheaders_block(aaruformat_context *ctx)
Serialize a MODE 2 (XA) subheaders data block.
Definition close.c:904
static void write_tape_partition_block(aaruformat_context *ctx)
Serialize the tape partition metadata block to the image file.
Definition close.c:3152
static void write_sector_prefix_ddt(aaruformat_context *ctx)
Serialize the per-sector CD prefix status / index DeDuplication Table (DDT v2, prefix variant).
Definition close.c:1305
static int32_t write_tape_ddt(aaruformat_context *ctx)
Converts tape DDT hash table to array format and writes it as a single-level DDT.
Definition close.c:623
static int32_t write_cached_secondary_ddt(aaruformat_context *ctx)
Flush a cached secondary (child) DeDuplication Table (DDT) to the image.
Definition close.c:77
static void write_metadata_block(aaruformat_context *ctx)
Serialize the metadata block containing image and media descriptive information.
Definition close.c:3440
static void write_sector_prefix(aaruformat_context *ctx)
Serialize the optional CD sector prefix block.
Definition close.c:1035
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
#define MD5_DIGEST_LENGTH
Definition context.h:69
struct TapeFileHashEntry tapeFileHashEntry
#define AARU_CALL
Definition decls.h:45
void aaruf_sha1_final(sha1_ctx *ctx, unsigned char *result)
Definition sha1.c:124
int32_t aaruf_cst_transform(const uint8_t *interleaved, uint8_t *sequential, size_t length)
Transforms interleaved subchannel data to sequential format.
Definition cst.c:35
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
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
void aaruf_sha256_final(sha256_ctx *ctx, unsigned char *result)
Definition sha256.c:115
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
void aaruf_md5_final(md5_ctx *ctx, unsigned char *result)
Definition md5.c:485
void aaruf_spamsum_free(spamsum_ctx *ctx)
Frees a spamsum (fuzzy hash) context.
Definition spamsum.c:75
int32_t aaruf_get_datatype_for_media_tag_type(int32_t tag_type)
Converts an Aaru media tag type to an image data type.
Definition helpers.c:199
#define AARU_EXPORT
Definition decls.h:54
int aaruf_spamsum_final(spamsum_ctx *ctx, uint8_t *result)
Definition spamsum.c:191
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
@ IndexBlock3
Block containing the index v3.
Definition enums.h:148
@ ChecksumBlock
Block containing contents checksums.
Definition enums.h:153
@ DataBlock
Block containing data.
Definition enums.h:142
@ TapePartitionBlock
Block containing list of partitions for a tape image.
Definition enums.h:159
@ GeometryBlock
Block containing logical geometry.
Definition enums.h:149
@ DeDuplicationTableSecondary
Block containing a secondary deduplication table (v2).
Definition enums.h:145
@ AaruMetadataJsonBlock
Block containing JSON version of Aaru Metadata.
Definition enums.h:160
@ CicmBlock
Block containing CICM XML metadata.
Definition enums.h:152
@ DeDuplicationTable2
Block containing a deduplication table v2.
Definition enums.h:144
@ TapeFileBlock
Block containing list of files for a tape image.
Definition enums.h:158
@ DumpHardwareBlock
Block containing an array of hardware used to create the image.
Definition enums.h:157
@ MetadataBlock
Block containing metadata.
Definition enums.h:150
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:151
@ OpticalDisc
Purely optical discs.
Definition enums.h:219
@ BlockMedia
Media that is physically block-based or abstracted like that.
Definition enums.h:220
@ Blake3
BLAKE3 hash.
Definition enums.h:174
@ Sha1
SHA-1 hash.
Definition enums.h:171
@ Md5
MD5 hash.
Definition enums.h:170
@ Sha256
SHA-256 hash.
Definition enums.h:172
@ SpamSum
SpamSum (context-triggered piecewise hash).
Definition enums.h:173
@ CdSectorSubchannel
Compact Disc subchannel data.
Definition enums.h:116
@ AppleProfileTag
Apple Profile (20‑byte) tag.
Definition enums.h:117
@ DvdSectorIed
DVD ID Error Detection Code (IED)
Definition enums.h:129
@ DvdSectorCprMai
DVD Copyright Management Information (CPR_MAI)
Definition enums.h:126
@ AppleSonyTag
Apple Sony (12‑byte) tag.
Definition enums.h:118
@ CdSectorPrefix
Compact Disc sector prefix (sync, header).
Definition enums.h:114
@ PriamDataTowerTag
Priam Data Tower (24‑byte) tag.
Definition enums.h:119
@ UserData
User (main) data.
Definition enums.h:46
@ DvdSectorEdc
DVD Error Detection Code (EDC)
Definition enums.h:130
@ DvdSectorTitleKeyDecrypted
Decrypted DVD Title Key.
Definition enums.h:127
@ DvdSectorId
DVD Identification Data (ID)
Definition enums.h:128
@ CdSectorSuffix
Compact Disc sector suffix (EDC, ECC P, ECC Q).
Definition enums.h:115
@ CompactDiscMode2Subheader
Compact Disc MODE 2 subheader.
Definition enums.h:123
@ AARU_FEATURE_RW_BLAKE3
BLAKE3 checksum is present (read/write support for BLAKE3 hashes).
Definition enums.h:266
@ AARUF_STATUS_INVALID_CONTEXT
Provided context/handle is invalid.
Definition enums.h:210
@ Lzma
LZMA compression.
Definition enums.h:34
@ LzmaClauniaSubchannelTransform
LZMA applied to Claunia Subchannel Transform processed data.
Definition enums.h:36
@ None
Not compressed.
Definition enums.h:33
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:75
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
#define AARUF_ERROR_CANNOT_WRITE_HEADER
Failure writing container header.
Definition errors.h:60
@ AppleProfile
Definition aaru.h:698
@ AppleSonySS
3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:247
@ PriamDataTower
Definition aaru.h:701
@ AppleFileWare
5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy
Definition aaru.h:249
@ AppleSonyDS
3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:248
void free_map(hash_map_t *map)
Frees all memory associated with a hash map.
Definition hash_map.c:73
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1427
#define FATAL(fmt,...)
Definition log.h:40
#define TRACE(fmt,...)
Definition log.h:25
#define SHA1_DIGEST_LENGTH
Definition sha1.h:39
#define SHA256_DIGEST_LENGTH
Definition sha256.h:38
#define FUZZY_MAX_RESULT
Definition spamsum.h:30
Version 2 container header with GUID, alignment shifts, and feature negotiation bitmaps.
Definition header.h:107
uint64_t featureCompatible
Feature bits: unimplemented bits are ignorable (still R/W safe).
Definition header.h:122
uint64_t indexOffset
Absolute byte offset to primary index block (MUST be > 0; 0 => corrupt/unreadable).
Definition header.h:115
Header for an Aaru metadata JSON block (identifier == BlockType::AaruMetadataJsonBlock).
Definition metadata.h:120
uint32_t identifier
Block identifier, must be BlockType::AaruMetadataJsonBlock.
Definition metadata.h:121
uint32_t length
Length in bytes of the Aaru metadata JSON payload that follows.
Definition metadata.h:122
Header preceding the compressed data payload of a data block (BlockType::DataBlock).
Definition data.h:71
uint32_t cmpLength
Size in bytes of the compressed payload immediately following this header.
Definition data.h:76
uint32_t length
Size in bytes of the uncompressed payload resulting after decompression.
Definition data.h:77
uint32_t identifier
Block identifier, must be BlockType::DataBlock.
Definition data.h:72
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload (cmpLength bytes).
Definition data.h:78
uint64_t crc64
CRC64-ECMA of the uncompressed payload (length bytes).
Definition data.h:79
uint16_t type
Logical data classification (value from DataType).
Definition data.h:73
uint16_t compression
Compression algorithm used (value from CompressionType).
Definition data.h:74
Per-checksum metadata immediately followed by the digest / signature bytes.
Definition checksum.h:91
uint32_t length
Length in bytes of the digest that immediately follows this structure.
Definition checksum.h:93
uint8_t type
Algorithm used (value from ChecksumAlgorithm).
Definition checksum.h:92
Header that precedes the sequence of checksum entries for a checksum block.
Definition checksum.h:74
uint32_t identifier
Block identifier, must be BlockType::ChecksumBlock.
Definition checksum.h:75
uint32_t length
Length in bytes of the payload (all entries + their digest data, excluding this header).
Definition checksum.h:76
uint8_t entries
Number of checksum entries that follow in the payload.
Definition checksum.h:77
uint8_t * spamsum
SpamSum fuzzy hash (ASCII), allocated length+1 with trailing 0.
Definition context.h:110
bool hasSha256
True if sha256[] buffer populated.
Definition context.h:103
uint8_t sha1[20]
SHA-1 digest (20 bytes).
Definition context.h:107
uint8_t sha256[32]
SHA-256 digest (32 bytes).
Definition context.h:108
uint8_t md5[16]
MD5 digest (16 bytes).
Definition context.h:106
bool hasSpamSum
True if spamsum pointer allocated and signature read.
Definition context.h:105
bool hasSha1
True if sha1[] buffer populated.
Definition context.h:102
uint8_t blake3[BLAKE3_OUT_LEN]
BLAKE3 digest (32 bytes).
Definition context.h:109
bool hasMd5
True if md5[] buffer populated.
Definition context.h:101
bool hasBlake3
True if blake3[] buffer populated.
Definition context.h:104
Header for a CICM XML metadata block (identifier == BlockType::CicmBlock).
Definition metadata.h:108
uint32_t length
Length in bytes of the CICM metadata payload that follows.
Definition metadata.h:110
uint32_t identifier
Block identifier, must be BlockType::CicmBlock.
Definition metadata.h:109
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
uint16_t overflow
Trailing dumped sectors beyond user area (overflow range), still mapped with entries.
Definition ddt.h:151
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
uint16_t negative
Leading negative LBA count; added to external L to build internal index.
Definition ddt.h:149
uint8_t blockAlignmentShift
2^blockAlignmentShift = block alignment boundary in bytes.
Definition ddt.h:154
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
Inclusive [start,end] logical sector range contributed by a single hardware environment.
Definition context.h:359
uint8_t * firmware
Firmware version string or NULL.
Definition context.h:346
uint8_t * revision
Hardware revision string or NULL.
Definition context.h:345
uint8_t * model
Model string or NULL.
Definition context.h:344
uint8_t * softwareName
Dump software name or NULL.
Definition context.h:348
struct DumpExtent * extents
Array of extents (entry.extents elements) or NULL.
Definition context.h:342
uint8_t * manufacturer
Manufacturer string (UTF-8) or NULL.
Definition context.h:343
uint8_t * softwareVersion
Dump software version or NULL.
Definition context.h:349
uint8_t * serial
Serial number string or NULL.
Definition context.h:347
DumpHardwareEntry entry
Fixed-size header with lengths & counts.
Definition context.h:341
uint8_t * softwareOperatingSystem
Host operating system string or NULL.
Definition context.h:350
Per-environment length table describing subsequent UTF-8 strings and optional extent array.
Definition dump.h:113
uint32_t softwareNameLength
Length in bytes of dumping software name string.
Definition dump.h:119
uint32_t manufacturerLength
Length in bytes of manufacturer UTF-8 string.
Definition dump.h:114
uint32_t softwareVersionLength
Length in bytes of dumping software version string.
Definition dump.h:120
uint32_t firmwareLength
Length in bytes of firmware version string.
Definition dump.h:117
uint32_t extents
Number of DumpExtent records following the strings (0 = none).
Definition dump.h:122
uint32_t modelLength
Length in bytes of model UTF-8 string.
Definition dump.h:115
uint32_t serialLength
Length in bytes of device serial number string.
Definition dump.h:118
uint32_t revisionLength
Length in bytes of revision / hardware revision string.
Definition dump.h:116
uint32_t softwareOperatingSystemLength
Length in bytes of host operating system string.
Definition dump.h:121
Header that precedes a sequence of dump hardware entries and their variable-length payload.
Definition dump.h:91
uint64_t crc64
CRC64-ECMA of the payload (byte-swapped for legacy v1 images, handled automatically).
Definition dump.h:95
uint32_t identifier
Block identifier, must be BlockType::DumpHardwareBlock.
Definition dump.h:92
uint32_t length
Total payload bytes after this header (sum of entries, strings, and extents arrays).
Definition dump.h:94
uint16_t entries
Number of DumpHardwareEntry records that follow.
Definition dump.h:93
Legacy CHS style logical geometry metadata (BlockType::GeometryBlock).
Definition data.h:91
uint32_t identifier
Block identifier, must be BlockType::GeometryBlock.
Definition data.h:92
uint32_t MediaType
Media type identifier (see MediaType enum; 0=Unknown)
Definition aaru.h:933
uint8_t MetadataMediaType
Media type for sidecar generation (internal archival use)
Definition aaru.h:934
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:926
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
Index header (version 3) adding hierarchical chaining (identifier == IndexBlock3).
Definition index.h:93
uint64_t previous
File offset of a previous IndexBlock3 header (0 if none / root segment).
Definition index.h:97
uint32_t identifier
Block identifier (must be BlockType::IndexBlock3).
Definition index.h:94
uint64_t crc64
CRC64-ECMA of the local entries array (does NOT cover subindexes or previous chains).
Definition index.h:96
uint64_t entries
Number of IndexEntry records that follow in this (sub)index block.
Definition index.h:95
Header for a metadata block containing offsets and lengths to UTF-16LE descriptive strings.
Definition metadata.h:69
uint32_t commentsLength
Length in bytes (including null) of comments string.
Definition metadata.h:78
int32_t mediaSequence
Sequence number within a multi-disc / multi-volume set (0-based or 1-based as producer defines).
Definition metadata.h:72
uint32_t identifier
Block identifier, must be BlockType::MetadataBlock.
Definition metadata.h:70
uint32_t mediaTitleOffset
Offset to UTF-16LE media title string.
Definition metadata.h:79
uint32_t driveModelLength
Length in bytes (including null) of drive model string.
Definition metadata.h:94
uint32_t driveManufacturerLength
Length in bytes (including null) of drive manufacturer string.
Definition metadata.h:92
uint32_t blockSize
Total size in bytes of the entire metadata block (header + strings).
Definition metadata.h:71
uint32_t driveModelOffset
Offset to UTF-16LE drive model string.
Definition metadata.h:93
uint32_t mediaModelOffset
Offset to UTF-16LE media model string.
Definition metadata.h:83
uint32_t creatorOffset
Offset to UTF-16LE creator string (or undefined if creatorLength==0).
Definition metadata.h:75
uint32_t mediaTitleLength
Length in bytes (including null) of media title string.
Definition metadata.h:80
uint32_t mediaManufacturerOffset
Offset to UTF-16LE media manufacturer string.
Definition metadata.h:81
uint32_t driveSerialNumberLength
Length in bytes (including null) of drive serial number string.
Definition metadata.h:96
uint32_t driveSerialNumberOffset
Offset to UTF-16LE drive serial number string.
Definition metadata.h:95
uint32_t mediaManufacturerLength
Length in bytes (including null) of media manufacturer string.
Definition metadata.h:82
uint32_t mediaModelLength
Length in bytes (including null) of media model string.
Definition metadata.h:84
uint32_t driveFirmwareRevisionOffset
Offset to UTF-16LE drive firmware revision string.
Definition metadata.h:97
int32_t lastMediaSequence
Total number of media in the set; 0 or 1 if single item.
Definition metadata.h:74
uint32_t commentsOffset
Offset to UTF-16LE comments string.
Definition metadata.h:77
uint32_t driveManufacturerOffset
Offset to UTF-16LE drive manufacturer string.
Definition metadata.h:91
uint32_t mediaSerialNumberOffset
Offset to UTF-16LE media serial number string.
Definition metadata.h:85
uint32_t mediaSerialNumberLength
Length in bytes (including null) of media serial number string.
Definition metadata.h:86
uint32_t mediaPartNumberOffset
Offset to UTF-16LE media part number string.
Definition metadata.h:89
uint32_t mediaPartNumberLength
Length in bytes (including null) of media part number string.
Definition metadata.h:90
uint32_t mediaBarcodeLength
Length in bytes (including null) of media barcode string.
Definition metadata.h:88
uint32_t creatorLength
Length in bytes (including null) of creator string (0 if absent).
Definition metadata.h:76
uint32_t driveFirmwareRevisionLength
Length in bytes (including null) of drive firmware revision string.
Definition metadata.h:98
uint32_t mediaBarcodeOffset
Offset to UTF-16LE media barcode string.
Definition metadata.h:87
uint64_t key
Key: sector address.
Definition context.h:142
uint64_t value
Value: DDT entry.
Definition context.h:143
Describes a single logical file on a tape medium.
Definition tape.h:134
TapeFileEntry fileEntry
The actual tape file data.
Definition context.h:129
Header for a tape file metadata block containing file layout information.
Definition tape.h:238
uint32_t identifier
Block type identifier.
Definition tape.h:239
uint64_t crc64
CRC64-ECMA checksum of the entry data.
Definition tape.h:245
uint64_t length
Size of entry data in bytes (excluding this header).
Definition tape.h:243
Describes a single physical partition on a tape medium.
Definition tape.h:320
TapePartitionEntry partitionEntry
The actual tape partition data.
Definition context.h:136
Header for a tape partition metadata block containing partition layout information.
Definition tape.h:441
uint64_t crc64
CRC64-ECMA checksum of the entry data.
Definition tape.h:448
uint64_t length
Size of entry data in bytes (excluding this header).
Definition tape.h:446
uint32_t identifier
Block type identifier.
Definition tape.h:442
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
Header for an optical tracks block listing track entries.
Definition optical.h:62
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
Master context representing an open or in‑creation Aaru image.
Definition context.h:172
uint8_t * media_barcode
Barcode of the media represented by the image.
Definition context.h:222
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:189
Checksums checksums
Whole-image checksums discovered.
Definition context.h:269
uint8_t * creator
Who (person) created the image?
Definition context.h:216
bool dirty_checksum_block
True if checksum block should be written during close.
Definition context.h:311
bool deduplicate
Storage deduplication active (duplicates coalesce).
Definition context.h:299
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:300
uint8_t * cicm_block
CICM XML payload.
Definition context.h:214
uint8_t * sector_cpr_mai
DVD sector CPR_MAI (6 bytes) if present.
Definition context.h:207
bool dirty_media_tags
True if media tags should be written during close.
Definition context.h:321
hash_map_t * sector_hash_map
Deduplication hash map (fingerprint->entry mapping).
Definition context.h:253
uint8_t * sector_prefix_corrected
Corrected variant (post error correction) if stored.
Definition context.h:200
uint64_t * user_data_ddt
Legacy flat DDT pointer (NULL when using v2 mini/big arrays).
Definition context.h:181
sha256_ctx sha256_context
Opaque SHA-256 context for streaming updates.
Definition context.h:272
bool calculating_sha256
True if whole-image SHA-256 being calculated on-the-fly.
Definition context.h:275
bool dirty_primary_ddt
True if primary DDT table should be written during close.
Definition context.h:309
uint8_t * drive_firmware_revision
Firmware revision of the drive used to read the media represented by the image.
Definition context.h:228
uint8_t * media_serial_number
Serial number of the media represented by the image.
Definition context.h:221
uint8_t * sector_ied
DVD sector IED (2 bytes) if present.
Definition context.h:206
md5_ctx md5_context
Opaque MD5 context for streaming updates.
Definition context.h:270
bool dirty_sector_suffix_block
True if sector suffix block should be written during close.
Definition context.h:316
uint64_t * user_data_ddt2
DDT entries (big variant) primary/secondary current.
Definition context.h:187
MetadataBlockHeader metadata_block_header
Metadata block header.
Definition context.h:230
uint8_t * sector_prefix
Raw per-sector prefix (e.g., sync+header) uncorrected.
Definition context.h:199
bool dirty_dvd_title_key_decrypted_block
True if decrypted title key block should be written during close.
Definition context.h:320
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:186
tapeFileHashEntry * tape_files
Hash table root for tape files.
Definition context.h:303
bool dirty_mode2_subheaders_block
True if MODE2 subheader block should be written during close.
Definition context.h:313
bool dirty_tape_partition_block
True if tape partition block should be written during close.
Definition context.h:324
uint64_t cached_ddt_offset
File offset of currently cached secondary DDT (0=none).
Definition context.h:190
bool dirty_cicm_block
True if CICM metadata block should be written during close.
Definition context.h:328
bool is_tape
True if the image is a tape image.
Definition context.h:305
uint8_t * sector_edc
DVD sector EDC (4 bytes) if present.
Definition context.h:208
bool calculating_sha1
True if whole-image SHA-1 being calculated on-the-fly.
Definition context.h:274
uint8_t * media_model
Model of the media represented by the image.
Definition context.h:220
bool dirty_tracks_block
True if tracks block should be written during close.
Definition context.h:312
uint8_t * drive_serial_number
Serial number of the drive used to read the media represented by the image.
Definition context.h:226
CdEccContext * ecc_cd_context
CD ECC/EDC helper tables (allocated on demand).
Definition context.h:248
uint32_t * sector_suffix_ddt
Legacy CD sector suffix DDT.
Definition context.h:184
bool dirty_sector_prefix_block
True if sector prefix block should be written during close.
Definition context.h:314
uint8_t * drive_manufacturer
Manufacturer of the drive used to read the media represented by the image.
Definition context.h:224
bool in_memory_ddt
True if primary (and possibly secondary) DDT loaded.
Definition context.h:196
bool dirty_index_block
True if index block should be written during close.
Definition context.h:330
uint8_t * sector_suffix
Raw per-sector suffix (EDC/ECC) uncorrected.
Definition context.h:201
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:175
bool dirty_json_block
True if JSON metadata block should be written during close.
Definition context.h:329
bool is_writing
True if context opened/created for writing.
Definition context.h:292
TapeDdtHashEntry * tape_ddt
Hash table root for tape DDT entries.
Definition context.h:182
spamsum_ctx * spamsum_context
Opaque SpamSum context for streaming updates.
Definition context.h:267
CicmMetadataBlock cicm_block_header
CICM metadata header (if present).
Definition context.h:231
size_t sector_prefix_offset
Current position in sector_prefix.
Definition context.h:286
uint8_t * drive_model
Model of the drive used to read the media represented by the image.
Definition context.h:225
bool dirty_single_level_ddt
True if single-level DDT should be written during close.
Definition context.h:310
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:174
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:280
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:185
bool calculating_spamsum
True if whole-image SpamSum being calculated on-the-fly.
Definition context.h:276
uint64_t primary_ddt_offset
File offset of the primary DDT v2 table.
Definition context.h:192
mediaTagEntry * mediaTags
Hash table of extra media tags (uthash root).
Definition context.h:264
blake3_hasher * blake3_context
Opaque BLAKE3 context for streaming updates.
Definition context.h:268
bool calculating_blake3
True if whole-image BLAKE3 being calculated on-the-fly.
Definition context.h:277
struct DumpHardwareEntriesWithData * dump_hardware_entries_with_data
Array of dump hardware entries + strings.
Definition context.h:212
bool calculating_md5
True if whole-image MD5 being calculated on-the-fly.
Definition context.h:273
bool dirty_sector_suffix_ddt
True if sector suffix DDT should be written during close.
Definition context.h:317
GeometryBlockHeader geometry_block
Logical geometry block (if present).
Definition context.h:229
size_t sector_suffix_offset
Current position in sector_suffix.
Definition context.h:287
uint64_t * cached_secondary_ddt2
Cached secondary table (big entries) or NULL.
Definition context.h:188
uint8_t * json_block
JSON metadata block payload (UTF-8).
Definition context.h:215
uint8_t * media_part_number
Part number of the media represented by the image.
Definition context.h:223
uint8_t * sector_decrypted_title_key
DVD decrypted title key (5 bytes) if present.
Definition context.h:209
bool dirty_dumphw_block
True if dump hardware block should be written during close.
Definition context.h:327
AaruMetadataJsonBlockHeader json_block_header
JSON metadata block header (if present).
Definition context.h:233
bool dirty_metadata_block
True if metadata block should be written during close.
Definition context.h:326
uint8_t * sector_subchannel
Raw 96-byte subchannel (if captured).
Definition context.h:203
uint8_t * comments
Image comments.
Definition context.h:218
bool dirty_sector_subchannel_block
True if subchannel block should be written during close.
Definition context.h:318
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:176
bool dirty_dvd_long_sector_blocks
True if DVD long sector blocks should be written during close.
Definition context.h:319
bool dirty_sector_prefix_ddt
True if sector prefix DDT should be written during close.
Definition context.h:315
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:252
bool dirty_tape_file_block
True if tape file block should be written during close.
Definition context.h:323
uint8_t * mode2_subheaders
MODE2 Form1/Form2 8-byte subheaders (concatenated).
Definition context.h:204
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:260
uint8_t * sector_id
DVD sector ID (4 bytes) if present.
Definition context.h:205
DumpHardwareHeader dump_hardware_header
Dump hardware header.
Definition context.h:232
sha1_ctx sha1_context
Opaque SHA-1 context for streaming updates.
Definition context.h:271
bool dirty_secondary_ddt
True if secondary DDT tables should be written during close.
Definition context.h:308
bool * readableSectorTags
Per-sector boolean array (optical tags read successfully?).
Definition context.h:263
bool dirty_tape_ddt
True if tape DDT should be written during close.
Definition context.h:322
TapePartitionHashEntry * tape_partitions
Hash table root for tape partitions.
Definition context.h:304
uint32_t * sector_prefix_ddt
Legacy CD sector prefix DDT (deprecated by *2).
Definition context.h:183
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:298
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:242
uint8_t * sector_suffix_corrected
Corrected suffix if stored separately.
Definition context.h:202
uint8_t * metadata_block
Raw metadata UTF-16LE concatenated strings.
Definition context.h:213
uint64_t cached_ddt_position
Position index of cached secondary DDT.
Definition context.h:191
uint8_t * media_title
Title of the media represented by the image.
Definition context.h:217
bool dirty_geometry_block
True if geometry block should be written during close.
Definition context.h:325
size_t mapped_memory_ddt_size
Length of mmapped DDT if userDataDdt is mmapped.
Definition context.h:193
uint8_t * media_manufacturer
Manufacturer of the media represented by the image.
Definition context.h:219
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:244
Minimal ECMA-182 CRC64 incremental state container (running value only).
Definition crc64.h:56
Hash table entry for an arbitrary media tag (e.g., proprietary drive/medium descriptor).
Definition context.h:119
uint8_t * data
Tag data blob (opaque to library core); length bytes long.
Definition context.h:120
int32_t type
Numeric type identifier.
Definition context.h:121
uint32_t length
Length in bytes of data.
Definition context.h:122