Loading...
Searching...
No Matches
flux.c
Go to the documentation of this file.
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2025 Natalia Portillo.
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "aaruformat/context.h"
#include "aaruformat/errors.h"
#include "aaruformat/structs/index.h"
#include "consts.h"
#include "decls.h"
#include "log.h"
#include "utarray.h"
#include "uthash.h"
struct FluxCaptureMapEntry
{
FluxCaptureKey key;
uint32_t index;
UT_hash_handle hh;
};
static void flux_capture_record_dtor(void *element)
{
if(element == NULL) return;
FluxCaptureRecord *record = element;
free(record->data_buffer);
free(record->index_buffer);
record->data_buffer = NULL;
record->index_buffer = NULL;
}
static const UT_icd FLUX_CAPTURE_RECORD_ICD = {sizeof(FluxCaptureRecord), NULL, NULL, flux_capture_record_dtor};
static void flux_map_clear(aaruformat_context *ctx)
{
if(ctx->flux_map == NULL) return;
FluxCaptureMapEntry *entry;
FluxCaptureMapEntry *tmp;
HASH_ITER(hh, ctx->flux_map, entry, tmp)
{
HASH_DEL(ctx->flux_map, entry);
free(entry);
}
ctx->flux_map = NULL;
}
static int flux_map_add(aaruformat_context *ctx, const FluxCaptureKey *key, uint32_t index)
{
FluxCaptureMapEntry *entry = NULL;
HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
if(entry == NULL)
{
entry = malloc(sizeof(FluxCaptureMapEntry));
if(entry == NULL) return -1;
entry->key = *key;
HASH_ADD(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), entry);
}
entry->index = index;
return 0;
}
int32_t flux_map_rebuild_from_entries(aaruformat_context *ctx)
{
flux_map_clear(ctx);
if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0) return AARUF_STATUS_OK;
for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
{
const FluxEntry *entry = &ctx->flux_entries[i];
FluxCaptureKey key = {entry->head, entry->track, entry->subtrack, entry->captureIndex};
if(flux_map_add(ctx, &key, i) != 0)
{
FATAL("Could not add flux capture to lookup map");
flux_map_clear(ctx);
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
return AARUF_STATUS_OK;
}
void process_flux_data_block(aaruformat_context *ctx, const IndexEntry *entry)
{
int pos = 0;
size_t read_bytes = 0;
uint64_t crc64 = 0;
// Check if the context and image stream are valid
if(ctx == NULL || ctx->imageStream == NULL)
{
FATAL("Invalid context or image stream.");
return;
}
if(ctx->flux_captures != NULL)
{
utarray_free(ctx->flux_captures);
ctx->flux_captures = NULL;
}
if(ctx->flux_entries != NULL)
{
free(ctx->flux_entries);
ctx->flux_entries = NULL;
}
flux_map_clear(ctx);
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
// Seek to block
pos = fseek(ctx->imageStream, entry->offset, SEEK_SET);
if(pos < 0 || ftell(ctx->imageStream) != entry->offset)
{
FATAL("Could not seek to %" PRIu64 " as indicated by index entry...\n", entry->offset);
return;
}
// Even if those two checks shall have been done before
read_bytes = fread(&ctx->flux_data_header, 1, sizeof(FluxHeader), ctx->imageStream);
if(read_bytes != sizeof(FluxHeader))
{
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
TRACE("Could not read flux data header, continuing...\n");
return;
}
if(ctx->flux_data_header.identifier != FluxDataBlock)
{
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
TRACE("Incorrect identifier for flux data block at position %" PRIu64 "\n", entry->offset);
return;
}
ctx->image_info.ImageSize += sizeof(FluxEntry) * ctx->flux_data_header.entries;
ctx->flux_entries = (FluxEntry *)malloc(sizeof(FluxEntry) * ctx->flux_data_header.entries);
if(ctx->flux_entries == NULL)
{
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
FATAL("Could not allocate memory for flux data block, continuing...\n");
return;
}
read_bytes = fread(ctx->flux_entries, sizeof(FluxEntry), ctx->flux_data_header.entries, ctx->imageStream);
if(read_bytes != ctx->flux_data_header.entries)
{
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
free(ctx->flux_entries);
ctx->flux_entries = NULL;
FATAL("Could not read flux data block, continuing...\n");
return;
}
crc64 = aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
if(crc64 != ctx->flux_data_header.crc64)
{
TRACE("Incorrect CRC found: 0x%" PRIx64 " found, expected 0x%" PRIx64 ", continuing...\n", crc64,
ctx->flux_data_header.crc64);
return;
}
if(flux_map_rebuild_from_entries(ctx) != AARUF_STATUS_OK)
{
free(ctx->flux_entries);
ctx->flux_entries = NULL;
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
return;
}
TRACE("Found %d flux entries at position %" PRIu64 ".\n", ctx->flux_data_header.entries, entry->offset);
}
static int32_t ensure_flux_entries_loaded(aaruformat_context *ctx)
{
// If already loaded, nothing to do
if(ctx->flux_entries != NULL || ctx->flux_data_header.entries > 0)
{
return AARUF_STATUS_OK;
}
// Find FluxDataBlock in index
if(ctx->index_entries == NULL)
{
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
IndexEntry *entry = NULL;
for(unsigned int i = 0; i < utarray_len(ctx->index_entries); i++)
{
entry = (IndexEntry *)utarray_eltptr(ctx->index_entries, i);
if(entry && entry->blockType == FluxDataBlock)
{
break;
}
entry = NULL;
}
if(entry == NULL)
{
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
// Load the flux data block
process_flux_data_block(ctx, entry);
// Check if loading was successful
if(ctx->flux_entries == NULL || ctx->flux_data_header.entries == 0)
{
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_get_flux_captures(void *context, uint8_t *buffer, size_t *length)
{
TRACE("Entering aaruf_get_flux_captures(%p, %p, %zu)", context, buffer, (length ? *length : 0));
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = (aaruformat_context *)context;
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Lazy load flux entries if not already loaded
int32_t res = ensure_flux_entries_loaded(ctx);
if(res != AARUF_STATUS_OK)
{
TRACE("Exiting aaruf_get_flux_captures() = %d", res);
return res;
}
if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
{
FATAL("Image contains no flux captures");
TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
size_t required_length = ctx->flux_data_header.entries * sizeof(FluxCaptureMeta);
if(length == NULL)
{
TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
if(buffer == NULL || *length < required_length)
{
*length = required_length;
TRACE("Buffer too small for flux captures, required %zu bytes", required_length);
TRACE("Exiting aaruf_get_flux_captures() = AARUF_ERROR_BUFFER_TOO_SMALL");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
FluxCaptureMeta *out_entries = (FluxCaptureMeta *)buffer;
for(uint16_t i = 0; i < ctx->flux_data_header.entries; i++)
{
const FluxEntry *entry = &ctx->flux_entries[i];
out_entries[i].head = entry->head;
out_entries[i].track = entry->track;
out_entries[i].subtrack = entry->subtrack;
out_entries[i].captureIndex = entry->captureIndex;
out_entries[i].indexResolution = entry->indexResolution;
out_entries[i].dataResolution = entry->dataResolution;
}
*length = required_length;
TRACE("Exiting aaruf_get_flux_captures(%p, %p, %zu) = AARUF_STATUS_OK", context, buffer, *length);
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL 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)
{
TRACE("Entering aaruf_add_flux_capture(%p, %u, %u, %u, %u, %" PRIu64 ", %" PRIu64 ", %p, %u, %p, %u)", context,
head, track, subtrack, capture_index, data_resolution, index_resolution, data, data_length, index,
index_length);
if(context == NULL)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = context;
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(!ctx->is_writing)
{
FATAL("Flux captures can only be added when writing");
return AARUF_READ_ONLY;
}
if((index_length != 0 && index == NULL) || (data_length != 0 && data == NULL))
{
FATAL("Invalid flux capture buffers");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if((uint64_t)data_length + index_length > UINT32_MAX)
{
FATAL("Flux capture too large (%" PRIu64 " bytes)", (uint64_t)data_length + index_length);
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(ctx->flux_captures == NULL)
{
utarray_new(ctx->flux_captures, &FLUX_CAPTURE_RECORD_ICD);
if(ctx->flux_captures == NULL)
{
FATAL("Could not allocate flux capture storage");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
size_t existing_captures = utarray_len(ctx->flux_captures);
if(existing_captures >= UINT16_MAX)
{
FATAL("Flux capture limit exceeded (%zu >= %u)", existing_captures, UINT16_MAX);
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
uint8_t *data_copy = NULL;
uint8_t *index_copy = NULL;
if(data_length != 0)
{
data_copy = malloc(data_length);
if(data_copy == NULL)
{
FATAL("Could not allocate %u bytes for flux data", data_length);
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(data_copy, data, data_length);
}
if(index_length != 0)
{
index_copy = malloc(index_length);
if(index_copy == NULL)
{
free(data_copy);
FATAL("Could not allocate %u bytes for flux index", index_length);
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
memcpy(index_copy, index, index_length);
}
FluxCaptureRecord record = {0};
record.entry.head = head;
record.entry.track = track;
record.entry.subtrack = subtrack;
record.entry.captureIndex = capture_index;
record.entry.dataResolution = data_resolution;
record.entry.indexResolution = index_resolution;
record.entry.indexOffset = data_length;
record.entry.payloadOffset = 0;
record.data_buffer = data_copy;
record.data_length = data_length;
record.index_buffer = index_copy;
record.index_length = index_length;
FluxEntry *new_entries = realloc(ctx->flux_entries, (existing_captures + 1) * sizeof(FluxEntry));
if(new_entries == NULL)
{
free(data_copy);
free(index_copy);
FATAL("Could not grow flux entry array");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
ctx->flux_entries = new_entries;
utarray_push_back(ctx->flux_captures, &record);
ctx->flux_entries[existing_captures] = record.entry;
ctx->flux_data_header.identifier = FluxDataBlock;
ctx->flux_data_header.entries = (uint16_t)(existing_captures + 1);
ctx->flux_data_header.crc64 =
aaruf_crc64_data((const uint8_t *)ctx->flux_entries, ctx->flux_data_header.entries * sizeof(FluxEntry));
ctx->dirty_flux_block = true;
FluxCaptureKey key = {head, track, subtrack, capture_index};
if(flux_map_add(ctx, &key, (uint32_t)existing_captures) != 0)
{
FATAL("Could not add flux capture to lookup map");
size_t len = utarray_len(ctx->flux_captures);
if(len > 0) utarray_erase(ctx->flux_captures, len - 1, 1);
if(existing_captures == 0)
{
free(ctx->flux_entries);
ctx->flux_entries = NULL;
}
else
{
FluxEntry *shrunk = realloc(ctx->flux_entries, existing_captures * sizeof(FluxEntry));
if(shrunk != NULL) ctx->flux_entries = shrunk;
}
ctx->flux_data_header.entries = (uint16_t)existing_captures;
ctx->flux_data_header.crc64 = existing_captures == 0 ? 0
: aaruf_crc64_data((const uint8_t *)ctx->flux_entries,
existing_captures * sizeof(FluxEntry));
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
TRACE("Exiting aaruf_add_flux_capture() = AARUF_STATUS_OK (captures=%u)", ctx->flux_data_header.entries);
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_clear_flux_captures(void *context)
{
TRACE("Entering aaruf_clear_flux_captures(%p)", context);
if(context == NULL)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = context;
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(ctx->flux_captures != NULL)
{
utarray_free(ctx->flux_captures);
ctx->flux_captures = NULL;
}
flux_map_clear(ctx);
free(ctx->flux_entries);
ctx->flux_entries = NULL;
memset(&ctx->flux_data_header, 0, sizeof(FluxHeader));
TRACE("Exiting aaruf_clear_flux_captures() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
static const FluxEntry *find_flux_entry_by_key(const aaruformat_context *ctx, const FluxCaptureKey *key)
{
// Try lookup map first (O(1))
if(ctx->flux_map != NULL)
{
FluxCaptureMapEntry *map_entry = NULL;
HASH_FIND(hh, ctx->flux_map, key, sizeof(FluxCaptureKey), map_entry);
if(map_entry != NULL && map_entry->index < ctx->flux_data_header.entries)
return &ctx->flux_entries[map_entry->index];
}
// Fall back to linear search (O(n))
for(uint32_t i = 0; i < ctx->flux_data_header.entries; i++)
{
const FluxEntry *entry = &ctx->flux_entries[i];
if(entry->head == key->head && entry->track == key->track && entry->subtrack == key->subtrack &&
entry->captureIndex == key->captureIndex)
return entry;
}
return NULL;
}
static int32_t read_flux_payload_header(const aaruformat_context *ctx, uint64_t payload_offset,
DataStreamPayloadHeader *header)
{
if(fseek(ctx->imageStream, payload_offset, SEEK_SET) < 0)
{
FATAL("Could not seek to flux payload at offset %" PRIu64, payload_offset);
TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
long file_position = ftell(ctx->imageStream);
if(file_position < 0 || (uint64_t)file_position != payload_offset)
{
FATAL("Invalid flux payload position (expected %" PRIu64 ", got %ld)", payload_offset, file_position);
TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
size_t read_bytes = fread(header, 1, sizeof(DataStreamPayloadHeader), ctx->imageStream);
if(read_bytes != sizeof(DataStreamPayloadHeader))
{
FATAL("Could not read flux payload header at offset %" PRIu64, payload_offset);
TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
if(header->identifier != DataStreamPayloadBlock)
{
FATAL("Incorrect identifier 0x%08" PRIx32 " for flux payload at offset %" PRIu64, header->identifier,
payload_offset);
TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
if(header->dataType != FluxData)
{
FATAL("Incorrect data type %u for flux payload at offset %" PRIu64 " (expected FluxData)", header->dataType,
payload_offset);
TRACE("Exiting read_flux_payload_header() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
return AARUF_STATUS_OK;
}
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)
{
if(cmp_length != raw_length)
{
FATAL("Flux payload lengths mismatch for uncompressed block (cmp=%zu, raw=%zu)", cmp_length, raw_length);
TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
if(cmp_length == 0)
{
*cmp_buffer = NULL;
*payload = NULL;
return AARUF_STATUS_OK;
}
*cmp_buffer = (uint8_t *)malloc(cmp_length);
if(*cmp_buffer == NULL)
{
FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
if(read_bytes != cmp_length)
{
FATAL("Could not read %zu bytes of flux payload", cmp_length);
free(*cmp_buffer);
*cmp_buffer = NULL;
TRACE("Exiting read_uncompressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
*payload = *cmp_buffer;
return AARUF_STATUS_OK;
}
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)
{
if(cmp_length <= LZMA_PROPERTIES_LENGTH)
{
FATAL("Flux payload compressed length %zu too small for LZMA", cmp_length);
TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
*cmp_buffer = (uint8_t *)malloc(cmp_length);
if(*cmp_buffer == NULL)
{
FATAL("Could not allocate %zu bytes for flux payload", cmp_length);
TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
size_t read_bytes = fread(*cmp_buffer, 1, cmp_length, ctx->imageStream);
if(read_bytes != cmp_length)
{
FATAL("Could not read %zu bytes of flux payload", cmp_length);
free(*cmp_buffer);
*cmp_buffer = NULL;
TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_READ_BLOCK\n");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
if(raw_length == 0)
{
*payload = NULL;
return AARUF_STATUS_OK;
}
*payload = (uint8_t *)malloc(raw_length);
if(*payload == NULL)
{
FATAL("Could not allocate %zu bytes for decompressed flux payload", raw_length);
free(*cmp_buffer);
*cmp_buffer = NULL;
TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_NOT_ENOUGH_MEMORY\n");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
size_t cmp_stream_len = cmp_length - LZMA_PROPERTIES_LENGTH;
size_t dst_len = raw_length;
size_t src_len = cmp_stream_len;
const uint8_t *cmp_props = *cmp_buffer;
const uint8_t *cmp_stream = *cmp_buffer + LZMA_PROPERTIES_LENGTH;
int32_t error_no =
aaruf_lzma_decode_buffer(*payload, &dst_len, cmp_stream, &src_len, cmp_props, LZMA_PROPERTIES_LENGTH);
if(error_no != 0 || dst_len != raw_length)
{
FATAL("LZMA decompression failed for flux payload (err=%d, dst=%zu/%zu)", error_no, dst_len, raw_length);
free(*payload);
free(*cmp_buffer);
*payload = NULL;
*cmp_buffer = NULL;
TRACE("Exiting read_lzma_compressed_payload() = AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK\n");
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
return AARUF_STATUS_OK;
}
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)
{
uint64_t cmp_crc = 0;
if(cmp_length != 0 && cmp_buffer != NULL) cmp_crc = aaruf_crc64_data(cmp_buffer, cmp_length);
if(cmp_crc != expected_cmp_crc)
{
FATAL("Flux payload compressed CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_cmp_crc,
cmp_crc);
TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
return AARUF_ERROR_INVALID_BLOCK_CRC;
}
uint64_t raw_crc = 0;
if(raw_length != 0 && payload != NULL) raw_crc = aaruf_crc64_data(payload, raw_length);
if(raw_crc != expected_raw_crc)
{
FATAL("Flux payload raw CRC mismatch (expected 0x%" PRIx64 ", got 0x%" PRIx64 ")", expected_raw_crc, raw_crc);
TRACE("Exiting validate_flux_payload_crcs() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
return AARUF_ERROR_INVALID_BLOCK_CRC;
}
return AARUF_STATUS_OK;
}
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)
{
if(flux_entry->indexOffset > raw_length)
{
FATAL("Flux index offset %" PRIu64 " beyond payload length %zu", flux_entry->indexOffset, raw_length);
TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INVALID_BLOCK_CRC\n");
return AARUF_ERROR_INVALID_BLOCK_CRC;
}
uint64_t data_length_required64 = flux_entry->indexOffset;
uint64_t index_length_required64 = raw_length - flux_entry->indexOffset;
if(data_length_required64 > UINT32_MAX || index_length_required64 > UINT32_MAX)
{
FATAL("Flux payload section length exceeds 32-bit limits (data=%" PRIu64 ", index=%" PRIu64 ")",
data_length_required64, index_length_required64);
TRACE("Exiting extract_flux_data_buffers() = AARUF_ERROR_INCORRECT_DATA_SIZE\n");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
uint32_t data_required = (uint32_t)data_length_required64;
uint32_t index_required = (uint32_t)index_length_required64;
uint32_t data_capacity = *data_length;
uint32_t index_capacity = *index_length;
*data_length = data_required;
*index_length = index_required;
if(data_data == NULL || index_data == NULL || data_capacity < data_required || index_capacity < index_required)
{
TRACE("Returning required flux capture sizes (data=%u, index=%u)\n", data_required, index_required);
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
const uint8_t *index_ptr = payload ? payload + data_length_required64 : NULL;
const uint8_t *data_ptr = payload;
if(data_required != 0 && data_ptr != NULL) memcpy(data_data, data_ptr, data_required);
if(index_required != 0 && index_ptr != NULL) memcpy(index_data, index_ptr, index_required);
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL 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)
{
TRACE("Entering aaruf_read_flux_capture(%p, %u, %u, %u, %u, %p, %p, %p, %p)", context, head, track, subtrack,
capture_index, index_data, index_length, data_data, data_length);
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = (aaruformat_context *)context;
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Lazy load flux entries if not already loaded
int32_t load_res = ensure_flux_entries_loaded(ctx);
if(load_res != AARUF_STATUS_OK)
{
TRACE("Exiting aaruf_read_flux_capture() = %d", load_res);
return load_res;
}
if(ctx->flux_data_header.entries == 0 || ctx->flux_entries == NULL)
{
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND");
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
if(index_length == NULL || data_length == NULL)
{
FATAL("index_length or data_length pointers are NULL");
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_BUFFER_TOO_SMALL\n");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
if(ctx->imageStream == NULL)
{
FATAL("Invalid image stream");
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_NOT_AARUFORMAT\n");
return AARUF_ERROR_NOT_AARUFORMAT;
}
FluxCaptureKey key = {head, track, subtrack, capture_index};
const FluxEntry *flux_entry = find_flux_entry_by_key(ctx, &key);
if(flux_entry == NULL)
{
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_FLUX_DATA_NOT_FOUND\n");
return AARUF_ERROR_FLUX_DATA_NOT_FOUND;
}
TRACE("Requested flux capture: head=%u track=%u subtrack=%u captureIndex=%u payloadOffset=%" PRIu64 "\n",
flux_entry->head, flux_entry->track, flux_entry->subtrack, flux_entry->captureIndex,
flux_entry->payloadOffset);
// Get block alignment shift from FluxHeader
uint8_t block_alignment_shift = ctx->flux_data_header.blockAlignmentShift;
// Convert payloadOffset from block-aligned units to absolute file offset
// payloadOffset is stored divided by (1 << blockAlignmentShift), consistent with DDT
uint64_t absolute_payload_offset = flux_entry->payloadOffset << block_alignment_shift;
DataStreamPayloadHeader payload_header;
int32_t res = read_flux_payload_header(ctx, absolute_payload_offset, &payload_header);
if(res != AARUF_STATUS_OK)
{
TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
return res;
}
const CompressionType compression = (CompressionType)payload_header.compression;
uint8_t *cmp_buffer = NULL;
uint8_t *payload = NULL;
size_t cmp_length = payload_header.cmpLength;
size_t raw_length = payload_header.length;
if(compression == None)
{
res = read_uncompressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
}
else if(compression == Lzma)
{
res = read_lzma_compressed_payload(ctx, cmp_length, raw_length, &cmp_buffer, &payload);
}
else
{
FATAL("Unsupported flux payload compression type %u", payload_header.compression);
TRACE("Exiting aaruf_read_flux_capture() = AARUF_ERROR_UNSUPPORTED_COMPRESSION\n");
return AARUF_ERROR_UNSUPPORTED_COMPRESSION;
}
if(res != AARUF_STATUS_OK)
{
TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
return res;
}
res = validate_flux_payload_crcs(cmp_buffer, cmp_length, payload, raw_length, payload_header.cmpCrc64,
payload_header.crc64);
if(res != AARUF_STATUS_OK)
{
if(payload != NULL && payload != cmp_buffer) free(payload);
if(cmp_buffer != NULL) free(cmp_buffer);
TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
return res;
}
res = extract_flux_data_buffers(flux_entry, payload, raw_length, data_data, data_length, index_data,
index_length);
if(res != AARUF_STATUS_OK)
{
if(payload != NULL && payload != cmp_buffer) free(payload);
if(cmp_buffer != NULL) free(cmp_buffer);
TRACE("Exiting aaruf_read_flux_capture() = %d\n", res);
return res;
}
if(payload != NULL && payload != cmp_buffer) free(payload);
if(cmp_buffer != NULL) free(cmp_buffer);
TRACE("Exiting aaruf_read_flux_capture() = AARUF_STATUS_OK\n");
return AARUF_STATUS_OK;
}
consts.h
Core public constants and compile‑time limits for the Aaru container format implementation.
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
context.h
Central runtime context structures for libaaruformat (image state, caches, checksum buffers).
decls.h
AARU_CALL
#define AARU_CALL
Definition decls.h:46
aaruf_crc64_data
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
aaruf_lzma_decode_buffer
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
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:55
FluxDataBlock
@ FluxDataBlock
Block containing flux data metadata.
Definition enums.h:164
DataStreamPayloadBlock
@ DataStreamPayloadBlock
Block containing compressed data stream payload (e.g., flux data, bitstreams).
Definition enums.h:165
FluxData
@ FluxData
Flux data.
Definition enums.h:134
CompressionType
CompressionType
List of known compression types.
Definition enums.h:32
Lzma
@ Lzma
LZMA compression.
Definition enums.h:34
None
@ None
Not compressed.
Definition enums.h:33
errors.h
Public error and status code definitions for libaaruformat.
AARUF_STATUS_OK
#define AARUF_STATUS_OK
Sector present and read without uncorrectable errors.
Definition errors.h:77
AARUF_READ_ONLY
#define AARUF_READ_ONLY
Operation requires write mode but context is read-only.
Definition errors.h:61
AARUF_ERROR_NOT_ENOUGH_MEMORY
#define AARUF_ERROR_NOT_ENOUGH_MEMORY
Memory allocation failure (critical).
Definition errors.h:48
AARUF_ERROR_CANNOT_READ_BLOCK
#define AARUF_ERROR_CANNOT_READ_BLOCK
Generic block read failure (seek/read error).
Definition errors.h:46
AARUF_ERROR_INCORRECT_DATA_SIZE
#define AARUF_ERROR_INCORRECT_DATA_SIZE
Data size does not match expected size.
Definition errors.h:65
AARUF_ERROR_INVALID_BLOCK_CRC
#define AARUF_ERROR_INVALID_BLOCK_CRC
CRC64 mismatch indicating corruption.
Definition errors.h:57
AARUF_ERROR_NOT_AARUFORMAT
#define AARUF_ERROR_NOT_AARUFORMAT
Input file/stream failed magic or structural validation.
Definition errors.h:40
AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK
#define AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK
Decompression routine failed or size mismatch.
Definition errors.h:56
AARUF_ERROR_FLUX_DATA_NOT_FOUND
#define AARUF_ERROR_FLUX_DATA_NOT_FOUND
Requested flux data not present in image.
Definition errors.h:71
AARUF_ERROR_BUFFER_TOO_SMALL
#define AARUF_ERROR_BUFFER_TOO_SMALL
Caller-supplied buffer insufficient for data.
Definition errors.h:49
AARUF_ERROR_UNSUPPORTED_COMPRESSION
#define AARUF_ERROR_UNSUPPORTED_COMPRESSION
Block marked with unsupported compression algorithm.
Definition errors.h:47
validate_flux_payload_crcs
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
find_flux_entry_by_key
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
extract_flux_data_buffers
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
flux_map_add
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
aaruf_clear_flux_captures
int32_t aaruf_clear_flux_captures(void *context)
Clear all flux captures from the context.
Definition flux.c:790
aaruf_read_flux_capture
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
flux_capture_record_dtor
static void flux_capture_record_dtor(void *element)
Destructor callback for FluxCaptureRecord elements in a utarray.
Definition flux.c:54
aaruf_get_flux_captures
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
flux_map_clear
static void flux_map_clear(aaruformat_context *ctx)
Clear and deallocate the flux capture lookup map.
Definition flux.c:84
read_lzma_compressed_payload
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
ensure_flux_entries_loaded
static int32_t ensure_flux_entries_loaded(aaruformat_context *ctx)
Lazy load flux data block if not already loaded.
Definition flux.c:415
read_uncompressed_payload
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
process_flux_data_block
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
read_flux_payload_header
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
aaruf_write_flux_capture
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
flux_map_rebuild_from_entries
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
FLUX_CAPTURE_RECORD_ICD
static const UT_icd FLUX_CAPTURE_RECORD_ICD
Definition flux.c:65
index.h
On‑disk index block header and entry structures (versions 1, 2 and 3).
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
DataStreamPayloadHeader
Header structure for a DataStreamPayloadBlock containing data stream payload.
Definition flux.h:243
DataStreamPayloadHeader::dataType
uint16_t dataType
Data type classification (value from DataType), e.g., FluxData or BitstreamData.
Definition flux.h:245
DataStreamPayloadHeader::cmpLength
uint32_t cmpLength
Compressed length in bytes (includes LZMA properties if compression = Lzma).
Definition flux.h:247
DataStreamPayloadHeader::cmpCrc64
uint64_t cmpCrc64
CRC64-ECMA checksum of the compressed payload (or same as crc64 if uncompressed).
Definition flux.h:249
DataStreamPayloadHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::DataStreamPayloadBlock (0x4C505344, "DSPL").
Definition flux.h:244
DataStreamPayloadHeader::compression
uint16_t compression
Compression type: 0 = None, 1 = Lzma.
Definition flux.h:246
DataStreamPayloadHeader::length
uint32_t length
Uncompressed length in bytes.
Definition flux.h:248
DataStreamPayloadHeader::crc64
uint64_t crc64
CRC64-ECMA checksum of the uncompressed payload.
Definition flux.h:250
FluxCaptureKey
Key structure for flux capture lookup map.
Definition flux.h:297
FluxCaptureKey::track
uint16_t track
Track number identifying the capture location.
Definition flux.h:299
FluxCaptureKey::subtrack
uint8_t subtrack
Subtrack number identifying the capture location.
Definition flux.h:300
FluxCaptureKey::head
uint32_t head
Head number identifying the capture location.
Definition flux.h:298
FluxCaptureKey::captureIndex
uint32_t captureIndex
Capture index, allowing multiple captures for the same location.
Definition flux.h:301
FluxCaptureMapEntry
Internal hash table entry for flux capture lookup.
Definition flux.c:34
FluxCaptureMapEntry::key
FluxCaptureKey key
Definition flux.c:35
FluxCaptureMapEntry::hh
UT_hash_handle hh
Definition flux.c:37
FluxCaptureMapEntry::index
uint32_t index
Definition flux.c:36
FluxCaptureMeta
Metadata structure returned by aaruf_get_flux_captures().
Definition flux.h:183
FluxCaptureMeta::head
uint32_t head
Head number the flux capture corresponds to.
Definition flux.h:184
FluxCaptureMeta::indexResolution
uint64_t indexResolution
Resolution in picoseconds at which the index stream was sampled.
Definition flux.h:188
FluxCaptureMeta::captureIndex
uint32_t captureIndex
Capture index, allowing multiple captures for the same location.
Definition flux.h:187
FluxCaptureMeta::subtrack
uint8_t subtrack
Subtrack number the flux capture corresponds to.
Definition flux.h:186
FluxCaptureMeta::dataResolution
uint64_t dataResolution
Resolution in picoseconds at which the data stream was sampled.
Definition flux.h:189
FluxCaptureMeta::track
uint16_t track
Track number the flux capture corresponds to.
Definition flux.h:185
FluxCaptureRecord
Internal structure for storing flux capture data during write mode.
Definition flux.h:271
FluxCaptureRecord::entry
FluxEntry entry
Flux entry metadata describing this capture.
Definition flux.h:272
FluxCaptureRecord::data_length
uint32_t data_length
Length of the data buffer in bytes.
Definition flux.h:274
FluxCaptureRecord::index_length
uint32_t index_length
Length of the index buffer in bytes.
Definition flux.h:276
FluxCaptureRecord::index_buffer
uint8_t * index_buffer
Pointer to the flux index buffer. Owned by the utarray, freed automatically.
Definition flux.h:275
FluxCaptureRecord::data_buffer
uint8_t * data_buffer
Pointer to the flux data buffer. Owned by the utarray, freed automatically.
Definition flux.h:273
FluxEntry
Metadata entry describing a single flux capture in the FluxDataBlock.
Definition flux.h:156
FluxEntry::indexResolution
uint64_t indexResolution
Resolution in picoseconds at which the index stream was sampled.
Definition flux.h:161
FluxEntry::dataResolution
uint64_t dataResolution
Resolution in picoseconds at which the data stream was sampled.
Definition flux.h:162
FluxEntry::head
uint32_t head
Head number the flux capture corresponds to. Typically 0 or 1 for double-sided media.
Definition flux.h:157
FluxEntry::payloadOffset
uint64_t payloadOffset
Block-aligned file offset where the DataStreamPayloadBlock containing this capture's data is stored,...
Definition flux.h:164
FluxEntry::track
uint16_t track
Track number the flux capture corresponds to. Track numbering is format-dependent.
Definition flux.h:158
FluxEntry::indexOffset
uint64_t indexOffset
Byte offset within the payload where the index buffer starts (equals data_length).
Definition flux.h:163
FluxEntry::subtrack
uint8_t subtrack
Subtrack number, allowing sub-stepping within a track. Used for fine positioning.
Definition flux.h:159
FluxEntry::captureIndex
uint32_t captureIndex
Capture index, allowing multiple captures for the same location (e.g., multiple revolutions).
Definition flux.h:160
FluxHeader
Header structure for a FluxDataBlock containing flux capture metadata.
Definition flux.h:117
FluxHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::FluxDataBlock (0x58554C46, "FLUX").
Definition flux.h:118
FluxHeader::entries
uint16_t entries
Number of FluxEntry records following this header. Maximum value: 65535.
Definition flux.h:119
FluxHeader::crc64
uint64_t crc64
CRC64-ECMA checksum of the FluxEntry array (header excluded).
Definition flux.h:121
FluxHeader::blockAlignmentShift
uint8_t blockAlignmentShift
Block alignment shift: 2^blockAlignmentShift = block alignment boundary in bytes.
Definition flux.h:120
ImageInfo::ImageSize
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:935
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
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
aaruformat_context::flux_data_header
FluxHeader flux_data_header
Flux data header (if present).
Definition context.h:311
aaruformat_context::flux_captures
UT_array * flux_captures
Pending flux capture payloads (write path).
Definition context.h:313
aaruformat_context::flux_map
FluxCaptureMapEntry * flux_map
Hash map for flux capture lookup by head/track/subtrack/capture index.
Definition context.h:314
aaruformat_context::flux_entries
FluxEntry * flux_entries
Array of flux entries (flux_data_header.entries elements).
Definition context.h:312
aaruformat_context::is_writing
bool is_writing
True if context opened/created for writing.
Definition context.h:295
aaruformat_context::magic
uint64_t magic
File magic (AARU_MAGIC) post-open.
Definition context.h:177
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
aaruformat_context::index_entries
UT_array * index_entries
Flattened index entries (UT_array of IndexEntry).
Definition context.h:255
aaruformat_context::image_info
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
aaruformat_context::dirty_flux_block
bool dirty_flux_block
True if flux block should be written during close.
Definition context.h:339