Loading...
Searching...
No Matches
dump.c
Go to the documentation of this file.
/*
* This file is part of the Aaru Data Preservation Suite.
* Copyright (c) 2019-2026 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 <stdint.h>
#include <stdlib.h>
#include "aaruformat.h"
#include "internal.h"
#include "log.h"
static void free_dump_hardware_entries(DumpHardwareEntriesWithData *entries, uint32_t count)
{
if(entries == NULL) return;
for(uint32_t e = 0; e < count; e++)
{
free(entries[e].manufacturer);
free(entries[e].model);
free(entries[e].revision);
free(entries[e].firmware);
free(entries[e].serial);
free(entries[e].softwareName);
free(entries[e].softwareVersion);
free(entries[e].softwareOperatingSystem);
free(entries[e].extents);
}
free(entries);
}
AARU_EXPORT int32_t AARU_CALL aaruf_get_dumphw(void *context, uint8_t *buffer, size_t *length)
{
size_t length_value = 0;
if(length != NULL) length_value = *length;
TRACE("Entering aaruf_get_dumphw(%p, %p, %zu)", context, buffer, length_value);
aaruformat_context *ctx = NULL;
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(length == NULL)
{
FATAL("Invalid length pointer");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
ctx = context;
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(ctx->dump_hardware_entries_with_data == NULL || ctx->dump_hardware_header.entries == 0 ||
ctx->dump_hardware_header.identifier != DumpHardwareBlock)
{
FATAL("No dump hardware information present");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_CANNOT_READ_BLOCK");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
size_t required_length = sizeof(DumpHardwareHeader) + (size_t)ctx->dump_hardware_header.length;
if(required_length < sizeof(DumpHardwareHeader))
{
FATAL("Dump hardware payload length overflow");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(buffer == NULL || *length < required_length)
{
TRACE("Buffer too small for dump hardware block, required %zu bytes", required_length);
*length = required_length;
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_BUFFER_TOO_SMALL");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
*length = required_length;
// Start to iterate and copy the data
size_t offset = sizeof(DumpHardwareHeader);
for(uint32_t i = 0; i < ctx->dump_hardware_header.entries; i++)
{
size_t entry_size = sizeof(DumpHardwareEntry);
const DumpHardwareEntry *entry = &ctx->dump_hardware_entries_with_data[i].entry;
const size_t extent_bytes = (size_t)entry->extents * sizeof(DumpExtent);
if(entry->extents != 0 && extent_bytes / sizeof(DumpExtent) != entry->extents)
{
FATAL("Dump hardware extent size overflow");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
const size_t additive_lengths[] = {entry->manufacturerLength,
entry->modelLength,
entry->revisionLength,
entry->firmwareLength,
entry->serialLength,
entry->softwareNameLength,
entry->softwareVersionLength,
entry->softwareOperatingSystemLength,
extent_bytes};
for(size_t j = 0; j < sizeof(additive_lengths) / sizeof(additive_lengths[0]); j++)
{
if(additive_lengths[j] > SIZE_MAX - entry_size)
{
FATAL("Dump hardware entry size overflow");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
entry_size += additive_lengths[j];
}
if(offset + entry_size > *length)
{
FATAL("Calculated size exceeds provided buffer length");
TRACE("Exiting aaruf_get_dumphw() = AARUF_ERROR_BUFFER_TOO_SMALL");
return AARUF_ERROR_BUFFER_TOO_SMALL;
}
memcpy(buffer + offset, &ctx->dump_hardware_entries_with_data[i].entry, sizeof(DumpHardwareEntry));
offset += sizeof(DumpHardwareEntry);
if(ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength > 0 &&
ctx->dump_hardware_entries_with_data[i].manufacturer != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].manufacturer,
ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.manufacturerLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.modelLength > 0 &&
ctx->dump_hardware_entries_with_data[i].model != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].model,
ctx->dump_hardware_entries_with_data[i].entry.modelLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.modelLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.revisionLength > 0 &&
ctx->dump_hardware_entries_with_data[i].revision != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].revision,
ctx->dump_hardware_entries_with_data[i].entry.revisionLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.revisionLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.firmwareLength > 0 &&
ctx->dump_hardware_entries_with_data[i].firmware != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].firmware,
ctx->dump_hardware_entries_with_data[i].entry.firmwareLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.firmwareLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.serialLength > 0 &&
ctx->dump_hardware_entries_with_data[i].serial != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].serial,
ctx->dump_hardware_entries_with_data[i].entry.serialLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.serialLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength > 0 &&
ctx->dump_hardware_entries_with_data[i].softwareName != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareName,
ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.softwareNameLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength > 0 &&
ctx->dump_hardware_entries_with_data[i].softwareVersion != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareVersion,
ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.softwareVersionLength;
}
if(ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength > 0 &&
ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].softwareOperatingSystem,
ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength);
offset += ctx->dump_hardware_entries_with_data[i].entry.softwareOperatingSystemLength;
}
if(entry->extents > 0 && ctx->dump_hardware_entries_with_data[i].extents != NULL)
{
memcpy(buffer + offset, ctx->dump_hardware_entries_with_data[i].extents, extent_bytes);
offset += extent_bytes;
}
}
// Calculate CRC64
ctx->dump_hardware_header.crc64 =
aaruf_crc64_data(buffer + sizeof(DumpHardwareHeader), ctx->dump_hardware_header.length);
// Copy header
memcpy(buffer, &ctx->dump_hardware_header, sizeof(DumpHardwareHeader));
TRACE("Exiting aaruf_get_dumphw() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
AARU_EXPORT int32_t AARU_CALL aaruf_set_dumphw(void *context, uint8_t *data, size_t length)
{
TRACE("Entering aaruf_set_dumphw(%p, %p, %zu)", context, data, length);
// Check context is correct AaruFormat context
if(context == NULL)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
aaruformat_context *ctx = context;
// Not a libaaruformat context
if(ctx->magic != AARU_MAGIC)
{
FATAL("Invalid context");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Check we are writing
if(!ctx->is_writing)
{
FATAL("Trying to write a read-only image");
TRACE("Exiting aaruf_set_dumphw() = AARUF_READ_ONLY");
return AARUF_READ_ONLY;
}
if(data == NULL || length == 0)
{
FATAL("Invalid data or length");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
if(length < sizeof(DumpHardwareHeader))
{
FATAL("Dump hardware block shorter than header");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
DumpHardwareHeader header;
memcpy(&header, data, sizeof(DumpHardwareHeader));
if(header.identifier != DumpHardwareBlock)
{
FATAL("Invalid dump hardware block identifier");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_CANNOT_READ_BLOCK");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
if(length != sizeof(DumpHardwareHeader) + header.length)
{
FATAL("Invalid dump hardware block length");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
uint64_t crc64 = aaruf_crc64_data(data + sizeof(DumpHardwareHeader), header.length);
if(header.crc64 != crc64)
{
FATAL("Invalid dump hardware block CRC64");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_INVALID_BLOCK_CRC");
return AARUF_ERROR_INVALID_BLOCK_CRC;
}
DumpHardwareEntriesWithData *copy = NULL;
if(header.entries > 0)
{
copy = calloc(header.entries, sizeof(DumpHardwareEntriesWithData));
if(copy == NULL)
{
TRACE("Could not allocate memory for dump hardware block entries");
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
}
TRACE("Processing %u dump hardware block entries", header.entries);
size_t pos = sizeof(DumpHardwareHeader);
#define COPY_STRING_FIELD(field) \
do \
{ \
const size_t field##_length = copy[e].entry.field##Length; \
if(field##_length > 0) \
{ \
if(field##_length > length - pos) goto invalid_data; \
/* Allocate only field##_length bytes, since input is NUL-terminated */ \
copy[e].field = (uint8_t *)calloc(1, field##_length); \
if(copy[e].field == NULL) goto free_copy_and_error; \
memcpy(copy[e].field, data + pos, field##_length); \
/* Ensure NUL-termination in case input is malformed */ \
copy[e].field[field##_length - 1] = '\0'; \
pos += field##_length; \
} \
} while(0)
for(uint32_t e = 0; e < header.entries; e++)
{
if(length - pos < sizeof(DumpHardwareEntry)) goto invalid_data;
memcpy(&copy[e].entry, data + pos, sizeof(DumpHardwareEntry));
pos += sizeof(DumpHardwareEntry);
COPY_STRING_FIELD(manufacturer);
COPY_STRING_FIELD(model);
COPY_STRING_FIELD(revision);
COPY_STRING_FIELD(firmware);
COPY_STRING_FIELD(serial);
COPY_STRING_FIELD(softwareName);
COPY_STRING_FIELD(softwareVersion);
COPY_STRING_FIELD(softwareOperatingSystem);
const uint32_t extent_count = copy[e].entry.extents;
if(extent_count > 0)
{
const size_t extent_bytes = (size_t)extent_count * sizeof(DumpExtent);
if(extent_bytes / sizeof(DumpExtent) != extent_count || extent_bytes > length - pos) goto invalid_data;
copy[e].extents = (DumpExtent *)malloc(extent_bytes);
if(copy[e].extents == NULL) goto free_copy_and_error;
memcpy(copy[e].extents, data + pos, extent_bytes);
pos += extent_bytes;
qsort(copy[e].extents, extent_count, sizeof(DumpExtent), compare_extents);
TRACE("Sorted %u extents for entry %u", extent_count, e);
}
}
#undef COPY_STRING_FIELD
if(pos != length)
{
FATAL("Dump hardware block contains trailing data");
goto invalid_data;
}
free_dump_hardware_entries(ctx->dump_hardware_entries_with_data, ctx->dump_hardware_header.entries);
ctx->dump_hardware_entries_with_data = copy;
ctx->dump_hardware_header = header;
ctx->dirty_dumphw_block = true; // Mark dump hardware block as dirty
TRACE("Exiting aaruf_set_dumphw() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
invalid_data:
TRACE("Dump hardware block truncated or malformed");
free_dump_hardware_entries(copy, header.entries);
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
free_copy_and_error:
free_dump_hardware_entries(copy, header.entries);
TRACE("Exiting aaruf_set_dumphw() = AARUF_ERROR_NOT_ENOUGH_MEMORY");
return AARUF_ERROR_NOT_ENOUGH_MEMORY;
}
aaruformat.h
AARU_MAGIC
#define AARU_MAGIC
Magic identifier for AaruFormat container (ASCII "AARUFRMT").
Definition consts.h:64
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
AARU_EXPORT
#define AARU_EXPORT
Definition decls.h:55
aaruf_get_dumphw
int32_t aaruf_get_dumphw(void *context, uint8_t *buffer, size_t *length)
Retrieves the dump hardware block containing acquisition environment information.
Definition dump.c:186
COPY_STRING_FIELD
#define COPY_STRING_FIELD(field)
free_dump_hardware_entries
static void free_dump_hardware_entries(DumpHardwareEntriesWithData *entries, uint32_t count)
Definition dump.c:26
aaruf_set_dumphw
int32_t aaruf_set_dumphw(void *context, uint8_t *data, size_t length)
Sets the dump hardware block for the image during creation.
Definition dump.c:531
DumpHardwareBlock
@ DumpHardwareBlock
Block containing an array of hardware used to create the image.
Definition enums.h:160
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_BUFFER_TOO_SMALL
#define AARUF_ERROR_BUFFER_TOO_SMALL
Caller-supplied buffer insufficient for data.
Definition errors.h:49
internal.h
compare_extents
int compare_extents(const void *a, const void *b)
Comparison function for sorting DumpExtent arrays by start sector.
Definition helpers.c:463
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
DumpExtent
Inclusive [start,end] logical sector range contributed by a single hardware environment.
Definition context.h:369
DumpHardwareEntriesWithData
In-memory representation of a dump hardware entry plus decoded variable-length fields & extents.
Definition context.h:350
DumpHardwareEntriesWithData::firmware
uint8_t * firmware
Firmware version string or NULL.
Definition context.h:356
DumpHardwareEntriesWithData::revision
uint8_t * revision
Hardware revision string or NULL.
Definition context.h:355
DumpHardwareEntriesWithData::model
uint8_t * model
Model string or NULL.
Definition context.h:354
DumpHardwareEntriesWithData::softwareName
uint8_t * softwareName
Dump software name or NULL.
Definition context.h:358
DumpHardwareEntriesWithData::extents
struct DumpExtent * extents
Array of extents (entry.extents elements) or NULL.
Definition context.h:352
DumpHardwareEntriesWithData::manufacturer
uint8_t * manufacturer
Manufacturer string (UTF-8) or NULL.
Definition context.h:353
DumpHardwareEntriesWithData::softwareVersion
uint8_t * softwareVersion
Dump software version or NULL.
Definition context.h:359
DumpHardwareEntriesWithData::serial
uint8_t * serial
Serial number string or NULL.
Definition context.h:357
DumpHardwareEntriesWithData::entry
DumpHardwareEntry entry
Fixed-size header with lengths & counts.
Definition context.h:351
DumpHardwareEntriesWithData::softwareOperatingSystem
uint8_t * softwareOperatingSystem
Host operating system string or NULL.
Definition context.h:360
DumpHardwareEntry
Per-environment length table describing subsequent UTF-8 strings and optional extent array.
Definition dump.h:113
DumpHardwareEntry::softwareNameLength
uint32_t softwareNameLength
Length in bytes of dumping software name string.
Definition dump.h:119
DumpHardwareEntry::manufacturerLength
uint32_t manufacturerLength
Length in bytes of manufacturer UTF-8 string.
Definition dump.h:114
DumpHardwareEntry::softwareVersionLength
uint32_t softwareVersionLength
Length in bytes of dumping software version string.
Definition dump.h:120
DumpHardwareEntry::firmwareLength
uint32_t firmwareLength
Length in bytes of firmware version string.
Definition dump.h:117
DumpHardwareEntry::extents
uint32_t extents
Number of DumpExtent records following the strings (0 = none).
Definition dump.h:122
DumpHardwareEntry::modelLength
uint32_t modelLength
Length in bytes of model UTF-8 string.
Definition dump.h:115
DumpHardwareEntry::serialLength
uint32_t serialLength
Length in bytes of device serial number string.
Definition dump.h:118
DumpHardwareEntry::revisionLength
uint32_t revisionLength
Length in bytes of revision / hardware revision string.
Definition dump.h:116
DumpHardwareEntry::softwareOperatingSystemLength
uint32_t softwareOperatingSystemLength
Length in bytes of host operating system string.
Definition dump.h:121
DumpHardwareHeader
Header that precedes a sequence of dump hardware entries and their variable-length payload.
Definition dump.h:91
DumpHardwareHeader::crc64
uint64_t crc64
CRC64-ECMA of the payload (byte-swapped for legacy v1 images, handled automatically).
Definition dump.h:95
DumpHardwareHeader::identifier
uint32_t identifier
Block identifier, must be BlockType::DumpHardwareBlock.
Definition dump.h:92
DumpHardwareHeader::length
uint32_t length
Total payload bytes after this header (sum of entries, strings, and extents arrays).
Definition dump.h:94
DumpHardwareHeader::entries
uint16_t entries
Number of DumpHardwareEntry records that follow.
Definition dump.h:93
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
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::dump_hardware_entries_with_data
struct DumpHardwareEntriesWithData * dump_hardware_entries_with_data
Array of dump hardware entries + strings.
Definition context.h:215
aaruformat_context::dirty_dumphw_block
bool dirty_dumphw_block
True if dump hardware block should be written during close.
Definition context.h:336
aaruformat_context::dump_hardware_header
DumpHardwareHeader dump_hardware_header
Dump hardware header.
Definition context.h:235