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
77 static int32_t write_cached_secondary_ddt(aaruformat_context *ctx)
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,
104 ctx->user_data_ddt_header.blockAlignmentShift);
105 }
106
107 // Prepare DDT header for the cached table
108 DdtHeader2 ddt_header = {0};
109 ddt_header.identifier = DeDuplicationTableSecondary;
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;
117 ddt_header.blockAlignmentShift = ctx->user_data_ddt_header.blockAlignmentShift;
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");
154 return AARUF_ERROR_NOT_ENOUGH_MEMORY;
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 &&
212 entry->blockType == DeDuplicationTableSecondary)
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;
223 new_ddt_entry.blockType = DeDuplicationTableSecondary;
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 TRACE("Added new DDT index entry at offset %" PRIu64, end_of_file);
229
230 // Write the updated primary table back to its original position in the file
231 long saved_pos = ftell(ctx->imageStream);
232 fseek(ctx->imageStream, ctx->primary_ddt_offset + sizeof(DdtHeader2), SEEK_SET);
233
234 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
235
236 size_t primary_written_bytes = 0;
237 primary_written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
238
239 if(primary_written_bytes != 1)
240 {
241 TRACE("Could not flush primary DDT table to file.");
242 return AARUF_ERROR_CANNOT_WRITE_HEADER;
243 }
244
245 fseek(ctx->imageStream, saved_pos, SEEK_SET);
246 }
247 else
248 TRACE("Failed to write cached secondary DDT data");
249 }
250 else
251 TRACE("Failed to write cached secondary DDT header");
252
253 // Free the cached table
254 free(ctx->cached_secondary_ddt2);
255 ctx->cached_secondary_ddt2 = NULL;
256 ctx->cached_ddt_offset = 0;
257
258 // Set position
259 fseek(ctx->imageStream, 0, SEEK_END);
260
261 if(ddt_header.compression == Lzma) free(buffer);
262
263 return AARUF_STATUS_OK;
264}
265
283 static int32_t write_primary_ddt(aaruformat_context *ctx)
284{
285 // Write the cached primary DDT table back to its position in the file
286 if(ctx->user_data_ddt_header.tableShift <= 0 || ctx->user_data_ddt2 == NULL) return AARUF_STATUS_OK;
287
288 TRACE("Writing cached primary DDT table back to file");
289
290 // Calculate CRC64 of the primary DDT table data first
291 crc64_ctx *crc64_context = aaruf_crc64_init();
292 if(crc64_context != NULL)
293 {
294 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
295
296 aaruf_crc64_update(crc64_context, (uint8_t *)ctx->user_data_ddt2, primary_table_size);
297
298 uint64_t crc64;
299 aaruf_crc64_final(crc64_context, &crc64);
300
301 // Properly populate all header fields for multi-level DDT primary table
302 ctx->user_data_ddt_header.identifier = DeDuplicationTable2;
303 ctx->user_data_ddt_header.type = UserData;
304 ctx->user_data_ddt_header.compression = None;
305 // levels, tableLevel, previousLevelOffset, negative, overflow, blockAlignmentShift,
306 // dataShift, tableShift, sizeType, entries, blocks, start are already set during creation
307 ctx->user_data_ddt_header.crc64 = crc64;
308 ctx->user_data_ddt_header.cmpCrc64 = crc64;
309 ctx->user_data_ddt_header.length = primary_table_size;
310 ctx->user_data_ddt_header.cmpLength = primary_table_size;
311
312 TRACE("Calculated CRC64 for primary DDT: 0x%16lX", crc64);
313 }
314
315 // First write the DDT header
316 fseek(ctx->imageStream, ctx->primary_ddt_offset, SEEK_SET);
317
318 size_t headerWritten = fwrite(&ctx->user_data_ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
319 if(headerWritten != 1)
320 {
321 TRACE("Failed to write primary DDT header to file");
322 return AARUF_ERROR_CANNOT_WRITE_HEADER;
323 }
324
325 // Then write the table data (position is already after the header)
326 size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
327
328 // Write the primary table data
329 size_t written_bytes = 0;
330 written_bytes = fwrite(ctx->user_data_ddt2, primary_table_size, 1, ctx->imageStream);
331
332 if(written_bytes == 1)
333 {
334 TRACE("Successfully wrote primary DDT header and table to file (%" PRIu64 " entries, %zu bytes)",
335 ctx->user_data_ddt_header.entries, primary_table_size);
336
337 // Add primary DDT to index
338 TRACE("Adding primary DDT to index");
339 IndexEntry primary_ddt_entry;
340 primary_ddt_entry.blockType = DeDuplicationTable2;
341 primary_ddt_entry.dataType = UserData;
342 primary_ddt_entry.offset = ctx->primary_ddt_offset;
343
344 utarray_push_back(ctx->index_entries, &primary_ddt_entry);
345 TRACE("Added primary DDT index entry at offset %" PRIu64, ctx->primary_ddt_offset);
346 }
347 else
348 TRACE("Failed to write primary DDT table to file");
349
350 return AARUF_STATUS_OK;
351}
352
369 static int32_t write_single_level_ddt(aaruformat_context *ctx)
370{
371 // Write the single level DDT table block aligned just after the header
372 if(ctx->user_data_ddt_header.tableShift != 0 || ctx->user_data_ddt2 == NULL) return AARUF_STATUS_OK;
373
374 TRACE("Writing single-level DDT table to file");
375
376 // Calculate CRC64 of the primary DDT table data
377 const size_t primary_table_size = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
378
379 // Properly populate all header fields
380 ctx->user_data_ddt_header.identifier = DeDuplicationTable2;
381 ctx->user_data_ddt_header.type = UserData;
382 ctx->user_data_ddt_header.compression = ctx->compression_enabled ? Lzma : None;
383 ctx->user_data_ddt_header.levels = 1; // Single level
384 ctx->user_data_ddt_header.tableLevel = 0; // Top level
385 ctx->user_data_ddt_header.previousLevelOffset = 0; // No previous level for single-level DDT
386 // negative and overflow are already set during creation
387 // blockAlignmentShift, dataShift, tableShift, sizeType, entries, blocks, start are already set
388 ctx->user_data_ddt_header.length = primary_table_size;
389 ctx->user_data_ddt_header.cmpLength = primary_table_size;
390
391 ctx->user_data_ddt_header.crc64 = aaruf_crc64_data((uint8_t *)ctx->user_data_ddt2, primary_table_size);
392
393 TRACE("Calculated CRC64 for single-level DDT: 0x%16lX", ctx->user_data_ddt_header.crc64);
394
395 uint8_t *cmp_buffer = NULL;
396 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
397
398 if(ctx->user_data_ddt_header.compression == None)
399 {
400
401 cmp_buffer = (uint8_t *)ctx->user_data_ddt2;
402 ctx->user_data_ddt_header.cmpCrc64 = ctx->user_data_ddt_header.crc64;
403 }
404 else
405 {
406 cmp_buffer = malloc((size_t)ctx->user_data_ddt_header.length * 2); // Allocate double size for compression
407 if(cmp_buffer == NULL)
408 {
409 TRACE("Failed to allocate memory for secondary DDT v2 compression");
410 return AARUF_ERROR_NOT_ENOUGH_MEMORY;
411 }
412
413 size_t dst_size = (size_t)ctx->user_data_ddt_header.length * 2 * 2;
414 size_t props_size = LZMA_PROPERTIES_LENGTH;
415 aaruf_lzma_encode_buffer(cmp_buffer, &dst_size, (uint8_t *)ctx->user_data_ddt2,
416 ctx->user_data_ddt_header.length, lzma_properties, &props_size, 9, ctx->lzma_dict_size,
417 4, 0, 2, 273, 8);
418
419 ctx->user_data_ddt_header.cmpLength = (uint32_t)dst_size;
420
421 if(ctx->user_data_ddt_header.cmpLength >= ctx->user_data_ddt_header.length)
422 {
423 ctx->user_data_ddt_header.compression = None;
424 free(cmp_buffer);
425
426 cmp_buffer = (uint8_t *)ctx->user_data_ddt2;
427 }
428 }
429
430 if(ctx->user_data_ddt_header.compression == None)
431 {
432 ctx->user_data_ddt_header.cmpLength = ctx->user_data_ddt_header.length;
433 ctx->user_data_ddt_header.cmpCrc64 = ctx->user_data_ddt_header.crc64;
434 }
435 else
436 ctx->user_data_ddt_header.cmpCrc64 =
437 aaruf_crc64_data(cmp_buffer, (uint32_t)ctx->user_data_ddt_header.cmpLength);
438
439 if(ctx->user_data_ddt_header.compression == Lzma) ctx->user_data_ddt_header.cmpLength += LZMA_PROPERTIES_LENGTH;
440
441 // Write the DDT header first
442 fseek(ctx->imageStream, 0, SEEK_END);
443 long ddt_position = ftell(ctx->imageStream);
444 // Align index position to block boundary if needed
445 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
446 if(ddt_position & alignment_mask)
447 {
448 const uint64_t aligned_position = ddt_position + alignment_mask & ~alignment_mask;
449 fseek(ctx->imageStream, aligned_position, SEEK_SET);
450 ddt_position = aligned_position;
451 }
452
453 const size_t header_written = fwrite(&ctx->user_data_ddt_header, sizeof(DdtHeader2), 1, ctx->imageStream);
454 if(header_written != 1)
455 {
456 TRACE("Failed to write single-level DDT header to file");
457 return AARUF_ERROR_CANNOT_WRITE_HEADER;
458 }
459
460 // Write the primary table data
461 size_t written_bytes = 0;
462 if(ctx->user_data_ddt_header.compression == Lzma)
463 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
464
465 written_bytes = fwrite(cmp_buffer, ctx->user_data_ddt_header.cmpLength, 1, ctx->imageStream);
466
467 if(written_bytes == 1)
468 {
469 TRACE("Successfully wrote single-level DDT header and table to file (%" PRIu64
470 " entries, %zu bytes, %zu compressed bytes)",
471 ctx->user_data_ddt_header.entries, ctx->user_data_ddt_header.length, ctx->user_data_ddt_header.cmpLength);
472
473 // Add single-level DDT to index
474 TRACE("Adding single-level DDT to index");
475 IndexEntry single_ddt_entry;
476 single_ddt_entry.blockType = DeDuplicationTable2;
477 single_ddt_entry.dataType = UserData;
478 single_ddt_entry.offset = ddt_position;
479
480 utarray_push_back(ctx->index_entries, &single_ddt_entry);
481 TRACE("Added single-level DDT index entry at offset %" PRIu64, ddt_position);
482 }
483 else
484 TRACE("Failed to write single-level DDT table data to file");
485
486 return AARUF_STATUS_OK;
487}
488
596 static int32_t write_tape_ddt(aaruformat_context *ctx)
597{
598 if(!ctx->is_tape) return AARUF_STATUS_INVALID_CONTEXT;
599
600 // Traverse the tape DDT uthash and find the biggest key
601 uint64_t max_key = 0;
602 TapeDdtHashEntry *entry, *tmp;
603 HASH_ITER(hh, ctx->tape_ddt, entry, tmp)
604 if(entry->key > max_key) max_key = entry->key;
605
606 // Initialize context user data DDT header
607 ctx->user_data_ddt_header.identifier = DeDuplicationTable2;
608 ctx->user_data_ddt_header.type = UserData;
609 ctx->user_data_ddt_header.compression = ctx->compression_enabled ? Lzma : None;
610 ctx->user_data_ddt_header.levels = 1; // Single level
611 ctx->user_data_ddt_header.tableLevel = 0; // Top level
612 ctx->user_data_ddt_header.previousLevelOffset = 0; // No previous level for single-level DDT
613 ctx->user_data_ddt_header.negative = 0;
614 ctx->user_data_ddt_header.overflow = 0;
615 ctx->user_data_ddt_header.tableShift = 0; // Single level
616 ctx->user_data_ddt_header.entries = max_key + 1;
617 ctx->user_data_ddt_header.blocks = max_key + 1;
618 ctx->user_data_ddt_header.start = 0;
619 ctx->user_data_ddt_header.length = ctx->user_data_ddt_header.entries * sizeof(uint64_t);
620 ctx->user_data_ddt_header.cmpLength = ctx->user_data_ddt_header.length;
621
622 // Initialize memory for user data DDT
623 ctx->user_data_ddt2 = calloc(ctx->user_data_ddt_header.entries, sizeof(uint64_t));
624 if(ctx->user_data_ddt2 == NULL)
625 {
626 TRACE("Failed to allocate memory for tape DDT table");
627 return AARUF_ERROR_NOT_ENOUGH_MEMORY;
628 }
629
630 // Populate user data DDT from tape DDT uthash
631 HASH_ITER(hh, ctx->tape_ddt, entry, tmp)
632 if(entry->key < ctx->user_data_ddt_header.blocks) ctx->user_data_ddt2[entry->key] = entry->value;
633
634 // Do not repeat code
635 return write_single_level_ddt(ctx);
636}
637
654 static void write_checksum_block(aaruformat_context *ctx)
655{
656 uint64_t alignment_mask;
657 uint64_t aligned_position;
658
659 // Finalize pending checksums
660 if(ctx->calculating_md5)
661 {
662 ctx->checksums.hasMd5 = true;
663 aaruf_md5_final(&ctx->md5_context, ctx->checksums.md5);
664 }
665 if(ctx->calculating_sha1)
666 {
667 ctx->checksums.hasSha1 = true;
668 aaruf_sha1_final(&ctx->sha1_context, ctx->checksums.sha1);
669 }
670 if(ctx->calculating_sha256)
671 {
672 ctx->checksums.hasSha256 = true;
673 aaruf_sha256_final(&ctx->sha256_context, ctx->checksums.sha256);
674 }
675 if(ctx->calculating_spamsum)
676 {
677 ctx->checksums.hasSpamSum = true;
678 ctx->checksums.spamsum = calloc(1, FUZZY_MAX_RESULT);
679 aaruf_spamsum_final(ctx->spamsum_context, ctx->checksums.spamsum);
680 aaruf_spamsum_free(ctx->spamsum_context);
681 }
682 if(ctx->calculating_blake3)
683 {
684 ctx->checksums.hasBlake3 = true;
685 blake3_hasher_finalize(ctx->blake3_context, ctx->checksums.blake3, BLAKE3_OUT_LEN);
686 free(ctx->blake3_context);
687 }
688
689 // Write the checksums block
690 bool has_checksums = ctx->checksums.hasMd5 || ctx->checksums.hasSha1 || ctx->checksums.hasSha256 ||
691 ctx->checksums.hasSpamSum || ctx->checksums.hasBlake3;
692
693 if(!has_checksums) return;
694
695 ChecksumHeader checksum_header = {0};
696 checksum_header.identifier = ChecksumBlock;
697
698 fseek(ctx->imageStream, 0, SEEK_END);
699 long checksum_position = ftell(ctx->imageStream);
700 // Align index position to block boundary if needed
701 alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
702 if(checksum_position & alignment_mask)
703 {
704 aligned_position = checksum_position + alignment_mask & ~alignment_mask;
705 fseek(ctx->imageStream, aligned_position, SEEK_SET);
706 checksum_position = aligned_position;
707 }
708
709 // Skip checksum_header
710 fseek(ctx->imageStream, sizeof(checksum_header), SEEK_CUR);
711
712 if(ctx->checksums.hasMd5)
713 {
714 TRACE("Writing MD5 checksum entry");
715 ChecksumEntry md5_entry = {0};
716 md5_entry.length = MD5_DIGEST_LENGTH;
717 md5_entry.type = Md5;
718 fwrite(&md5_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
719 fwrite(&ctx->checksums.md5, MD5_DIGEST_LENGTH, 1, ctx->imageStream);
720 checksum_header.length += sizeof(ChecksumEntry) + MD5_DIGEST_LENGTH;
721 checksum_header.entries++;
722 }
723
724 if(ctx->checksums.hasSha1)
725 {
726 TRACE("Writing SHA1 checksum entry");
727 ChecksumEntry sha1_entry = {0};
728 sha1_entry.length = SHA1_DIGEST_LENGTH;
729 sha1_entry.type = Sha1;
730 fwrite(&sha1_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
731 fwrite(&ctx->checksums.sha1, SHA1_DIGEST_LENGTH, 1, ctx->imageStream);
732 checksum_header.length += sizeof(ChecksumEntry) + SHA1_DIGEST_LENGTH;
733 checksum_header.entries++;
734 }
735
736 if(ctx->checksums.hasSha256)
737 {
738 TRACE("Writing SHA256 checksum entry");
739 ChecksumEntry sha256_entry = {0};
740 sha256_entry.length = SHA256_DIGEST_LENGTH;
741 sha256_entry.type = Sha256;
742 fwrite(&sha256_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
743 fwrite(&ctx->checksums.sha256, SHA256_DIGEST_LENGTH, 1, ctx->imageStream);
744 checksum_header.length += sizeof(ChecksumEntry) + SHA256_DIGEST_LENGTH;
745 checksum_header.entries++;
746 }
747
748 if(ctx->checksums.hasSpamSum)
749 {
750 TRACE("Writing SpamSum checksum entry");
751 ChecksumEntry spamsum_entry = {0};
752 spamsum_entry.length = strlen((const char *)ctx->checksums.spamsum);
753 spamsum_entry.type = SpamSum;
754 fwrite(&spamsum_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
755 fwrite(ctx->checksums.spamsum, spamsum_entry.length, 1, ctx->imageStream);
756 checksum_header.length += sizeof(ChecksumEntry) + spamsum_entry.length;
757 checksum_header.entries++;
758 }
759
760 if(ctx->checksums.hasBlake3)
761 {
762 TRACE("Writing BLAKE3 checksum entry");
763 ChecksumEntry blake3_entry = {0};
764 blake3_entry.length = BLAKE3_OUT_LEN;
765 blake3_entry.type = Blake3;
766 fwrite(&blake3_entry, sizeof(ChecksumEntry), 1, ctx->imageStream);
767 fwrite(&ctx->checksums.blake3, BLAKE3_OUT_LEN, 1, ctx->imageStream);
768 checksum_header.length += sizeof(ChecksumEntry) + BLAKE3_OUT_LEN;
769 checksum_header.entries++;
770 ctx->header.featureCompatible |= AARU_FEATURE_RW_BLAKE3;
771 }
772
773 fseek(ctx->imageStream, checksum_position, SEEK_SET);
774 TRACE("Writing checksum header");
775 fwrite(&checksum_header, sizeof(ChecksumHeader), 1, ctx->imageStream);
776
777 // Add checksum block to index
778 TRACE("Adding checksum block to index");
779 IndexEntry checksum_index_entry;
780 checksum_index_entry.blockType = ChecksumBlock;
781 checksum_index_entry.dataType = 0;
782 checksum_index_entry.offset = checksum_position;
783
784 utarray_push_back(ctx->index_entries, &checksum_index_entry);
785 TRACE("Added checksum block index entry at offset %" PRIu64, checksum_position);
786}
787
798 static void write_tracks_block(aaruformat_context *ctx)
799{
800 // Write tracks block
801 if(ctx->tracks_header.entries <= 0 || ctx->track_entries == NULL) return;
802
803 fseek(ctx->imageStream, 0, SEEK_END);
804 long tracks_position = ftell(ctx->imageStream);
805 // Align index position to block boundary if needed
806 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
807 if(tracks_position & alignment_mask)
808 {
809 uint64_t aligned_position = tracks_position + alignment_mask & ~alignment_mask;
810 fseek(ctx->imageStream, aligned_position, SEEK_SET);
811 tracks_position = aligned_position;
812 }
813
814 TRACE("Writing tracks block at position %ld", tracks_position);
815 // Write header
816 if(fwrite(&ctx->tracks_header, sizeof(TracksHeader), 1, ctx->imageStream) == 1)
817 {
818 // Write entries
819 size_t written_entries =
820 fwrite(ctx->track_entries, sizeof(TrackEntry), ctx->tracks_header.entries, ctx->imageStream);
821
822 if(written_entries == ctx->tracks_header.entries)
823 {
824 TRACE("Successfully wrote tracks block with %u entries", ctx->tracks_header.entries);
825 // Add tracks block to index
826 TRACE("Adding tracks block to index");
827
828 IndexEntry tracks_index_entry;
829 tracks_index_entry.blockType = TracksBlock;
830 tracks_index_entry.dataType = 0;
831 tracks_index_entry.offset = tracks_position;
832 utarray_push_back(ctx->index_entries, &tracks_index_entry);
833 TRACE("Added tracks block index entry at offset %" PRIu64, tracks_position);
834 }
835 }
836}
837
850 static void write_mode2_subheaders_block(aaruformat_context *ctx)
851{
852 // Write MODE 2 subheader data block
853 if(ctx->mode2_subheaders == NULL) return;
854
855 fseek(ctx->imageStream, 0, SEEK_END);
856 long mode2_subheaders_position = ftell(ctx->imageStream);
857 // Align index position to block boundary if needed
858 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
859 if(mode2_subheaders_position & alignment_mask)
860 {
861 uint64_t aligned_position = mode2_subheaders_position + alignment_mask & ~alignment_mask;
862 fseek(ctx->imageStream, aligned_position, SEEK_SET);
863 mode2_subheaders_position = aligned_position;
864 }
865
866 TRACE("Writing MODE 2 subheaders block at position %ld", mode2_subheaders_position);
867 BlockHeader subheaders_block = {0};
868 subheaders_block.identifier = DataBlock;
869 subheaders_block.type = CompactDiscMode2Subheader;
870 subheaders_block.compression = ctx->compression_enabled ? Lzma : None;
871 subheaders_block.length =
872 (uint32_t)(ctx->user_data_ddt_header.negative + ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) *
873 8;
874
875 // Calculate CRC64
876 subheaders_block.crc64 = aaruf_crc64_data(ctx->mode2_subheaders, subheaders_block.length);
877
878 uint8_t *buffer = NULL;
879 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
880
881 if(subheaders_block.compression == None)
882 {
883 buffer = ctx->mode2_subheaders;
884 subheaders_block.cmpCrc64 = subheaders_block.crc64;
885 }
886 else
887 {
888 buffer = malloc((size_t)subheaders_block.length * 2); // Allocate double size for compression
889 if(buffer == NULL)
890 {
891 TRACE("Failed to allocate memory for MODE 2 subheaders compression");
892 return;
893 }
894
895 size_t dst_size = (size_t)subheaders_block.length * 2 * 2;
896 size_t props_size = LZMA_PROPERTIES_LENGTH;
897 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->mode2_subheaders, subheaders_block.length, lzma_properties,
898 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
899
900 subheaders_block.cmpLength = (uint32_t)dst_size;
901
902 if(subheaders_block.cmpLength >= subheaders_block.length)
903 {
904 subheaders_block.compression = None;
905 free(buffer);
906 buffer = ctx->mode2_subheaders;
907 }
908 }
909
910 if(subheaders_block.compression == None)
911 {
912 subheaders_block.cmpLength = subheaders_block.length;
913 subheaders_block.cmpCrc64 = subheaders_block.crc64;
914 }
915 else
916 subheaders_block.cmpCrc64 = aaruf_crc64_data(buffer, subheaders_block.cmpLength);
917
918 if(subheaders_block.compression == Lzma) subheaders_block.cmpLength += LZMA_PROPERTIES_LENGTH;
919
920 // Write header
921 if(fwrite(&subheaders_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
922 {
923 if(subheaders_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
924
925 // Write data
926 size_t written_bytes = fwrite(buffer, subheaders_block.cmpLength, 1, ctx->imageStream);
927 if(written_bytes == 1)
928 {
929 TRACE("Successfully wrote MODE 2 subheaders block (%" PRIu64 " bytes)", subheaders_block.cmpLength);
930 // Add MODE 2 subheaders block to index
931 TRACE("Adding MODE 2 subheaders block to index");
932 IndexEntry mode2_subheaders_index_entry;
933 mode2_subheaders_index_entry.blockType = DataBlock;
934 mode2_subheaders_index_entry.dataType = CompactDiscMode2Subheader;
935 mode2_subheaders_index_entry.offset = mode2_subheaders_position;
936 utarray_push_back(ctx->index_entries, &mode2_subheaders_index_entry);
937 TRACE("Added MODE 2 subheaders block index entry at offset %" PRIu64, mode2_subheaders_position);
938 }
939 }
940
941 if(subheaders_block.compression == Lzma) free(buffer);
942}
943
966 static void write_sector_prefix(aaruformat_context *ctx)
967{
968 if(ctx->sector_prefix == NULL) return;
969
970 fseek(ctx->imageStream, 0, SEEK_END);
971 long prefix_position = ftell(ctx->imageStream);
972 // Align index position to block boundary if needed
973 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
974 if(prefix_position & alignment_mask)
975 {
976 uint64_t aligned_position = prefix_position + alignment_mask & ~alignment_mask;
977 fseek(ctx->imageStream, aligned_position, SEEK_SET);
978 prefix_position = aligned_position;
979 }
980
981 TRACE("Writing sector prefix block at position %ld", prefix_position);
982 BlockHeader prefix_block = {0};
983 prefix_block.identifier = DataBlock;
984 prefix_block.type = CdSectorPrefix;
985 prefix_block.compression = ctx->compression_enabled ? Lzma : None;
986 prefix_block.length = (uint32_t)ctx->sector_prefix_offset;
987
988 // Calculate CRC64
989 prefix_block.crc64 = aaruf_crc64_data(ctx->sector_prefix, prefix_block.length);
990
991 uint8_t *buffer = NULL;
992 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
993
994 if(prefix_block.compression == None)
995 {
996 buffer = ctx->sector_prefix;
997 prefix_block.cmpCrc64 = prefix_block.crc64;
998 }
999 else
1000 {
1001 buffer = malloc((size_t)prefix_block.length * 2); // Allocate double size for compression
1002 if(buffer == NULL)
1003 {
1004 TRACE("Failed to allocate memory for CD sector prefix compression");
1005 return;
1006 }
1007
1008 size_t dst_size = (size_t)prefix_block.length * 2 * 2;
1009 size_t props_size = LZMA_PROPERTIES_LENGTH;
1010 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_prefix, prefix_block.length, lzma_properties,
1011 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1012
1013 prefix_block.cmpLength = (uint32_t)dst_size;
1014
1015 if(prefix_block.cmpLength >= prefix_block.length)
1016 {
1017 prefix_block.compression = None;
1018 free(buffer);
1019 buffer = ctx->sector_prefix;
1020 }
1021 }
1022
1023 if(prefix_block.compression == None)
1024 {
1025 prefix_block.cmpLength = prefix_block.length;
1026 prefix_block.cmpCrc64 = prefix_block.crc64;
1027 }
1028 else
1029 prefix_block.cmpCrc64 = aaruf_crc64_data(buffer, prefix_block.cmpLength);
1030
1031 if(prefix_block.compression == Lzma) prefix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1032
1033 // Write header
1034 if(fwrite(&prefix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1035 {
1036 if(prefix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1037
1038 // Write data
1039 const size_t written_bytes = fwrite(buffer, prefix_block.cmpLength, 1, ctx->imageStream);
1040 if(written_bytes == 1)
1041 {
1042 TRACE("Successfully wrote CD sector prefix block (%" PRIu64 " bytes)", prefix_block.cmpLength);
1043 // Add prefix block to index
1044 TRACE("Adding CD sector prefix block to index");
1045 IndexEntry prefix_index_entry;
1046 prefix_index_entry.blockType = DataBlock;
1047 prefix_index_entry.dataType = CdSectorPrefix;
1048 prefix_index_entry.offset = prefix_position;
1049 utarray_push_back(ctx->index_entries, &prefix_index_entry);
1050 TRACE("Added CD sector prefix block index entry at offset %" PRIu64, prefix_position);
1051 }
1052 }
1053
1054 if(prefix_block.compression == Lzma) free(buffer);
1055}
1056
1088 static void write_sector_suffix(aaruformat_context *ctx)
1089{
1090 if(ctx->sector_suffix == NULL) return;
1091
1092 fseek(ctx->imageStream, 0, SEEK_END);
1093 long suffix_position = ftell(ctx->imageStream);
1094 // Align index position to block boundary if needed
1095 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1096 if(suffix_position & alignment_mask)
1097 {
1098 const uint64_t aligned_position = suffix_position + alignment_mask & ~alignment_mask;
1099 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1100 suffix_position = aligned_position;
1101 }
1102
1103 TRACE("Writing sector suffix block at position %ld", suffix_position);
1104 BlockHeader suffix_block = {0};
1105 suffix_block.identifier = DataBlock;
1106 suffix_block.type = CdSectorSuffix;
1107 suffix_block.compression = ctx->compression_enabled ? Lzma : None;
1108 suffix_block.length = (uint32_t)ctx->sector_suffix_offset;
1109
1110 // Calculate CRC64
1111 suffix_block.crc64 = aaruf_crc64_data(ctx->sector_suffix, suffix_block.length);
1112
1113 uint8_t *buffer = NULL;
1114 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1115
1116 if(suffix_block.compression == None)
1117 {
1118 buffer = ctx->sector_suffix;
1119 suffix_block.cmpCrc64 = suffix_block.crc64;
1120 }
1121 else
1122 {
1123 buffer = malloc((size_t)suffix_block.length * 2); // Allocate double size for compression
1124 if(buffer == NULL)
1125 {
1126 TRACE("Failed to allocate memory for CD sector suffix compression");
1127 return;
1128 }
1129
1130 size_t dst_size = (size_t)suffix_block.length * 2 * 2;
1131 size_t props_size = LZMA_PROPERTIES_LENGTH;
1132 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_suffix, suffix_block.length, lzma_properties,
1133 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1134
1135 suffix_block.cmpLength = (uint32_t)dst_size;
1136
1137 if(suffix_block.cmpLength >= suffix_block.length)
1138 {
1139 suffix_block.compression = None;
1140 free(buffer);
1141 buffer = ctx->sector_suffix;
1142 }
1143 }
1144
1145 if(suffix_block.compression == None)
1146 {
1147 suffix_block.cmpLength = suffix_block.length;
1148 suffix_block.cmpCrc64 = suffix_block.crc64;
1149 }
1150 else
1151 suffix_block.cmpCrc64 = aaruf_crc64_data(buffer, suffix_block.cmpLength);
1152
1153 if(suffix_block.compression == Lzma) suffix_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1154
1155 // Write header
1156 if(fwrite(&suffix_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1157 {
1158 if(suffix_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1159
1160 // Write data
1161 const size_t written_bytes = fwrite(buffer, suffix_block.cmpLength, 1, ctx->imageStream);
1162 if(written_bytes == 1)
1163 {
1164 TRACE("Successfully wrote CD sector suffix block (%" PRIu64 " bytes)", suffix_block.cmpLength);
1165 // Add suffix block to index
1166 TRACE("Adding CD sector suffix block to index");
1167 IndexEntry suffix_index_entry;
1168 suffix_index_entry.blockType = DataBlock;
1169 suffix_index_entry.dataType = CdSectorSuffix;
1170 suffix_index_entry.offset = suffix_position;
1171 utarray_push_back(ctx->index_entries, &suffix_index_entry);
1172 TRACE("Added CD sector suffix block index entry at offset %" PRIu64, suffix_position);
1173 }
1174 }
1175
1176 if(suffix_block.compression == Lzma) free(buffer);
1177}
1178
1206 static void write_sector_prefix_ddt(aaruformat_context *ctx)
1207{
1208 if(ctx->sector_prefix_ddt2 == NULL) return;
1209
1210 fseek(ctx->imageStream, 0, SEEK_END);
1211 long prefix_ddt_position = ftell(ctx->imageStream);
1212 // Align index position to block boundary if needed
1213 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1214 if(prefix_ddt_position & alignment_mask)
1215 {
1216 const uint64_t aligned_position = prefix_ddt_position + alignment_mask & ~alignment_mask;
1217 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1218 prefix_ddt_position = aligned_position;
1219 }
1220
1221 TRACE("Writing sector prefix DDT v2 at position %ld", prefix_ddt_position);
1222 DdtHeader2 ddt_header2 = {0};
1223 ddt_header2.identifier = DeDuplicationTable2;
1224 ddt_header2.type = CdSectorPrefix;
1225 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1226 ddt_header2.levels = 1;
1227 ddt_header2.tableLevel = 0;
1228 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1229 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1230 ddt_header2.blockAlignmentShift = ctx->user_data_ddt_header.blockAlignmentShift;
1231 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1232 ddt_header2.tableShift = 0; // Single-level DDT
1233 ddt_header2.entries =
1234 ctx->image_info.Sectors + ctx->user_data_ddt_header.negative + ctx->user_data_ddt_header.overflow;
1235 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1236 ddt_header2.start = 0;
1237 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1238 // Calculate CRC64
1239 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_prefix_ddt2, (uint32_t)ddt_header2.length);
1240
1241 uint8_t *buffer = NULL;
1242 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1243
1244 if(ddt_header2.compression == None)
1245 {
1246 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1247 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1248 }
1249 else
1250 {
1251 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1252 if(buffer == NULL)
1253 {
1254 TRACE("Failed to allocate memory for sector prefix DDT v2 compression");
1255 return;
1256 }
1257
1258 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1259 size_t props_size = LZMA_PROPERTIES_LENGTH;
1260 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_prefix_ddt2, ddt_header2.length,
1261 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1262
1263 ddt_header2.cmpLength = (uint32_t)dst_size;
1264
1265 if(ddt_header2.cmpLength >= ddt_header2.length)
1266 {
1267 ddt_header2.compression = None;
1268 free(buffer);
1269 buffer = (uint8_t *)ctx->sector_prefix_ddt2;
1270 }
1271 }
1272
1273 if(ddt_header2.compression == None)
1274 {
1275 ddt_header2.cmpLength = ddt_header2.length;
1276 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1277 }
1278 else
1279 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1280
1281 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1282
1283 // Write header
1284 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1285 {
1286 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1287
1288 // Write data
1289 const size_t written_bytes = fwrite(buffer, ddt_header2.cmpLength, 1, ctx->imageStream);
1290 if(written_bytes == 1)
1291 {
1292 TRACE("Successfully wrote sector prefix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1293 // Add prefix block to index
1294 TRACE("Adding sector prefix DDT v2 to index");
1295 IndexEntry prefix_ddt_index_entry;
1296 prefix_ddt_index_entry.blockType = DeDuplicationTable2;
1297 prefix_ddt_index_entry.dataType = CdSectorPrefix;
1298 prefix_ddt_index_entry.offset = prefix_ddt_position;
1299 utarray_push_back(ctx->index_entries, &prefix_ddt_index_entry);
1300 TRACE("Added sector prefix DDT v2 index entry at offset %" PRIu64, prefix_ddt_position);
1301 }
1302 }
1303
1304 if(ddt_header2.compression == Lzma) free(buffer);
1305}
1306
1350 static void write_sector_suffix_ddt(aaruformat_context *ctx)
1351{
1352 if(ctx->sector_suffix_ddt2 == NULL) return;
1353
1354 fseek(ctx->imageStream, 0, SEEK_END);
1355 long suffix_ddt_position = ftell(ctx->imageStream);
1356 // Align index position to block boundary if needed
1357 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1358 if(suffix_ddt_position & alignment_mask)
1359 {
1360 const uint64_t aligned_position = suffix_ddt_position + alignment_mask & ~alignment_mask;
1361 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1362 suffix_ddt_position = aligned_position;
1363 }
1364
1365 TRACE("Writing sector suffix DDT v2 at position %ld", suffix_ddt_position);
1366 DdtHeader2 ddt_header2 = {0};
1367 ddt_header2.identifier = DeDuplicationTable2;
1368 ddt_header2.type = CdSectorSuffix;
1369 ddt_header2.compression = ctx->compression_enabled ? Lzma : None;
1370 ddt_header2.levels = 1;
1371 ddt_header2.tableLevel = 0;
1372 ddt_header2.negative = ctx->user_data_ddt_header.negative;
1373 ddt_header2.overflow = ctx->user_data_ddt_header.overflow;
1374 ddt_header2.blockAlignmentShift = ctx->user_data_ddt_header.blockAlignmentShift;
1375 ddt_header2.dataShift = ctx->user_data_ddt_header.dataShift;
1376 ddt_header2.tableShift = 0; // Single-level DDT
1377 ddt_header2.entries =
1378 ctx->image_info.Sectors + ctx->user_data_ddt_header.negative + ctx->user_data_ddt_header.overflow;
1379 ddt_header2.blocks = ctx->user_data_ddt_header.blocks;
1380 ddt_header2.start = 0;
1381 ddt_header2.length = ddt_header2.entries * sizeof(uint64_t);
1382 // Calculate CRC64
1383 ddt_header2.crc64 = aaruf_crc64_data((uint8_t *)ctx->sector_suffix_ddt2, (uint32_t)ddt_header2.length);
1384
1385 uint8_t *buffer = NULL;
1386 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1387
1388 if(ddt_header2.compression == None)
1389 {
1390 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1391 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1392 }
1393 else
1394 {
1395 buffer = malloc((size_t)ddt_header2.length * 2); // Allocate double size for compression
1396 if(buffer == NULL)
1397 {
1398 TRACE("Failed to allocate memory for sector suffix DDT v2 compression");
1399 return;
1400 }
1401
1402 size_t dst_size = (size_t)ddt_header2.length * 2 * 2;
1403 size_t props_size = LZMA_PROPERTIES_LENGTH;
1404 aaruf_lzma_encode_buffer(buffer, &dst_size, (uint8_t *)ctx->sector_suffix_ddt2, ddt_header2.length,
1405 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1406
1407 ddt_header2.cmpLength = (uint32_t)dst_size;
1408
1409 if(ddt_header2.cmpLength >= ddt_header2.length)
1410 {
1411 ddt_header2.compression = None;
1412 free(buffer);
1413 buffer = (uint8_t *)ctx->sector_suffix_ddt2;
1414 }
1415 }
1416
1417 if(ddt_header2.compression == None)
1418 {
1419 ddt_header2.cmpLength = ddt_header2.length;
1420 ddt_header2.cmpCrc64 = ddt_header2.crc64;
1421 }
1422 else
1423 ddt_header2.cmpCrc64 = aaruf_crc64_data(buffer, (uint32_t)ddt_header2.cmpLength);
1424
1425 if(ddt_header2.compression == Lzma) ddt_header2.cmpLength += LZMA_PROPERTIES_LENGTH;
1426
1427 // Write header
1428 if(fwrite(&ddt_header2, sizeof(DdtHeader2), 1, ctx->imageStream) == 1)
1429 {
1430 if(ddt_header2.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1431
1432 // Write data
1433 const size_t written_bytes = fwrite(buffer, ddt_header2.cmpLength, 1, ctx->imageStream);
1434 if(written_bytes == 1)
1435 {
1436 TRACE("Successfully wrote sector suffix DDT v2 (%" PRIu64 " bytes)", ddt_header2.cmpLength);
1437 // Add suffix block to index
1438 TRACE("Adding sector suffix DDT v2 to index");
1439 IndexEntry suffix_ddt_index_entry;
1440 suffix_ddt_index_entry.blockType = DeDuplicationTable2;
1441 suffix_ddt_index_entry.dataType = CdSectorSuffix;
1442 suffix_ddt_index_entry.offset = suffix_ddt_position;
1443 utarray_push_back(ctx->index_entries, &suffix_ddt_index_entry);
1444 TRACE("Added sector suffix DDT v2 index entry at offset %" PRIu64, suffix_ddt_position);
1445 }
1446 }
1447
1448 if(ddt_header2.compression == Lzma) free(buffer);
1449}
1450
1508 static void write_sector_subchannel(const aaruformat_context *ctx)
1509{
1510 if(ctx->sector_subchannel == NULL) return;
1511
1512 fseek(ctx->imageStream, 0, SEEK_END);
1513 long block_position = ftell(ctx->imageStream);
1514 // Align index position to block boundary if needed
1515 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1516 if(block_position & alignment_mask)
1517 {
1518 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
1519 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1520 block_position = aligned_position;
1521 }
1522
1523 TRACE("Writing sector subchannel block at position %ld", block_position);
1524 BlockHeader subchannel_block = {0};
1525 subchannel_block.identifier = DataBlock;
1526 subchannel_block.compression = None;
1527
1528 uint8_t *buffer = ctx->sector_subchannel;
1529 bool owns_buffer = false;
1530 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1531
1532 subchannel_block.cmpLength = subchannel_block.length;
1533
1534 if(ctx->image_info.MetadataMediaType == OpticalDisc)
1535 {
1536 subchannel_block.type = CdSectorSubchannel;
1537 subchannel_block.length = (uint32_t)(ctx->user_data_ddt_header.negative + ctx->image_info.Sectors +
1538 ctx->user_data_ddt_header.overflow) *
1539 96;
1540
1541 if(ctx->compression_enabled)
1542 {
1543 uint8_t *cst_buffer = malloc(subchannel_block.length);
1544
1545 if(cst_buffer == NULL)
1546 {
1547 TRACE("Failed to allocate memory for Claunia Subchannel Transform output");
1548 return;
1549 }
1550
1551 uint8_t *dst_buffer = malloc(subchannel_block.length);
1552
1553 if(dst_buffer == NULL)
1554 {
1555 TRACE("Failed to allocate memory for LZMA output");
1556 free(cst_buffer);
1557 return;
1558 }
1559
1560 aaruf_cst_transform(ctx->sector_subchannel, cst_buffer, subchannel_block.length);
1561 size_t dst_size = subchannel_block.length;
1562 size_t props_size = LZMA_PROPERTIES_LENGTH;
1563
1564 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, cst_buffer, subchannel_block.length, lzma_properties,
1565 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1566
1567 free(cst_buffer);
1568
1569 if(dst_size < subchannel_block.length)
1570 {
1571 subchannel_block.compression = LzmaClauniaSubchannelTransform;
1572 subchannel_block.cmpLength = (uint32_t)dst_size;
1573 buffer = dst_buffer;
1574 owns_buffer = true;
1575 }
1576 else
1577 {
1578 subchannel_block.compression = None;
1579 free(dst_buffer);
1580 subchannel_block.cmpLength = subchannel_block.length;
1581 }
1582 }
1583 }
1584 else if(ctx->image_info.MetadataMediaType == BlockMedia)
1585 {
1586 switch(ctx->image_info.MediaType)
1587 {
1588 case AppleProfile:
1589 case AppleFileWare:
1590 subchannel_block.type = AppleProfileTag;
1591 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 20;
1592 break;
1593 case AppleSonyDS:
1594 case AppleSonySS:
1595 subchannel_block.type = AppleSonyTag;
1596 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 12;
1597 break;
1598 case PriamDataTower:
1599 subchannel_block.type = PriamDataTowerTag;
1600 subchannel_block.length = (uint32_t)(ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) * 24;
1601 break;
1602 default:
1603 TRACE("Incorrect media type, not writing sector subchannel block");
1604 return; // Incorrect media type
1605 }
1606 subchannel_block.compression = Lzma;
1607
1608 uint8_t *dst_buffer = malloc(subchannel_block.length);
1609
1610 if(dst_buffer == NULL)
1611 {
1612 TRACE("Failed to allocate memory for LZMA output");
1613 return;
1614 }
1615
1616 size_t dst_size = subchannel_block.length;
1617 size_t props_size = LZMA_PROPERTIES_LENGTH;
1618
1619 aaruf_lzma_encode_buffer(dst_buffer, &dst_size, ctx->sector_subchannel, subchannel_block.length,
1620 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1621
1622 if(dst_size < subchannel_block.length)
1623 {
1624 subchannel_block.cmpLength = (uint32_t)dst_size;
1625 buffer = dst_buffer;
1626 owns_buffer = true;
1627 }
1628 else
1629 {
1630 subchannel_block.compression = None;
1631 free(dst_buffer);
1632 subchannel_block.cmpLength = subchannel_block.length;
1633 }
1634 }
1635 else
1636 {
1637 TRACE("Incorrect media type, not writing sector subchannel block");
1638 return; // Incorrect media type
1639 }
1640
1641 // Calculate CRC64 for raw subchannel data and compressed payload when present
1642 subchannel_block.crc64 = aaruf_crc64_data(ctx->sector_subchannel, subchannel_block.length);
1643 if(subchannel_block.compression == None)
1644 subchannel_block.cmpCrc64 = subchannel_block.crc64;
1645 else
1646 subchannel_block.cmpCrc64 = aaruf_crc64_data(buffer, subchannel_block.cmpLength);
1647
1648 if(subchannel_block.compression != None) subchannel_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1649
1650 // Write header
1651 if(fwrite(&subchannel_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1652 {
1653 if(subchannel_block.compression != None) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1654
1655 // Write data
1656 const size_t written_bytes = fwrite(buffer, subchannel_block.cmpLength, 1, ctx->imageStream);
1657 if(written_bytes == 1)
1658 {
1659 TRACE("Successfully wrote sector subchannel block (%" PRIu64 " bytes)", subchannel_block.cmpLength);
1660 // Add subchannel block to index
1661 TRACE("Adding sector subchannel block to index");
1662 IndexEntry subchannel_index_entry;
1663 subchannel_index_entry.blockType = DataBlock;
1664 subchannel_index_entry.dataType = subchannel_block.type;
1665 subchannel_index_entry.offset = block_position;
1666 utarray_push_back(ctx->index_entries, &subchannel_index_entry);
1667 TRACE("Added sector subchannel block index entry at offset %" PRIu64, block_position);
1668 }
1669 }
1670
1671 if(owns_buffer) free(buffer);
1672}
1673
1810 void write_dvd_long_sector_blocks(aaruformat_context *ctx)
1811{
1812 if(ctx->sector_id == NULL || ctx->sector_ied == NULL || ctx->sector_cpr_mai == NULL || ctx->sector_edc == NULL)
1813 return;
1814
1815 uint64_t total_sectors =
1816 ctx->user_data_ddt_header.negative + ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow;
1817
1818 // Write DVD sector ID block
1819 fseek(ctx->imageStream, 0, SEEK_END);
1820 long id_position = ftell(ctx->imageStream);
1821 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
1822 if(id_position & alignment_mask)
1823 {
1824 const uint64_t aligned_position = id_position + alignment_mask & ~alignment_mask;
1825 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1826 id_position = aligned_position;
1827 }
1828 TRACE("Writing DVD sector ID block at position %ld", id_position);
1829 BlockHeader id_block = {0};
1830 id_block.identifier = DataBlock;
1831 id_block.type = DvdSectorId;
1832 id_block.compression = ctx->compression_enabled ? Lzma : None;
1833 id_block.length = (uint32_t)total_sectors * 4;
1834
1835 // Calculate CRC64
1836 id_block.crc64 = aaruf_crc64_data(ctx->sector_id, id_block.length);
1837
1838 uint8_t *buffer = NULL;
1839 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
1840
1841 if(id_block.compression == None)
1842 {
1843 buffer = ctx->sector_id;
1844 id_block.cmpCrc64 = id_block.crc64;
1845 }
1846 else
1847 {
1848 buffer = malloc((size_t)id_block.length * 2); // Allocate double size for compression
1849 if(buffer == NULL)
1850 {
1851 TRACE("Failed to allocate memory for DVD sector ID compression");
1852 return;
1853 }
1854
1855 size_t dst_size = (size_t)id_block.length * 2 * 2;
1856 size_t props_size = LZMA_PROPERTIES_LENGTH;
1857 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_id, id_block.length, lzma_properties, &props_size, 9,
1858 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1859
1860 id_block.cmpLength = (uint32_t)dst_size;
1861
1862 if(id_block.cmpLength >= id_block.length)
1863 {
1864 id_block.compression = None;
1865 free(buffer);
1866 buffer = ctx->sector_id;
1867 }
1868 }
1869
1870 if(id_block.compression == None)
1871 {
1872 id_block.cmpLength = id_block.length;
1873 id_block.cmpCrc64 = id_block.crc64;
1874 }
1875 else
1876 id_block.cmpCrc64 = aaruf_crc64_data(buffer, id_block.cmpLength);
1877
1878 if(id_block.compression == Lzma) id_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1879
1880 // Write header
1881 if(fwrite(&id_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1882 {
1883 if(id_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1884
1885 // Write data
1886 const size_t written_bytes = fwrite(buffer, id_block.cmpLength, 1, ctx->imageStream);
1887 if(written_bytes == 1)
1888 {
1889 TRACE("Successfully wrote DVD sector ID block (%" PRIu64 " bytes)", id_block.cmpLength);
1890 // Add ID block to index
1891 TRACE("Adding DVD sector ID block to index");
1892 IndexEntry id_index_entry;
1893 id_index_entry.blockType = DataBlock;
1894 id_index_entry.dataType = DvdSectorId;
1895 id_index_entry.offset = id_position;
1896 utarray_push_back(ctx->index_entries, &id_index_entry);
1897 TRACE("Added DVD sector ID block index entry at offset %" PRIu64, id_position);
1898 }
1899 }
1900
1901 if(id_block.compression == Lzma) free(buffer);
1902
1903 // Write DVD sector IED block
1904 fseek(ctx->imageStream, 0, SEEK_END);
1905 long ied_position = ftell(ctx->imageStream);
1906 if(ied_position & alignment_mask)
1907 {
1908 const uint64_t aligned_position = ied_position + alignment_mask & ~alignment_mask;
1909 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1910 ied_position = aligned_position;
1911 }
1912 TRACE("Writing DVD sector IED block at position %ld", ied_position);
1913 BlockHeader ied_block = {0};
1914 ied_block.identifier = DataBlock;
1915 ied_block.type = DvdSectorIed;
1916 ied_block.compression = ctx->compression_enabled ? Lzma : None;
1917 ied_block.length = (uint32_t)total_sectors * 2;
1918 // Calculate CRC64
1919 ied_block.crc64 = aaruf_crc64_data(ctx->sector_ied, ied_block.length);
1920
1921 buffer = NULL;
1922
1923 if(ied_block.compression == None)
1924 {
1925 buffer = ctx->sector_ied;
1926 ied_block.cmpCrc64 = ied_block.crc64;
1927 }
1928 else
1929 {
1930 buffer = malloc((size_t)ied_block.length * 2); // Allocate double size for compression
1931 if(buffer == NULL)
1932 {
1933 TRACE("Failed to allocate memory for DVD sector IED compression");
1934 return;
1935 }
1936
1937 size_t dst_size = (size_t)ied_block.length * 2 * 2;
1938 size_t props_size = LZMA_PROPERTIES_LENGTH;
1939 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_ied, ied_block.length, lzma_properties, &props_size, 9,
1940 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
1941
1942 ied_block.cmpLength = (uint32_t)dst_size;
1943
1944 if(ied_block.cmpLength >= ied_block.length)
1945 {
1946 ied_block.compression = None;
1947 free(buffer);
1948 buffer = ctx->sector_ied;
1949 }
1950 }
1951
1952 if(ied_block.compression == None)
1953 {
1954 ied_block.cmpLength = ied_block.length;
1955 ied_block.cmpCrc64 = ied_block.crc64;
1956 }
1957 else
1958 ied_block.cmpCrc64 = aaruf_crc64_data(buffer, ied_block.cmpLength);
1959
1960 if(ied_block.compression == Lzma) ied_block.cmpLength += LZMA_PROPERTIES_LENGTH;
1961
1962 // Write header
1963 if(fwrite(&ied_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
1964 {
1965 if(ied_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
1966
1967 // Write data
1968 const size_t written_bytes = fwrite(buffer, ied_block.cmpLength, 1, ctx->imageStream);
1969 if(written_bytes == 1)
1970 {
1971 TRACE("Successfully wrote DVD sector IED block (%" PRIu64 " bytes)", ied_block.cmpLength);
1972 // Add IED block to index
1973 TRACE("Adding DVD sector IED block to index");
1974 IndexEntry ied_index_entry;
1975 ied_index_entry.blockType = DataBlock;
1976 ied_index_entry.dataType = DvdSectorIed;
1977 ied_index_entry.offset = ied_position;
1978 utarray_push_back(ctx->index_entries, &ied_index_entry);
1979 TRACE("Added DVD sector IED block index entry at offset %" PRIu64, ied_position);
1980 }
1981 }
1982
1983 if(ied_block.compression == Lzma) free(buffer);
1984
1985 // Write DVD sector CPR/MAI block
1986 fseek(ctx->imageStream, 0, SEEK_END);
1987 long cpr_mai_position = ftell(ctx->imageStream);
1988 if(cpr_mai_position & alignment_mask)
1989 {
1990 const uint64_t aligned_position = cpr_mai_position + alignment_mask & ~alignment_mask;
1991 fseek(ctx->imageStream, aligned_position, SEEK_SET);
1992 cpr_mai_position = aligned_position;
1993 }
1994 TRACE("Writing DVD sector CPR/MAI block at position %ld", cpr_mai_position);
1995 BlockHeader cpr_mai_block = {0};
1996 cpr_mai_block.identifier = DataBlock;
1997 cpr_mai_block.type = DvdSectorCprMai;
1998 cpr_mai_block.compression = ctx->compression_enabled ? Lzma : None;
1999 cpr_mai_block.length = (uint32_t)total_sectors * 6;
2000 // Calculate CRC64
2001 cpr_mai_block.crc64 = aaruf_crc64_data(ctx->sector_cpr_mai, cpr_mai_block.length);
2002
2003 buffer = NULL;
2004
2005 if(cpr_mai_block.compression == None)
2006 {
2007 buffer = ctx->sector_cpr_mai;
2008 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2009 }
2010 else
2011 {
2012 buffer = malloc((size_t)cpr_mai_block.length * 2); // Allocate double size for compression
2013 if(buffer == NULL)
2014 {
2015 TRACE("Failed to allocate memory for DVD sector CPR/MAI compression");
2016 return;
2017 }
2018
2019 size_t dst_size = (size_t)cpr_mai_block.length * 2 * 2;
2020 size_t props_size = LZMA_PROPERTIES_LENGTH;
2021 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_cpr_mai, cpr_mai_block.length, lzma_properties,
2022 &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2023
2024 cpr_mai_block.cmpLength = (uint32_t)dst_size;
2025
2026 if(cpr_mai_block.cmpLength >= cpr_mai_block.length)
2027 {
2028 cpr_mai_block.compression = None;
2029 free(buffer);
2030 buffer = ctx->sector_cpr_mai;
2031 }
2032 }
2033
2034 if(cpr_mai_block.compression == None)
2035 {
2036 cpr_mai_block.cmpLength = cpr_mai_block.length;
2037 cpr_mai_block.cmpCrc64 = cpr_mai_block.crc64;
2038 }
2039 else
2040 cpr_mai_block.cmpCrc64 = aaruf_crc64_data(buffer, cpr_mai_block.cmpLength);
2041
2042 if(cpr_mai_block.compression == Lzma) cpr_mai_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2043
2044 // Write header
2045 if(fwrite(&cpr_mai_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2046 {
2047 if(cpr_mai_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2048
2049 // Write data
2050 const size_t written_bytes = fwrite(buffer, cpr_mai_block.cmpLength, 1, ctx->imageStream);
2051 if(written_bytes == 1)
2052 {
2053 TRACE("Successfully wrote DVD sector CPR/MAI block (%" PRIu64 " bytes)", cpr_mai_block.cmpLength);
2054 // Add CPR/MAI block to index
2055 TRACE("Adding DVD sector CPR/MAI block to index");
2056 IndexEntry cpr_mai_index_entry;
2057 cpr_mai_index_entry.blockType = DataBlock;
2058 cpr_mai_index_entry.dataType = DvdSectorCprMai;
2059 cpr_mai_index_entry.offset = cpr_mai_position;
2060 utarray_push_back(ctx->index_entries, &cpr_mai_index_entry);
2061 TRACE("Added DVD sector CPR/MAI block index entry at offset %" PRIu64, cpr_mai_position);
2062 }
2063 }
2064
2065 if(cpr_mai_block.compression == Lzma) free(buffer);
2066
2067 // Write DVD sector EDC block
2068 fseek(ctx->imageStream, 0, SEEK_END);
2069 long edc_position = ftell(ctx->imageStream);
2070 if(edc_position & alignment_mask)
2071 {
2072 const uint64_t aligned_position = edc_position + alignment_mask & ~alignment_mask;
2073 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2074 edc_position = aligned_position;
2075 }
2076 TRACE("Writing DVD sector EDC block at position %ld", edc_position);
2077 BlockHeader edc_block = {0};
2078 edc_block.identifier = DataBlock;
2079 edc_block.type = DvdSectorEdc;
2080 edc_block.compression = ctx->compression_enabled ? Lzma : None;
2081 edc_block.length = (uint32_t)total_sectors * 4;
2082 // Calculate CRC64
2083 edc_block.crc64 = aaruf_crc64_data(ctx->sector_edc, edc_block.length);
2084
2085 buffer = NULL;
2086
2087 if(edc_block.compression == None)
2088 {
2089 buffer = ctx->sector_edc;
2090 edc_block.cmpCrc64 = edc_block.crc64;
2091 }
2092 else
2093 {
2094 buffer = malloc((size_t)edc_block.length * 2); // Allocate double size for compression
2095 if(buffer == NULL)
2096 {
2097 TRACE("Failed to allocate memory for DVD sector EDC compression");
2098 return;
2099 }
2100
2101 size_t dst_size = (size_t)edc_block.length * 2 * 2;
2102 size_t props_size = LZMA_PROPERTIES_LENGTH;
2103 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_edc, edc_block.length, lzma_properties, &props_size, 9,
2104 ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2105
2106 edc_block.cmpLength = (uint32_t)dst_size;
2107
2108 if(edc_block.cmpLength >= edc_block.length)
2109 {
2110 edc_block.compression = None;
2111 free(buffer);
2112 buffer = ctx->sector_edc;
2113 }
2114 }
2115
2116 if(edc_block.compression == None)
2117 {
2118 edc_block.cmpLength = edc_block.length;
2119 edc_block.cmpCrc64 = edc_block.crc64;
2120 }
2121 else
2122 edc_block.cmpCrc64 = aaruf_crc64_data(buffer, edc_block.cmpLength);
2123
2124 if(edc_block.compression == Lzma) edc_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2125
2126 // Write header
2127 if(fwrite(&edc_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2128 {
2129 if(edc_block.compression == Lzma) fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2130
2131 // Write data
2132 const size_t written_bytes = fwrite(buffer, edc_block.cmpLength, 1, ctx->imageStream);
2133 if(written_bytes == 1)
2134 {
2135 TRACE("Successfully wrote DVD sector EDC block (%" PRIu64 " bytes)", edc_block.cmpLength);
2136 // Add EDC block to index
2137 TRACE("Adding DVD sector EDC block to index");
2138 IndexEntry edc_index_entry;
2139 edc_index_entry.blockType = DataBlock;
2140 edc_index_entry.dataType = DvdSectorEdc;
2141 edc_index_entry.offset = edc_position;
2142 utarray_push_back(ctx->index_entries, &edc_index_entry);
2143 TRACE("Added DVD sector EDC block index entry at offset %" PRIu64, edc_position);
2144 }
2145 }
2146
2147 if(edc_block.compression == Lzma) free(buffer);
2148}
2149
2247 static void write_dvd_title_key_decrypted_block(const aaruformat_context *ctx)
2248{
2249 if(ctx->sector_decrypted_title_key == NULL) return;
2250
2251 fseek(ctx->imageStream, 0, SEEK_END);
2252 long block_position = ftell(ctx->imageStream);
2253 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2254 if(block_position & alignment_mask)
2255 {
2256 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2257 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2258 block_position = aligned_position;
2259 }
2260 TRACE("Writing DVD decrypted title key block at position %ld", block_position);
2261 BlockHeader decrypted_title_key_block = {0};
2262 decrypted_title_key_block.identifier = DataBlock;
2263 decrypted_title_key_block.type = DvdSectorTitleKeyDecrypted;
2264 decrypted_title_key_block.compression = ctx->compression_enabled ? Lzma : None;
2265 decrypted_title_key_block.length =
2266 (uint32_t)(ctx->user_data_ddt_header.negative + ctx->image_info.Sectors + ctx->user_data_ddt_header.overflow) *
2267 5;
2268 // Calculate CRC64
2269 decrypted_title_key_block.crc64 =
2270 aaruf_crc64_data(ctx->sector_decrypted_title_key, decrypted_title_key_block.length);
2271
2272 uint8_t *buffer = NULL;
2273 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2274
2275 if(decrypted_title_key_block.compression == None)
2276 {
2277 buffer = ctx->sector_decrypted_title_key;
2278 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2279 }
2280 else
2281 {
2282 buffer = malloc((size_t)decrypted_title_key_block.length * 2); // Allocate double size for compression
2283 if(buffer == NULL)
2284 {
2285 TRACE("Failed to allocate memory for DVD decrypted title key compression");
2286 return;
2287 }
2288
2289 size_t dst_size = (size_t)decrypted_title_key_block.length * 2 * 2;
2290 size_t props_size = LZMA_PROPERTIES_LENGTH;
2291 aaruf_lzma_encode_buffer(buffer, &dst_size, ctx->sector_decrypted_title_key, decrypted_title_key_block.length,
2292 lzma_properties, &props_size, 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2293
2294 decrypted_title_key_block.cmpLength = (uint32_t)dst_size;
2295
2296 if(decrypted_title_key_block.cmpLength >= decrypted_title_key_block.length)
2297 {
2298 decrypted_title_key_block.compression = None;
2299 free(buffer);
2300 buffer = ctx->sector_decrypted_title_key;
2301 }
2302 }
2303
2304 if(decrypted_title_key_block.compression == None)
2305 {
2306 decrypted_title_key_block.cmpLength = decrypted_title_key_block.length;
2307 decrypted_title_key_block.cmpCrc64 = decrypted_title_key_block.crc64;
2308 }
2309 else
2310 decrypted_title_key_block.cmpCrc64 = aaruf_crc64_data(buffer, decrypted_title_key_block.cmpLength);
2311
2312 if(decrypted_title_key_block.compression == Lzma) decrypted_title_key_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2313
2314 // Write header
2315 if(fwrite(&decrypted_title_key_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2316 {
2317 if(decrypted_title_key_block.compression == Lzma)
2318 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream);
2319
2320 // Write data
2321 const size_t written_bytes = fwrite(buffer, decrypted_title_key_block.cmpLength, 1, ctx->imageStream);
2322 if(written_bytes == 1)
2323 {
2324 TRACE("Successfully wrote DVD decrypted title key block (%" PRIu64 " bytes)",
2325 decrypted_title_key_block.cmpLength);
2326 // Add decrypted title key block to index
2327 TRACE("Adding DVD decrypted title key block to index");
2328 IndexEntry decrypted_title_key_index_entry;
2329 decrypted_title_key_index_entry.blockType = DataBlock;
2330 decrypted_title_key_index_entry.dataType = DvdSectorTitleKeyDecrypted;
2331 decrypted_title_key_index_entry.offset = block_position;
2332 utarray_push_back(ctx->index_entries, &decrypted_title_key_index_entry);
2333 TRACE("Added DVD decrypted title key block index entry at offset %" PRIu64, block_position);
2334 }
2335 }
2336
2337 if(decrypted_title_key_block.compression == Lzma) free(buffer);
2338}
2339
2411 static void write_media_tags(const aaruformat_context *ctx)
2412{
2413 if(ctx->mediaTags == NULL) return;
2414
2415 mediaTagEntry *media_tag = NULL;
2416 mediaTagEntry *tmp_media_tag = NULL;
2417
2418 HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
2419 {
2420 fseek(ctx->imageStream, 0, SEEK_END);
2421 long tag_position = ftell(ctx->imageStream);
2422 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2423 if(tag_position & alignment_mask)
2424 {
2425 const uint64_t aligned_position = tag_position + alignment_mask & ~alignment_mask;
2426 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2427 tag_position = aligned_position;
2428 }
2429
2430 TRACE("Writing media tag block type %d at position %ld", aaruf_get_datatype_for_media_tag_type(media_tag->type),
2431 tag_position);
2432 BlockHeader tag_block = {0};
2433 tag_block.identifier = DataBlock;
2434 tag_block.type = (uint16_t)aaruf_get_datatype_for_media_tag_type(media_tag->type);
2435 tag_block.compression = ctx->compression_enabled ? Lzma : None;
2436 tag_block.length = media_tag->length;
2437
2438 // Calculate CRC64
2439 tag_block.crc64 = aaruf_crc64_data(media_tag->data, tag_block.length);
2440
2441 uint8_t *buffer = NULL;
2442 uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH] = {0};
2443
2444 if(tag_block.compression == None)
2445 {
2446 buffer = media_tag->data;
2447 tag_block.cmpCrc64 = tag_block.crc64;
2448 }
2449 else
2450 {
2451 buffer = malloc((size_t)tag_block.length * 2); // Allocate double size for compression
2452 if(buffer == NULL)
2453 {
2454 TRACE("Failed to allocate memory for media tag compression");
2455 return;
2456 }
2457
2458 size_t dst_size = (size_t)tag_block.length * 2 * 2;
2459 size_t props_size = LZMA_PROPERTIES_LENGTH;
2460 aaruf_lzma_encode_buffer(buffer, &dst_size, media_tag->data, tag_block.length, lzma_properties, &props_size,
2461 9, ctx->lzma_dict_size, 4, 0, 2, 273, 8);
2462
2463 tag_block.cmpLength = (uint32_t)dst_size;
2464
2465 if(tag_block.cmpLength >= tag_block.length)
2466 {
2467 tag_block.compression = None;
2468 free(buffer);
2469 buffer = media_tag->data;
2470 }
2471 }
2472
2473 if(tag_block.compression == None)
2474 {
2475 tag_block.cmpLength = tag_block.length;
2476 tag_block.cmpCrc64 = tag_block.crc64;
2477 }
2478 else
2479 tag_block.cmpCrc64 = aaruf_crc64_data(buffer, tag_block.cmpLength);
2480
2481 if(tag_block.compression == Lzma) tag_block.cmpLength += LZMA_PROPERTIES_LENGTH;
2482
2483 // Write header
2484 if(fwrite(&tag_block, sizeof(BlockHeader), 1, ctx->imageStream) == 1)
2485 {
2486 if(tag_block.compression == Lzma)
2487 fwrite(lzma_properties, LZMA_PROPERTIES_LENGTH, 1, ctx->imageStream); // Write data
2488
2489 const size_t written_bytes = fwrite(buffer, tag_block.cmpLength, 1, ctx->imageStream);
2490 if(written_bytes == 1)
2491 {
2492 TRACE("Successfully wrote media tag block type %d (%" PRIu64 " bytes)", tag_block.type,
2493 tag_block.cmpLength);
2494 // Add media tag block to index
2495 TRACE("Adding media tag type %d block to index", tag_block.type);
2496 IndexEntry tag_index_entry;
2497 tag_index_entry.blockType = DataBlock;
2498 tag_index_entry.dataType = tag_block.type;
2499 tag_index_entry.offset = tag_position;
2500 utarray_push_back(ctx->index_entries, &tag_index_entry);
2501 TRACE("Added media tag block type %d index entry at offset %" PRIu64, tag_block.type, tag_position);
2502 }
2503 }
2504
2505 if(tag_block.compression == Lzma) free(buffer);
2506 }
2507}
2508
2671 static void write_tape_file_block(const aaruformat_context *ctx)
2672{
2673 if(ctx->tape_files == NULL) return;
2674
2675 // Iterate the uthash and count how many entries do we have
2676 const tapeFileHashEntry *tape_file = NULL;
2677 const tapeFileHashEntry *tmp_tape_file = NULL;
2678 size_t tape_file_count = 0;
2679 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file) tape_file_count++;
2680
2681 // Create a memory buffer to copy all the file entries
2682 const size_t buffer_size = tape_file_count * sizeof(TapeFileEntry);
2683 TapeFileEntry *buffer = malloc(buffer_size);
2684 if(buffer == NULL)
2685 {
2686 TRACE("Failed to allocate memory for tape file entries");
2687 return;
2688 }
2689 memset(buffer, 0, buffer_size);
2690 size_t index = 0;
2691 HASH_ITER(hh, ctx->tape_files, tape_file, tmp_tape_file)
2692 {
2693 if(index >= tape_file_count) break;
2694 memcpy(&buffer[index], &tape_file->fileEntry, sizeof(TapeFileEntry));
2695 index++;
2696 }
2697
2698 // Create the tape file block in memory
2699 TapeFileHeader tape_file_block = {0};
2700 tape_file_block.identifier = TapeFileBlock;
2701 tape_file_block.length = (uint32_t)buffer_size;
2702 tape_file_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_file_block.length);
2703
2704 // Write tape file block to file, block aligned
2705 fseek(ctx->imageStream, 0, SEEK_END);
2706 long block_position = ftell(ctx->imageStream);
2707 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2708 if(block_position & alignment_mask)
2709 {
2710 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2711 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2712 block_position = aligned_position;
2713 }
2714 TRACE("Writing tape file block at position %ld", block_position);
2715 if(fwrite(&tape_file_block, sizeof(TapeFileHeader), 1, ctx->imageStream) == 1)
2716 {
2717 const size_t written_bytes = fwrite(buffer, tape_file_block.length, 1, ctx->imageStream);
2718 if(written_bytes == 1)
2719 {
2720 TRACE("Successfully wrote tape file block (%" PRIu64 " bytes)", tape_file_block.length);
2721 // Add tape file block to index
2722 TRACE("Adding tape file block to index");
2723 IndexEntry index_entry;
2724 index_entry.blockType = TapeFileBlock;
2725 index_entry.dataType = 0;
2726 index_entry.offset = block_position;
2727 utarray_push_back(ctx->index_entries, &index_entry);
2728 TRACE("Added tape file block index entry at offset %" PRIu64, block_position);
2729 }
2730 }
2731
2732 free(buffer);
2733}
2734
2903 static void write_tape_partition_block(const aaruformat_context *ctx)
2904{
2905 if(ctx->tape_partitions == NULL) return;
2906
2907 // Iterate the uthash and count how many entries do we have
2908 const TapePartitionHashEntry *tape_partition = NULL;
2909 const TapePartitionHashEntry *tmp_tape_partition = NULL;
2910 size_t tape_partition_count = 0;
2911 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition) tape_partition_count++;
2912
2913 // Create a memory buffer to copy all the partition entries
2914 const size_t buffer_size = tape_partition_count * sizeof(TapePartitionEntry);
2915 TapePartitionEntry *buffer = malloc(buffer_size);
2916 if(buffer == NULL)
2917 {
2918 TRACE("Failed to allocate memory for tape partition entries");
2919 return;
2920 }
2921 memset(buffer, 0, buffer_size);
2922 size_t index = 0;
2923 HASH_ITER(hh, ctx->tape_partitions, tape_partition, tmp_tape_partition)
2924 {
2925 if(index >= tape_partition_count) break;
2926 memcpy(&buffer[index], &tape_partition->partitionEntry, sizeof(TapePartitionEntry));
2927 index++;
2928 }
2929
2930 // Create the tape partition block in memory
2931 TapePartitionHeader tape_partition_block = {0};
2932 tape_partition_block.identifier = TapePartitionBlock;
2933 tape_partition_block.length = (uint32_t)buffer_size;
2934 tape_partition_block.crc64 = aaruf_crc64_data((uint8_t *)buffer, (uint32_t)tape_partition_block.length);
2935
2936 // Write tape partition block to partition, block aligned
2937 fseek(ctx->imageStream, 0, SEEK_END);
2938 long block_position = ftell(ctx->imageStream);
2939 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
2940 if(block_position & alignment_mask)
2941 {
2942 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
2943 fseek(ctx->imageStream, aligned_position, SEEK_SET);
2944 block_position = aligned_position;
2945 }
2946 TRACE("Writing tape partition block at position %ld", block_position);
2947 if(fwrite(&tape_partition_block, sizeof(TapePartitionHeader), 1, ctx->imageStream) == 1)
2948 {
2949 const size_t written_bytes = fwrite(buffer, tape_partition_block.length, 1, ctx->imageStream);
2950 if(written_bytes == 1)
2951 {
2952 TRACE("Successfully wrote tape partition block (%" PRIu64 " bytes)", tape_partition_block.length);
2953 // Add tape partition block to index
2954 TRACE("Adding tape partition block to index");
2955 IndexEntry index_entry;
2956 index_entry.blockType = TapePartitionBlock;
2957 index_entry.dataType = 0;
2958 index_entry.offset = block_position;
2959 utarray_push_back(ctx->index_entries, &index_entry);
2960 TRACE("Added tape partition block index entry at offset %" PRIu64, block_position);
2961 }
2962 }
2963
2964 free(buffer);
2965}
2966
3028 static void write_geometry_block(const aaruformat_context *ctx)
3029{
3030 if(ctx->geometry_block.identifier != GeometryBlock) return;
3031
3032 fseek(ctx->imageStream, 0, SEEK_END);
3033 long block_position = ftell(ctx->imageStream);
3034 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3035 if(block_position & alignment_mask)
3036 {
3037 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3038 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3039 block_position = aligned_position;
3040 }
3041
3042 TRACE("Writing geometry block at position %ld", block_position);
3043
3044 // Write header
3045 if(fwrite(&ctx->geometry_block, sizeof(GeometryBlockHeader), 1, ctx->imageStream) == 1)
3046 {
3047 TRACE("Successfully wrote geometry block");
3048
3049 // Add geometry block to index
3050 TRACE("Adding geometry block to index");
3051 IndexEntry index_entry;
3052 index_entry.blockType = GeometryBlock;
3053 index_entry.dataType = 0;
3054 index_entry.offset = block_position;
3055 utarray_push_back(ctx->index_entries, &index_entry);
3056 TRACE("Added geometry block index entry at offset %" PRIu64, block_position);
3057 }
3058}
3059
3164 static void write_metadata_block(aaruformat_context *ctx)
3165{
3166 if(ctx->metadata_block_header.identifier != MetadataBlock && ctx->metadata_block_header.mediaSequence == 0 &&
3167 ctx->metadata_block_header.lastMediaSequence == 0 && ctx->creator == NULL && ctx->comments == NULL &&
3168 ctx->media_title == NULL && ctx->media_manufacturer == NULL && ctx->media_model == NULL &&
3169 ctx->media_serial_number == NULL && ctx->media_barcode == NULL && ctx->media_part_number == NULL &&
3170 ctx->drive_manufacturer == NULL && ctx->drive_model == NULL && ctx->drive_serial_number == NULL &&
3171 ctx->drive_firmware_revision == NULL)
3172 return;
3173
3174 ctx->metadata_block_header.blockSize =
3175 sizeof(MetadataBlockHeader) + ctx->metadata_block_header.creatorLength +
3176 ctx->metadata_block_header.commentsLength + ctx->metadata_block_header.mediaTitleLength +
3177 ctx->metadata_block_header.mediaManufacturerLength + ctx->metadata_block_header.mediaModelLength +
3178 ctx->metadata_block_header.mediaSerialNumberLength + ctx->metadata_block_header.mediaBarcodeLength +
3179 ctx->metadata_block_header.mediaPartNumberLength + ctx->metadata_block_header.driveManufacturerLength +
3180 ctx->metadata_block_header.driveModelLength + ctx->metadata_block_header.driveSerialNumberLength +
3181 ctx->metadata_block_header.driveFirmwareRevisionLength;
3182
3183 ctx->metadata_block_header.identifier = MetadataBlock;
3184
3185 int pos = sizeof(MetadataBlockHeader);
3186
3187 uint8_t *buffer = calloc(1, ctx->metadata_block_header.blockSize);
3188 if(buffer == NULL) return;
3189
3190 if(ctx->creator != NULL && ctx->metadata_block_header.creatorLength > 0)
3191 {
3192 memcpy(buffer + pos, ctx->creator, ctx->metadata_block_header.creatorLength);
3193 ctx->metadata_block_header.creatorOffset = pos;
3194 pos += ctx->metadata_block_header.creatorLength;
3195 }
3196
3197 if(ctx->comments != NULL && ctx->metadata_block_header.commentsLength > 0)
3198 {
3199 memcpy(buffer + pos, ctx->comments, ctx->metadata_block_header.commentsLength);
3200 ctx->metadata_block_header.commentsOffset = pos;
3201 pos += ctx->metadata_block_header.commentsLength;
3202 }
3203
3204 if(ctx->media_title != NULL && ctx->metadata_block_header.mediaTitleLength > 0)
3205 {
3206 memcpy(buffer + pos, ctx->media_title, ctx->metadata_block_header.mediaTitleLength);
3207 ctx->metadata_block_header.mediaTitleOffset = pos;
3208 pos += ctx->metadata_block_header.mediaTitleLength;
3209 }
3210
3211 if(ctx->media_manufacturer != NULL && ctx->metadata_block_header.mediaManufacturerLength > 0)
3212 {
3213 memcpy(buffer + pos, ctx->media_manufacturer, ctx->metadata_block_header.mediaManufacturerLength);
3214 ctx->metadata_block_header.mediaManufacturerOffset = pos;
3215 pos += ctx->metadata_block_header.mediaManufacturerLength;
3216 }
3217
3218 if(ctx->media_model != NULL && ctx->metadata_block_header.mediaModelLength > 0)
3219 {
3220 memcpy(buffer + pos, ctx->media_model, ctx->metadata_block_header.mediaModelLength);
3221 ctx->metadata_block_header.mediaModelOffset = pos;
3222 pos += ctx->metadata_block_header.mediaModelLength;
3223 }
3224
3225 if(ctx->media_serial_number != NULL && ctx->metadata_block_header.mediaSerialNumberLength > 0)
3226 {
3227 memcpy(buffer + pos, ctx->media_serial_number, ctx->metadata_block_header.mediaSerialNumberLength);
3228 ctx->metadata_block_header.mediaSerialNumberOffset = pos;
3229 pos += ctx->metadata_block_header.mediaSerialNumberLength;
3230 }
3231
3232 if(ctx->media_barcode != NULL && ctx->metadata_block_header.mediaBarcodeLength > 0)
3233 {
3234 memcpy(buffer + pos, ctx->media_barcode, ctx->metadata_block_header.mediaBarcodeLength);
3235 ctx->metadata_block_header.mediaBarcodeOffset = pos;
3236 pos += ctx->metadata_block_header.mediaBarcodeLength;
3237 }
3238
3239 if(ctx->media_part_number != NULL && ctx->metadata_block_header.mediaPartNumberLength > 0)
3240 {
3241 memcpy(buffer + pos, ctx->media_part_number, ctx->metadata_block_header.mediaPartNumberLength);
3242 ctx->metadata_block_header.mediaPartNumberOffset = pos;
3243 pos += ctx->metadata_block_header.mediaPartNumberLength;
3244 }
3245
3246 if(ctx->drive_manufacturer != NULL && ctx->metadata_block_header.driveManufacturerLength > 0)
3247 {
3248 memcpy(buffer + pos, ctx->drive_manufacturer, ctx->metadata_block_header.driveManufacturerLength);
3249 ctx->metadata_block_header.driveManufacturerOffset = pos;
3250 pos += ctx->metadata_block_header.driveManufacturerLength;
3251 }
3252
3253 if(ctx->drive_model != NULL && ctx->metadata_block_header.driveModelLength > 0)
3254 {
3255 memcpy(buffer + pos, ctx->drive_model, ctx->metadata_block_header.driveModelLength);
3256 ctx->metadata_block_header.driveModelOffset = pos;
3257 pos += ctx->metadata_block_header.driveModelLength;
3258 }
3259
3260 if(ctx->drive_serial_number != NULL && ctx->metadata_block_header.driveSerialNumberLength > 0)
3261 {
3262 memcpy(buffer + pos, ctx->drive_serial_number, ctx->metadata_block_header.driveSerialNumberLength);
3263 ctx->metadata_block_header.driveSerialNumberOffset = pos;
3264 pos += ctx->metadata_block_header.driveSerialNumberLength;
3265 }
3266
3267 if(ctx->drive_firmware_revision != NULL && ctx->metadata_block_header.driveFirmwareRevisionLength > 0)
3268 {
3269 memcpy(buffer + pos, ctx->drive_firmware_revision, ctx->metadata_block_header.driveFirmwareRevisionLength);
3270 ctx->metadata_block_header.driveFirmwareRevisionOffset = pos;
3271 }
3272
3273 fseek(ctx->imageStream, 0, SEEK_END);
3274 long block_position = ftell(ctx->imageStream);
3275 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3276 if(block_position & alignment_mask)
3277 {
3278 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3279 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3280 block_position = aligned_position;
3281 }
3282
3283 TRACE("Writing metadata block at position %ld", block_position);
3284
3285 if(fwrite(buffer, ctx->metadata_block_header.blockSize, 1, ctx->imageStream) == 1)
3286 {
3287 TRACE("Successfully wrote metadata block");
3288
3289 // Add metadata block to index
3290 TRACE("Adding metadata block to index");
3291 IndexEntry index_entry;
3292 index_entry.blockType = MetadataBlock;
3293 index_entry.dataType = 0;
3294 index_entry.offset = block_position;
3295 utarray_push_back(ctx->index_entries, &index_entry);
3296 TRACE("Added metadata block index entry at offset %" PRIu64, block_position);
3297 }
3298
3299 free(buffer);
3300}
3301
3447 static void write_dumphw_block(aaruformat_context *ctx)
3448{
3449
3450 if(ctx->dump_hardware_entries_with_data == NULL || ctx->dump_hardware_header.entries == 0 ||
3451 ctx->dump_hardware_header.identifier != DumpHardwareBlock)
3452 return;
3453
3454 const size_t required_length = sizeof(DumpHardwareHeader) + ctx->dump_hardware_header.length;
3455
3456 uint8_t *buffer = calloc(1, required_length);
3457
3458 if(buffer == NULL) return;
3459
3460 // Start to iterate and copy the data
3461 size_t offset = sizeof(DumpHardwareHeader);
3462 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
3463 {
3464 size_t entry_size = sizeof(DumpHardwareEntry) +
3465 ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength +
3466 ctx->dump_hardware_entries_with_data[i].entry.modelLength +
3467 ctx->dump_hardware_entries_with_data[i].entry.revisionLength +
3468 ctx->dump_hardware_entries_with_data[i].entry.firmwareLength +
3469 ctx->dump_hardware_entries_with_data[i].entry.serialLength +
3470 ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength +
3471 ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength +
3472 ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength +
3473 ctx->dump_hardware_entries_with_data[i].entry.extents * sizeof(DumpExtent);
3474
3475 if(offset + entry_size > required_length)
3476 {
3477 FATAL("Calculated size exceeds provided buffer length");
3478 free(buffer);
3479 return;
3480 }
3481
3482 memcpy(buffer + offset, &ctx->dump_hardware_entries_with_data[i].entry, sizeof(DumpHardwareEntry));
3483 offset += sizeof(DumpHardwareEntry);
3484 if(ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength > 0 &&
3485 ctx->dump_hardware_entries_with_data[i].manufacturer != NULL)
3486 {
3487 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].manufacturer,
3488 ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength);
3489 offset += ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength;
3490 }
3491 if(ctx->dump_hardware_entries_with_data[i].entry.modelLength > 0 &&
3492 ctx->dump_hardware_entries_with_data[i].model != NULL)
3493 {
3494 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].model,
3495 ctx->dump_hardware_entries_with_data[i].entry.modelLength);
3496 offset += ctx->dump_hardware_entries_with_data[i].entry.modelLength;
3497 }
3498 if(ctx->dump_hardware_entries_with_data[i].entry.revisionLength > 0 &&
3499 ctx->dump_hardware_entries_with_data[i].revision != NULL)
3500 {
3501 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].revision,
3502 ctx->dump_hardware_entries_with_data[i].entry.revisionLength);
3503 offset += ctx->dump_hardware_entries_with_data[i].entry.revisionLength;
3504 }
3505 if(ctx->dump_hardware_entries_with_data[i].entry.firmwareLength > 0 &&
3506 ctx->dump_hardware_entries_with_data[i].firmware != NULL)
3507 {
3508 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].firmware,
3509 ctx->dump_hardware_entries_with_data[i].entry.firmwareLength);
3510 offset += ctx->dump_hardware_entries_with_data[i].entry.firmwareLength;
3511 }
3512 if(ctx->dump_hardware_entries_with_data[i].entry.serialLength > 0 &&
3513 ctx->dump_hardware_entries_with_data[i].serial != NULL)
3514 {
3515 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].serial,
3516 ctx->dump_hardware_entries_with_data[i].entry.serialLength);
3517 offset += ctx->dump_hardware_entries_with_data[i].entry.serialLength;
3518 }
3519 if(ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength > 0 &&
3520 ctx->dump_hardware_entries_with_data[i].softwareName != NULL)
3521 {
3522 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareName,
3523 ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength);
3524 offset += ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength;
3525 }
3526 if(ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength > 0 &&
3527 ctx->dump_hardware_entries_with_data[i].softwareVersion != NULL)
3528 {
3529 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareVersion,
3530 ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength);
3531 offset += ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength;
3532 }
3533 if(ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength > 0 &&
3534 ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem != NULL)
3535 {
3536 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem,
3537 ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength);
3538 offset += ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength;
3539 }
3540 if(ctx->dump_hardware_entries_with_data[i].entry.extents > 0 &&
3541 ctx->dump_hardware_entries_with_data[i].extents != NULL)
3542 {
3543 memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].extents,
3544 ctx->dump_hardware_entries_with_data[i].entry.extents * sizeof(DumpExtent));
3545 offset += ctx->dump_hardware_entries_with_data[i].entry.extents * sizeof(DumpExtent);
3546 }
3547 }
3548
3549 // Calculate CRC64
3550 ctx->dump_hardware_header.crc64 =
3551 aaruf_crc64_data(buffer + sizeof(DumpHardwareHeader), ctx->dump_hardware_header.length);
3552
3553 // Copy header
3554 memcpy(buffer, &ctx->dump_hardware_header, sizeof(DumpHardwareHeader));
3555
3556 fseek(ctx->imageStream, 0, SEEK_END);
3557 long block_position = ftell(ctx->imageStream);
3558 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3559 if(block_position & alignment_mask)
3560 {
3561 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3562 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3563 block_position = aligned_position;
3564 }
3565 TRACE("Writing dump hardware block at position %ld", block_position);
3566 if(fwrite(buffer, required_length, 1, ctx->imageStream) == 1)
3567 {
3568 TRACE("Successfully wrote dump hardware block");
3569
3570 // Add dump hardware block to index
3571 TRACE("Adding dump hardware block to index");
3572 IndexEntry index_entry;
3573 index_entry.blockType = DumpHardwareBlock;
3574 index_entry.dataType = 0;
3575 index_entry.offset = block_position;
3576 utarray_push_back(ctx->index_entries, &index_entry);
3577 TRACE("Added dump hardware block index entry at offset %" PRIu64, block_position);
3578 }
3579
3580 free(buffer);
3581}
3582
3677 static void write_cicm_block(const aaruformat_context *ctx)
3678{
3679 if(ctx->cicm_block == NULL || ctx->cicm_block_header.length == 0 || ctx->cicm_block_header.identifier != CicmBlock)
3680 return;
3681
3682 fseek(ctx->imageStream, 0, SEEK_END);
3683 long block_position = ftell(ctx->imageStream);
3684 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3685
3686 if(block_position & alignment_mask)
3687 {
3688 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3689 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3690 block_position = aligned_position;
3691 }
3692
3693 TRACE("Writing CICM XML block at position %ld", block_position);
3694 if(fwrite(&ctx->cicm_block_header, sizeof(CicmMetadataBlock), 1, ctx->imageStream) == 1)
3695 if(fwrite(ctx->cicm_block, ctx->cicm_block_header.length, 1, ctx->imageStream) == 1)
3696 {
3697 TRACE("Successfully wrote CICM XML block");
3698
3699 // Add CICM block to index
3700 TRACE("Adding CICM XML block to index");
3701 IndexEntry index_entry;
3702 index_entry.blockType = CicmBlock;
3703 index_entry.dataType = 0;
3704 index_entry.offset = block_position;
3705 utarray_push_back(ctx->index_entries, &index_entry);
3706 TRACE("Added CICM XML block index entry at offset %" PRIu64, block_position);
3707 }
3708}
3709
3814 static void write_aaru_json_block(const aaruformat_context *ctx)
3815{
3816 if(ctx->json_block == NULL || ctx->json_block_header.length == 0 ||
3817 ctx->json_block_header.identifier != AaruMetadataJsonBlock)
3818 return;
3819
3820 fseek(ctx->imageStream, 0, SEEK_END);
3821 long block_position = ftell(ctx->imageStream);
3822 const uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3823
3824 if(block_position & alignment_mask)
3825 {
3826 const uint64_t aligned_position = block_position + alignment_mask & ~alignment_mask;
3827 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3828 block_position = aligned_position;
3829 }
3830
3831 TRACE("Writing Aaru metadata JSON block at position %ld", block_position);
3832 if(fwrite(&ctx->json_block_header, sizeof(AaruMetadataJsonBlockHeader), 1, ctx->imageStream) == 1)
3833 if(fwrite(ctx->json_block, ctx->json_block_header.length, 1, ctx->imageStream) == 1)
3834 {
3835 TRACE("Successfully wrote Aaru metadata JSON block");
3836
3837 // Add Aaru metadata JSON block to index
3838 TRACE("Adding Aaru metadata JSON block to index");
3839 IndexEntry index_entry;
3840 index_entry.blockType = AaruMetadataJsonBlock;
3841 index_entry.dataType = 0;
3842 index_entry.offset = block_position;
3843 utarray_push_back(ctx->index_entries, &index_entry);
3844 TRACE("Added Aaru metadata JSON block index entry at offset %" PRIu64, block_position);
3845 }
3846}
3847
3864 static int32_t write_index_block(aaruformat_context *ctx)
3865{
3866 // Write the complete index at the end of the file
3867 TRACE("Writing index at the end of the file");
3868 fseek(ctx->imageStream, 0, SEEK_END);
3869 long index_position = ftell(ctx->imageStream);
3870
3871 // Align index position to block boundary if needed
3872 uint64_t alignment_mask = (1ULL << ctx->user_data_ddt_header.blockAlignmentShift) - 1;
3873 if(index_position & alignment_mask)
3874 {
3875 uint64_t aligned_position = index_position + alignment_mask & ~alignment_mask;
3876 fseek(ctx->imageStream, aligned_position, SEEK_SET);
3877 index_position = aligned_position;
3878 TRACE("Aligned index position to %" PRIu64, aligned_position);
3879 }
3880
3881 // Prepare index header
3882 IndexHeader3 index_header;
3883 index_header.identifier = IndexBlock3;
3884 index_header.entries = utarray_len(ctx->index_entries);
3885 index_header.previous = 0; // No previous index for now
3886
3887 TRACE("Writing index with %" PRIu64 " entries at position %ld", index_header.entries, index_position);
3888
3889 // Calculate CRC64 of index entries
3890 crc64_ctx *index_crc64_context = aaruf_crc64_init();
3891 if(index_crc64_context != NULL && index_header.entries > 0)
3892 {
3893 size_t index_data_size = index_header.entries * sizeof(IndexEntry);
3894 aaruf_crc64_update(index_crc64_context, utarray_front(ctx->index_entries), index_data_size);
3895 aaruf_crc64_final(index_crc64_context, &index_header.crc64);
3896 TRACE("Calculated index CRC64: 0x%16lX", index_header.crc64);
3897 }
3898 else
3899 index_header.crc64 = 0;
3900
3901 // Write index header
3902 if(fwrite(&index_header, sizeof(IndexHeader3), 1, ctx->imageStream) == 1)
3903 {
3904 TRACE("Successfully wrote index header");
3905
3906 // Write index entries
3907 if(index_header.entries > 0)
3908 {
3909 size_t entries_written = 0;
3910 IndexEntry *entry = NULL;
3911
3912 for(entry = (IndexEntry *)utarray_front(ctx->index_entries); entry != NULL;
3913 entry = (IndexEntry *)utarray_next(ctx->index_entries, entry))
3914 if(fwrite(entry, sizeof(IndexEntry), 1, ctx->imageStream) == 1)
3915 {
3916 entries_written++;
3917 TRACE("Wrote index entry: blockType=0x%08X dataType=%u offset=%" PRIu64, entry->blockType,
3918 entry->dataType, entry->offset);
3919 }
3920 else
3921 {
3922 TRACE("Failed to write index entry %zu", entries_written);
3923 break;
3924 }
3925
3926 if(entries_written == index_header.entries)
3927 {
3928 TRACE("Successfully wrote all %zu index entries", entries_written);
3929
3930 // Update header with index offset and rewrite it
3931 ctx->header.indexOffset = index_position;
3932 TRACE("Updating header with index offset: %" PRIu64, ctx->header.indexOffset);
3933
3934 // Seek back to beginning and rewrite header
3935 fseek(ctx->imageStream, 0, SEEK_SET);
3936 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) == 1)
3937 TRACE("Successfully updated header with index offset");
3938 else
3939 {
3940 TRACE("Failed to update header with index offset");
3941 return AARUF_ERROR_CANNOT_WRITE_HEADER;
3942 }
3943 }
3944 else
3945 {
3946 TRACE("Failed to write all index entries (wrote %zu of %" PRIu64 ")", entries_written,
3947 index_header.entries);
3948 return AARUF_ERROR_CANNOT_WRITE_HEADER;
3949 }
3950 }
3951 }
3952 else
3953 {
3954 TRACE("Failed to write index header");
3955 return AARUF_ERROR_CANNOT_WRITE_HEADER;
3956 }
3957
3958 return AARUF_STATUS_OK;
3959}
3960
3997 AARU_EXPORT int AARU_CALL aaruf_close(void *context)
3998{
3999 TRACE("Entering aaruf_close(%p)", context);
4000
4001 mediaTagEntry *media_tag = NULL;
4002 mediaTagEntry *tmp_media_tag = NULL;
4003
4004 if(context == NULL)
4005 {
4006 FATAL("Invalid context");
4007 errno = EINVAL;
4008 return -1;
4009 }
4010
4011 aaruformat_context *ctx = context;
4012
4013 // Not a libaaruformat context
4014 if(ctx->magic != AARU_MAGIC)
4015 {
4016 FATAL("Invalid context");
4017 errno = EINVAL;
4018 return -1;
4019 }
4020
4021 if(ctx->is_writing)
4022 {
4023 TRACE("File is writing");
4024
4025 TRACE("Seeking to start of image");
4026 // Write the header at the beginning of the file
4027 fseek(ctx->imageStream, 0, SEEK_SET);
4028
4029 TRACE("Writing header at position 0");
4030 if(fwrite(&ctx->header, sizeof(AaruHeaderV2), 1, ctx->imageStream) != 1)
4031 {
4032 fclose(ctx->imageStream);
4033 ctx->imageStream = NULL;
4034 errno = AARUF_ERROR_CANNOT_WRITE_HEADER;
4035 return -1;
4036 }
4037
4038 // Close current block first
4039 TRACE("Closing current block if any");
4040 if(ctx->writing_buffer != NULL)
4041 {
4042 int error = aaruf_close_current_block(ctx);
4043
4044 if(error != AARUF_STATUS_OK) return error;
4045 }
4046
4047 int32_t res;
4048 if(ctx->is_tape)
4049 {
4050 // Write tape DDT
4051 res = write_tape_ddt(ctx);
4052 if(res != AARUF_STATUS_OK) return res;
4053 }
4054 else
4055 {
4056 // Write cached secondary DDT table if any
4057 res = write_cached_secondary_ddt(ctx);
4058 if(res != AARUF_STATUS_OK) return res;
4059
4060 // Write primary DDT table (multi-level) if applicable
4061 res = write_primary_ddt(ctx);
4062 if(res != AARUF_STATUS_OK) return res;
4063
4064 // Write single-level DDT table if applicable
4065 res = write_single_level_ddt(ctx);
4066 if(res != AARUF_STATUS_OK) return res;
4067 }
4068
4069 // Finalize checksums and write checksum block
4070 write_checksum_block(ctx);
4071
4072 // Write tracks block
4073 write_tracks_block(ctx);
4074
4075 // Write MODE 2 subheader data block
4076 write_mode2_subheaders_block(ctx);
4077
4078 // Write CD sector prefix data block
4079 write_sector_prefix(ctx);
4080
4081 // Write sector prefix DDT (statuses + optional indexes)
4082 write_sector_prefix_ddt(ctx);
4083
4084 // Write CD sector suffix data block (EDC/ECC captures)
4085 write_sector_suffix(ctx);
4086
4087 // Write sector prefix DDT (EDC/ECC captures)
4088 write_sector_suffix_ddt(ctx);
4089
4090 // Write sector subchannel data block
4091 write_sector_subchannel(ctx);
4092
4093 // Write DVD long sector data blocks
4094 write_dvd_long_sector_blocks(ctx);
4095
4096 // Write DVD decrypted title keys
4097 write_dvd_title_key_decrypted_block(ctx);
4098
4099 // Write media tags data blocks
4100 write_media_tags(ctx);
4101
4102 // Write tape files
4103 write_tape_file_block(ctx);
4104
4105 // Write tape partitions
4106 write_tape_partition_block(ctx);
4107
4108 // Write geometry block if any
4109 write_geometry_block(ctx);
4110
4111 // Write metadata block
4112 write_metadata_block(ctx);
4113
4114 // Write dump hardware block if any
4115 write_dumphw_block(ctx);
4116
4117 // Write CICM XML block if any
4118 write_cicm_block(ctx);
4119
4120 // Write Aaru metadata JSON block if any
4121 write_aaru_json_block(ctx);
4122
4123 // Write the complete index at the end of the file
4124 res = write_index_block(ctx);
4125 if(res != AARUF_STATUS_OK) return res;
4126
4127 if(ctx->deduplicate && ctx->sector_hash_map != NULL)
4128 {
4129 TRACE("Clearing sector hash map");
4130 // Clear sector hash map
4131 free_map(ctx->sector_hash_map);
4132 ctx->sector_hash_map = NULL;
4133 }
4134 }
4135
4136 TRACE("Freeing memory pointers");
4137 // This may do nothing if imageStream is NULL, but as the behaviour is undefined, better sure than sorry
4138 if(ctx->imageStream != NULL)
4139 {
4140 fclose(ctx->imageStream);
4141 ctx->imageStream = NULL;
4142 }
4143
4144 // Free index entries array
4145 if(ctx->index_entries != NULL)
4146 {
4147 utarray_free(ctx->index_entries);
4148 ctx->index_entries = NULL;
4149 }
4150
4151 free(ctx->sector_prefix);
4152 ctx->sector_prefix = NULL;
4153 free(ctx->sector_prefix_corrected);
4154 ctx->sector_prefix_corrected = NULL;
4155 free(ctx->sector_suffix);
4156 ctx->sector_suffix = NULL;
4157 free(ctx->sector_suffix_corrected);
4158 ctx->sector_suffix_corrected = NULL;
4159 free(ctx->sector_subchannel);
4160 ctx->sector_subchannel = NULL;
4161 free(ctx->mode2_subheaders);
4162 ctx->mode2_subheaders = NULL;
4163
4164 TRACE("Freeing media tags");
4165 if(ctx->mediaTags != NULL) HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag)
4166 {
4167 HASH_DEL(ctx->mediaTags, media_tag);
4168 free(media_tag->data);
4169 free(media_tag);
4170 }
4171
4172#ifdef __linux__ // TODO: Implement
4173 TRACE("Unmapping user data DDT if it is not in memory");
4174 if(!ctx->in_memory_ddt)
4175 {
4176 munmap(ctx->user_data_ddt, ctx->mapped_memory_ddt_size);
4177 ctx->user_data_ddt = NULL;
4178 }
4179#endif
4180
4181 free(ctx->sector_prefix_ddt2);
4182 ctx->sector_prefix_ddt2 = NULL;
4183 free(ctx->sector_prefix_ddt);
4184 ctx->sector_prefix_ddt = NULL;
4185 free(ctx->sector_suffix_ddt2);
4186 ctx->sector_suffix_ddt2 = NULL;
4187 free(ctx->sector_suffix_ddt);
4188 ctx->sector_suffix_ddt = NULL;
4189
4190 free(ctx->metadata_block);
4191 ctx->metadata_block = NULL;
4192 free(ctx->track_entries);
4193 ctx->track_entries = NULL;
4194 free(ctx->cicm_block);
4195 ctx->cicm_block = NULL;
4196
4197 if(ctx->dump_hardware_entries_with_data != NULL)
4198 {
4199 for(int i = 0; i < ctx->dump_hardware_header.entries; i++)
4200 {
4201 free(ctx->dump_hardware_entries_with_data[i].extents);
4202 ctx->dump_hardware_entries_with_data[i].extents = NULL;
4203 free(ctx->dump_hardware_entries_with_data[i].manufacturer);
4204 ctx->dump_hardware_entries_with_data[i].manufacturer = NULL;
4205 free(ctx->dump_hardware_entries_with_data[i].model);
4206 ctx->dump_hardware_entries_with_data[i].model = NULL;
4207 free(ctx->dump_hardware_entries_with_data[i].revision);
4208 ctx->dump_hardware_entries_with_data[i].revision = NULL;
4209 free(ctx->dump_hardware_entries_with_data[i].firmware);
4210 ctx->dump_hardware_entries_with_data[i].firmware = NULL;
4211 free(ctx->dump_hardware_entries_with_data[i].serial);
4212 ctx->dump_hardware_entries_with_data[i].serial = NULL;
4213 free(ctx->dump_hardware_entries_with_data[i].softwareName);
4214 ctx->dump_hardware_entries_with_data[i].softwareName = NULL;
4215 free(ctx->dump_hardware_entries_with_data[i].softwareVersion);
4216 ctx->dump_hardware_entries_with_data[i].softwareVersion = NULL;
4217 free(ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem);
4218 ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem = NULL;
4219 }
4220 ctx->dump_hardware_entries_with_data = NULL;
4221 }
4222
4223 free(ctx->readableSectorTags);
4224 ctx->readableSectorTags = NULL;
4225
4226 free(ctx->ecc_cd_context);
4227 ctx->ecc_cd_context = NULL;
4228
4229 free(ctx->checksums.spamsum);
4230 ctx->checksums.spamsum = NULL;
4231
4232 free(ctx->sector_id);
4233 free(ctx->sector_ied);
4234 free(ctx->sector_cpr_mai);
4235 free(ctx->sector_edc);
4236
4237 // TODO: Free caches
4238
4239 free(context);
4240
4241 TRACE("Exiting aaruf_close() = 0");
4242 return 0;
4243}
aaruformat.h
write_dvd_long_sector_blocks
void write_dvd_long_sector_blocks(aaruformat_context *ctx)
Serialize DVD long sector auxiliary data blocks to the image file.
Definition close.c:1810
write_primary_ddt
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:283
write_index_block
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:3864
write_media_tags
static void write_media_tags(const aaruformat_context *ctx)
Serialize all accumulated media tags to the image file.
Definition close.c:2411
write_geometry_block
static void write_geometry_block(const aaruformat_context *ctx)
Serialize the geometry metadata block to the image file.
Definition close.c:3028
aaruf_close
int aaruf_close(void *context)
Close an Aaru image context, flushing pending data structures and releasing resources.
Definition close.c:3997
write_single_level_ddt
static int32_t write_single_level_ddt(aaruformat_context *ctx)
Serialize a single-level DDT (tableShift == 0) directly after its header.
Definition close.c:369
write_dumphw_block
static void write_dumphw_block(aaruformat_context *ctx)
Serialize the dump hardware block containing acquisition environment information.
Definition close.c:3447
write_tape_partition_block
static void write_tape_partition_block(const aaruformat_context *ctx)
Serialize the tape partition metadata block to the image file.
Definition close.c:2903
write_checksum_block
static void write_checksum_block(aaruformat_context *ctx)
Finalize any active checksum calculations and append a checksum block.
Definition close.c:654
write_sector_suffix
static void write_sector_suffix(aaruformat_context *ctx)
Serialize the optional CD sector suffix block (EDC/ECC region capture).
Definition close.c:1088
write_sector_suffix_ddt
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:1350
write_tracks_block
static void write_tracks_block(aaruformat_context *ctx)
Serialize the tracks metadata block and add it to the index.
Definition close.c:798
write_cicm_block
static void write_cicm_block(const aaruformat_context *ctx)
Serialize the CICM XML metadata block to the image file.
Definition close.c:3677
write_aaru_json_block
static void write_aaru_json_block(const aaruformat_context *ctx)
Serialize the Aaru metadata JSON block to the image file.
Definition close.c:3814
write_mode2_subheaders_block
static void write_mode2_subheaders_block(aaruformat_context *ctx)
Serialize a MODE 2 (XA) subheaders data block.
Definition close.c:850
write_sector_subchannel
static void write_sector_subchannel(const aaruformat_context *ctx)
Serialize the per-sector subchannel or tag data block.
Definition close.c:1508
write_sector_prefix_ddt
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:1206
write_tape_ddt
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:596
write_cached_secondary_ddt
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
write_metadata_block
static void write_metadata_block(aaruformat_context *ctx)
Serialize the metadata block containing image and media descriptive information.
Definition close.c:3164
write_sector_prefix
static void write_sector_prefix(aaruformat_context *ctx)
Serialize the optional CD sector prefix block.
Definition close.c:966
write_dvd_title_key_decrypted_block
static void write_dvd_title_key_decrypted_block(const aaruformat_context *ctx)
Serialize the DVD decrypted title key data block to the image file.
Definition close.c:2247
write_tape_file_block
static void write_tape_file_block(const aaruformat_context *ctx)
Serialize the tape file metadata block to the image file.
Definition close.c:2671
LZMA_PROPERTIES_LENGTH
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
AARU_MAGIC
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
MD5_DIGEST_LENGTH
#define MD5_DIGEST_LENGTH
Definition context.h:69
tapeFileHashEntry
struct TapeFileHashEntry tapeFileHashEntry
AARU_CALL
#define AARU_CALL
Definition decls.h:45
aaruf_sha1_final
void aaruf_sha1_final(sha1_ctx *ctx, unsigned char *result)
Definition sha1.c:124
aaruf_cst_transform
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
aaruf_lzma_encode_buffer
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
aaruf_crc64_data
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
aaruf_crc64_update
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
aaruf_sha256_final
void aaruf_sha256_final(sha256_ctx *ctx, unsigned char *result)
Definition sha256.c:115
aaruf_crc64_init
crc64_ctx * aaruf_crc64_init()
Initializes a CRC64 context.
Definition crc64.c:32
aaruf_md5_final
void aaruf_md5_final(md5_ctx *ctx, unsigned char *result)
Definition md5.c:485
aaruf_spamsum_free
void aaruf_spamsum_free(spamsum_ctx *ctx)
Frees a spamsum (fuzzy hash) context.
Definition spamsum.c:75
aaruf_get_datatype_for_media_tag_type
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
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:54
aaruf_spamsum_final
int aaruf_spamsum_final(spamsum_ctx *ctx, uint8_t *result)
Definition spamsum.c:191
aaruf_crc64_final
int aaruf_crc64_final(crc64_ctx *ctx, uint64_t *crc)
Computes the final CRC64 value from the context.
Definition crc64.c:141
IndexBlock3
@ IndexBlock3
Block containing the index v3.
Definition enums.h:147
ChecksumBlock
@ ChecksumBlock
Block containing contents checksums.
Definition enums.h:152
DataBlock
@ DataBlock
Block containing data.
Definition enums.h:141
TapePartitionBlock
@ TapePartitionBlock
Block containing list of partitions for a tape image.
Definition enums.h:158
GeometryBlock
@ GeometryBlock
Block containing logical geometry.
Definition enums.h:148
DeDuplicationTableSecondary
@ DeDuplicationTableSecondary
Block containing a secondary deduplication table (v2).
Definition enums.h:144
AaruMetadataJsonBlock
@ AaruMetadataJsonBlock
Block containing JSON version of Aaru Metadata.
Definition enums.h:159
CicmBlock
@ CicmBlock
Block containing CICM XML metadata.
Definition enums.h:151
DeDuplicationTable2
@ DeDuplicationTable2
Block containing a deduplication table v2.
Definition enums.h:143
TapeFileBlock
@ TapeFileBlock
Block containing list of files for a tape image.
Definition enums.h:157
DumpHardwareBlock
@ DumpHardwareBlock
Block containing an array of hardware used to create the image.
Definition enums.h:156
MetadataBlock
@ MetadataBlock
Block containing metadata.
Definition enums.h:149
TracksBlock
@ TracksBlock
Block containing optical disc tracks.
Definition enums.h:150
OpticalDisc
@ OpticalDisc
Purely optical discs.
Definition enums.h:218
BlockMedia
@ BlockMedia
Media that is physically block-based or abstracted like that.
Definition enums.h:219
Blake3
@ Blake3
BLAKE3 hash.
Definition enums.h:173
Sha1
@ Sha1
SHA-1 hash.
Definition enums.h:170
Md5
@ Md5
MD5 hash.
Definition enums.h:169
Sha256
@ Sha256
SHA-256 hash.
Definition enums.h:171
SpamSum
@ SpamSum
SpamSum (context-triggered piecewise hash).
Definition enums.h:172
CdSectorSubchannel
@ CdSectorSubchannel
Compact Disc subchannel data.
Definition enums.h:116
AppleProfileTag
@ AppleProfileTag
Apple Profile (20‑byte) tag.
Definition enums.h:117
DvdSectorIed
@ DvdSectorIed
DVD ID Error Detection Code (IED)
Definition enums.h:129
DvdSectorCprMai
@ DvdSectorCprMai
DVD Copyright Management Information (CPR_MAI)
Definition enums.h:126
AppleSonyTag
@ AppleSonyTag
Apple Sony (12‑byte) tag.
Definition enums.h:118
CdSectorPrefix
@ CdSectorPrefix
Compact Disc sector prefix (sync, header).
Definition enums.h:114
PriamDataTowerTag
@ PriamDataTowerTag
Priam Data Tower (24‑byte) tag.
Definition enums.h:119
UserData
@ UserData
User (main) data.
Definition enums.h:46
DvdSectorEdc
@ DvdSectorEdc
DVD Error Detection Code (EDC)
Definition enums.h:130
DvdSectorTitleKeyDecrypted
@ DvdSectorTitleKeyDecrypted
Decrypted DVD Title Key.
Definition enums.h:127
DvdSectorId
@ DvdSectorId
DVD Identification Data (ID)
Definition enums.h:128
CdSectorSuffix
@ CdSectorSuffix
Compact Disc sector suffix (EDC, ECC P, ECC Q).
Definition enums.h:115
CompactDiscMode2Subheader
@ CompactDiscMode2Subheader
Compact Disc MODE 2 subheader.
Definition enums.h:123
AARU_FEATURE_RW_BLAKE3
@ AARU_FEATURE_RW_BLAKE3
BLAKE3 checksum is present (read/write support for BLAKE3 hashes).
Definition enums.h:265
AARUF_STATUS_INVALID_CONTEXT
@ AARUF_STATUS_INVALID_CONTEXT
Provided context/handle is invalid.
Definition enums.h:209
Lzma
@ Lzma
LZMA compression.
Definition enums.h:34
LzmaClauniaSubchannelTransform
@ LzmaClauniaSubchannelTransform
LZMA applied to Claunia Subchannel Transform processed data.
Definition enums.h:36
None
@ None
Not compressed.
Definition enums.h:33
AARUF_STATUS_OK
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:75
AARUF_ERROR_NOT_ENOUGH_MEMORY
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
AARUF_ERROR_CANNOT_WRITE_HEADER
#define AARUF_ERROR_CANNOT_WRITE_HEADER
Failure writing container header.
Definition errors.h:60
AppleProfile
@ AppleProfile
Definition aaru.h:698
AppleSonySS
@ AppleSonySS
3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:247
PriamDataTower
@ PriamDataTower
Definition aaru.h:701
AppleFileWare
@ AppleFileWare
5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy
Definition aaru.h:249
AppleSonyDS
@ AppleSonyDS
3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR
Definition aaru.h:248
free_map
void free_map(hash_map_t *map)
Frees all memory associated with a hash map.
Definition hash_map.c:73
internal.h
aaruf_close_current_block
int32_t aaruf_close_current_block(aaruformat_context *ctx)
Finalizes and writes the current data block to the AaruFormat image file.
Definition write.c:1403
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
SHA1_DIGEST_LENGTH
#define SHA1_DIGEST_LENGTH
Definition sha1.h:39
SHA256_DIGEST_LENGTH
#define SHA256_DIGEST_LENGTH
Definition sha256.h:38
FUZZY_MAX_RESULT
#define FUZZY_MAX_RESULT
Definition spamsum.h:30
AaruHeaderV2
Version 2 container header with GUID, alignment shifts, and feature negotiation bitmaps.
Definition header.h:107
AaruHeaderV2::featureCompatible
uint64_t featureCompatible
Feature bits: unimplemented bits are ignorable (still R/W safe).
Definition header.h:122
AaruHeaderV2::indexOffset
uint64_t indexOffset
Absolute byte offset to primary index block (MUST be > 0; 0 => corrupt/unreadable).
Definition header.h:115
AaruMetadataJsonBlockHeader
Header for an Aaru metadata JSON block (identifier == BlockType::AaruMetadataJsonBlock).
Definition metadata.h:120
AaruMetadataJsonBlockHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::AaruMetadataJsonBlock.
Definition metadata.h:121
AaruMetadataJsonBlockHeader::length
uint32_t length
Length in bytes of the Aaru metadata JSON payload that follows.
Definition metadata.h:122
BlockHeader
Header preceding the compressed data payload of a data block (BlockType::DataBlock).
Definition data.h:71
BlockHeader::cmpLength
uint32_t cmpLength
Size in bytes of the compressed payload immediately following this header.
Definition data.h:76
BlockHeader::length
uint32_t length
Size in bytes of the uncompressed payload resulting after decompression.
Definition data.h:77
BlockHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::DataBlock.
Definition data.h:72
BlockHeader::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of the compressed payload (cmpLength bytes).
Definition data.h:78
BlockHeader::crc64
uint64_t crc64
CRC64-ECMA of the uncompressed payload (length bytes).
Definition data.h:79
BlockHeader::type
uint16_t type
Logical data classification (value from DataType).
Definition data.h:73
BlockHeader::compression
uint16_t compression
Compression algorithm used (value from CompressionType).
Definition data.h:74
ChecksumEntry
Per-checksum metadata immediately followed by the digest / signature bytes.
Definition checksum.h:91
ChecksumEntry::length
uint32_t length
Length in bytes of the digest that immediately follows this structure.
Definition checksum.h:93
ChecksumEntry::type
uint8_t type
Algorithm used (value from ChecksumAlgorithm).
Definition checksum.h:92
ChecksumHeader
Header that precedes the sequence of checksum entries for a checksum block.
Definition checksum.h:74
ChecksumHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::ChecksumBlock.
Definition checksum.h:75
ChecksumHeader::length
uint32_t length
Length in bytes of the payload (all entries + their digest data, excluding this header).
Definition checksum.h:76
ChecksumHeader::entries
uint8_t entries
Number of checksum entries that follow in the payload.
Definition checksum.h:77
Checksums::spamsum
uint8_t * spamsum
SpamSum fuzzy hash (ASCII), allocated length+1 with trailing 0.
Definition context.h:110
Checksums::hasSha256
bool hasSha256
True if sha256[] buffer populated.
Definition context.h:103
Checksums::sha1
uint8_t sha1[20]
SHA-1 digest (20 bytes).
Definition context.h:107
Checksums::sha256
uint8_t sha256[32]
SHA-256 digest (32 bytes).
Definition context.h:108
Checksums::md5
uint8_t md5[16]
MD5 digest (16 bytes).
Definition context.h:106
Checksums::hasSpamSum
bool hasSpamSum
True if spamsum pointer allocated and signature read.
Definition context.h:105
Checksums::hasSha1
bool hasSha1
True if sha1[] buffer populated.
Definition context.h:102
Checksums::blake3
uint8_t blake3[BLAKE3_OUT_LEN]
BLAKE3 digest (32 bytes).
Definition context.h:109
Checksums::hasMd5
bool hasMd5
True if md5[] buffer populated.
Definition context.h:101
Checksums::hasBlake3
bool hasBlake3
True if blake3[] buffer populated.
Definition context.h:104
CicmMetadataBlock
Header for a CICM XML metadata block (identifier == BlockType::CicmBlock).
Definition metadata.h:108
CicmMetadataBlock::length
uint32_t length
Length in bytes of the CICM metadata payload that follows.
Definition metadata.h:110
CicmMetadataBlock::identifier
uint32_t identifier
Block identifier, must be BlockType::CicmBlock.
Definition metadata.h:109
DdtHeader2
Header preceding a version 2 hierarchical deduplication table.
Definition ddt.h:142
DdtHeader2::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA of compressed table payload.
Definition ddt.h:161
DdtHeader2::type
uint16_t type
Data classification (DataType) for sectors referenced by this table.
Definition ddt.h:144
DdtHeader2::start
uint64_t start
Base internal index covered by this table (used for secondary tables; currently informational).
Definition ddt.h:153
DdtHeader2::overflow
uint16_t overflow
Trailing dumped sectors beyond user area (overflow range), still mapped with entries.
Definition ddt.h:151
DdtHeader2::crc64
uint64_t crc64
CRC64-ECMA of uncompressed table payload.
Definition ddt.h:162
DdtHeader2::entries
uint64_t entries
Number of entries contained in (uncompressed) table payload.
Definition ddt.h:158
DdtHeader2::levels
uint8_t levels
Total number of hierarchy levels (root depth); > 0.
Definition ddt.h:146
DdtHeader2::length
uint64_t length
Uncompressed payload size in bytes.
Definition ddt.h:160
DdtHeader2::identifier
uint32_t identifier
Block identifier, must be BlockType::DeDuplicationTable2.
Definition ddt.h:143
DdtHeader2::tableShift
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
DdtHeader2::blocks
uint64_t blocks
Total internal span (negative + usable + overflow) in logical sectors.
Definition ddt.h:150
DdtHeader2::negative
uint16_t negative
Leading negative LBA count; added to external L to build internal index.
Definition ddt.h:149
DdtHeader2::blockAlignmentShift
uint8_t blockAlignmentShift
2^blockAlignmentShift = block alignment boundary in bytes.
Definition ddt.h:154
DdtHeader2::tableLevel
uint8_t tableLevel
Zero-based level index of this table (0 = root, increases downward).
Definition ddt.h:147
DdtHeader2::compression
uint16_t compression
Compression algorithm for this table body (CompressionType).
Definition ddt.h:145
DdtHeader2::dataShift
uint8_t dataShift
2^dataShift = sectors represented per increment in blockIndex field.
Definition ddt.h:155
DdtHeader2::cmpLength
uint64_t cmpLength
Compressed payload size in bytes.
Definition ddt.h:159
DdtHeader2::previousLevelOffset
uint64_t previousLevelOffset
Absolute byte offset of the parent (previous) level table; 0 if root.
Definition ddt.h:148
DumpExtent
Inclusive [start,end] logical sector range contributed by a single hardware environment.
Definition context.h:334
DumpHardwareEntriesWithData::firmware
uint8_t * firmware
Firmware version string or NULL.
Definition context.h:321
DumpHardwareEntriesWithData::revision
uint8_t * revision
Hardware revision string or NULL.
Definition context.h:320
DumpHardwareEntriesWithData::model
uint8_t * model
Model string or NULL.
Definition context.h:319
DumpHardwareEntriesWithData::softwareName
uint8_t * softwareName
Dump software name or NULL.
Definition context.h:323
DumpHardwareEntriesWithData::extents
struct DumpExtent * extents
Array of extents (entry.extents elements) or NULL.
Definition context.h:317
DumpHardwareEntriesWithData::manufacturer
uint8_t * manufacturer
Manufacturer string (UTF-8) or NULL.
Definition context.h:318
DumpHardwareEntriesWithData::softwareVersion
uint8_t * softwareVersion
Dump software version or NULL.
Definition context.h:324
DumpHardwareEntriesWithData::serial
uint8_t * serial
Serial number string or NULL.
Definition context.h:322
DumpHardwareEntriesWithData::entry
DumpHardwareEntry entry
Fixed-size header with lengths & counts.
Definition context.h:316
DumpHardwareEntriesWithData::softwareOperatingSystem
uint8_t * softwareOperatingSystem
Host operating system string or NULL.
Definition context.h:325
DumpHardwareEntry
Per-environment length table describing subsequent UTF-8 strings and optional extent array.
Definition dump.h:113
DumpHardwareEntry::softwareNameLength
uint32_t softwareNameLength
Length in bytes of dumping software name string.
Definition dump.h:119
DumpHardwareEntry::manufacturerLength
uint32_t manufacturerLength
Length in bytes of manufacturer UTF-8 string.
Definition dump.h:114
DumpHardwareEntry::softwareVersionLength
uint32_t softwareVersionLength
Length in bytes of dumping software version string.
Definition dump.h:120
DumpHardwareEntry::firmwareLength
uint32_t firmwareLength
Length in bytes of firmware version string.
Definition dump.h:117
DumpHardwareEntry::extents
uint32_t extents
Number of DumpExtent records following the strings (0 = none).
Definition dump.h:122
DumpHardwareEntry::modelLength
uint32_t modelLength
Length in bytes of model UTF-8 string.
Definition dump.h:115
DumpHardwareEntry::serialLength
uint32_t serialLength
Length in bytes of device serial number string.
Definition dump.h:118
DumpHardwareEntry::revisionLength
uint32_t revisionLength
Length in bytes of revision / hardware revision string.
Definition dump.h:116
DumpHardwareEntry::softwareOperatingSystemLength
uint32_t softwareOperatingSystemLength
Length in bytes of host operating system string.
Definition dump.h:121
DumpHardwareHeader
Header that precedes a sequence of dump hardware entries and their variable-length payload.
Definition dump.h:91
DumpHardwareHeader::crc64
uint64_t crc64
CRC64-ECMA of the payload (byte-swapped for legacy v1 images, handled automatically).
Definition dump.h:95
DumpHardwareHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::DumpHardwareBlock.
Definition dump.h:92
DumpHardwareHeader::length
uint32_t length
Total payload bytes after this header (sum of entries, strings, and extents arrays).
Definition dump.h:94
DumpHardwareHeader::entries
uint16_t entries
Number of DumpHardwareEntry records that follow.
Definition dump.h:93
GeometryBlockHeader
Legacy CHS style logical geometry metadata (BlockType::GeometryBlock).
Definition data.h:91
GeometryBlockHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::GeometryBlock.
Definition data.h:92
ImageInfo::MediaType
uint32_t MediaType
Media type identifier (see MediaType enum; 0=Unknown)
Definition aaru.h:881
ImageInfo::MetadataMediaType
uint8_t MetadataMediaType
Media type for sidecar generation (internal archival use)
Definition aaru.h:882
ImageInfo::Sectors
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:874
IndexEntry
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
IndexEntry::blockType
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
IndexEntry::offset
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
IndexEntry::dataType
uint16_t dataType
Data classification (value from DataType) or unused for untyped blocks.
Definition index.h:111
IndexHeader3
Index header (version 3) adding hierarchical chaining (identifier == IndexBlock3).
Definition index.h:93
IndexHeader3::previous
uint64_t previous
File offset of a previous IndexBlock3 header (0 if none / root segment).
Definition index.h:97
IndexHeader3::identifier
uint32_t identifier
Block identifier (must be BlockType::IndexBlock3).
Definition index.h:94
IndexHeader3::crc64
uint64_t crc64
CRC64-ECMA of the local entries array (does NOT cover subindexes or previous chains).
Definition index.h:96
IndexHeader3::entries
uint64_t entries
Number of IndexEntry records that follow in this (sub)index block.
Definition index.h:95
MetadataBlockHeader
Header for a metadata block containing offsets and lengths to UTF-16LE descriptive strings.
Definition metadata.h:69
MetadataBlockHeader::commentsLength
uint32_t commentsLength
Length in bytes (including null) of comments string.
Definition metadata.h:78
MetadataBlockHeader::mediaSequence
int32_t mediaSequence
Sequence number within a multi-disc / multi-volume set (0-based or 1-based as producer defines).
Definition metadata.h:72
MetadataBlockHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::MetadataBlock.
Definition metadata.h:70
MetadataBlockHeader::mediaTitleOffset
uint32_t mediaTitleOffset
Offset to UTF-16LE media title string.
Definition metadata.h:79
MetadataBlockHeader::driveModelLength
uint32_t driveModelLength
Length in bytes (including null) of drive model string.
Definition metadata.h:94
MetadataBlockHeader::driveManufacturerLength
uint32_t driveManufacturerLength
Length in bytes (including null) of drive manufacturer string.
Definition metadata.h:92
MetadataBlockHeader::blockSize
uint32_t blockSize
Total size in bytes of the entire metadata block (header + strings).
Definition metadata.h:71
MetadataBlockHeader::driveModelOffset
uint32_t driveModelOffset
Offset to UTF-16LE drive model string.
Definition metadata.h:93
MetadataBlockHeader::mediaModelOffset
uint32_t mediaModelOffset
Offset to UTF-16LE media model string.
Definition metadata.h:83
MetadataBlockHeader::creatorOffset
uint32_t creatorOffset
Offset to UTF-16LE creator string (or undefined if creatorLength==0).
Definition metadata.h:75
MetadataBlockHeader::mediaTitleLength
uint32_t mediaTitleLength
Length in bytes (including null) of media title string.
Definition metadata.h:80
MetadataBlockHeader::mediaManufacturerOffset
uint32_t mediaManufacturerOffset
Offset to UTF-16LE media manufacturer string.
Definition metadata.h:81
MetadataBlockHeader::driveSerialNumberLength
uint32_t driveSerialNumberLength
Length in bytes (including null) of drive serial number string.
Definition metadata.h:96
MetadataBlockHeader::driveSerialNumberOffset
uint32_t driveSerialNumberOffset
Offset to UTF-16LE drive serial number string.
Definition metadata.h:95
MetadataBlockHeader::mediaManufacturerLength
uint32_t mediaManufacturerLength
Length in bytes (including null) of media manufacturer string.
Definition metadata.h:82
MetadataBlockHeader::mediaModelLength
uint32_t mediaModelLength
Length in bytes (including null) of media model string.
Definition metadata.h:84
MetadataBlockHeader::driveFirmwareRevisionOffset
uint32_t driveFirmwareRevisionOffset
Offset to UTF-16LE drive firmware revision string.
Definition metadata.h:97
MetadataBlockHeader::lastMediaSequence
int32_t lastMediaSequence
Total number of media in the set; 0 or 1 if single item.
Definition metadata.h:74
MetadataBlockHeader::commentsOffset
uint32_t commentsOffset
Offset to UTF-16LE comments string.
Definition metadata.h:77
MetadataBlockHeader::driveManufacturerOffset
uint32_t driveManufacturerOffset
Offset to UTF-16LE drive manufacturer string.
Definition metadata.h:91
MetadataBlockHeader::mediaSerialNumberOffset
uint32_t mediaSerialNumberOffset
Offset to UTF-16LE media serial number string.
Definition metadata.h:85
MetadataBlockHeader::mediaSerialNumberLength
uint32_t mediaSerialNumberLength
Length in bytes (including null) of media serial number string.
Definition metadata.h:86
MetadataBlockHeader::mediaPartNumberOffset
uint32_t mediaPartNumberOffset
Offset to UTF-16LE media part number string.
Definition metadata.h:89
MetadataBlockHeader::mediaPartNumberLength
uint32_t mediaPartNumberLength
Length in bytes (including null) of media part number string.
Definition metadata.h:90
MetadataBlockHeader::mediaBarcodeLength
uint32_t mediaBarcodeLength
Length in bytes (including null) of media barcode string.
Definition metadata.h:88
MetadataBlockHeader::creatorLength
uint32_t creatorLength
Length in bytes (including null) of creator string (0 if absent).
Definition metadata.h:76
MetadataBlockHeader::driveFirmwareRevisionLength
uint32_t driveFirmwareRevisionLength
Length in bytes (including null) of drive firmware revision string.
Definition metadata.h:98
MetadataBlockHeader::mediaBarcodeOffset
uint32_t mediaBarcodeOffset
Offset to UTF-16LE media barcode string.
Definition metadata.h:87
TapeDdtHashEntry
Definition context.h:141
TapeDdtHashEntry::key
uint64_t key
Key: sector address.
Definition context.h:142
TapeDdtHashEntry::value
uint64_t value
Value: DDT entry.
Definition context.h:143
TapeFileEntry
Describes a single logical file on a tape medium.
Definition tape.h:134
TapeFileHashEntry::fileEntry
TapeFileEntry fileEntry
The actual tape file data.
Definition context.h:129
TapeFileHeader
Header for a tape file metadata block containing file layout information.
Definition tape.h:238
TapeFileHeader::identifier
uint32_t identifier
Block type identifier.
Definition tape.h:239
TapeFileHeader::crc64
uint64_t crc64
CRC64-ECMA checksum of the entry data.
Definition tape.h:245
TapeFileHeader::length
uint64_t length
Size of entry data in bytes (excluding this header).
Definition tape.h:243
TapePartitionEntry
Describes a single physical partition on a tape medium.
Definition tape.h:320
TapePartitionHashEntry
Definition context.h:134
TapePartitionHashEntry::partitionEntry
TapePartitionEntry partitionEntry
The actual tape partition data.
Definition context.h:136
TapePartitionHeader
Header for a tape partition metadata block containing partition layout information.
Definition tape.h:441
TapePartitionHeader::crc64
uint64_t crc64
CRC64-ECMA checksum of the entry data.
Definition tape.h:448
TapePartitionHeader::length
uint64_t length
Size of entry data in bytes (excluding this header).
Definition tape.h:446
TapePartitionHeader::identifier
uint32_t identifier
Block type identifier.
Definition tape.h:442
TrackEntry
Single optical disc track descriptor (sequence, type, LBAs, session, ISRC, flags).
Definition optical.h:72
TracksHeader
Header for an optical tracks block listing track entries.
Definition optical.h:62
TracksHeader::entries
uint16_t entries
Number of TrackEntry records following this header.
Definition optical.h:64
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:172
aaruformat_context::media_barcode
uint8_t * media_barcode
Barcode of the media represented by the image.
Definition context.h:222
aaruformat_context::user_data_ddt_header
DdtHeader2 user_data_ddt_header
Active user data DDT v2 header (primary table meta).
Definition context.h:189
aaruformat_context::checksums
Checksums checksums
Whole-image checksums discovered.
Definition context.h:269
aaruformat_context::creator
uint8_t * creator
Who (person) created the image?
Definition context.h:216
aaruformat_context::deduplicate
bool deduplicate
Storage deduplication active (duplicates coalesce).
Definition context.h:299
aaruformat_context::compression_enabled
bool compression_enabled
True if block compression enabled (writing path).
Definition context.h:300
aaruformat_context::cicm_block
uint8_t * cicm_block
CICM XML payload.
Definition context.h:214
aaruformat_context::sector_cpr_mai
uint8_t * sector_cpr_mai
DVD sector CPR_MAI (6 bytes) if present.
Definition context.h:207
aaruformat_context::sector_hash_map
hash_map_t * sector_hash_map
Deduplication hash map (fingerprint->entry mapping).
Definition context.h:253
aaruformat_context::sector_prefix_corrected
uint8_t * sector_prefix_corrected
Corrected variant (post error correction) if stored.
Definition context.h:200
aaruformat_context::user_data_ddt
uint64_t * user_data_ddt
Legacy flat DDT pointer (NULL when using v2 mini/big arrays).
Definition context.h:181
aaruformat_context::sha256_context
sha256_ctx sha256_context
Opaque SHA-256 context for streaming updates.
Definition context.h:272
aaruformat_context::calculating_sha256
bool calculating_sha256
True if whole-image SHA-256 being calculated on-the-fly.
Definition context.h:275
aaruformat_context::drive_firmware_revision
uint8_t * drive_firmware_revision
Firmware revision of the drive used to read the media represented by the image.
Definition context.h:228
aaruformat_context::media_serial_number
uint8_t * media_serial_number
Serial number of the media represented by the image.
Definition context.h:221
aaruformat_context::sector_ied
uint8_t * sector_ied
DVD sector IED (2 bytes) if present.
Definition context.h:206
aaruformat_context::md5_context
md5_ctx md5_context
Opaque MD5 context for streaming updates.
Definition context.h:270
aaruformat_context::user_data_ddt2
uint64_t * user_data_ddt2
DDT entries (big variant) primary/secondary current.
Definition context.h:187
aaruformat_context::metadata_block_header
MetadataBlockHeader metadata_block_header
Metadata block header.
Definition context.h:230
aaruformat_context::sector_prefix
uint8_t * sector_prefix
Raw per-sector prefix (e.g., sync+header) uncorrected.
Definition context.h:199
aaruformat_context::sector_suffix_ddt2
uint64_t * sector_suffix_ddt2
CD sector suffix DDT V2.
Definition context.h:186
aaruformat_context::tape_files
tapeFileHashEntry * tape_files
Hash table root for tape files.
Definition context.h:303
aaruformat_context::cached_ddt_offset
uint64_t cached_ddt_offset
File offset of currently cached secondary DDT (0=none).
Definition context.h:190
aaruformat_context::is_tape
bool is_tape
True if the image is a tape image.
Definition context.h:305
aaruformat_context::sector_edc
uint8_t * sector_edc
DVD sector EDC (4 bytes) if present.
Definition context.h:208
aaruformat_context::calculating_sha1
bool calculating_sha1
True if whole-image SHA-1 being calculated on-the-fly.
Definition context.h:274
aaruformat_context::media_model
uint8_t * media_model
Model of the media represented by the image.
Definition context.h:220
aaruformat_context::drive_serial_number
uint8_t * drive_serial_number
Serial number of the drive used to read the media represented by the image.
Definition context.h:226
aaruformat_context::ecc_cd_context
CdEccContext * ecc_cd_context
CD ECC/EDC helper tables (allocated on demand).
Definition context.h:248
aaruformat_context::sector_suffix_ddt
uint32_t * sector_suffix_ddt
Legacy CD sector suffix DDT.
Definition context.h:184
aaruformat_context::drive_manufacturer
uint8_t * drive_manufacturer
Manufacturer of the drive used to read the media represented by the image.
Definition context.h:224
aaruformat_context::in_memory_ddt
bool in_memory_ddt
True if primary (and possibly secondary) DDT loaded.
Definition context.h:196
aaruformat_context::sector_suffix
uint8_t * sector_suffix
Raw per-sector suffix (EDC/ECC) uncorrected.
Definition context.h:201
aaruformat_context::header
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:175
aaruformat_context::is_writing
bool is_writing
True if context opened/created for writing.
Definition context.h:292
aaruformat_context::tape_ddt
TapeDdtHashEntry * tape_ddt
Hash table root for tape DDT entries.
Definition context.h:182
aaruformat_context::spamsum_context
spamsum_ctx * spamsum_context
Opaque SpamSum context for streaming updates.
Definition context.h:267
aaruformat_context::cicm_block_header
CicmMetadataBlock cicm_block_header
CICM metadata header (if present).
Definition context.h:231
aaruformat_context::sector_prefix_offset
size_t sector_prefix_offset
Current position in sector_prefix.
Definition context.h:286
aaruformat_context::drive_model
uint8_t * drive_model
Model of the drive used to read the media represented by the image.
Definition context.h:225
aaruformat_context::magic
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:174
aaruformat_context::writing_buffer
uint8_t * writing_buffer
Accumulation buffer for current block data.
Definition context.h:280
aaruformat_context::sector_prefix_ddt2
uint64_t * sector_prefix_ddt2
CD sector prefix DDT V2.
Definition context.h:185
aaruformat_context::calculating_spamsum
bool calculating_spamsum
True if whole-image SpamSum being calculated on-the-fly.
Definition context.h:276
aaruformat_context::primary_ddt_offset
uint64_t primary_ddt_offset
File offset of the primary DDT v2 table.
Definition context.h:192
aaruformat_context::mediaTags
mediaTagEntry * mediaTags
Hash table of extra media tags (uthash root).
Definition context.h:264
aaruformat_context::blake3_context
blake3_hasher * blake3_context
Opaque BLAKE3 context for streaming updates.
Definition context.h:268
aaruformat_context::calculating_blake3
bool calculating_blake3
True if whole-image BLAKE3 being calculated on-the-fly.
Definition context.h:277
aaruformat_context::dump_hardware_entries_with_data
struct DumpHardwareEntriesWithData * dump_hardware_entries_with_data
Array of dump hardware entries + strings.
Definition context.h:212
aaruformat_context::calculating_md5
bool calculating_md5
True if whole-image MD5 being calculated on-the-fly.
Definition context.h:273
aaruformat_context::geometry_block
GeometryBlockHeader geometry_block
Logical geometry block (if present).
Definition context.h:229
aaruformat_context::sector_suffix_offset
size_t sector_suffix_offset
Current position in sector_suffix.
Definition context.h:287
aaruformat_context::cached_secondary_ddt2
uint64_t * cached_secondary_ddt2
Cached secondary table (big entries) or NULL.
Definition context.h:188
aaruformat_context::json_block
uint8_t * json_block
JSON metadata block payload (UTF-8).
Definition context.h:215
aaruformat_context::media_part_number
uint8_t * media_part_number
Part number of the media represented by the image.
Definition context.h:223
aaruformat_context::sector_decrypted_title_key
uint8_t * sector_decrypted_title_key
DVD decrypted title key (5 bytes) if present.
Definition context.h:209
aaruformat_context::json_block_header
AaruMetadataJsonBlockHeader json_block_header
JSON metadata block header (if present).
Definition context.h:233
aaruformat_context::sector_subchannel
uint8_t * sector_subchannel
Raw 96-byte subchannel (if captured).
Definition context.h:203
aaruformat_context::comments
uint8_t * comments
Image comments.
Definition context.h:218
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:176
aaruformat_context::index_entries
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:252
aaruformat_context::mode2_subheaders
uint8_t * mode2_subheaders
MODE2 Form1/Form2 8-byte subheaders (concatenated).
Definition context.h:204
aaruformat_context::image_info
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:260
aaruformat_context::sector_id
uint8_t * sector_id
DVD sector ID (4 bytes) if present.
Definition context.h:205
aaruformat_context::dump_hardware_header
DumpHardwareHeader dump_hardware_header
Dump hardware header.
Definition context.h:232
aaruformat_context::sha1_context
sha1_ctx sha1_context
Opaque SHA-1 context for streaming updates.
Definition context.h:271
aaruformat_context::readableSectorTags
bool * readableSectorTags
Per-sector boolean array (optical tags read successfully?).
Definition context.h:263
aaruformat_context::tape_partitions
TapePartitionHashEntry * tape_partitions
Hash table root for tape partitions.
Definition context.h:304
aaruformat_context::sector_prefix_ddt
uint32_t * sector_prefix_ddt
Legacy CD sector prefix DDT (deprecated by *2).
Definition context.h:183
aaruformat_context::lzma_dict_size
uint32_t lzma_dict_size
LZMA dictionary size (writing path).
Definition context.h:298
aaruformat_context::track_entries
TrackEntry * track_entries
Full track list (tracksHeader.entries elements).
Definition context.h:242
aaruformat_context::sector_suffix_corrected
uint8_t * sector_suffix_corrected
Corrected suffix if stored separately.
Definition context.h:202
aaruformat_context::metadata_block
uint8_t * metadata_block
Raw metadata UTF-16LE concatenated strings.
Definition context.h:213
aaruformat_context::cached_ddt_position
uint64_t cached_ddt_position
Position index of cached secondary DDT.
Definition context.h:191
aaruformat_context::media_title
uint8_t * media_title
Title of the media represented by the image.
Definition context.h:217
aaruformat_context::mapped_memory_ddt_size
size_t mapped_memory_ddt_size
Length of mmapped DDT if userDataDdt is mmapped.
Definition context.h:193
aaruformat_context::media_manufacturer
uint8_t * media_manufacturer
Manufacturer of the media represented by the image.
Definition context.h:219
aaruformat_context::tracks_header
TracksHeader tracks_header
Tracks header (optical) if present.
Definition context.h:244
crc64_ctx
Minimal ECMA-182 CRC64 incremental state container (running value only).
Definition crc64.h:56
mediaTagEntry
Hash table entry for an arbitrary media tag (e.g., proprietary drive/medium descriptor).
Definition context.h:119
mediaTagEntry::data
uint8_t * data
Tag data blob (opaque to library core); length bytes long.
Definition context.h:120
mediaTagEntry::type
int32_t type
Numeric type identifier.
Definition context.h:121
mediaTagEntry::length
uint32_t length
Length in bytes of data.
Definition context.h:122