libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
flux.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 */
18
19#include <inttypes.h>
20#include <limits.h>
21#include <stdint.h>
22#include <stdlib.h>
23#include <string.h>
24#include "aaruformat/context.h"
25#include "aaruformat/errors.h"
27#include "consts.h"
28#include "decls.h"
29#include "log.h"
30#include "utarray.h"
31#include "uthash.h"
32
34{
36 uint32_t index;
37 UT_hash_handle hh;
38};
39
54static void flux_capture_record_dtor(void *element)
55{
56 if(element == NULL) return;
57
58 FluxCaptureRecord *record = element;
59 free(record->data_buffer);
60 free(record->index_buffer);
61 record->data_buffer = NULL;
62 record->index_buffer = NULL;
63}
64
65static const UT_icd FLUX_CAPTURE_RECORD_ICD = {sizeof(FluxCaptureRecord), NULL, NULL, flux_capture_record_dtor};
66
85{
86 if(ctx->flux_map == NULL) return;
87
90 HASH_ITER(hh, ctx->flux_map, entry, tmp)
91 {
92 HASH_DEL(ctx->flux_map, entry);
93 free(entry);
94 }
95
96 ctx->flux_map = NULL;
97}
98
122static int flux_map_add(aaruformat_context *ctx, const FluxCaptureKey *key, uint32_t index)
123{
124 FluxCaptureMapEntry *entry = NULL;
125 HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
126
127 if(entry == NULL)
128 {
129 entry = malloc(sizeof(FluxCaptureMapEntry));
130 if(entry == NULL) return -1;
131 entry->key = *key;
132 HASH_ADD(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
133 }
134
135 entry->index = index;
136 return 0;
137}
138
172{
173 flux_map_clear(ctx);
174
175 if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0) return AARUF_STATUS_OK;
176
177 for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
178 {
179 const FluxEntry *entry = &ctx->flux_entries[i];
180 FluxCaptureKey key = {entry->head, entry->track, entry->subtrack, entry->captureIndex};
181
182 if(flux_map_add(ctx, &key, i) != 0)
183 {
184 FATAL("Could not add flux capture to lookup map");
185 flux_map_clear(ctx);
187 }
188 }
189
190 return AARUF_STATUS_OK;
191}
192
308{
309 int pos = 0;
310 size_t read_bytes = 0;
311 uint64_t crc64 = 0;
312
313 // Check if the context and image stream are valid
314 if(ctx == NULL || ctx->imageStream == NULL)
315 {
316 FATAL("Invalid context or image stream.");
317 return;
318 }
319
320 if(ctx->flux_captures != NULL)
321 {
322 utarray_free(ctx->flux_captures);
323 ctx->flux_captures = NULL;
324 }
325
326 if(ctx->flux_entries != NULL)
327 {
328 free(ctx->flux_entries);
329 ctx->flux_entries = NULL;
330 }
331
332 flux_map_clear(ctx);
333
334 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
335
336 // Seek to block
337 pos = fseek(ctx->imageStream, entry->offset, SEEK_SET);
338 if(pos < 0 || ftell(ctx->imageStream) != entry->offset)
339 {
340 FATAL("Could not seek to %" PRIu64 " as indicated by index entry...\n", entry->offset);
341 return;
342 }
343
344 // Even if those two checks shall have been done before
345 read_bytes = fread(&ctx->flux_data_header, 1, sizeof(FluxHeader), ctx->imageStream);
346
347 if(read_bytes != sizeof(FluxHeader))
348 {
349 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
350 TRACE("Could not read flux data header, continuing...\n");
351 return;
352 }
353
355 {
356 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
357 TRACE("Incorrect identifier for flux data block at position %" PRIu64 "\n", entry->offset);
358 return;
359 }
360
362
363 ctx->flux_entries = (FluxEntry *)malloc(sizeof(FluxEntry) * ctx->flux_data_header.entries);
364
365 if(ctx->flux_entries == NULL)
366 {
367 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
368 FATAL("Could not allocate memory for flux data block, continuing...\n");
369 return;
370 }
371
372 read_bytes = fread(ctx->flux_entries, sizeof(FluxEntry), ctx->flux_data_header.entries, ctx->imageStream);
373
374 if(read_bytes != ctx->flux_data_header.entries)
375 {
376 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
377 free(ctx->flux_entries);
378 ctx->flux_entries = NULL;
379 FATAL("Could not read flux data block, continuing...\n");
380 return;
381 }
382
383 crc64 = aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
384
385 if(crc64 != ctx->flux_data_header.crc64)
386 {
387 TRACE("Incorrect CRC found: 0x%" PRIx64 " found, expected 0x%" PRIx64 ", continuing...\n", crc64,
389 return;
390 }
391
393 {
394 free(ctx->flux_entries);
395 ctx->flux_entries = NULL;
396 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
397 return;
398 }
399
400 TRACE("Found %d flux entries at position %" PRIu64 ".\n", ctx->flux_data_header.entries, entry->offset);
401}
402
416{
417 // If already loaded, nothing to do
418 if(ctx->flux_entries != NULL || ctx->flux_data_header.entries > 0)
419 {
420 return AARUF_STATUS_OK;
421 }
422
423 // Find FluxDataBlock in index
424 if(ctx->index_entries == NULL)
425 {
427 }
428
429 IndexEntry *entry = NULL;
430 for(unsigned int i = 0; i < utarray_len(ctx->index_entries); i++)
431 {
432 entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, i);
433 if(entry && entry->blockType == FluxDataBlock)
434 {
435 break;
436 }
437 entry = NULL;
438 }
439
440 if(entry == NULL)
441 {
443 }
444
445 // Load the flux data block
446 process_flux_data_block(ctx, entry);
447
448 // Check if loading was successful
449 if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0)
450 {
452 }
453
454 return AARUF_STATUS_OK;
455}
456
497AARU_EXPORT int32_t AARU_CALL aaruf_get_flux_captures(void *context, uint8_t *buffer, size_t *length)
498{
499 TRACE("Entering aaruf_get_flux_captures(%p, %p, %zu)", context, buffer, (length ? *length : 0));
500
501 // Check context is correct AaruFormat context
502 if(context == NULL)
503 {
504 FATAL("Invalid context");
506 }
507
508 aaruformat_context *ctx = (aaruformat_context *)context;
509
510 // Not a libaaruformat context
511 if(ctx->magic != AARU_MAGIC)
512 {
513 FATAL("Invalid context");
515 }
516
517 // Lazy load flux entries if not already loaded
518 int32_t res = ensure_flux_entries_loaded(ctx);
519 if(res != AARUF_STATUS_OK)
520 {
521 TRACE("Exiting aaruf_get_flux_captures() = %d", res);
522 return res;
523 }
524
525 if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
526 {
527 FATAL("Image contains no flux captures");
528 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
530 }
531
532 size_t required_length = ctx->flux_data_header.entries * sizeof(FluxCaptureMeta);
533
534 if(length == NULL)
535 {
536 TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
537 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
539 }
540
541 if(buffer == NULL || *length < required_length)
542 {
543 *length = required_length;
544 TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
545 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
547 }
548
549 FluxCaptureMeta *out_entries = (FluxCaptureMeta *)buffer;
550 for(uint16_t i = 0; i < ctx->flux_data_header.entries; i++)
551 {
552 const FluxEntry *entry = &ctx->flux_entries[i];
553 out_entries[i].head = entry->head;
554 out_entries[i].track = entry->track;
555 out_entries[i].subtrack = entry->subtrack;
556 out_entries[i].captureIndex = entry->captureIndex;
557 out_entries[i].indexResolution = entry->indexResolution;
558 out_entries[i].dataResolution = entry->dataResolution;
559 }
560
561 *length = required_length;
562
563 TRACE("Exiting aaruf_get_flux_captures(%p, %p, %zu) = AARUF_STATUS_OK", context, buffer, *length);
564 return AARUF_STATUS_OK;
565}
566
616AARU_EXPORT int32_t AARU_CALL aaruf_write_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack,
617 uint32_t capture_index, uint64_t data_resolution,
618 uint64_t index_resolution, const uint8_t *data,
619 uint32_t data_length, const uint8_t *index,
620 uint32_t index_length)
621{
622 TRACE("Entering aaruf_add_flux_capture(%p, %u, %u, %u, %u, %" PRIu64 ", %" PRIu64 ", %p, %u, %p, %u)", context,
623 head, track, subtrack, capture_index, data_resolution, index_resolution, data, data_length, index,
624 index_length);
625
626 if(context == NULL)
627 {
628 FATAL("Invalid context");
630 }
631
632 aaruformat_context *ctx = context;
633
634 if(ctx->magic != AARU_MAGIC)
635 {
636 FATAL("Invalid context");
638 }
639
640 if(!ctx->is_writing)
641 {
642 FATAL("Flux captures can only be added when writing");
643 return AARUF_READ_ONLY;
644 }
645
646 if((index_length != 0 && index == NULL) || (data_length != 0 && data == NULL))
647 {
648 FATAL("Invalid flux capture buffers");
650 }
651
652 if((uint64_t)data_length + index_length > UINT32_MAX)
653 {
654 FATAL("Flux capture too large (%" PRIu64 " bytes)", (uint64_t)data_length + index_length);
656 }
657
658 if(ctx->flux_captures == NULL)
659 {
660 utarray_new(ctx->flux_captures, &FLUX_CAPTURE_RECORD_ICD);
661 if(ctx->flux_captures == NULL)
662 {
663 FATAL("Could not allocate flux capture storage");
665 }
666 }
667
668 size_t existing_captures = utarray_len(ctx->flux_captures);
669 if(existing_captures >= UINT16_MAX)
670 {
671 FATAL("Flux capture limit exceeded (%zu >= %u)", existing_captures, UINT16_MAX);
673 }
674
675 uint8_t *data_copy = NULL;
676 uint8_t *index_copy = NULL;
677
678 if(data_length != 0)
679 {
680 data_copy = malloc(data_length);
681 if(data_copy == NULL)
682 {
683 FATAL("Could not allocate %u bytes for flux data", data_length);
685 }
686 memcpy(data_copy, data, data_length);
687 }
688
689 if(index_length != 0)
690 {
691 index_copy = malloc(index_length);
692 if(index_copy == NULL)
693 {
694 free(data_copy);
695 FATAL("Could not allocate %u bytes for flux index", index_length);
697 }
698 memcpy(index_copy, index, index_length);
699 }
700
701 FluxCaptureRecord record = {0};
702 record.entry.head = head;
703 record.entry.track = track;
704 record.entry.subtrack = subtrack;
705 record.entry.captureIndex = capture_index;
706 record.entry.dataResolution = data_resolution;
707 record.entry.indexResolution = index_resolution;
708 record.entry.indexOffset = data_length;
709 record.entry.payloadOffset = 0;
710 record.data_buffer = data_copy;
711 record.data_length = data_length;
712 record.index_buffer = index_copy;
713 record.index_length = index_length;
714
715 FluxEntry *new_entries = realloc(ctx->flux_entries, (existing_captures + 1) * sizeof(FluxEntry));
716 if(new_entries == NULL)
717 {
718 free(data_copy);
719 free(index_copy);
720 FATAL("Could not grow flux entry array");
722 }
723
724 ctx->flux_entries = new_entries;
725 utarray_push_back(ctx->flux_captures, &record);
726
727 ctx->flux_entries[existing_captures] = record.entry;
729 ctx->flux_data_header.entries = (uint16_t)(existing_captures + 1);
731 aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
732 ctx->dirty_flux_block = true;
733
734 FluxCaptureKey key = {head, track, subtrack, capture_index};
735 if(flux_map_add(ctx, &key, (uint32_t)existing_captures) != 0)
736 {
737 FATAL("Could not add flux capture to lookup map");
738
739 size_t len = utarray_len(ctx->flux_captures);
740 if(len > 0) utarray_erase(ctx->flux_captures, len - 1, 1);
741
742 if(existing_captures == 0)
743 {
744 free(ctx->flux_entries);
745 ctx->flux_entries = NULL;
746 }
747 else
748 {
749 FluxEntry *shrunk = realloc(ctx->flux_entries, existing_captures * sizeof(FluxEntry));
750 if(shrunk != NULL) ctx->flux_entries = shrunk;
751 }
752
753 ctx->flux_data_header.entries = (uint16_t)existing_captures;
754 ctx->flux_data_header.crc64 = existing_captures == 0 ? 0
755 : aaruf_crc64_data((const uint8_t *)ctx->flux_entries,
756 existing_captures * sizeof(FluxEntry));
758 }
759
760 TRACE("Exiting aaruf_add_flux_capture() = AARUF_STATUS_OK (captures=%u)", ctx->flux_data_header.entries);
761 return AARUF_STATUS_OK;
762}
763
791{
792 TRACE("Entering aaruf_clear_flux_captures(%p)", context);
793
794 if(context == NULL)
795 {
796 FATAL("Invalid context");
798 }
799
800 aaruformat_context *ctx = context;
801
802 if(ctx->magic != AARU_MAGIC)
803 {
804 FATAL("Invalid context");
806 }
807
808 if(ctx->flux_captures != NULL)
809 {
810 utarray_free(ctx->flux_captures);
811 ctx->flux_captures = NULL;
812 }
813
814 flux_map_clear(ctx);
815
816 free(ctx->flux_entries);
817 ctx->flux_entries = NULL;
818
819 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
820
821 TRACE("Exiting aaruf_clear_flux_captures() = AARUF_STATUS_OK");
822 return AARUF_STATUS_OK;
823}
824
834{
835 // Try lookup map first (O(1))
836 if(ctx->flux_map != NULL)
837 {
838 FluxCaptureMapEntry *map_entry = NULL;
839 HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), map_entry);
840 if(map_entry != NULL && map_entry->index < ctx->flux_data_header.entries)
841 return &ctx->flux_entries[map_entry->index];
842 }
843
844 // Fall back to linear search (O(n))
845 for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
846 {
847 const FluxEntry *entry = &ctx->flux_entries[i];
848 if(entry->head == key->head && entry->track == key->track && entry->subtrack == key->subtrack &&
849 entry->captureIndex == key->captureIndex)
850 return entry;
851 }
852
853 return NULL;
854}
855
865static int32_t read_flux_payload_header(const aaruformat_context *ctx, uint64_t payload_offset,
867{
868 if(fseek(ctx->imageStream, payload_offset, SEEK_SET) < 0)
869 {
870 FATAL("Could not seek to flux payload at offset %" PRIu64, payload_offset);
871 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
873 }
874
875 long file_position = ftell(ctx->imageStream);
876 if(file_position < 0 || (uint64_t)file_position != payload_offset)
877 {
878 FATAL("Invalid flux payload position (expected %" PRIu64 ", got %ld)", payload_offset, file_position);
879 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
881 }
882
883 size_t read_bytes = fread(header, 1, sizeof(DataStreamPayloadHeader), ctx->imageStream);
884 if(read_bytes != sizeof(DataStreamPayloadHeader))
885 {
886 FATAL("Could not read flux payload header at offset %" PRIu64, payload_offset);
887 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
889 }
890
892 {
893 FATAL("Incorrect identifier 0x%08" PRIx32 " for flux payload at offset %" PRIu64, header->identifier,
894 payload_offset);
895 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
897 }
898
899 if(header->dataType != FluxData)
900 {
901 FATAL("Incorrect data type %u for flux payload at offset %" PRIu64 " (expected FluxData)", header->dataType,
902 payload_offset);
903 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
905 }
906
907 return AARUF_STATUS_OK;
908}
909
921static int32_t read_uncompressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length,
922 uint8_t **cmp_buffer, uint8_t **payload)
923{
924 if(cmp_length != raw_length)
925 {
926 FATAL("Flux payload lengths mismatch for uncompressed block (cmp=%zu, raw=%zu)", cmp_length, raw_length);
927 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
929 }
930
931 if(cmp_length == 0)
932 {
933 *cmp_buffer = NULL;
934 *payload = NULL;
935 return AARUF_STATUS_OK;
936 }
937
938 *cmp_buffer = (uint8_t *)malloc(cmp_length);
939 if(*cmp_buffer == NULL)
940 {
941 FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
942 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
944 }
945
946 size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
947 if(read_bytes != cmp_length)
948 {
949 FATAL("Could not read %zu bytes of flux payload", cmp_length);
950 free(*cmp_buffer);
951 *cmp_buffer = NULL;
952 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
954 }
955
956 *payload = *cmp_buffer;
957 return AARUF_STATUS_OK;
958}
959
971static int32_t read_lzma_compressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length,
972 uint8_t **cmp_buffer, uint8_t **payload)
973{
974 if(cmp_length <= LZMA_PROPERTIES_LENGTH)
975 {
976 FATAL("Flux payload compressed length %zu too small for LZMA", cmp_length);
977 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
979 }
980
981 *cmp_buffer = (uint8_t *)malloc(cmp_length);
982 if(*cmp_buffer == NULL)
983 {
984 FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
985 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
987 }
988
989 size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
990 if(read_bytes != cmp_length)
991 {
992 FATAL("Could not read %zu bytes of flux payload", cmp_length);
993 free(*cmp_buffer);
994 *cmp_buffer = NULL;
995 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
997 }
998
999 if(raw_length == 0)
1000 {
1001 *payload = NULL;
1002 return AARUF_STATUS_OK;
1003 }
1004
1005 *payload = (uint8_t *)malloc(raw_length);
1006 if(*payload == NULL)
1007 {
1008 FATAL("Could not allocate %zu bytes for decompressed flux payload", raw_length);
1009 free(*cmp_buffer);
1010 *cmp_buffer = NULL;
1011 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
1013 }
1014
1015 size_t cmp_stream_len = cmp_length - LZMA_PROPERTIES_LENGTH;
1016 size_t dst_len = raw_length;
1017 size_t src_len = cmp_stream_len;
1018 const uint8_t *cmp_props = *cmp_buffer;
1019 const uint8_t *cmp_stream = *cmp_buffer + LZMA_PROPERTIES_LENGTH;
1020 int32_t error_no =
1021 aaruf_lzma_decode_buffer(*payload, &dst_len, cmp_stream, &src_len, cmp_props, LZMA_PROPERTIES_LENGTH);
1022 if(error_no != 0 || dst_len != raw_length)
1023 {
1024 FATAL("LZMA decompression failed for flux payload (err=%d, dst=%zu/%zu)", error_no, dst_len, raw_length);
1025 free(*payload);
1026 free(*cmp_buffer);
1027 *payload = NULL;
1028 *cmp_buffer = NULL;
1029 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
1031 }
1032
1033 return AARUF_STATUS_OK;
1034}
1035
1048static int32_t validate_flux_payload_crcs(const uint8_t *cmp_buffer, size_t cmp_length, const uint8_t *payload,
1049 size_t raw_length, uint64_t expected_cmp_crc, uint64_t expected_raw_crc)
1050{
1051 uint64_t cmp_crc = 0;
1052 if(cmp_length != 0 && cmp_buffer != NULL) cmp_crc = aaruf_crc64_data(cmp_buffer, cmp_length);
1053
1054 if(cmp_crc != expected_cmp_crc)
1055 {
1056 FATAL("Flux payload compressed CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_cmp_crc,
1057 cmp_crc);
1058 TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1060 }
1061
1062 uint64_t raw_crc = 0;
1063 if(raw_length != 0 && payload != NULL) raw_crc = aaruf_crc64_data(payload, raw_length);
1064
1065 if(raw_crc != expected_raw_crc)
1066 {
1067 FATAL("Flux payload raw CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_raw_crc, raw_crc);
1068 TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1070 }
1071
1072 return AARUF_STATUS_OK;
1073}
1074
1088static int32_t extract_flux_data_buffers(const FluxEntry *flux_entry, const uint8_t *payload, size_t raw_length,
1089 uint8_t *data_data, uint32_t *data_length, uint8_t *index_data,
1090 uint32_t *index_length)
1091{
1092 if(flux_entry->indexOffset > raw_length)
1093 {
1094 FATAL("Flux index offset %" PRIu64 " beyond payload length %zu", flux_entry->indexOffset, raw_length);
1095 TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1097 }
1098
1099 uint64_t data_length_required64 = flux_entry->indexOffset;
1100 uint64_t index_length_required64 = raw_length - flux_entry->indexOffset;
1101
1102 if(data_length_required64 > UINT32_MAX || index_length_required64 > UINT32_MAX)
1103 {
1104 FATAL("Flux payload section length exceeds 32-bit limits (data=%" PRIu64 ", index=%" PRIu64 ")",
1105 data_length_required64, index_length_required64);
1106 TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INCORRECT_DATA_SIZE\n");
1108 }
1109
1110 uint32_t data_required = (uint32_t)data_length_required64;
1111 uint32_t index_required = (uint32_t)index_length_required64;
1112
1113 uint32_t data_capacity = *data_length;
1114 uint32_t index_capacity = *index_length;
1115
1116 *data_length = data_required;
1117 *index_length = index_required;
1118
1119 if(data_data == NULL || index_data == NULL || data_capacity < data_required || index_capacity < index_required)
1120 {
1121 TRACE("Returning required flux capture sizes (data=%u, index=%u)\n", data_required, index_required);
1123 }
1124
1125 const uint8_t *index_ptr = payload ? payload + data_length_required64 : NULL;
1126 const uint8_t *data_ptr = payload;
1127
1128 if(data_required != 0 && data_ptr != NULL) memcpy(data_data, data_ptr, data_required);
1129 if(index_required != 0 && index_ptr != NULL) memcpy(index_data, index_ptr, index_required);
1130
1131 return AARUF_STATUS_OK;
1132}
1133
1201AARU_EXPORT int32_t AARU_CALL aaruf_read_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack,
1202 uint32_t capture_index, uint8_t *index_data,
1203 uint32_t *index_length, uint8_t *data_data, uint32_t *data_length)
1204{
1205 TRACE("Entering aaruf_read_flux_capture(%p, %u, %u, %u, %u, %p, %p, %p, %p)", context, head, track, subtrack,
1206 capture_index, index_data, index_length, data_data, data_length);
1207
1208 if(context == NULL)
1209 {
1210 FATAL("Invalid context");
1211 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
1213 }
1214
1215 aaruformat_context *ctx = (aaruformat_context *)context;
1216
1217 if(ctx->magic != AARU_MAGIC)
1218 {
1219 FATAL("Invalid context");
1220 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
1222 }
1223
1224 // Lazy load flux entries if not already loaded
1225 int32_t load_res = ensure_flux_entries_loaded(ctx);
1226 if(load_res != AARUF_STATUS_OK)
1227 {
1228 TRACE("Exiting aaruf_read_flux_capture() = %d", load_res);
1229 return load_res;
1230 }
1231
1232 if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
1233 {
1234 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
1236 }
1237
1238 if(index_length == NULL || data_length == NULL)
1239 {
1240 FATAL("index_length or data_length pointers are NULL");
1241 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_BUFFER_TOO_SMALL\n");
1243 }
1244
1245 if(ctx->imageStream == NULL)
1246 {
1247 FATAL("Invalid image stream");
1248 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT\n");
1250 }
1251
1252 FluxCaptureKey key = {head, track, subtrack, capture_index};
1253 const FluxEntry *flux_entry = find_flux_entry_by_key(ctx, &key);
1254 if(flux_entry == NULL)
1255 {
1256 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND\n");
1258 }
1259
1260 TRACE("Requested flux capture: head=%u track=%u subtrack=%u captureIndex=%u payloadOffset=%" PRIu64 "\n",
1261 flux_entry->head, flux_entry->track, flux_entry->subtrack, flux_entry->captureIndex,
1262 flux_entry->payloadOffset);
1263
1264 // Get block alignment shift from FluxHeader
1265 uint8_t block_alignment_shift = ctx->flux_data_header.blockAlignmentShift;
1266
1267 // Convert payloadOffset from block-aligned units to absolute file offset
1268 // payloadOffset is stored divided by (1 << blockAlignmentShift), consistent with DDT
1269 uint64_t absolute_payload_offset = flux_entry->payloadOffset << block_alignment_shift;
1270
1271 DataStreamPayloadHeader payload_header;
1272 int32_t res = read_flux_payload_header(ctx, absolute_payload_offset, &payload_header);
1273 if(res != AARUF_STATUS_OK)
1274 {
1275 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1276 return res;
1277 }
1278
1279 const CompressionType compression = (CompressionType)payload_header.compression;
1280 uint8_t *cmp_buffer = NULL;
1281 uint8_t *payload = NULL;
1282 size_t cmp_length = payload_header.cmpLength;
1283 size_t raw_length = payload_header.length;
1284
1285 if(compression == None)
1286 {
1287 res = read_uncompressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
1288 }
1289 else if(compression == Lzma)
1290 {
1291 res = read_lzma_compressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
1292 }
1293 else
1294 {
1295 FATAL("Unsupported flux payload compression type %u", payload_header.compression);
1296 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_UNSUPPORTED_COMPRESSION\n");
1298 }
1299
1300 if(res != AARUF_STATUS_OK)
1301 {
1302 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1303 return res;
1304 }
1305
1306 res = validate_flux_payload_crcs(cmp_buffer, cmp_length, payload, raw_length, payload_header.cmpCrc64,
1307 payload_header.crc64);
1308 if(res != AARUF_STATUS_OK)
1309 {
1310 if(payload != NULL && payload != cmp_buffer) free(payload);
1311 if(cmp_buffer != NULL) free(cmp_buffer);
1312 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1313 return res;
1314 }
1315
1316 res = extract_flux_data_buffers(flux_entry, payload, raw_length, data_data, data_length, index_data,
1317 index_length);
1318 if(res != AARUF_STATUS_OK)
1319 {
1320 if(payload != NULL && payload != cmp_buffer) free(payload);
1321 if(cmp_buffer != NULL) free(cmp_buffer);
1322 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1323 return res;
1324 }
1325
1326 if(payload != NULL && payload != cmp_buffer) free(payload);
1327 if(cmp_buffer != NULL) free(cmp_buffer);
1328
1329 TRACE("Exiting aaruf_read_flux_capture() = AARUF_STATUS_OK\n");
1330 return AARUF_STATUS_OK;
1331}
Core public constants and compile‑time limits for the Aaru container format implementation.
#define LZMA_PROPERTIES_LENGTH
Size in bytes of the fixed LZMA properties header (lc/lp/pb + dictionary size).
Definition consts.h:82
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
Central runtime context structures for libaaruformat (image state, caches, checksum buffers).
#define AARU_CALL
Definition decls.h:46
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
int32_t aaruf_lzma_decode_buffer(uint8_t *dst_buffer, size_t *dst_size, const uint8_t *src_buffer, size_t *src_size, const uint8_t *props, size_t props_size)
Decodes an LZMA-compressed buffer.
Definition lzma.c:39
#define AARU_EXPORT
Definition decls.h:55
@ FluxDataBlock
Block containing flux data metadata.
Definition enums.h:164
@ DataStreamPayloadBlock
Block containing compressed data stream payload (e.g., flux data, bitstreams).
Definition enums.h:165
@ FluxData
Flux data.
Definition enums.h:134
CompressionType
List of known compression types.
Definition enums.h:32
@ Lzma
LZMA compression.
Definition enums.h:34
@ None
Not compressed.
Definition enums.h:33
Public error and status code definitions for libaaruformat.
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:77
#define AARUF_READ_ONLY
Operation requires write mode but context is read-only.
Definition errors.h:61
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
#define AARUF_ERROR_CANNOT_READ_BLOCK
Generic block read failure (seek/read error).
Definition errors.h:46
#define AARUF_ERROR_INCORRECT_DATA_SIZE
Data size does not match expected size.
Definition errors.h:65
#define AARUF_ERROR_INVALID_BLOCK_CRC
CRC64 mismatch indicating corruption.
Definition errors.h:57
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
#define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK
Decompression routine failed or size mismatch.
Definition errors.h:56
#define AARUF_ERROR_FLUX_DATA_NOT_FOUND
Requested flux data not present in image.
Definition errors.h:71
#define AARUF_ERROR_BUFFER_TOO_SMALL
Caller-supplied buffer insufficient for data.
Definition errors.h:49
#define AARUF_ERROR_UNSUPPORTED_COMPRESSION
Block marked with unsupported compression algorithm.
Definition errors.h:47
static int32_t validate_flux_payload_crcs(const uint8_t *cmp_buffer, size_t cmp_length, const uint8_t *payload, size_t raw_length, uint64_t expected_cmp_crc, uint64_t expected_raw_crc)
Validate CRC64 checksums for a flux payload block.
Definition flux.c:1048
static const FluxEntry * find_flux_entry_by_key(const aaruformat_context *ctx, const FluxCaptureKey *key)
Find a flux entry by its identifier key.
Definition flux.c:833
static int32_t extract_flux_data_buffers(const FluxEntry *flux_entry, const uint8_t *payload, size_t raw_length, uint8_t *data_data, uint32_t *data_length, uint8_t *index_data, uint32_t *index_length)
Extract data and index buffers from decompressed payload and copy to output buffers.
Definition flux.c:1088
static int flux_map_add(aaruformat_context *ctx, const FluxCaptureKey *key, uint32_t index)
Add or update a flux capture entry in the lookup map.
Definition flux.c:122
int32_t aaruf_clear_flux_captures(void *context)
Clear all flux captures from the context.
Definition flux.c:790
int32_t aaruf_read_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack, uint32_t capture_index, uint8_t *index_data, uint32_t *index_length, uint8_t *data_data, uint32_t *data_length)
Read a specific flux capture's data and index buffers from the image.
Definition flux.c:1201
static void flux_capture_record_dtor(void *element)
Destructor callback for FluxCaptureRecord elements in a utarray.
Definition flux.c:54
int32_t aaruf_get_flux_captures(void *context, uint8_t *buffer, size_t *length)
Retrieve metadata for all flux captures in the image.
Definition flux.c:497
static void flux_map_clear(aaruformat_context *ctx)
Clear and deallocate the flux capture lookup map.
Definition flux.c:84
static int32_t read_lzma_compressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length, uint8_t **cmp_buffer, uint8_t **payload)
Read and decompress an LZMA-compressed flux payload from the image stream.
Definition flux.c:971
static int32_t ensure_flux_entries_loaded(aaruformat_context *ctx)
Lazy load flux data block if not already loaded.
Definition flux.c:415
static int32_t read_uncompressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length, uint8_t **cmp_buffer, uint8_t **payload)
Read an uncompressed flux payload from the image stream.
Definition flux.c:921
void process_flux_data_block(aaruformat_context *ctx, const IndexEntry *entry)
Parse and integrate a Flux Data block from the image stream into the context.
Definition flux.c:307
static int32_t read_flux_payload_header(const aaruformat_context *ctx, uint64_t payload_offset, DataStreamPayloadHeader *header)
Read and validate a flux payload block header from the image stream.
Definition flux.c:865
int32_t aaruf_write_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack, uint32_t capture_index, uint64_t data_resolution, uint64_t index_resolution, const uint8_t *data, uint32_t data_length, const uint8_t *index, uint32_t index_length)
Add a flux capture to the image during write mode.
Definition flux.c:616
int32_t flux_map_rebuild_from_entries(aaruformat_context *ctx)
Rebuild the flux capture lookup map from the flux_entries array.
Definition flux.c:171
static const UT_icd FLUX_CAPTURE_RECORD_ICD
Definition flux.c:65
On‑disk index block header and entry structures (versions 1, 2 and 3).
#define FATAL(fmt,...)
Definition log.h:40
#define TRACE(fmt,...)
Definition log.h:25
Header structure for a DataStreamPayloadBlock containing data stream payload.
Definition flux.h:243
uint16_t dataType
Data type classification (value from DataType), e.g., FluxData or BitstreamData.
Definition flux.h:245
uint32_t cmpLength
Compressed length in bytes (includes LZMA properties if compression = Lzma).
Definition flux.h:247
uint64_t cmpCrc64
CRC64-ECMA checksum of the compressed payload (or same as crc64 if uncompressed).
Definition flux.h:249
uint32_t identifier
Block identifier, must be BlockType::DataStreamPayloadBlock (0x4C505344, "DSPL").
Definition flux.h:244
uint16_t compression
Compression type: 0 = None, 1 = Lzma.
Definition flux.h:246
uint32_t length
Uncompressed length in bytes.
Definition flux.h:248
uint64_t crc64
CRC64-ECMA checksum of the uncompressed payload.
Definition flux.h:250
Key structure for flux capture lookup map.
Definition flux.h:297
uint16_t track
Track number identifying the capture location.
Definition flux.h:299
uint8_t subtrack
Subtrack number identifying the capture location.
Definition flux.h:300
uint32_t head
Head number identifying the capture location.
Definition flux.h:298
uint32_t captureIndex
Capture index, allowing multiple captures for the same location.
Definition flux.h:301
Internal hash table entry for flux capture lookup.
Definition flux.c:34
FluxCaptureKey key
Definition flux.c:35
UT_hash_handle hh
Definition flux.c:37
uint32_t index
Definition flux.c:36
Metadata structure returned by aaruf_get_flux_captures().
Definition flux.h:183
uint32_t head
Head number the flux capture corresponds to.
Definition flux.h:184
uint64_t indexResolution
Resolution in picoseconds at which the index stream was sampled.
Definition flux.h:188
uint32_t captureIndex
Capture index, allowing multiple captures for the same location.
Definition flux.h:187
uint8_t subtrack
Subtrack number the flux capture corresponds to.
Definition flux.h:186
uint64_t dataResolution
Resolution in picoseconds at which the data stream was sampled.
Definition flux.h:189
uint16_t track
Track number the flux capture corresponds to.
Definition flux.h:185
Internal structure for storing flux capture data during write mode.
Definition flux.h:271
FluxEntry entry
Flux entry metadata describing this capture.
Definition flux.h:272
uint32_t data_length
Length of the data buffer in bytes.
Definition flux.h:274
uint32_t index_length
Length of the index buffer in bytes.
Definition flux.h:276
uint8_t * index_buffer
Pointer to the flux index buffer. Owned by the utarray, freed automatically.
Definition flux.h:275
uint8_t * data_buffer
Pointer to the flux data buffer. Owned by the utarray, freed automatically.
Definition flux.h:273
Metadata entry describing a single flux capture in the FluxDataBlock.
Definition flux.h:156
uint64_t indexResolution
Resolution in picoseconds at which the index stream was sampled.
Definition flux.h:161
uint64_t dataResolution
Resolution in picoseconds at which the data stream was sampled.
Definition flux.h:162
uint32_t head
Head number the flux capture corresponds to. Typically 0 or 1 for double-sided media.
Definition flux.h:157
uint64_t payloadOffset
Block-aligned file offset where the DataStreamPayloadBlock containing this capture's data is stored,...
Definition flux.h:164
uint16_t track
Track number the flux capture corresponds to. Track numbering is format-dependent.
Definition flux.h:158
uint64_t indexOffset
Byte offset within the payload where the index buffer starts (equals data_length).
Definition flux.h:163
uint8_t subtrack
Subtrack number, allowing sub-stepping within a track. Used for fine positioning.
Definition flux.h:159
uint32_t captureIndex
Capture index, allowing multiple captures for the same location (e.g., multiple revolutions).
Definition flux.h:160
Header structure for a FluxDataBlock containing flux capture metadata.
Definition flux.h:117
uint32_t identifier
Block identifier, must be BlockType::FluxDataBlock (0x58554C46, "FLUX").
Definition flux.h:118
uint16_t entries
Number of FluxEntry records following this header. Maximum value: 65535.
Definition flux.h:119
uint64_t crc64
CRC64-ECMA checksum of the FluxEntry array (header excluded).
Definition flux.h:121
uint8_t blockAlignmentShift
Block alignment shift: 2^blockAlignmentShift = block alignment boundary in bytes.
Definition flux.h:120
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:935
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
uint32_t blockType
Block identifier of the referenced block (value from BlockType).
Definition index.h:110
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
FluxHeader flux_data_header
Flux data header (if present).
Definition context.h:311
UT_array * flux_captures
Pending flux capture payloads (write path).
Definition context.h:313
FluxCaptureMapEntry * flux_map
Hash map for flux capture lookup by head/track/subtrack/capture index.
Definition context.h:314
FluxEntry * flux_entries
Array of flux entries (flux_data_header.entries elements).
Definition context.h:312
bool is_writing
True if context opened/created for writing.
Definition context.h:295
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:177
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:255
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
bool dirty_flux_block
True if flux block should be written during close.
Definition context.h:339