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 "internal.h"
30#include "log.h"
31#include "utarray.h"
32#include "uthash.h"
33
35{
37 uint32_t index;
38 UT_hash_handle hh;
39};
40
55static void flux_capture_record_dtor(void *element)
56{
57 if(element == NULL) return;
58
59 FluxCaptureRecord *record = element;
60 free(record->data_buffer);
61 free(record->index_buffer);
62 record->data_buffer = NULL;
63 record->index_buffer = NULL;
64}
65
66static const UT_icd FLUX_CAPTURE_RECORD_ICD = {sizeof(FluxCaptureRecord), NULL, NULL, flux_capture_record_dtor};
67
86{
87 if(ctx->flux_map == NULL) return;
88
91 HASH_ITER(hh, ctx->flux_map, entry, tmp)
92 {
93 HASH_DEL(ctx->flux_map, entry);
94 free(entry);
95 }
96
97 ctx->flux_map = NULL;
98}
99
123static int flux_map_add(aaruformat_context *ctx, const FluxCaptureKey *key, uint32_t index)
124{
125 FluxCaptureMapEntry *entry = NULL;
126 HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
127
128 if(entry == NULL)
129 {
130 entry = malloc(sizeof(FluxCaptureMapEntry));
131 if(entry == NULL) return -1;
132 entry->key = *key;
133 HASH_ADD(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
134 }
135
136 entry->index = index;
137 return 0;
138}
139
173{
174 flux_map_clear(ctx);
175
176 if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0) return AARUF_STATUS_OK;
177
178 for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
179 {
180 const FluxEntry *entry = &ctx->flux_entries[i];
181 FluxCaptureKey key = {entry->head, entry->track, entry->subtrack, entry->captureIndex};
182
183 if(flux_map_add(ctx, &key, i) != 0)
184 {
185 FATAL("Could not add flux capture to lookup map");
186 flux_map_clear(ctx);
188 }
189 }
190
191 return AARUF_STATUS_OK;
192}
193
309{
310 int pos = 0;
311 size_t read_bytes = 0;
312 uint64_t crc64 = 0;
313
314 // Check if the context and image stream are valid
315 if(ctx == NULL || ctx->imageStream == NULL)
316 {
317 FATAL("Invalid context or image stream.");
318 return;
319 }
320
321 if(ctx->flux_captures != NULL)
322 {
323 utarray_free(ctx->flux_captures);
324 ctx->flux_captures = NULL;
325 }
326
327 if(ctx->flux_entries != NULL)
328 {
329 free(ctx->flux_entries);
330 ctx->flux_entries = NULL;
331 }
332
333 flux_map_clear(ctx);
334
335 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
336
337 // Seek to block
338 pos = aaruf_fseek(ctx->imageStream, (aaru_off_t)entry->offset, SEEK_SET);
339 if(pos < 0 || aaruf_ftell(ctx->imageStream) != (aaru_off_t)entry->offset)
340 {
341 FATAL("Could not seek to %" PRIu64 " as indicated by index entry...\n", entry->offset);
342 return;
343 }
344
345 // Even if those two checks shall have been done before
346 read_bytes = fread(&ctx->flux_data_header, 1, sizeof(FluxHeader), ctx->imageStream);
347
348 if(read_bytes != sizeof(FluxHeader))
349 {
350 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
351 TRACE("Could not read flux data header, continuing...\n");
352 return;
353 }
354
356 {
357 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
358 TRACE("Incorrect identifier for flux data block at position %" PRIu64 "\n", entry->offset);
359 return;
360 }
361
363
364 ctx->flux_entries = (FluxEntry *)malloc(sizeof(FluxEntry) * ctx->flux_data_header.entries);
365
366 if(ctx->flux_entries == NULL)
367 {
368 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
369 FATAL("Could not allocate memory for flux data block, continuing...\n");
370 return;
371 }
372
373 read_bytes = fread(ctx->flux_entries, sizeof(FluxEntry), ctx->flux_data_header.entries, ctx->imageStream);
374
375 if(read_bytes != ctx->flux_data_header.entries)
376 {
377 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
378 free(ctx->flux_entries);
379 ctx->flux_entries = NULL;
380 FATAL("Could not read flux data block, continuing...\n");
381 return;
382 }
383
384 crc64 = aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
385
386 if(crc64 != ctx->flux_data_header.crc64)
387 {
388 TRACE("Incorrect CRC found: 0x%" PRIx64 " found, expected 0x%" PRIx64 ", continuing...\n", crc64,
390 return;
391 }
392
394 {
395 free(ctx->flux_entries);
396 ctx->flux_entries = NULL;
397 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
398 return;
399 }
400
401 TRACE("Found %d flux entries at position %" PRIu64 ".\n", ctx->flux_data_header.entries, entry->offset);
402}
403
417{
418 // If already loaded, nothing to do
419 if(ctx->flux_entries != NULL || ctx->flux_data_header.entries > 0) { return AARUF_STATUS_OK; }
420
421 // Find FluxDataBlock in index
422 if(ctx->index_entries == NULL) { return AARUF_ERROR_FLUX_DATA_NOT_FOUND; }
423
424 IndexEntry *entry = NULL;
425 for(unsigned int i = 0; i < utarray_len(ctx->index_entries); i++)
426 {
427 entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, i);
428 if(entry && entry->blockType == FluxDataBlock) { break; }
429 entry = NULL;
430 }
431
432 if(entry == NULL) { return AARUF_ERROR_FLUX_DATA_NOT_FOUND; }
433
434 // Load the flux data block
435 process_flux_data_block(ctx, entry);
436
437 // Check if loading was successful
438 if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0) { return AARUF_ERROR_FLUX_DATA_NOT_FOUND; }
439
440 return AARUF_STATUS_OK;
441}
442
483AARU_EXPORT int32_t AARU_CALL aaruf_get_flux_captures(void *context, uint8_t *buffer, size_t *length)
484{
485 TRACE("Entering aaruf_get_flux_captures(%p, %p, %zu)", context, buffer, (length ? *length : 0));
486
487 // Check context is correct AaruFormat context
488 if(context == NULL)
489 {
490 FATAL("Invalid context");
492 }
493
494 aaruformat_context *ctx = (aaruformat_context *)context;
495
496 // Not a libaaruformat context
497 if(ctx->magic != AARU_MAGIC)
498 {
499 FATAL("Invalid context");
501 }
502
503 // Lazy load flux entries if not already loaded
504 int32_t res = ensure_flux_entries_loaded(ctx);
505 if(res != AARUF_STATUS_OK)
506 {
507 TRACE("Exiting aaruf_get_flux_captures() = %d", res);
508 return res;
509 }
510
511 if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
512 {
513 FATAL("Image contains no flux captures");
514 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
516 }
517
518 size_t required_length = ctx->flux_data_header.entries * sizeof(FluxCaptureMeta);
519
520 if(length == NULL)
521 {
522 TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
523 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
525 }
526
527 if(buffer == NULL || *length < required_length)
528 {
529 *length = required_length;
530 TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
531 TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
533 }
534
535 FluxCaptureMeta *out_entries = (FluxCaptureMeta *)buffer;
536 for(uint16_t i = 0; i < ctx->flux_data_header.entries; i++)
537 {
538 const FluxEntry *entry = &ctx->flux_entries[i];
539 out_entries[i].head = entry->head;
540 out_entries[i].track = entry->track;
541 out_entries[i].subtrack = entry->subtrack;
542 out_entries[i].captureIndex = entry->captureIndex;
543 out_entries[i].indexResolution = entry->indexResolution;
544 out_entries[i].dataResolution = entry->dataResolution;
545 }
546
547 *length = required_length;
548
549 TRACE("Exiting aaruf_get_flux_captures(%p, %p, %zu) = AARUF_STATUS_OK", context, buffer, *length);
550 return AARUF_STATUS_OK;
551}
552
602AARU_EXPORT int32_t AARU_CALL aaruf_write_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack,
603 uint32_t capture_index, uint64_t data_resolution,
604 uint64_t index_resolution, const uint8_t *data,
605 uint32_t data_length, const uint8_t *index,
606 uint32_t index_length)
607{
608 TRACE("Entering aaruf_add_flux_capture(%p, %u, %u, %u, %u, %" PRIu64 ", %" PRIu64 ", %p, %u, %p, %u)", context,
609 head, track, subtrack, capture_index, data_resolution, index_resolution, data, data_length, index,
610 index_length);
611
612 if(context == NULL)
613 {
614 FATAL("Invalid context");
616 }
617
618 aaruformat_context *ctx = context;
619
620 if(ctx->magic != AARU_MAGIC)
621 {
622 FATAL("Invalid context");
624 }
625
626 if(!ctx->is_writing)
627 {
628 FATAL("Flux captures can only be added when writing");
629 return AARUF_READ_ONLY;
630 }
631
632 if((index_length != 0 && index == NULL) || (data_length != 0 && data == NULL))
633 {
634 FATAL("Invalid flux capture buffers");
636 }
637
638 if((uint64_t)data_length + index_length > UINT32_MAX)
639 {
640 FATAL("Flux capture too large (%" PRIu64 " bytes)", (uint64_t)data_length + index_length);
642 }
643
644 if(ctx->flux_captures == NULL)
645 {
646 utarray_new(ctx->flux_captures, &FLUX_CAPTURE_RECORD_ICD);
647 if(ctx->flux_captures == NULL)
648 {
649 FATAL("Could not allocate flux capture storage");
651 }
652 }
653
654 size_t existing_captures = utarray_len(ctx->flux_captures);
655 if(existing_captures >= UINT16_MAX)
656 {
657 FATAL("Flux capture limit exceeded (%zu >= %u)", existing_captures, UINT16_MAX);
659 }
660
661 uint8_t *data_copy = NULL;
662 uint8_t *index_copy = NULL;
663
664 if(data_length != 0)
665 {
666 data_copy = malloc(data_length);
667 if(data_copy == NULL)
668 {
669 FATAL("Could not allocate %u bytes for flux data", data_length);
671 }
672 memcpy(data_copy, data, data_length);
673 }
674
675 if(index_length != 0)
676 {
677 index_copy = malloc(index_length);
678 if(index_copy == NULL)
679 {
680 free(data_copy);
681 FATAL("Could not allocate %u bytes for flux index", index_length);
683 }
684 memcpy(index_copy, index, index_length);
685 }
686
687 FluxCaptureRecord record = {0};
688 record.entry.head = head;
689 record.entry.track = track;
690 record.entry.subtrack = subtrack;
691 record.entry.captureIndex = capture_index;
692 record.entry.dataResolution = data_resolution;
693 record.entry.indexResolution = index_resolution;
694 record.entry.indexOffset = data_length;
695 record.entry.payloadOffset = 0;
696 record.data_buffer = data_copy;
697 record.data_length = data_length;
698 record.index_buffer = index_copy;
699 record.index_length = index_length;
700
701 FluxEntry *new_entries = realloc(ctx->flux_entries, (existing_captures + 1) * sizeof(FluxEntry));
702 if(new_entries == NULL)
703 {
704 free(data_copy);
705 free(index_copy);
706 FATAL("Could not grow flux entry array");
708 }
709
710 ctx->flux_entries = new_entries;
711 utarray_push_back(ctx->flux_captures, &record);
712
713 ctx->flux_entries[existing_captures] = record.entry;
715 ctx->flux_data_header.entries = (uint16_t)(existing_captures + 1);
717 aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
718 ctx->dirty_flux_block = true;
719
720 FluxCaptureKey key = {head, track, subtrack, capture_index};
721 if(flux_map_add(ctx, &key, (uint32_t)existing_captures) != 0)
722 {
723 FATAL("Could not add flux capture to lookup map");
724
725 size_t len = utarray_len(ctx->flux_captures);
726 if(len > 0) utarray_erase(ctx->flux_captures, len - 1, 1);
727
728 if(existing_captures == 0)
729 {
730 free(ctx->flux_entries);
731 ctx->flux_entries = NULL;
732 }
733 else
734 {
735 FluxEntry *shrunk = realloc(ctx->flux_entries, existing_captures * sizeof(FluxEntry));
736 if(shrunk != NULL) ctx->flux_entries = shrunk;
737 }
738
739 ctx->flux_data_header.entries = (uint16_t)existing_captures;
740 ctx->flux_data_header.crc64 = existing_captures == 0 ? 0
741 : aaruf_crc64_data((const uint8_t *)ctx->flux_entries,
742 existing_captures * sizeof(FluxEntry));
744 }
745
746 TRACE("Exiting aaruf_add_flux_capture() = AARUF_STATUS_OK (captures=%u)", ctx->flux_data_header.entries);
747 return AARUF_STATUS_OK;
748}
749
777{
778 TRACE("Entering aaruf_clear_flux_captures(%p)", context);
779
780 if(context == NULL)
781 {
782 FATAL("Invalid context");
784 }
785
786 aaruformat_context *ctx = context;
787
788 if(ctx->magic != AARU_MAGIC)
789 {
790 FATAL("Invalid context");
792 }
793
794 if(ctx->flux_captures != NULL)
795 {
796 utarray_free(ctx->flux_captures);
797 ctx->flux_captures = NULL;
798 }
799
800 flux_map_clear(ctx);
801
802 free(ctx->flux_entries);
803 ctx->flux_entries = NULL;
804
805 memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
806
807 TRACE("Exiting aaruf_clear_flux_captures() = AARUF_STATUS_OK");
808 return AARUF_STATUS_OK;
809}
810
820{
821 // Try lookup map first (O(1))
822 if(ctx->flux_map != NULL)
823 {
824 FluxCaptureMapEntry *map_entry = NULL;
825 HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), map_entry);
826 if(map_entry != NULL && map_entry->index < ctx->flux_data_header.entries)
827 return &ctx->flux_entries[map_entry->index];
828 }
829
830 // Fall back to linear search (O(n))
831 for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
832 {
833 const FluxEntry *entry = &ctx->flux_entries[i];
834 if(entry->head == key->head && entry->track == key->track && entry->subtrack == key->subtrack &&
835 entry->captureIndex == key->captureIndex)
836 return entry;
837 }
838
839 return NULL;
840}
841
851static int32_t read_flux_payload_header(const aaruformat_context *ctx, uint64_t payload_offset,
853{
854 if(aaruf_fseek(ctx->imageStream, (aaru_off_t)payload_offset, SEEK_SET) < 0)
855 {
856 FATAL("Could not seek to flux payload at offset %" PRIu64, payload_offset);
857 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
859 }
860
861 aaru_off_t file_position = aaruf_ftell(ctx->imageStream);
862 if(file_position < 0 || (uint64_t)file_position != payload_offset)
863 {
864 FATAL("Invalid flux payload position (expected %" PRIu64 ", got %ld)", payload_offset, file_position);
865 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
867 }
868
869 size_t read_bytes = fread(header, 1, sizeof(DataStreamPayloadHeader), ctx->imageStream);
870 if(read_bytes != sizeof(DataStreamPayloadHeader))
871 {
872 FATAL("Could not read flux payload header at offset %" PRIu64, payload_offset);
873 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
875 }
876
878 {
879 FATAL("Incorrect identifier 0x%08" PRIx32 " for flux payload at offset %" PRIu64, header->identifier,
880 payload_offset);
881 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
883 }
884
885 if(header->dataType != kDataTypeFluxData)
886 {
887 FATAL("Incorrect data type %u for flux payload at offset %" PRIu64 " (expected FluxData)", header->dataType,
888 payload_offset);
889 TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
891 }
892
893 return AARUF_STATUS_OK;
894}
895
907static int32_t read_uncompressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length,
908 uint8_t **cmp_buffer, uint8_t **payload)
909{
910 if(cmp_length != raw_length)
911 {
912 FATAL("Flux payload lengths mismatch for uncompressed block (cmp=%zu, raw=%zu)", cmp_length, raw_length);
913 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
915 }
916
917 if(cmp_length == 0)
918 {
919 *cmp_buffer = NULL;
920 *payload = NULL;
921 return AARUF_STATUS_OK;
922 }
923
924 *cmp_buffer = (uint8_t *)malloc(cmp_length);
925 if(*cmp_buffer == NULL)
926 {
927 FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
928 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
930 }
931
932 size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
933 if(read_bytes != cmp_length)
934 {
935 FATAL("Could not read %zu bytes of flux payload", cmp_length);
936 free(*cmp_buffer);
937 *cmp_buffer = NULL;
938 TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
940 }
941
942 *payload = *cmp_buffer;
943 return AARUF_STATUS_OK;
944}
945
958static int32_t read_lzma_compressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length,
959 uint8_t **cmp_buffer, uint8_t **payload)
960{
961 if(cmp_length <= LZMA_PROPERTIES_LENGTH)
962 {
963 FATAL("Flux payload compressed length %zu too small for LZMA", cmp_length);
964 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
966 }
967
968 *cmp_buffer = (uint8_t *)malloc(cmp_length);
969 if(*cmp_buffer == NULL)
970 {
971 FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
972 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
974 }
975
976 size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
977 if(read_bytes != cmp_length)
978 {
979 FATAL("Could not read %zu bytes of flux payload", cmp_length);
980 free(*cmp_buffer);
981 *cmp_buffer = NULL;
982 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
984 }
985
986 if(raw_length == 0)
987 {
988 *payload = NULL;
989 return AARUF_STATUS_OK;
990 }
991
992 *payload = (uint8_t *)malloc(raw_length);
993 if(*payload == NULL)
994 {
995 FATAL("Could not allocate %zu bytes for decompressed flux payload", raw_length);
996 free(*cmp_buffer);
997 *cmp_buffer = NULL;
998 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
1000 }
1001
1002 size_t cmp_stream_len = cmp_length - LZMA_PROPERTIES_LENGTH;
1003 size_t dst_len = raw_length;
1004 size_t src_len = cmp_stream_len;
1005 const uint8_t *cmp_props = *cmp_buffer;
1006 const uint8_t *cmp_stream = *cmp_buffer + LZMA_PROPERTIES_LENGTH;
1007 int32_t error_no =
1008 aaruf_lzma_decode_buffer(*payload, &dst_len, cmp_stream, &src_len, cmp_props, LZMA_PROPERTIES_LENGTH);
1009 if(error_no != 0 || dst_len != raw_length)
1010 {
1011 FATAL("LZMA decompression failed for flux payload (err=%d, dst=%zu/%zu)", error_no, dst_len, raw_length);
1012 free(*payload);
1013 free(*cmp_buffer);
1014 *payload = NULL;
1015 *cmp_buffer = NULL;
1016 TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
1018 }
1019
1020 return AARUF_STATUS_OK;
1021}
1022
1034static int32_t read_zstd_compressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length,
1035 uint8_t **cmp_buffer, uint8_t **payload)
1036{
1037 if(cmp_length == 0)
1038 {
1039 FATAL("Flux payload compressed length is zero for zstd");
1040 TRACE("Exiting read_zstd_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
1042 }
1043
1044 *cmp_buffer = (uint8_t *)malloc(cmp_length);
1045 if(*cmp_buffer == NULL)
1046 {
1047 FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
1048 TRACE("Exiting read_zstd_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
1050 }
1051
1052 size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
1053 if(read_bytes != cmp_length)
1054 {
1055 FATAL("Could not read %zu bytes of flux payload", cmp_length);
1056 free(*cmp_buffer);
1057 *cmp_buffer = NULL;
1058 TRACE("Exiting read_zstd_compressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
1060 }
1061
1062 if(raw_length == 0)
1063 {
1064 *payload = NULL;
1065 return AARUF_STATUS_OK;
1066 }
1067
1068 *payload = (uint8_t *)malloc(raw_length);
1069 if(*payload == NULL)
1070 {
1071 FATAL("Could not allocate %zu bytes for decompressed flux payload", raw_length);
1072 free(*cmp_buffer);
1073 *cmp_buffer = NULL;
1074 TRACE("Exiting read_zstd_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
1076 }
1077
1078 size_t dst_len = aaruf_zstd_decode_buffer(*payload, raw_length, *cmp_buffer, cmp_length);
1079 if(dst_len != raw_length)
1080 {
1081 FATAL("Zstd decompression failed for flux payload (dst=%zu/%zu)", dst_len, raw_length);
1082 free(*payload);
1083 free(*cmp_buffer);
1084 *payload = NULL;
1085 *cmp_buffer = NULL;
1086 TRACE("Exiting read_zstd_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
1088 }
1089
1090 return AARUF_STATUS_OK;
1091}
1092
1105static int32_t validate_flux_payload_crcs(const uint8_t *cmp_buffer, size_t cmp_length, const uint8_t *payload,
1106 size_t raw_length, uint64_t expected_cmp_crc, uint64_t expected_raw_crc)
1107{
1108 uint64_t cmp_crc = 0;
1109 if(cmp_length != 0 && cmp_buffer != NULL) cmp_crc = aaruf_crc64_data(cmp_buffer, cmp_length);
1110
1111 if(cmp_crc != expected_cmp_crc)
1112 {
1113 FATAL("Flux payload compressed CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_cmp_crc,
1114 cmp_crc);
1115 TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1117 }
1118
1119 uint64_t raw_crc = 0;
1120 if(raw_length != 0 && payload != NULL) raw_crc = aaruf_crc64_data(payload, raw_length);
1121
1122 if(raw_crc != expected_raw_crc)
1123 {
1124 FATAL("Flux payload raw CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_raw_crc, raw_crc);
1125 TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1127 }
1128
1129 return AARUF_STATUS_OK;
1130}
1131
1145static int32_t extract_flux_data_buffers(const FluxEntry *flux_entry, const uint8_t *payload, size_t raw_length,
1146 uint8_t *data_data, uint32_t *data_length, uint8_t *index_data,
1147 uint32_t *index_length)
1148{
1149 if(flux_entry->indexOffset > raw_length)
1150 {
1151 FATAL("Flux index offset %" PRIu64 " beyond payload length %zu", flux_entry->indexOffset, raw_length);
1152 TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
1154 }
1155
1156 uint64_t data_length_required64 = flux_entry->indexOffset;
1157 uint64_t index_length_required64 = raw_length - flux_entry->indexOffset;
1158
1159 if(data_length_required64 > UINT32_MAX || index_length_required64 > UINT32_MAX)
1160 {
1161 FATAL("Flux payload section length exceeds 32-bit limits (data=%" PRIu64 ", index=%" PRIu64 ")",
1162 data_length_required64, index_length_required64);
1163 TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INCORRECT_DATA_SIZE\n");
1165 }
1166
1167 uint32_t data_required = (uint32_t)data_length_required64;
1168 uint32_t index_required = (uint32_t)index_length_required64;
1169
1170 uint32_t data_capacity = *data_length;
1171 uint32_t index_capacity = *index_length;
1172
1173 *data_length = data_required;
1174 *index_length = index_required;
1175
1176 if(data_data == NULL || index_data == NULL || data_capacity < data_required || index_capacity < index_required)
1177 {
1178 TRACE("Returning required flux capture sizes (data=%u, index=%u)\n", data_required, index_required);
1180 }
1181
1182 const uint8_t *index_ptr = payload ? payload + data_length_required64 : NULL;
1183 const uint8_t *data_ptr = payload;
1184
1185 if(data_required != 0 && data_ptr != NULL) memcpy(data_data, data_ptr, data_required);
1186 if(index_required != 0 && index_ptr != NULL) memcpy(index_data, index_ptr, index_required);
1187
1188 return AARUF_STATUS_OK;
1189}
1190
1258AARU_EXPORT int32_t AARU_CALL aaruf_read_flux_capture(void *context, uint32_t head, uint16_t track, uint8_t subtrack,
1259 uint32_t capture_index, uint8_t *index_data,
1260 uint32_t *index_length, uint8_t *data_data, uint32_t *data_length)
1261{
1262 TRACE("Entering aaruf_read_flux_capture(%p, %u, %u, %u, %u, %p, %p, %p, %p)", context, head, track, subtrack,
1263 capture_index, index_data, index_length, data_data, data_length);
1264
1265 if(context == NULL)
1266 {
1267 FATAL("Invalid context");
1268 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
1270 }
1271
1272 aaruformat_context *ctx = (aaruformat_context *)context;
1273
1274 if(ctx->magic != AARU_MAGIC)
1275 {
1276 FATAL("Invalid context");
1277 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
1279 }
1280
1281 // Lazy load flux entries if not already loaded
1282 int32_t load_res = ensure_flux_entries_loaded(ctx);
1283 if(load_res != AARUF_STATUS_OK)
1284 {
1285 TRACE("Exiting aaruf_read_flux_capture() = %d", load_res);
1286 return load_res;
1287 }
1288
1289 if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
1290 {
1291 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
1293 }
1294
1295 if(index_length == NULL || data_length == NULL)
1296 {
1297 FATAL("index_length or data_length pointers are NULL");
1298 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_BUFFER_TOO_SMALL\n");
1300 }
1301
1302 if(ctx->imageStream == NULL)
1303 {
1304 FATAL("Invalid image stream");
1305 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT\n");
1307 }
1308
1309 FluxCaptureKey key = {head, track, subtrack, capture_index};
1310 const FluxEntry *flux_entry = find_flux_entry_by_key(ctx, &key);
1311 if(flux_entry == NULL)
1312 {
1313 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND\n");
1315 }
1316
1317 TRACE("Requested flux capture: head=%u track=%u subtrack=%u captureIndex=%u payloadOffset=%" PRIu64 "\n",
1318 flux_entry->head, flux_entry->track, flux_entry->subtrack, flux_entry->captureIndex,
1319 flux_entry->payloadOffset);
1320
1321 // Get block alignment shift from FluxHeader
1322 uint8_t block_alignment_shift = ctx->flux_data_header.blockAlignmentShift;
1323
1324 // Convert payloadOffset from block-aligned units to absolute file offset
1325 // payloadOffset is stored divided by (1 << blockAlignmentShift), consistent with DDT
1326 uint64_t absolute_payload_offset = flux_entry->payloadOffset << block_alignment_shift;
1327
1328 DataStreamPayloadHeader payload_header;
1329 int32_t res = read_flux_payload_header(ctx, absolute_payload_offset, &payload_header);
1330 if(res != AARUF_STATUS_OK)
1331 {
1332 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1333 return res;
1334 }
1335
1336 const CompressionType compression = (CompressionType)payload_header.compression;
1337 uint8_t *cmp_buffer = NULL;
1338 uint8_t *payload = NULL;
1339 size_t cmp_length = payload_header.cmpLength;
1340 size_t raw_length = payload_header.length;
1341
1342 if(compression == kCompressionNone)
1343 {
1344 res = read_uncompressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
1345 }
1346 else if(compression == kCompressionLzma)
1347 {
1348 res = read_lzma_compressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
1349 }
1350 else if(compression == kCompressionZstd)
1351 {
1352 res = read_zstd_compressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
1353 }
1354 else
1355 {
1356 FATAL("Unsupported flux payload compression type %u", payload_header.compression);
1357 TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_UNSUPPORTED_COMPRESSION\n");
1359 }
1360
1361 if(res != AARUF_STATUS_OK)
1362 {
1363 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1364 return res;
1365 }
1366
1367 res = validate_flux_payload_crcs(cmp_buffer, cmp_length, payload, raw_length, payload_header.cmpCrc64,
1368 payload_header.crc64);
1369 if(res != AARUF_STATUS_OK)
1370 {
1371 if(payload != NULL && payload != cmp_buffer) free(payload);
1372 if(cmp_buffer != NULL) free(cmp_buffer);
1373 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1374 return res;
1375 }
1376
1377 res = extract_flux_data_buffers(flux_entry, payload, raw_length, data_data, data_length, index_data, index_length);
1378 if(res != AARUF_STATUS_OK)
1379 {
1380 if(payload != NULL && payload != cmp_buffer) free(payload);
1381 if(cmp_buffer != NULL) free(cmp_buffer);
1382 TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
1383 return res;
1384 }
1385
1386 if(payload != NULL && payload != cmp_buffer) free(payload);
1387 if(cmp_buffer != NULL) free(cmp_buffer);
1388
1389 TRACE("Exiting aaruf_read_flux_capture() = AARUF_STATUS_OK\n");
1390 return AARUF_STATUS_OK;
1391}
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
size_t aaruf_zstd_decode_buffer(uint8_t *dst_buffer, size_t dst_size, const uint8_t *src_buffer, size_t src_size)
Decodes a Zstandard-compressed buffer.
Definition zstd.c:34
@ FluxDataBlock
Block containing flux data metadata.
Definition enums.h:184
@ DataStreamPayloadBlock
Block containing compressed data stream payload (e.g., flux data, bitstreams).
Definition enums.h:185
@ kDataTypeFluxData
Flux data.
Definition enums.h:136
CompressionType
List of known compression types.
Definition enums.h:32
@ kCompressionLzma
LZMA compression.
Definition enums.h:34
@ kCompressionNone
Not compressed.
Definition enums.h:33
@ kCompressionZstd
Zstandard compression.
Definition enums.h:37
Public error and status code definitions for libaaruformat.
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:81
#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:1105
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:819
static int32_t read_zstd_compressed_payload(const aaruformat_context *ctx, size_t cmp_length, size_t raw_length, uint8_t **cmp_buffer, uint8_t **payload)
Read and decompress a Zstandard-compressed flux payload.
Definition flux.c:1034
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:1145
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:123
int32_t aaruf_clear_flux_captures(void *context)
Clear all flux captures from the context.
Definition flux.c:776
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:1258
static void flux_capture_record_dtor(void *element)
Destructor callback for FluxCaptureRecord elements in a utarray.
Definition flux.c:55
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:483
static void flux_map_clear(aaruformat_context *ctx)
Clear and deallocate the flux capture lookup map.
Definition flux.c:85
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:958
static int32_t ensure_flux_entries_loaded(aaruformat_context *ctx)
Lazy load flux data block if not already loaded.
Definition flux.c:416
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:907
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:308
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:851
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:602
int32_t flux_map_rebuild_from_entries(aaruformat_context *ctx)
Rebuild the flux capture lookup map from the flux_entries array.
Definition flux.c:172
static const UT_icd FLUX_CAPTURE_RECORD_ICD
Definition flux.c:66
On‑disk index block header and entry structures (versions 1, 2 and 3).
static int aaruf_fseek(FILE *stream, aaru_off_t offset, int origin)
Definition internal.h:46
static aaru_off_t aaruf_ftell(FILE *stream)
Definition internal.h:52
int64_t aaru_off_t
Definition internal.h:42
#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:35
FluxCaptureKey key
Definition flux.c:36
UT_hash_handle hh
Definition flux.c:38
uint32_t index
Definition flux.c:37
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:937
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:316
UT_array * flux_captures
Pending flux capture payloads (write path).
Definition context.h:318
FluxCaptureMapEntry * flux_map
Hash map for flux capture lookup by head/track/subtrack/capture index.
Definition context.h:319
FluxEntry * flux_entries
Array of flux entries (flux_data_header.entries elements).
Definition context.h:317
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:344