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 // Add checksum block to index
805 TRACE("Adding checksum block to index");
806 IndexEntry checksum_index_entry;
807 checksum_index_entry.blockType = ChecksumBlock;
808 checksum_index_entry.dataType = 0;
809 checksum_index_entry.offset = checksum_position;
810
811 utarray_push_back(ctx->index_entries, &checksum_index_entry);
812 ctx->dirty_index_block = true;
813 TRACE("Added checksum block index entry at offset %" PRIu64, checksum_position);
814}
815
827{
828 // Write tracks block
829 if(ctx->tracks_header.entries <= 0 || ctx->track_entries == NULL) return;
830
831 fseek(ctx->imageStream, 0, SEEK_END);
832 long tracks_position = ftell(ctx->imageStream);
833 // Align index position to block boundary if needed
834 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
835 if(tracks_position & alignment_mask)
836 {
837 uint64_t aligned_position = tracks_position + alignment_mask & ~alignment_mask;
838 fseek(ctx->imageStream, aligned_position, SEEK_SET);
839 tracks_position = aligned_position;
840 }
841
842 TRACE("Writing tracks block at position %ld", tracks_position);
843 // Write header
844 if(fwrite(&ctx->tracks_header, sizeof(TracksHeader), 1, ctx->imageStream) == 1)
845 {
846 // Write entries
847 size_t written_entries =
848 fwrite(ctx->track_entries, sizeof(TrackEntry), ctx->tracks_header.entries, ctx->imageStream);
849
850 if(written_entries == ctx->tracks_header.entries)
851 {
852 TRACE("Successfully wrote tracks block with %u entries", ctx->tracks_header.entries);
853 // Add tracks block to index
854 TRACE("Adding tracks block to index");
855
856 IndexEntry tracks_index_entry;
857 tracks_index_entry.blockType = TracksBlock;
858 tracks_index_entry.dataType = 0;
859 tracks_index_entry.offset = tracks_position;
860 utarray_push_back(ctx->index_entries, &tracks_index_entry);
861 ctx->dirty_index_block = true;
862 TRACE("Added tracks block index entry at offset %" PRIu64, tracks_position);
863 }
864 }
865}
866
880{
881 // Write MODE 2 subheader data block
882 if(ctx->mode2_subheaders == NULL) return;
883
884 fseek(ctx->imageStream, 0, SEEK_END);
885 long mode2_subheaders_position = ftell(ctx->imageStream);
886 // Align index position to block boundary if needed
887 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
888 if(mode2_subheaders_position & alignment_mask)
889 {
890 uint64_t aligned_position = mode2_subheaders_position + alignment_mask & ~alignment_mask;
891 fseek(ctx->imageStream, aligned_position, SEEK_SET);
892 mode2_subheaders_position = aligned_position;
893 }
894
895 TRACE("Writing MODE 2 subheaders block at position %ld", mode2_subheaders_position);
896 BlockHeader subheaders_block = {0};
897 subheaders_block.identifier = DataBlock;
898 subheaders_block.type = CompactDiscMode2Subheader;
899 subheaders_block.compression = ctx->compression_enabled ? Lzma : None;
900 subheaders_block.length =
902 8;
903
904 // Calculate CRC64
905 subheaders_block.crc64 = aaruf_crc64_data(ctx->mode2_subheaders, subheaders_block.length);
906
907 uint8_t *buffer = NULL;
908 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
909
910 if(subheaders_block.compression == None)
911 {
912 buffer = ctx->mode2_subheaders;
913 subheaders_block.cmpCrc64 = subheaders_block.crc64;
914 }
915 else
916 {
917 buffer = malloc((size_t)subheaders_block.length * 2); // Allocate double size for compression
918 if(buffer == NULL)
919 {
920 TRACE("Failed to allocate memory for MODE 2 subheaders compression");
921 return;
922 }
923
924 size_t dst_size = (size_t)subheaders_block.length * 2 * 2;
925 size_t props_size = LZMA_PROPERTIES_LENGTH;
926 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->mode2_subheaders, subheaders_block.length, lzma_properties,
927 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
928
929 subheaders_block.cmpLength = (uint32_t)dst_size;
930
931 if(subheaders_block.cmpLength >= subheaders_block.length)
932 {
933 subheaders_block.compression = None;
934 free(buffer);
935 buffer = ctx->mode2_subheaders;
936 }
937 }
938
939 if(subheaders_block.compression == None)
940 {
941 subheaders_block.cmpLength = subheaders_block.length;
942 subheaders_block.cmpCrc64 = subheaders_block.crc64;
943 }
944 else
945 subheaders_block.cmpCrc64 = aaruf_crc64_data(buffer, subheaders_block.cmpLength);
946
947 const size_t length_to_write = subheaders_block.cmpLength;
948 if(subheaders_block.compression == Lzma) subheaders_block.cmpLength += LZMA_PROPERTIES_LENGTH;
949
950 // Write header
951 if(fwrite(&subheaders_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
952 {
953 if(subheaders_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
954
955 // Write data
956 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
957 if(written_bytes == 1)
958 {
959 TRACE("Successfully wrote MODE 2 subheaders block (%" PRIu64 " bytes)", subheaders_block.cmpLength);
960 // Add MODE 2 subheaders block to index
961 TRACE("Adding MODE 2 subheaders block to index");
962 IndexEntry mode2_subheaders_index_entry;
963 mode2_subheaders_index_entry.blockType = DataBlock;
964 mode2_subheaders_index_entry.dataType = CompactDiscMode2Subheader;
965 mode2_subheaders_index_entry.offset = mode2_subheaders_position;
966 utarray_push_back(ctx->index_entries, &mode2_subheaders_index_entry);
967 ctx->dirty_index_block = true;
968 TRACE("Added MODE 2 subheaders block index entry at offset %" PRIu64, mode2_subheaders_position);
969 }
970 }
971
972 if(subheaders_block.compression == Lzma) free(buffer);
973}
974
998{
999 if(ctx->sector_prefix == NULL) return;
1000
1001 fseek(ctx->imageStream, 0, SEEK_END);
1002 long prefix_position = ftell(ctx->imageStream);
1003 // Align index position to block boundary if needed
1004 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1005 if(prefix_position & alignment_mask)
1006 {
1007 uint64_t aligned_position = prefix_position + alignment_mask & ~alignment_mask;
1008 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1009 prefix_position = aligned_position;
1010 }
1011
1012 TRACE("Writing sector prefix block at position %ld", prefix_position);
1013 BlockHeader prefix_block = {0};
1014 prefix_block.identifier = DataBlock;
1015 prefix_block.type = CdSectorPrefix;
1016 prefix_block.compression = ctx->compression_enabled ? Lzma : None;
1017 prefix_block.length = (uint32_t)ctx->sector_prefix_offset;
1018
1019 // Calculate CRC64
1020 prefix_block.crc64 = aaruf_crc64_data(ctx->sector_prefix, prefix_block.length);
1021
1022 uint8_t *buffer = NULL;
1023 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1024
1025 if(prefix_block.compression == None)
1026 {
1027 buffer = ctx->sector_prefix;
1028 prefix_block.cmpCrc64 = prefix_block.crc64;
1029 }
1030 else
1031 {
1032 buffer = malloc((size_t)prefix_block.length * 2); // Allocate double size for compression
1033 if(buffer == NULL)
1034 {
1035 TRACE("Failed to allocate memory for CD sector prefix compression");
1036 return;
1037 }
1038
1039 size_t dst_size = (size_t)prefix_block.length * 2 * 2;
1040 size_t props_size = LZMA_PROPERTIES_LENGTH;
1041 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_prefix, prefix_block.length, lzma_properties,
1042 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1043
1044 prefix_block.cmpLength = (uint32_t)dst_size;
1045
1046 if(prefix_block.cmpLength >= prefix_block.length)
1047 {
1048 prefix_block.compression = None;
1049 free(buffer);
1050 buffer = ctx->sector_prefix;
1051 }
1052 }
1053
1054 if(prefix_block.compression == None)
1055 {
1056 prefix_block.cmpLength = prefix_block.length;
1057 prefix_block.cmpCrc64 = prefix_block.crc64;
1058 }
1059 else
1060 prefix_block.cmpCrc64 = aaruf_crc64_data(buffer, prefix_block.cmpLength);
1061
1062 const size_t length_to_write = prefix_block.cmpLength;
1063 if(prefix_block.compression == Lzma) prefix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1064
1065 // Write header
1066 if(fwrite(&prefix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1067 {
1068 if(prefix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1069
1070 // Write data
1071 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1072 if(written_bytes == 1)
1073 {
1074 TRACE("Successfully wrote CD sector prefix block (%" PRIu64 " bytes)", prefix_block.cmpLength);
1075 // Add prefix block to index
1076 TRACE("Adding CD sector prefix block to index");
1077 IndexEntry prefix_index_entry;
1078 prefix_index_entry.blockType = DataBlock;
1079 prefix_index_entry.dataType = CdSectorPrefix;
1080 prefix_index_entry.offset = prefix_position;
1081 utarray_push_back(ctx->index_entries, &prefix_index_entry);
1082 ctx->dirty_index_block = true;
1083 TRACE("Added CD sector prefix block index entry at offset %" PRIu64, prefix_position);
1084 }
1085 }
1086
1087 if(prefix_block.compression == Lzma) free(buffer);
1088}
1089
1122{
1123 if(ctx->sector_suffix == NULL) return;
1124
1125 fseek(ctx->imageStream, 0, SEEK_END);
1126 long suffix_position = ftell(ctx->imageStream);
1127 // Align index position to block boundary if needed
1128 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1129 if(suffix_position & alignment_mask)
1130 {
1131 const uint64_t aligned_position = suffix_position + alignment_mask & ~alignment_mask;
1132 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1133 suffix_position = aligned_position;
1134 }
1135
1136 TRACE("Writing sector suffix block at position %ld", suffix_position);
1137 BlockHeader suffix_block = {0};
1138 suffix_block.identifier = DataBlock;
1139 suffix_block.type = CdSectorSuffix;
1140 suffix_block.compression = ctx->compression_enabled ? Lzma : None;
1141 suffix_block.length = (uint32_t)ctx->sector_suffix_offset;
1142
1143 // Calculate CRC64
1144 suffix_block.crc64 = aaruf_crc64_data(ctx->sector_suffix, suffix_block.length);
1145
1146 uint8_t *buffer = NULL;
1147 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1148
1149 if(suffix_block.compression == None)
1150 {
1151 buffer = ctx->sector_suffix;
1152 suffix_block.cmpCrc64 = suffix_block.crc64;
1153 }
1154 else
1155 {
1156 buffer = malloc((size_t)suffix_block.length * 2); // Allocate double size for compression
1157 if(buffer == NULL)
1158 {
1159 TRACE("Failed to allocate memory for CD sector suffix compression");
1160 return;
1161 }
1162
1163 size_t dst_size = (size_t)suffix_block.length * 2 * 2;
1164 size_t props_size = LZMA_PROPERTIES_LENGTH;
1165 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_suffix, suffix_block.length, lzma_properties,
1166 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1167
1168 suffix_block.cmpLength = (uint32_t)dst_size;
1169
1170 if(suffix_block.cmpLength >= suffix_block.length)
1171 {
1172 suffix_block.compression = None;
1173 free(buffer);
1174 buffer = ctx->sector_suffix;
1175 }
1176 }
1177
1178 if(suffix_block.compression == None)
1179 {
1180 suffix_block.cmpLength = suffix_block.length;
1181 suffix_block.cmpCrc64 = suffix_block.crc64;
1182 }
1183 else
1184 suffix_block.cmpCrc64 = aaruf_crc64_data(buffer, suffix_block.cmpLength);
1185
1186 const size_t length_to_write = suffix_block.cmpLength;
1187 if(suffix_block.compression == Lzma) suffix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1188
1189 // Write header
1190 if(fwrite(&suffix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1191 {
1192 if(suffix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1193
1194 // Write data
1195 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1196 if(written_bytes == 1)
1197 {
1198 TRACE("Successfully wrote CD sector suffix block (%" PRIu64 " bytes)", suffix_block.cmpLength);
1199 // Add suffix block to index
1200 TRACE("Adding CD sector suffix block to index");
1201 IndexEntry suffix_index_entry;
1202 suffix_index_entry.blockType = DataBlock;
1203 suffix_index_entry.dataType = CdSectorSuffix;
1204 suffix_index_entry.offset = suffix_position;
1205 utarray_push_back(ctx->index_entries, &suffix_index_entry);
1206 ctx->dirty_index_block = true;
1207 TRACE("Added CD sector suffix block index entry at offset %" PRIu64, suffix_position);
1208 }
1209 }
1210
1211 if(suffix_block.compression == Lzma) free(buffer);
1212}
1213
1242{
1243 if(ctx->sector_prefix_ddt2 == NULL) return;
1244
1245 fseek(ctx->imageStream, 0, SEEK_END);
1246 long prefix_ddt_position = ftell(ctx->imageStream);
1247 // Align index position to block boundary if needed
1248 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1249 if(prefix_ddt_position & alignment_mask)
1250 {
1251 const uint64_t aligned_position = prefix_ddt_position + alignment_mask & ~alignment_mask;
1252 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1253 prefix_ddt_position = aligned_position;
1254 }
1255
1256 TRACE("Writing sector prefix DDT v2 at position %ld", prefix_ddt_position);
1257 DdtHeader2 ddt_header2 = {0};
1258 ddt_header2.identifier = DeDuplicationTable2;
1259 ddt_header2.type = CdSectorPrefix;
1260 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1261 ddt_header2.levels = 1;
1262 ddt_header2.tableLevel = 0;
1263 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1264 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1266 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1267 ddt_header2.tableShift = 0; // Single-level DDT
1268 ddt_header2.entries =
1270 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1271 ddt_header2.start = 0;
1272 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1273 // Calculate CRC64
1274 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_prefix_ddt2, (uint32_t)ddt_header2.length);
1275
1276 uint8_t *buffer = NULL;
1277 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1278
1279 if(ddt_header2.compression == None)
1280 {
1281 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1282 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1283 }
1284 else
1285 {
1286 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1287 if(buffer == NULL)
1288 {
1289 TRACE("Failed to allocate memory for sector prefix DDT v2 compression");
1290 return;
1291 }
1292
1293 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1294 size_t props_size = LZMA_PROPERTIES_LENGTH;
1295 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_prefix_ddt2, ddt_header2.length,
1296 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1297
1298 ddt_header2.cmpLength = (uint32_t)dst_size;
1299
1300 if(ddt_header2.cmpLength >= ddt_header2.length)
1301 {
1302 ddt_header2.compression = None;
1303 free(buffer);
1304 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1305 }
1306 }
1307
1308 if(ddt_header2.compression == None)
1309 {
1310 ddt_header2.cmpLength = ddt_header2.length;
1311 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1312 }
1313 else
1314 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1315
1316 const size_t length_to_write = ddt_header2.cmpLength;
1317 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1318
1319 // Write header
1320 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1321 {
1322 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1323
1324 // Write data
1325 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1326 if(written_bytes == 1)
1327 {
1328 TRACE("Successfully wrote sector prefix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1329 // Add prefix block to index
1330 TRACE("Adding sector prefix DDT v2 to index");
1331 IndexEntry prefix_ddt_index_entry;
1332 prefix_ddt_index_entry.blockType = DeDuplicationTable2;
1333 prefix_ddt_index_entry.dataType = CdSectorPrefix;
1334 prefix_ddt_index_entry.offset = prefix_ddt_position;
1335 utarray_push_back(ctx->index_entries, &prefix_ddt_index_entry);
1336 ctx->dirty_index_block = true;
1337 TRACE("Added sector prefix DDT v2 index entry at offset %" PRIu64, prefix_ddt_position);
1338 }
1339 }
1340
1341 if(ddt_header2.compression == Lzma) free(buffer);
1342}
1343
1388{
1389 if(ctx->sector_suffix_ddt2 == NULL) return;
1390
1391 fseek(ctx->imageStream, 0, SEEK_END);
1392 long suffix_ddt_position = ftell(ctx->imageStream);
1393 // Align index position to block boundary if needed
1394 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1395 if(suffix_ddt_position & alignment_mask)
1396 {
1397 const uint64_t aligned_position = suffix_ddt_position + alignment_mask & ~alignment_mask;
1398 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1399 suffix_ddt_position = aligned_position;
1400 }
1401
1402 TRACE("Writing sector suffix DDT v2 at position %ld", suffix_ddt_position);
1403 DdtHeader2 ddt_header2 = {0};
1404 ddt_header2.identifier = DeDuplicationTable2;
1405 ddt_header2.type = CdSectorSuffix;
1406 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1407 ddt_header2.levels = 1;
1408 ddt_header2.tableLevel = 0;
1409 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1410 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1412 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1413 ddt_header2.tableShift = 0; // Single-level DDT
1414 ddt_header2.entries =
1416 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1417 ddt_header2.start = 0;
1418 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1419 // Calculate CRC64
1420 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_suffix_ddt2, (uint32_t)ddt_header2.length);
1421
1422 uint8_t *buffer = NULL;
1423 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1424
1425 if(ddt_header2.compression == None)
1426 {
1427 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1428 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1429 }
1430 else
1431 {
1432 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1433 if(buffer == NULL)
1434 {
1435 TRACE("Failed to allocate memory for sector suffix DDT v2 compression");
1436 return;
1437 }
1438
1439 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1440 size_t props_size = LZMA_PROPERTIES_LENGTH;
1441 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_suffix_ddt2, ddt_header2.length,
1442 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1443
1444 ddt_header2.cmpLength = (uint32_t)dst_size;
1445
1446 if(ddt_header2.cmpLength >= ddt_header2.length)
1447 {
1448 ddt_header2.compression = None;
1449 free(buffer);
1450 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1451 }
1452 }
1453
1454 if(ddt_header2.compression == None)
1455 {
1456 ddt_header2.cmpLength = ddt_header2.length;
1457 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1458 }
1459 else
1460 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1461
1462 const size_t length_to_write = ddt_header2.cmpLength;
1463 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1464
1465 // Write header
1466 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1467 {
1468 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1469
1470 // Write data
1471 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1472 if(written_bytes == 1)
1473 {
1474 TRACE("Successfully wrote sector suffix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1475 // Add suffix block to index
1476 TRACE("Adding sector suffix DDT v2 to index");
1477 IndexEntry suffix_ddt_index_entry;
1478 suffix_ddt_index_entry.blockType = DeDuplicationTable2;
1479 suffix_ddt_index_entry.dataType = CdSectorSuffix;
1480 suffix_ddt_index_entry.offset = suffix_ddt_position;
1481 utarray_push_back(ctx->index_entries, &suffix_ddt_index_entry);
1482 ctx->dirty_index_block = true;
1483 TRACE("Added sector suffix DDT v2 index entry at offset %" PRIu64, suffix_ddt_position);
1484 }
1485 }
1486
1487 if(ddt_header2.compression == Lzma) free(buffer);
1488}
1489
1548{
1549 if(ctx->sector_subchannel == NULL) return;
1550
1551 fseek(ctx->imageStream, 0, SEEK_END);
1552 long block_position = ftell(ctx->imageStream);
1553 // Align index position to block boundary if needed
1554 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1555 if(block_position & alignment_mask)
1556 {
1557 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
1558 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1559 block_position = aligned_position;
1560 }
1561
1562 TRACE("Writing sector subchannel block at position %ld", block_position);
1563 BlockHeader subchannel_block = {0};
1564 subchannel_block.identifier = DataBlock;
1565 subchannel_block.compression = None;
1566
1567 uint8_t *buffer = ctx->sector_subchannel;
1568 bool owns_buffer = false;
1569 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1570
1572 {
1573 subchannel_block.type = CdSectorSubchannel;
1574 subchannel_block.length = (uint32_t)(ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1576 96;
1577 subchannel_block.cmpLength = subchannel_block.length;
1578
1579 if(ctx->compression_enabled)
1580 {
1581 uint8_t *cst_buffer = malloc(subchannel_block.length);
1582
1583 if(cst_buffer == NULL)
1584 {
1585 TRACE("Failed to allocate memory for Claunia Subchannel Transform output");
1586 return;
1587 }
1588
1589 uint8_t *dst_buffer = malloc(subchannel_block.length);
1590
1591 if(dst_buffer == NULL)
1592 {
1593 TRACE("Failed to allocate memory for LZMA output");
1594 free(cst_buffer);
1595 return;
1596 }
1597
1598 aaruf_cst_transform(ctx->sector_subchannel, cst_buffer, subchannel_block.length);
1599 size_t dst_size = subchannel_block.length;
1600 size_t props_size = LZMA_PROPERTIES_LENGTH;
1601
1602 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, cst_buffer, subchannel_block.length, lzma_properties,
1603 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1604
1605 free(cst_buffer);
1606
1607 if(dst_size < subchannel_block.length)
1608 {
1610 subchannel_block.cmpLength = (uint32_t)dst_size;
1611 buffer = dst_buffer;
1612 owns_buffer = true;
1613 }
1614 else
1615 {
1616 subchannel_block.compression = None;
1617 free(dst_buffer);
1618 subchannel_block.cmpLength = subchannel_block.length;
1619 }
1620 }
1621 }
1622 else if(ctx->image_info.MetadataMediaType == BlockMedia)
1623 {
1624 switch(ctx->image_info.MediaType)
1625 {
1626 case AppleProfile:
1627 case AppleFileWare:
1628 subchannel_block.type = AppleProfileTag;
1629 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 20;
1630 break;
1631 case AppleSonyDS:
1632 case AppleSonySS:
1633 subchannel_block.type = AppleSonyTag;
1634 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 12;
1635 break;
1636 case PriamDataTower:
1637 subchannel_block.type = PriamDataTowerTag;
1638 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 24;
1639 break;
1640 default:
1641 TRACE("Incorrect media type, not writing sector subchannel block");
1642 return; // Incorrect media type
1643 }
1644 subchannel_block.cmpLength = subchannel_block.length;
1645 subchannel_block.compression = Lzma;
1646
1647 uint8_t *dst_buffer = malloc(subchannel_block.length);
1648
1649 if(dst_buffer == NULL)
1650 {
1651 TRACE("Failed to allocate memory for LZMA output");
1652 return;
1653 }
1654
1655 size_t dst_size = subchannel_block.length;
1656 size_t props_size = LZMA_PROPERTIES_LENGTH;
1657
1658 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, ctx->sector_subchannel, subchannel_block.length,
1659 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1660
1661 if(dst_size < subchannel_block.length)
1662 {
1663 subchannel_block.cmpLength = (uint32_t)dst_size;
1664 buffer = dst_buffer;
1665 owns_buffer = true;
1666 }
1667 else
1668 {
1669 subchannel_block.compression = None;
1670 free(dst_buffer);
1671 subchannel_block.cmpLength = subchannel_block.length;
1672 }
1673 }
1674 else
1675 {
1676 TRACE("Incorrect media type, not writing sector subchannel block");
1677 return; // Incorrect media type
1678 }
1679
1680 // Calculate CRC64 for raw subchannel data and compressed payload when present
1681 subchannel_block.crc64 = aaruf_crc64_data(ctx->sector_subchannel, subchannel_block.length);
1682 if(subchannel_block.compression == None)
1683 subchannel_block.cmpCrc64 = subchannel_block.crc64;
1684 else
1685 subchannel_block.cmpCrc64 = aaruf_crc64_data(buffer, subchannel_block.cmpLength);
1686
1687 const size_t length_to_write = subchannel_block.cmpLength;
1688 if(subchannel_block.compression != None) subchannel_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1689
1690 // Write header
1691 if(fwrite(&subchannel_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1692 {
1693 if(subchannel_block.compression != None) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1694
1695 // Write data
1696 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1697 if(written_bytes == 1)
1698 {
1699 TRACE("Successfully wrote sector subchannel block (%" PRIu64 " bytes)", subchannel_block.cmpLength);
1700 // Add subchannel block to index
1701 TRACE("Adding sector subchannel block to index");
1702 IndexEntry subchannel_index_entry;
1703 subchannel_index_entry.blockType = DataBlock;
1704 subchannel_index_entry.dataType = subchannel_block.type;
1705 subchannel_index_entry.offset = block_position;
1706 utarray_push_back(ctx->index_entries, &subchannel_index_entry);
1707 ctx->dirty_index_block = true;
1708 TRACE("Added sector subchannel block index entry at offset %" PRIu64, block_position);
1709 }
1710 }
1711
1712 if(owns_buffer) free(buffer);
1713}
1714
1852{
1853 if(ctx->sector_id == NULL || ctx->sector_ied == NULL || ctx->sector_cpr_mai == NULL || ctx->sector_edc == NULL)
1854 return;
1855
1856 uint64_t total_sectors =
1858
1859 // Write DVD sector ID block
1860 fseek(ctx->imageStream, 0, SEEK_END);
1861 long id_position = ftell(ctx->imageStream);
1862 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1863 if(id_position & alignment_mask)
1864 {
1865 const uint64_t aligned_position = id_position + alignment_mask & ~alignment_mask;
1866 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1867 id_position = aligned_position;
1868 }
1869 TRACE("Writing DVD sector ID block at position %ld", id_position);
1870 BlockHeader id_block = {0};
1871 id_block.identifier = DataBlock;
1872 id_block.type = DvdSectorId;
1873 id_block.compression = ctx->compression_enabled ? Lzma : None;
1874 id_block.length = (uint32_t)total_sectors * 4;
1875
1876 // Calculate CRC64
1877 id_block.crc64 = aaruf_crc64_data(ctx->sector_id, id_block.length);
1878
1879 uint8_t *buffer = NULL;
1880 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1881
1882 if(id_block.compression == None)
1883 {
1884 buffer = ctx->sector_id;
1885 id_block.cmpCrc64 = id_block.crc64;
1886 }
1887 else
1888 {
1889 buffer = malloc((size_t)id_block.length * 2); // Allocate double size for compression
1890 if(buffer == NULL)
1891 {
1892 TRACE("Failed to allocate memory for DVD sector ID compression");
1893 return;
1894 }
1895
1896 size_t dst_size = (size_t)id_block.length * 2 * 2;
1897 size_t props_size = LZMA_PROPERTIES_LENGTH;
1898 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_id, id_block.length, lzma_properties, &props_size, 9,
1899 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1900
1901 id_block.cmpLength = (uint32_t)dst_size;
1902
1903 if(id_block.cmpLength >= id_block.length)
1904 {
1905 id_block.compression = None;
1906 free(buffer);
1907 buffer = ctx->sector_id;
1908 }
1909 }
1910
1911 if(id_block.compression == None)
1912 {
1913 id_block.cmpLength = id_block.length;
1914 id_block.cmpCrc64 = id_block.crc64;
1915 }
1916 else
1917 id_block.cmpCrc64 = aaruf_crc64_data(buffer, id_block.cmpLength);
1918
1919 size_t length_to_write = id_block.cmpLength;
1920 if(id_block.compression == Lzma) id_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1921
1922 // Write header
1923 if(fwrite(&id_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1924 {
1925 if(id_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1926
1927 // Write data
1928 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
1929 if(written_bytes == 1)
1930 {
1931 TRACE("Successfully wrote DVD sector ID block (%" PRIu64 " bytes)", id_block.cmpLength);
1932 // Add ID block to index
1933 TRACE("Adding DVD sector ID block to index");
1934 IndexEntry id_index_entry;
1935 id_index_entry.blockType = DataBlock;
1936 id_index_entry.dataType = DvdSectorId;
1937 id_index_entry.offset = id_position;
1938 utarray_push_back(ctx->index_entries, &id_index_entry);
1939 ctx->dirty_index_block = true;
1940 TRACE("Added DVD sector ID block index entry at offset %" PRIu64, id_position);
1941 }
1942 }
1943
1944 if(id_block.compression == Lzma) free(buffer);
1945
1946 // Write DVD sector IED block
1947 fseek(ctx->imageStream, 0, SEEK_END);
1948 long ied_position = ftell(ctx->imageStream);
1949 if(ied_position & alignment_mask)
1950 {
1951 const uint64_t aligned_position = ied_position + alignment_mask & ~alignment_mask;
1952 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1953 ied_position = aligned_position;
1954 }
1955 TRACE("Writing DVD sector IED block at position %ld", ied_position);
1956 BlockHeader ied_block = {0};
1957 ied_block.identifier = DataBlock;
1958 ied_block.type = DvdSectorIed;
1959 ied_block.compression = ctx->compression_enabled ? Lzma : None;
1960 ied_block.length = (uint32_t)total_sectors * 2;
1961 // Calculate CRC64
1962 ied_block.crc64 = aaruf_crc64_data(ctx->sector_ied, ied_block.length);
1963
1964 buffer = NULL;
1965
1966 if(ied_block.compression == None)
1967 {
1968 buffer = ctx->sector_ied;
1969 ied_block.cmpCrc64 = ied_block.crc64;
1970 }
1971 else
1972 {
1973 buffer = malloc((size_t)ied_block.length * 2); // Allocate double size for compression
1974 if(buffer == NULL)
1975 {
1976 TRACE("Failed to allocate memory for DVD sector IED compression");
1977 return;
1978 }
1979
1980 size_t dst_size = (size_t)ied_block.length * 2 * 2;
1981 size_t props_size = LZMA_PROPERTIES_LENGTH;
1982 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_ied, ied_block.length, lzma_properties, &props_size, 9,
1983 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1984
1985 ied_block.cmpLength = (uint32_t)dst_size;
1986
1987 if(ied_block.cmpLength >= ied_block.length)
1988 {
1989 ied_block.compression = None;
1990 free(buffer);
1991 buffer = ctx->sector_ied;
1992 }
1993 }
1994
1995 if(ied_block.compression == None)
1996 {
1997 ied_block.cmpLength = ied_block.length;
1998 ied_block.cmpCrc64 = ied_block.crc64;
1999 }
2000 else
2001 ied_block.cmpCrc64 = aaruf_crc64_data(buffer, ied_block.cmpLength);
2002
2003 length_to_write = ied_block.cmpLength;
2004 if(ied_block.compression == Lzma) ied_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2005
2006 // Write header
2007 if(fwrite(&ied_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2008 {
2009 if(ied_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2010
2011 // Write data
2012 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2013 if(written_bytes == 1)
2014 {
2015 TRACE("Successfully wrote DVD sector IED block (%" PRIu64 " bytes)", ied_block.cmpLength);
2016 // Add IED block to index
2017 TRACE("Adding DVD sector IED block to index");
2018 IndexEntry ied_index_entry;
2019 ied_index_entry.blockType = DataBlock;
2020 ied_index_entry.dataType = DvdSectorIed;
2021 ied_index_entry.offset = ied_position;
2022 utarray_push_back(ctx->index_entries, &ied_index_entry);
2023 ctx->dirty_index_block = true;
2024 TRACE("Added DVD sector IED block index entry at offset %" PRIu64, ied_position);
2025 }
2026 }
2027
2028 if(ied_block.compression == Lzma) free(buffer);
2029
2030 // Write DVD sector CPR/MAI block
2031 fseek(ctx->imageStream, 0, SEEK_END);
2032 long cpr_mai_position = ftell(ctx->imageStream);
2033 if(cpr_mai_position & alignment_mask)
2034 {
2035 const uint64_t aligned_position = cpr_mai_position + alignment_mask & ~alignment_mask;
2036 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2037 cpr_mai_position = aligned_position;
2038 }
2039 TRACE("Writing DVD sector CPR/MAI block at position %ld", cpr_mai_position);
2040 BlockHeader cpr_mai_block = {0};
2041 cpr_mai_block.identifier = DataBlock;
2042 cpr_mai_block.type = DvdSectorCprMai;
2043 cpr_mai_block.compression = ctx->compression_enabled ? Lzma : None;
2044 cpr_mai_block.length = (uint32_t)total_sectors * 6;
2045 // Calculate CRC64
2046 cpr_mai_block.crc64 = aaruf_crc64_data(ctx->sector_cpr_mai, cpr_mai_block.length);
2047
2048 buffer = NULL;
2049
2050 if(cpr_mai_block.compression == None)
2051 {
2052 buffer = ctx->sector_cpr_mai;
2053 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2054 }
2055 else
2056 {
2057 buffer = malloc((size_t)cpr_mai_block.length * 2); // Allocate double size for compression
2058 if(buffer == NULL)
2059 {
2060 TRACE("Failed to allocate memory for DVD sector CPR/MAI compression");
2061 return;
2062 }
2063
2064 size_t dst_size = (size_t)cpr_mai_block.length * 2 * 2;
2065 size_t props_size = LZMA_PROPERTIES_LENGTH;
2066 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_cpr_mai, cpr_mai_block.length, lzma_properties,
2067 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2068
2069 cpr_mai_block.cmpLength = (uint32_t)dst_size;
2070
2071 if(cpr_mai_block.cmpLength >= cpr_mai_block.length)
2072 {
2073 cpr_mai_block.compression = None;
2074 free(buffer);
2075 buffer = ctx->sector_cpr_mai;
2076 }
2077 }
2078
2079 if(cpr_mai_block.compression == None)
2080 {
2081 cpr_mai_block.cmpLength = cpr_mai_block.length;
2082 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2083 }
2084 else
2085 cpr_mai_block.cmpCrc64 = aaruf_crc64_data(buffer, cpr_mai_block.cmpLength);
2086
2087 length_to_write = cpr_mai_block.cmpLength;
2088 if(cpr_mai_block.compression == Lzma) cpr_mai_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2089
2090 // Write header
2091 if(fwrite(&cpr_mai_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2092 {
2093 if(cpr_mai_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2094
2095 // Write data
2096 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2097 if(written_bytes == 1)
2098 {
2099 TRACE("Successfully wrote DVD sector CPR/MAI block (%" PRIu64 " bytes)", cpr_mai_block.cmpLength);
2100 // Add CPR/MAI block to index
2101 TRACE("Adding DVD sector CPR/MAI block to index");
2102 IndexEntry cpr_mai_index_entry;
2103 cpr_mai_index_entry.blockType = DataBlock;
2104 cpr_mai_index_entry.dataType = DvdSectorCprMai;
2105 cpr_mai_index_entry.offset = cpr_mai_position;
2106 utarray_push_back(ctx->index_entries, &cpr_mai_index_entry);
2107 ctx->dirty_index_block = true;
2108 TRACE("Added DVD sector CPR/MAI block index entry at offset %" PRIu64, cpr_mai_position);
2109 }
2110 }
2111
2112 if(cpr_mai_block.compression == Lzma) free(buffer);
2113
2114 // Write DVD sector EDC block
2115 fseek(ctx->imageStream, 0, SEEK_END);
2116 long edc_position = ftell(ctx->imageStream);
2117 if(edc_position & alignment_mask)
2118 {
2119 const uint64_t aligned_position = edc_position + alignment_mask & ~alignment_mask;
2120 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2121 edc_position = aligned_position;
2122 }
2123 TRACE("Writing DVD sector EDC block at position %ld", edc_position);
2124 BlockHeader edc_block = {0};
2125 edc_block.identifier = DataBlock;
2126 edc_block.type = DvdSectorEdc;
2127 edc_block.compression = ctx->compression_enabled ? Lzma : None;
2128 edc_block.length = (uint32_t)total_sectors * 4;
2129 // Calculate CRC64
2130 edc_block.crc64 = aaruf_crc64_data(ctx->sector_edc, edc_block.length);
2131
2132 buffer = NULL;
2133
2134 if(edc_block.compression == None)
2135 {
2136 buffer = ctx->sector_edc;
2137 edc_block.cmpCrc64 = edc_block.crc64;
2138 }
2139 else
2140 {
2141 buffer = malloc((size_t)edc_block.length * 2); // Allocate double size for compression
2142 if(buffer == NULL)
2143 {
2144 TRACE("Failed to allocate memory for DVD sector EDC compression");
2145 return;
2146 }
2147
2148 size_t dst_size = (size_t)edc_block.length * 2 * 2;
2149 size_t props_size = LZMA_PROPERTIES_LENGTH;
2150 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_edc, edc_block.length, lzma_properties, &props_size, 9,
2151 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2152
2153 edc_block.cmpLength = (uint32_t)dst_size;
2154
2155 if(edc_block.cmpLength >= edc_block.length)
2156 {
2157 edc_block.compression = None;
2158 free(buffer);
2159 buffer = ctx->sector_edc;
2160 }
2161 }
2162
2163 if(edc_block.compression == None)
2164 {
2165 edc_block.cmpLength = edc_block.length;
2166 edc_block.cmpCrc64 = edc_block.crc64;
2167 }
2168 else
2169 edc_block.cmpCrc64 = aaruf_crc64_data(buffer, edc_block.cmpLength);
2170
2171 length_to_write = edc_block.cmpLength;
2172 if(edc_block.compression == Lzma) edc_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2173
2174 // Write header
2175 if(fwrite(&edc_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2176 {
2177 if(edc_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2178
2179 // Write data
2180 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2181 if(written_bytes == 1)
2182 {
2183 TRACE("Successfully wrote DVD sector EDC block (%" PRIu64 " bytes)", edc_block.cmpLength);
2184 // Add EDC block to index
2185 TRACE("Adding DVD sector EDC block to index");
2186 IndexEntry edc_index_entry;
2187 edc_index_entry.blockType = DataBlock;
2188 edc_index_entry.dataType = DvdSectorEdc;
2189 edc_index_entry.offset = edc_position;
2190 utarray_push_back(ctx->index_entries, &edc_index_entry);
2191 ctx->dirty_index_block = true;
2192 TRACE("Added DVD sector EDC block index entry at offset %" PRIu64, edc_position);
2193 }
2194 }
2195
2196 if(edc_block.compression == Lzma) free(buffer);
2197}
2198
2297{
2298 if(ctx->sector_decrypted_title_key == NULL) return;
2299
2300 fseek(ctx->imageStream, 0, SEEK_END);
2301 long block_position = ftell(ctx->imageStream);
2302 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2303 if(block_position & alignment_mask)
2304 {
2305 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2306 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2307 block_position = aligned_position;
2308 }
2309 TRACE("Writing DVD decrypted title key block at position %ld", block_position);
2310 BlockHeader decrypted_title_key_block = {0};
2311 decrypted_title_key_block.identifier = DataBlock;
2312 decrypted_title_key_block.type = DvdSectorTitleKeyDecrypted;
2313 decrypted_title_key_block.compression = ctx->compression_enabled ? Lzma : None;
2314 decrypted_title_key_block.length =
2316 5;
2317 // Calculate CRC64
2318 decrypted_title_key_block.crc64 =
2319 aaruf_crc64_data(ctx->sector_decrypted_title_key, decrypted_title_key_block.length);
2320
2321 uint8_t *buffer = NULL;
2322 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2323
2324 if(decrypted_title_key_block.compression == None)
2325 {
2326 buffer = ctx->sector_decrypted_title_key;
2327 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2328 }
2329 else
2330 {
2331 buffer = malloc((size_t)decrypted_title_key_block.length * 2); // Allocate double size for compression
2332 if(buffer == NULL)
2333 {
2334 TRACE("Failed to allocate memory for DVD decrypted title key compression");
2335 return;
2336 }
2337
2338 size_t dst_size = (size_t)decrypted_title_key_block.length * 2 * 2;
2339 size_t props_size = LZMA_PROPERTIES_LENGTH;
2340 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_decrypted_title_key, decrypted_title_key_block.length,
2341 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2342
2343 decrypted_title_key_block.cmpLength = (uint32_t)dst_size;
2344
2345 if(decrypted_title_key_block.cmpLength >= decrypted_title_key_block.length)
2346 {
2347 decrypted_title_key_block.compression = None;
2348 free(buffer);
2349 buffer = ctx->sector_decrypted_title_key;
2350 }
2351 }
2352
2353 if(decrypted_title_key_block.compression == None)
2354 {
2355 decrypted_title_key_block.cmpLength = decrypted_title_key_block.length;
2356 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2357 }
2358 else
2359 decrypted_title_key_block.cmpCrc64 = aaruf_crc64_data(buffer, decrypted_title_key_block.cmpLength);
2360
2361 const size_t length_to_write = decrypted_title_key_block.cmpLength;
2362 if(decrypted_title_key_block.compression == Lzma) decrypted_title_key_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2363
2364 // Write header
2365 if(fwrite(&decrypted_title_key_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2366 {
2367 if(decrypted_title_key_block.compression == Lzma)
2368 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2369
2370 // Write data
2371 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2372 if(written_bytes == 1)
2373 {
2374 TRACE("Successfully wrote DVD decrypted title key block (%" PRIu64 " bytes)",
2375 decrypted_title_key_block.cmpLength);
2376 // Add decrypted title key block to index
2377 TRACE("Adding DVD decrypted title key block to index");
2378 IndexEntry decrypted_title_key_index_entry;
2379 decrypted_title_key_index_entry.blockType = DataBlock;
2380 decrypted_title_key_index_entry.dataType = DvdSectorTitleKeyDecrypted;
2381 decrypted_title_key_index_entry.offset = block_position;
2382 utarray_push_back(ctx->index_entries, &decrypted_title_key_index_entry);
2383 ctx->dirty_index_block = true;
2384 TRACE("Added DVD decrypted title key block index entry at offset %" PRIu64, block_position);
2385 }
2386 }
2387
2388 if(decrypted_title_key_block.compression == Lzma) free(buffer);
2389}
2390
2463{
2464 if(ctx->mediaTags == NULL) return;
2465
2466 mediaTagEntry *media_tag = NULL;
2467 mediaTagEntry *tmp_media_tag = NULL;
2468
2469 HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
2470 {
2471 fseek(ctx->imageStream, 0, SEEK_END);
2472 long tag_position = ftell(ctx->imageStream);
2473 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2474 if(tag_position & alignment_mask)
2475 {
2476 const uint64_t aligned_position = tag_position + alignment_mask & ~alignment_mask;
2477 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2478 tag_position = aligned_position;
2479 }
2480
2481 TRACE("Writing media tag block type %d at position %ld", aaruf_get_datatype_for_media_tag_type(media_tag->type),
2482 tag_position);
2483 BlockHeader tag_block = {0};
2484 tag_block.identifier = DataBlock;
2485 tag_block.type = (uint16_t)aaruf_get_datatype_for_media_tag_type(media_tag->type);
2486 tag_block.compression = ctx->compression_enabled ? Lzma : None;
2487 tag_block.length = media_tag->length;
2488
2489 // Calculate CRC64
2490 tag_block.crc64 = aaruf_crc64_data(media_tag->data, tag_block.length);
2491
2492 uint8_t *buffer = NULL;
2493 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2494
2495 if(tag_block.compression == None)
2496 {
2497 buffer = media_tag->data;
2498 tag_block.cmpCrc64 = tag_block.crc64;
2499 }
2500 else
2501 {
2502 buffer = malloc((size_t)tag_block.length * 2); // Allocate double size for compression
2503 if(buffer == NULL)
2504 {
2505 TRACE("Failed to allocate memory for media tag compression");
2506 return;
2507 }
2508
2509 size_t dst_size = (size_t)tag_block.length * 2 * 2;
2510 size_t props_size = LZMA_PROPERTIES_LENGTH;
2511 aaruf_lzma_encode_buffer(buffer, &dst_size, media_tag->data, tag_block.length, lzma_properties, &props_size,
2512 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2513
2514 tag_block.cmpLength = (uint32_t)dst_size;
2515
2516 if(tag_block.cmpLength >= tag_block.length)
2517 {
2518 tag_block.compression = None;
2519 free(buffer);
2520 buffer = media_tag->data;
2521 }
2522 }
2523
2524 if(tag_block.compression == None)
2525 {
2526 tag_block.cmpLength = tag_block.length;
2527 tag_block.cmpCrc64 = tag_block.crc64;
2528 }
2529 else
2530 tag_block.cmpCrc64 = aaruf_crc64_data(buffer, tag_block.cmpLength);
2531
2532 const size_t length_to_write = tag_block.cmpLength;
2533 if(tag_block.compression == Lzma) tag_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2534
2535 // Write header
2536 if(fwrite(&tag_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2537 {
2538 if(tag_block.compression == Lzma)
2539 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream); // Write data
2540
2541 const size_t written_bytes = fwrite(buffer, length_to_write, 1, ctx->imageStream);
2542 if(written_bytes == 1)
2543 {
2544 TRACE("Successfully wrote media tag block type %d (%" PRIu64 " bytes)", tag_block.type,
2545 tag_block.cmpLength);
2546 // Add media tag block to index
2547 TRACE("Adding media tag type %d block to index", tag_block.type);
2548 IndexEntry tag_index_entry;
2549 tag_index_entry.blockType = DataBlock;
2550 tag_index_entry.dataType = tag_block.type;
2551 tag_index_entry.offset = tag_position;
2552 utarray_push_back(ctx->index_entries, &tag_index_entry);
2553 ctx->dirty_index_block = true;
2554 TRACE("Added media tag block type %d index entry at offset %" PRIu64, tag_block.type, tag_position);
2555 }
2556 }
2557
2558 if(tag_block.compression == Lzma) free(buffer);
2559 }
2560}
2561
2725{
2726 if(ctx->tape_files == NULL) return;
2727
2728 // Iterate the uthash and count how many entries do we have
2729 const tapeFileHashEntry *tape_file = NULL;
2730 const tapeFileHashEntry *tmp_tape_file = NULL;
2731 size_t tape_file_count = 0;
2732 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file) tape_file_count++;
2733
2734 // Create a memory buffer to copy all the file entries
2735 const size_t buffer_size = tape_file_count * sizeof(TapeFileEntry);
2736 TapeFileEntry *buffer = malloc(buffer_size);
2737 if(buffer == NULL)
2738 {
2739 TRACE("Failed to allocate memory for tape file entries");
2740 return;
2741 }
2742 memset(buffer, 0, buffer_size);
2743 size_t index = 0;
2744 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file)
2745 {
2746 if(index >= tape_file_count) break;
2747 memcpy(&buffer[index], &tape_file->fileEntry, sizeof(TapeFileEntry));
2748 index++;
2749 }
2750
2751 // Create the tape file block in memory
2752 TapeFileHeader tape_file_block = {0};
2753 tape_file_block.identifier = TapeFileBlock;
2754 tape_file_block.length = (uint32_t)buffer_size;
2755 tape_file_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_file_block.length);
2756
2757 // Write tape file block to file, block aligned
2758 fseek(ctx->imageStream, 0, SEEK_END);
2759 long block_position = ftell(ctx->imageStream);
2760 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2761 if(block_position & alignment_mask)
2762 {
2763 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2764 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2765 block_position = aligned_position;
2766 }
2767 TRACE("Writing tape file block at position %ld", block_position);
2768 if(fwrite(&tape_file_block, sizeof(TapeFileHeader), 1, ctx->imageStream) == 1)
2769 {
2770 const size_t written_bytes = fwrite(buffer, tape_file_block.length, 1, ctx->imageStream);
2771 if(written_bytes == 1)
2772 {
2773 TRACE("Successfully wrote tape file block (%" PRIu64 " bytes)", tape_file_block.length);
2774 // Add tape file block to index
2775 TRACE("Adding tape file block to index");
2776 IndexEntry index_entry;
2777 index_entry.blockType = TapeFileBlock;
2778 index_entry.dataType = 0;
2779 index_entry.offset = block_position;
2780 utarray_push_back(ctx->index_entries, &index_entry);
2781 ctx->dirty_index_block = true;
2782 TRACE("Added tape file block index entry at offset %" PRIu64, block_position);
2783 }
2784 }
2785
2786 free(buffer);
2787}
2788
2958{
2959 if(ctx->tape_partitions == NULL) return;
2960
2961 // Iterate the uthash and count how many entries do we have
2962 const TapePartitionHashEntry *tape_partition = NULL;
2963 const TapePartitionHashEntry *tmp_tape_partition = NULL;
2964 size_t tape_partition_count = 0;
2965 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition) tape_partition_count++;
2966
2967 // Create a memory buffer to copy all the partition entries
2968 const size_t buffer_size = tape_partition_count * sizeof(TapePartitionEntry);
2969 TapePartitionEntry *buffer = malloc(buffer_size);
2970 if(buffer == NULL)
2971 {
2972 TRACE("Failed to allocate memory for tape partition entries");
2973 return;
2974 }
2975 memset(buffer, 0, buffer_size);
2976 size_t index = 0;
2977 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition)
2978 {
2979 if(index >= tape_partition_count) break;
2980 memcpy(&buffer[index], &tape_partition->partitionEntry, sizeof(TapePartitionEntry));
2981 index++;
2982 }
2983
2984 // Create the tape partition block in memory
2985 TapePartitionHeader tape_partition_block = {0};
2986 tape_partition_block.identifier = TapePartitionBlock;
2987 tape_partition_block.length = (uint32_t)buffer_size;
2988 tape_partition_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_partition_block.length);
2989
2990 // Write tape partition block to partition, block aligned
2991 fseek(ctx->imageStream, 0, SEEK_END);
2992 long block_position = ftell(ctx->imageStream);
2993 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2994 if(block_position & alignment_mask)
2995 {
2996 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2997 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2998 block_position = aligned_position;
2999 }
3000 TRACE("Writing tape partition block at position %ld", block_position);
3001 if(fwrite(&tape_partition_block, sizeof(TapePartitionHeader), 1, ctx->imageStream) == 1)
3002 {
3003 const size_t written_bytes = fwrite(buffer, tape_partition_block.length, 1, ctx->imageStream);
3004 if(written_bytes == 1)
3005 {
3006 TRACE("Successfully wrote tape partition block (%" PRIu64 " bytes)", tape_partition_block.length);
3007 // Add tape partition block to index
3008 TRACE("Adding tape partition block to index");
3009 IndexEntry index_entry;
3010 index_entry.blockType = TapePartitionBlock;
3011 index_entry.dataType = 0;
3012 index_entry.offset = block_position;
3013 utarray_push_back(ctx->index_entries, &index_entry);
3014 ctx->dirty_index_block = true;
3015 TRACE("Added tape partition block index entry at offset %" PRIu64, block_position);
3016 }
3017 }
3018
3019 free(buffer);
3020}
3021
3084{
3085 if(ctx->geometry_block.identifier != GeometryBlock) return;
3086
3087 fseek(ctx->imageStream, 0, SEEK_END);
3088 long block_position = ftell(ctx->imageStream);
3089 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3090 if(block_position & alignment_mask)
3091 {
3092 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3093 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3094 block_position = aligned_position;
3095 }
3096
3097 TRACE("Writing geometry block at position %ld", block_position);
3098
3099 // Write header
3100 if(fwrite(&ctx->geometry_block, sizeof(GeometryBlockHeader), 1, ctx->imageStream) == 1)
3101 {
3102 TRACE("Successfully wrote geometry block");
3103
3104 // Add geometry block to index
3105 TRACE("Adding geometry block to index");
3106 IndexEntry index_entry;
3107 index_entry.blockType = GeometryBlock;
3108 index_entry.dataType = 0;
3109 index_entry.offset = block_position;
3110 utarray_push_back(ctx->index_entries, &index_entry);
3111 ctx->dirty_index_block = true;
3112 TRACE("Added geometry block index entry at offset %" PRIu64, block_position);
3113 }
3114}
3115
3221{
3223 ctx->metadata_block_header.lastMediaSequence == 0 && ctx->creator == NULL && ctx->comments == NULL &&
3224 ctx->media_title == NULL && ctx->media_manufacturer == NULL && ctx->media_model == NULL &&
3225 ctx->media_serial_number == NULL && ctx->media_barcode == NULL && ctx->media_part_number == NULL &&
3226 ctx->drive_manufacturer == NULL && ctx->drive_model == NULL && ctx->drive_serial_number == NULL &&
3227 ctx->drive_firmware_revision == NULL)
3228 return;
3229
3238
3240
3241 int pos = sizeof(MetadataBlockHeader);
3242
3243 uint8_t *buffer = calloc(1, ctx->metadata_block_header.blockSize);
3244 if(buffer == NULL) return;
3245
3246 if(ctx->creator != NULL && ctx->metadata_block_header.creatorLength > 0)
3247 {
3248 memcpy(buffer + pos, ctx->creator, ctx->metadata_block_header.creatorLength);
3251 }
3252
3253 if(ctx->comments != NULL && ctx->metadata_block_header.commentsLength > 0)
3254 {
3255 memcpy(buffer + pos, ctx->comments, ctx->metadata_block_header.commentsLength);
3258 }
3259
3260 if(ctx->media_title != NULL && ctx->metadata_block_header.mediaTitleLength > 0)
3261 {
3262 memcpy(buffer + pos, ctx->media_title, ctx->metadata_block_header.mediaTitleLength);
3265 }
3266
3268 {
3269 memcpy(buffer + pos, ctx->media_manufacturer, ctx->metadata_block_header.mediaManufacturerLength);
3272 }
3273
3274 if(ctx->media_model != NULL && ctx->metadata_block_header.mediaModelLength > 0)
3275 {
3276 memcpy(buffer + pos, ctx->media_model, ctx->metadata_block_header.mediaModelLength);
3279 }
3280
3282 {
3283 memcpy(buffer + pos, ctx->media_serial_number, ctx->metadata_block_header.mediaSerialNumberLength);
3286 }
3287
3288 if(ctx->media_barcode != NULL && ctx->metadata_block_header.mediaBarcodeLength > 0)
3289 {
3290 memcpy(buffer + pos, ctx->media_barcode, ctx->metadata_block_header.mediaBarcodeLength);
3293 }
3294
3296 {
3297 memcpy(buffer + pos, ctx->media_part_number, ctx->metadata_block_header.mediaPartNumberLength);
3300 }
3301
3303 {
3304 memcpy(buffer + pos, ctx->drive_manufacturer, ctx->metadata_block_header.driveManufacturerLength);
3307 }
3308
3309 if(ctx->drive_model != NULL && ctx->metadata_block_header.driveModelLength > 0)
3310 {
3311 memcpy(buffer + pos, ctx->drive_model, ctx->metadata_block_header.driveModelLength);
3314 }
3315
3317 {
3318 memcpy(buffer + pos, ctx->drive_serial_number, ctx->metadata_block_header.driveSerialNumberLength);
3321 }
3322
3324 {
3327 }
3328
3329 fseek(ctx->imageStream, 0, SEEK_END);
3330 long block_position = ftell(ctx->imageStream);
3331 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3332 if(block_position & alignment_mask)
3333 {
3334 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3335 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3336 block_position = aligned_position;
3337 }
3338
3339 memcpy(buffer, &ctx->metadata_block_header, sizeof(MetadataBlockHeader));
3340
3341 TRACE("Writing metadata block at position %ld", block_position);
3342
3343 if(fwrite(buffer, ctx->metadata_block_header.blockSize, 1, ctx->imageStream) == 1)
3344 {
3345 TRACE("Successfully wrote metadata block");
3346
3347 // Add metadata block to index
3348 TRACE("Adding metadata block to index");
3349 IndexEntry index_entry;
3350 index_entry.blockType = MetadataBlock;
3351 index_entry.dataType = 0;
3352 index_entry.offset = block_position;
3353 utarray_push_back(ctx->index_entries, &index_entry);
3354 ctx->dirty_index_block = true;
3355 TRACE("Added metadata block index entry at offset %" PRIu64, block_position);
3356 }
3357
3358 free(buffer);
3359}
3360
3507{
3508
3509 if(ctx->dump_hardware_entries_with_data == NULL || ctx->dump_hardware_header.entries == 0 ||
3511 return;
3512
3513 const size_t required_length = sizeof(DumpHardwareHeader) + ctx->dump_hardware_header.length;
3514
3515 uint8_t *buffer = calloc(1, required_length);
3516
3517 if(buffer == NULL) return;
3518
3519 // Start to iterate and copy the data
3520 size_t offset = sizeof(DumpHardwareHeader);
3521 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
3522 {
3523 size_t entry_size = sizeof(DumpHardwareEntry) +
3533
3534 if(offset + entry_size > required_length)
3535 {
3536 FATAL("Calculated size exceeds provided buffer length");
3537 free(buffer);
3538 return;
3539 }
3540
3541 memcpy(buffer + offset, &ctx->dump_hardware_entries_with_data[i].entry, sizeof(DumpHardwareEntry));
3542 offset += sizeof(DumpHardwareEntry);
3545 {
3546 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].manufacturer,
3549 }
3551 ctx->dump_hardware_entries_with_data[i].model != NULL)
3552 {
3553 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].model,
3556 }
3559 {
3560 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].revision,
3563 }
3566 {
3567 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].firmware,
3570 }
3573 {
3574 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].serial,
3577 }
3580 {
3581 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareName,
3584 }
3587 {
3588 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareVersion,
3591 }
3594 {
3595 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem,
3598 }
3601 {
3602 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].extents,
3604 offset += ctx->dump_hardware_entries_with_data[i].entry.extents * sizeof(DumpExtent);
3605 }
3606 }
3607
3608 // Calculate CRC64
3611
3612 // Copy header
3613 memcpy(buffer, &ctx->dump_hardware_header, sizeof(DumpHardwareHeader));
3614
3615 fseek(ctx->imageStream, 0, SEEK_END);
3616 long block_position = ftell(ctx->imageStream);
3617 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3618 if(block_position & alignment_mask)
3619 {
3620 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3621 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3622 block_position = aligned_position;
3623 }
3624 TRACE("Writing dump hardware block at position %ld", block_position);
3625 if(fwrite(buffer, required_length, 1, ctx->imageStream) == 1)
3626 {
3627 TRACE("Successfully wrote dump hardware block");
3628
3629 // Add dump hardware block to index
3630 TRACE("Adding dump hardware block to index");
3631 IndexEntry index_entry;
3632 index_entry.blockType = DumpHardwareBlock;
3633 index_entry.dataType = 0;
3634 index_entry.offset = block_position;
3635 utarray_push_back(ctx->index_entries, &index_entry);
3636 ctx->dirty_index_block = true;
3637 TRACE("Added dump hardware block index entry at offset %" PRIu64, block_position);
3638 }
3639
3640 free(buffer);
3641}
3642
3738{
3739 if(ctx->cicm_block == NULL || ctx->cicm_block_header.length == 0 || ctx->cicm_block_header.identifier != CicmBlock)
3740 return;
3741
3742 fseek(ctx->imageStream, 0, SEEK_END);
3743 long block_position = ftell(ctx->imageStream);
3744 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3745
3746 if(block_position & alignment_mask)
3747 {
3748 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3749 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3750 block_position = aligned_position;
3751 }
3752
3753 TRACE("Writing CICM XML block at position %ld", block_position);
3754 if(fwrite(&ctx->cicm_block_header, sizeof(CicmMetadataBlock), 1, ctx->imageStream) == 1)
3755 if(fwrite(ctx->cicm_block, ctx->cicm_block_header.length, 1, ctx->imageStream) == 1)
3756 {
3757 TRACE("Successfully wrote CICM XML block");
3758
3759 // Add CICM block to index
3760 TRACE("Adding CICM XML block to index");
3761 IndexEntry index_entry;
3762 index_entry.blockType = CicmBlock;
3763 index_entry.dataType = 0;
3764 index_entry.offset = block_position;
3765 utarray_push_back(ctx->index_entries, &index_entry);
3766 ctx->dirty_index_block = true;
3767 TRACE("Added CICM XML block index entry at offset %" PRIu64, block_position);
3768 }
3769}
3770
3876{
3877 if(ctx->json_block == NULL || ctx->json_block_header.length == 0 ||
3879 return;
3880
3881 fseek(ctx->imageStream, 0, SEEK_END);
3882 long block_position = ftell(ctx->imageStream);
3883 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3884
3885 if(block_position & alignment_mask)
3886 {
3887 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3888 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3889 block_position = aligned_position;
3890 }
3891
3892 TRACE("Writing Aaru metadata JSON block at position %ld", block_position);
3893 if(fwrite(&ctx->json_block_header, sizeof(AaruMetadataJsonBlockHeader), 1, ctx->imageStream) == 1)
3894 if(fwrite(ctx->json_block, ctx->json_block_header.length, 1, ctx->imageStream) == 1)
3895 {
3896 TRACE("Successfully wrote Aaru metadata JSON block");
3897
3898 // Add Aaru metadata JSON block to index
3899 TRACE("Adding Aaru metadata JSON block to index");
3900 IndexEntry index_entry;
3901 index_entry.blockType = AaruMetadataJsonBlock;
3902 index_entry.dataType = 0;
3903 index_entry.offset = block_position;
3904 utarray_push_back(ctx->index_entries, &index_entry);
3905 ctx->dirty_index_block = true;
3906 TRACE("Added Aaru metadata JSON block index entry at offset %" PRIu64, block_position);
3907 }
3908}
3909
3927{
3928 // Write the complete index at the end of the file
3929 TRACE("Writing index at the end of the file");
3930 fseek(ctx->imageStream, 0, SEEK_END);
3931 long index_position = ftell(ctx->imageStream);
3932
3933 // Align index position to block boundary if needed
3934 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3935 if(index_position & alignment_mask)
3936 {
3937 uint64_t aligned_position = index_position + alignment_mask & ~alignment_mask;
3938 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3939 index_position = aligned_position;
3940 TRACE("Aligned index position to %" PRIu64, aligned_position);
3941 }
3942
3943 // Prepare index header
3944 IndexHeader3 index_header;
3945 index_header.identifier = IndexBlock3;
3946 index_header.entries = utarray_len(ctx->index_entries);
3947 index_header.previous = 0; // No previous index for now
3948
3949 TRACE("Writing index with %" PRIu64 " entries at position %ld", index_header.entries, index_position);
3950
3951 // Calculate CRC64 of index entries
3952 crc64_ctx *index_crc64_context = aaruf_crc64_init();
3953 if(index_crc64_context != NULL && index_header.entries > 0)
3954 {
3955 size_t index_data_size = index_header.entries * sizeof(IndexEntry);
3956 aaruf_crc64_update(index_crc64_context, utarray_front(ctx->index_entries), index_data_size);
3957 aaruf_crc64_final(index_crc64_context, &index_header.crc64);
3958 TRACE("Calculated index CRC64: 0x%16lX", index_header.crc64);
3959 }
3960 else
3961 index_header.crc64 = 0;
3962
3963 // Write index header
3964 if(fwrite(&index_header, sizeof(IndexHeader3), 1, ctx->imageStream) == 1)
3965 {
3966 TRACE("Successfully wrote index header");
3967
3968 // Write index entries
3969 if(index_header.entries > 0)
3970 {
3971 size_t entries_written = 0;
3972 IndexEntry *entry = NULL;
3973
3974 for(entry = (IndexEntry *)utarray_front(ctx->index_entries); entry != NULL;
3975 entry = (IndexEntry *)utarray_next(ctx->index_entries, entry))
3976 if(fwrite(entry, sizeof(IndexEntry), 1, ctx->imageStream) == 1)
3977 {
3978 entries_written++;
3979 TRACE("Wrote index entry: blockType=0x%08X dataType=%u offset=%" PRIu64, entry->blockType,
3980 entry->dataType, entry->offset);
3981 }
3982 else
3983 {
3984 TRACE("Failed to write index entry %zu", entries_written);
3985 break;
3986 }
3987
3988 if(entries_written == index_header.entries)
3989 {
3990 TRACE("Successfully wrote all %zu index entries", entries_written);
3991
3992 // Update header with index offset and rewrite it
3993 ctx->header.indexOffset = index_position;
3994 TRACE("Updating header with index offset: %" PRIu64, ctx->header.indexOffset);
3995
3996 // Seek back to beginning and rewrite header
3997 fseek(ctx->imageStream, 0, SEEK_SET);
3998 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) == 1)
3999 TRACE("Successfully updated header with index offset");
4000 else
4001 {
4002 TRACE("Failed to update header with index offset");
4004 }
4005 }
4006 else
4007 {
4008 TRACE("Failed to write all index entries (wrote %zu of %" PRIu64 ")", entries_written,
4009 index_header.entries);
4011 }
4012 }
4013 }
4014 else
4015 {
4016 TRACE("Failed to write index header");
4018 }
4019
4020 return AARUF_STATUS_OK;
4021}
4022
4060{
4061 TRACE("Entering aaruf_close(%p)", context);
4062
4063 mediaTagEntry *media_tag = NULL;
4064 mediaTagEntry *tmp_media_tag = NULL;
4065
4066 if(context == NULL)
4067 {
4068 FATAL("Invalid context");
4069 errno = EINVAL;
4070 return -1;
4071 }
4072
4073 aaruformat_context *ctx = context;
4074
4075 // Not a libaaruformat context
4076 if(ctx->magic != AARU_MAGIC)
4077 {
4078 FATAL("Invalid context");
4079 errno = EINVAL;
4080 return -1;
4081 }
4082
4083 if(ctx->is_writing)
4084 {
4085 TRACE("File is writing");
4086
4087 TRACE("Seeking to start of image");
4088 // Write the header at the beginning of the file
4089 fseek(ctx->imageStream, 0, SEEK_SET);
4090
4091 TRACE("Writing header at position 0");
4092 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) != 1)
4093 {
4094 fclose(ctx->imageStream);
4095 ctx->imageStream = NULL;
4097 return -1;
4098 }
4099
4100 // Close current block first
4101 TRACE("Closing current block if any");
4102 if(ctx->writing_buffer != NULL)
4103 {
4104 int error = aaruf_close_current_block(ctx);
4105
4106 if(error != AARUF_STATUS_OK) return error;
4107 }
4108
4109 int32_t res;
4110 if(ctx->is_tape)
4111 {
4112 // Write tape DDT
4113 if(ctx->dirty_tape_ddt)
4114 {
4115 res = write_tape_ddt(ctx);
4116 if(res != AARUF_STATUS_OK) return res;
4117 }
4118 }
4119 else
4120 {
4121 // Write cached secondary DDT table if any
4122 if(ctx->dirty_secondary_ddt)
4123 {
4124 res = write_cached_secondary_ddt(ctx);
4125 if(res != AARUF_STATUS_OK) return res;
4126 }
4127
4128 // Write primary DDT table (multi-level) if applicable
4129 if(ctx->dirty_primary_ddt)
4130 {
4131 res = write_primary_ddt(ctx);
4132 if(res != AARUF_STATUS_OK) return res;
4133 }
4134
4135 // Write single-level DDT table if applicable
4136 if(ctx->dirty_single_level_ddt)
4137 {
4138 res = write_single_level_ddt(ctx);
4139 if(res != AARUF_STATUS_OK) return res;
4140 }
4141 }
4142
4143 // Finalize checksums and write checksum block
4145
4146 // Write tracks block
4148
4149 // Write MODE 2 subheader data block
4151
4152 // Write CD sector prefix data block
4154
4155 // Write sector prefix DDT (statuses + optional indexes)
4157
4158 // Write CD sector suffix data block (EDC/ECC captures)
4160
4161 // Write sector prefix DDT (EDC/ECC captures)
4163
4164 // Write sector subchannel data block
4166
4167 // Write DVD long sector data blocks
4169
4170 // Write DVD decrypted title keys
4172
4173 // Write media tags data blocks
4174 if(ctx->dirty_media_tags) write_media_tags(ctx);
4175
4176 // Write tape files
4178
4179 // Write tape partitions
4181
4182 // Write geometry block if any
4184
4185 // Write metadata block
4187
4188 // Write dump hardware block if any
4190
4191 // Write CICM XML block if any
4192 if(ctx->dirty_cicm_block) write_cicm_block(ctx);
4193
4194 // Write Aaru metadata JSON block if any
4196
4197 // Write the complete index at the end of the file
4198 if(ctx->dirty_index_block)
4199 {
4200 res = write_index_block(ctx);
4201 if(res != AARUF_STATUS_OK) return res;
4202 }
4203
4204 if(ctx->deduplicate && ctx->sector_hash_map != NULL)
4205 {
4206 TRACE("Clearing sector hash map");
4207 // Clear sector hash map
4209 ctx->sector_hash_map = NULL;
4210 }
4211 }
4212
4213 TRACE("Freeing memory pointers");
4214 // This may do nothing if imageStream is NULL, but as the behaviour is undefined, better sure than sorry
4215 if(ctx->imageStream != NULL)
4216 {
4217 fclose(ctx->imageStream);
4218 ctx->imageStream = NULL;
4219 }
4220
4221 // Free index entries array
4222 if(ctx->index_entries != NULL)
4223 {
4224 utarray_free(ctx->index_entries);
4225 ctx->index_entries = NULL;
4226 }
4227
4228 free(ctx->sector_prefix);
4229 ctx->sector_prefix = NULL;
4230 free(ctx->sector_prefix_corrected);
4231 ctx->sector_prefix_corrected = NULL;
4232 free(ctx->sector_suffix);
4233 ctx->sector_suffix = NULL;
4234 free(ctx->sector_suffix_corrected);
4235 ctx->sector_suffix_corrected = NULL;
4236 free(ctx->sector_subchannel);
4237 ctx->sector_subchannel = NULL;
4238 free(ctx->mode2_subheaders);
4239 ctx->mode2_subheaders = NULL;
4240
4241 TRACE("Freeing media tags");
4242 if(ctx->mediaTags != NULL) HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
4243 {
4244 HASH_DEL(ctx->mediaTags, media_tag);
4245 free(media_tag->data);
4246 free(media_tag);
4247 }
4248
4249#ifdef __linux__ // TODO: Implement
4250 TRACE("Unmapping user data DDT if it is not in memory");
4251 if(!ctx->in_memory_ddt)
4252 {
4253 munmap(ctx->user_data_ddt, ctx->mapped_memory_ddt_size);
4254 ctx->user_data_ddt = NULL;
4255 }
4256#endif
4257
4258 free(ctx->sector_prefix_ddt2);
4259 ctx->sector_prefix_ddt2 = NULL;
4260 free(ctx->sector_prefix_ddt);
4261 ctx->sector_prefix_ddt = NULL;
4262 free(ctx->sector_suffix_ddt2);
4263 ctx->sector_suffix_ddt2 = NULL;
4264 free(ctx->sector_suffix_ddt);
4265 ctx->sector_suffix_ddt = NULL;
4266
4267 free(ctx->metadata_block);
4268 ctx->metadata_block = NULL;
4269 free(ctx->track_entries);
4270 ctx->track_entries = NULL;
4271 free(ctx->cicm_block);
4272 ctx->cicm_block = NULL;
4273
4274 if(ctx->dump_hardware_entries_with_data != NULL)
4275 {
4276 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
4277 {
4296 }
4298 }
4299
4300 free(ctx->readableSectorTags);
4301 ctx->readableSectorTags = NULL;
4302
4303 free(ctx->ecc_cd_context);
4304 ctx->ecc_cd_context = NULL;
4305
4306 free(ctx->checksums.spamsum);
4307 ctx->checksums.spamsum = NULL;
4308
4309 free(ctx->sector_id);
4310 free(ctx->sector_ied);
4311 free(ctx->sector_cpr_mai);
4312 free(ctx->sector_edc);
4313
4314 // TODO: Free caches
4315
4316 free(context);
4317
4318 TRACE("Exiting aaruf_close() = 0");
4319 return 0;
4320}
void write_dvd_long_sector_blocks(aaruformat_context *ctx)
Serialize DVD long sector auxiliary data blocks to the image file.
Definition close.c:1851
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:3737
static void write_sector_subchannel(aaruformat_context *ctx)
Serialize the per-sector subchannel or tag data block.
Definition close.c:1547
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:3926
static void write_media_tags(aaruformat_context *ctx)
Serialize all accumulated media tags to the image file.
Definition close.c:2462
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:2296
int aaruf_close(void *context)
Close an Aaru image context, flushing pending data structures and releasing resources.
Definition close.c:4059
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:2724
static void write_dumphw_block(aaruformat_context *ctx)
Serialize the dump hardware block containing acquisition environment information.
Definition close.c:3506
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:1121
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:1387
static void write_tracks_block(aaruformat_context *ctx)
Serialize the tracks metadata block and add it to the index.
Definition close.c:826
static void write_aaru_json_block(aaruformat_context *ctx)
Serialize the Aaru metadata JSON block to the image file.
Definition close.c:3875
static void write_geometry_block(aaruformat_context *ctx)
Serialize the geometry metadata block to the image file.
Definition close.c:3083
static void write_mode2_subheaders_block(aaruformat_context *ctx)
Serialize a MODE 2 (XA) subheaders data block.
Definition close.c:879
static void write_tape_partition_block(aaruformat_context *ctx)
Serialize the tape partition metadata block to the image file.
Definition close.c:2957
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:1241
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:3220
static void write_sector_prefix(aaruformat_context *ctx)
Serialize the optional CD sector prefix block.
Definition close.c:997
#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:197
#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:147
@ ChecksumBlock
Block containing contents checksums.
Definition enums.h:152
@ DataBlock
Block containing data.
Definition enums.h:141
@ TapePartitionBlock
Block containing list of partitions for a tape image.
Definition enums.h:158
@ GeometryBlock
Block containing logical geometry.
Definition enums.h:148
@ DeDuplicationTableSecondary
Block containing a secondary deduplication table (v2).
Definition enums.h:144
@ AaruMetadataJsonBlock
Block containing JSON version of Aaru Metadata.
Definition enums.h:159
@ CicmBlock
Block containing CICM XML metadata.
Definition enums.h:151
@ DeDuplicationTable2
Block containing a deduplication table v2.
Definition enums.h:143
@ TapeFileBlock
Block containing list of files for a tape image.
Definition enums.h:157
@ DumpHardwareBlock
Block containing an array of hardware used to create the image.
Definition enums.h:156
@ MetadataBlock
Block containing metadata.
Definition enums.h:149
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:150
@ OpticalDisc
Purely optical discs.
Definition enums.h:218
@ BlockMedia
Media that is physically block-based or abstracted like that.
Definition enums.h:219
@ Blake3
BLAKE3 hash.
Definition enums.h:173
@ Sha1
SHA-1 hash.
Definition enums.h:170
@ Md5
MD5 hash.
Definition enums.h:169
@ Sha256
SHA-256 hash.
Definition enums.h:171
@ SpamSum
SpamSum (context-triggered piecewise hash).
Definition enums.h:172
@ 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:265
@ AARUF_STATUS_INVALID_CONTEXT
Provided context/handle is invalid.
Definition enums.h:209
@ 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