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 <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aaruformat/consts.h"
#include "aaruformat/context.h"
#include "aaruformat/decls.h"
#include "aaruformat/endian.h"
#include "aaruformat/enums.h"
#include "aaruformat/structs/dump.h"
#include "aaruformat/structs/index.h"
#include "internal.h"
#include "log.h"
static void free_dump_hardware_entries_array(DumpHardwareEntriesWithData *entries, uint16_t count)
{
if(entries == NULL) return;
for(uint16_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);
}
}
static void reset_dump_hardware_context(aaruformat_context *ctx)
{
if(ctx == NULL) return;
free_dump_hardware_entries_array(ctx->dump_hardware_entries_with_data, ctx->dump_hardware_header.entries);
free(ctx->dump_hardware_entries_with_data);
ctx->dump_hardware_entries_with_data = NULL;
memset(&ctx->dump_hardware_header, 0, sizeof(ctx->dump_hardware_header));
}
static bool read_dump_string(FILE *stream, const char *field_name, const uint32_t length, uint32_t *remaining,
uint8_t **destination)
{
if(length == 0) return true;
if(*remaining < length)
{
TRACE("Dump hardware %s length %u exceeds remaining payload %u", field_name, length,
remaining == NULL ? 0 : *remaining);
return false;
}
uint8_t *buffer = malloc(length);
if(buffer == NULL)
{
TRACE("Could not allocate %s buffer of length %u", field_name, length);
return false;
}
const size_t bytes_read = fread(buffer, 1, length, stream);
if(bytes_read != length)
{
TRACE("Could not read %s field, expected %u bytes got %zu", field_name, length, bytes_read);
free(buffer);
return false;
}
*remaining -= length;
*destination = buffer;
return true;
}
void process_dumphw_block(aaruformat_context *ctx, const IndexEntry *entry)
{
TRACE("Entering process_dumphw_block(%p, %p)", ctx, entry);
size_t read_bytes = 0;
if(ctx == NULL || ctx->imageStream == NULL || entry == NULL)
{
FATAL("Invalid context, image stream, or index entry pointer.");
TRACE("Exiting process_dumphw_block()");
if(ctx != NULL) reset_dump_hardware_context(ctx);
return;
}
if(entry->blockType != DumpHardwareBlock)
{
TRACE("Index entry block type %u is not DumpHardwareBlock, skipping.", entry->blockType);
TRACE("Exiting process_dumphw_block()");
return;
}
if(fseek(ctx->imageStream, entry->offset, SEEK_SET) < 0 || ftell(ctx->imageStream) != entry->offset)
{
FATAL("Could not seek to %" PRIu64 " as indicated by index entry...", entry->offset);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
TRACE("Reading dump hardware block header at position %" PRIu64, entry->offset);
DumpHardwareHeader header;
read_bytes = fread(&header, 1, sizeof(header), ctx->imageStream);
if(read_bytes != sizeof(header))
{
TRACE("Could not read dump hardware block header (read %zu bytes)", read_bytes);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
if(header.identifier != DumpHardwareBlock)
{
TRACE("Incorrect identifier 0x%08" PRIx32 " for dump hardware block at position %" PRIu64, header.identifier,
entry->offset);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
if(header.entries > 0 && header.length == 0)
{
TRACE("Dump hardware header indicates %u entries but zero payload length", header.entries);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
const uint32_t payload_length = header.length;
if(payload_length > 0)
{
uint8_t *payload = malloc(payload_length);
if(payload == NULL)
{
TRACE("Could not allocate %u bytes for dump hardware payload", payload_length);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
read_bytes = fread(payload, 1, payload_length, ctx->imageStream);
if(read_bytes != payload_length)
{
TRACE("Could not read dump hardware payload, expected %u bytes got %zu", payload_length, read_bytes);
free(payload);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
uint64_t crc64 = aaruf_crc64_data(payload, payload_length);
if(ctx->header.imageMajorVersion <= AARUF_VERSION_V1) crc64 = bswap_64(crc64);
if(crc64 != header.crc64)
{
TRACE("Dump hardware block CRC mismatch: computed 0x%" PRIx64 " expected 0x%" PRIx64, crc64, header.crc64);
free(payload);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
free(payload);
if(fseek(ctx->imageStream, -(long)payload_length, SEEK_CUR) != 0)
{
TRACE("Could not rewind after CRC verification");
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
}
if(header.entries == 0)
{
reset_dump_hardware_context(ctx);
ctx->dump_hardware_header = header;
TRACE("Dump hardware block contains no entries. Clearing existing metadata.");
TRACE("Exiting process_dumphw_block()");
return;
}
const size_t allocation_size = (size_t)header.entries * sizeof(DumpHardwareEntriesWithData);
if(allocation_size / sizeof(DumpHardwareEntriesWithData) != header.entries)
{
TRACE("Dump hardware entries multiplication overflow (%u entries)", header.entries);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
DumpHardwareEntriesWithData *entries = calloc(header.entries, sizeof(DumpHardwareEntriesWithData));
if(entries == NULL)
{
TRACE("Could not allocate %zu bytes for dump hardware entries", allocation_size);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
return;
}
uint32_t remaining_payload = payload_length;
uint16_t processed_entry = 0;
TRACE("Processing %u dump hardware block entries", header.entries);
for(uint16_t e = 0; e < header.entries; e++)
{
processed_entry = e;
DumpHardwareEntriesWithData *current = &entries[e];
if(remaining_payload < sizeof(DumpHardwareEntry))
{
TRACE("Remaining payload %u too small for dump hardware entry %u", remaining_payload, e);
goto parse_failure;
}
read_bytes = fread(&current->entry, 1, sizeof(DumpHardwareEntry), ctx->imageStream);
if(read_bytes != sizeof(DumpHardwareEntry))
{
TRACE("Could not read dump hardware entry %u header (read %zu bytes)", e, read_bytes);
goto parse_failure;
}
remaining_payload -= sizeof(DumpHardwareEntry);
if(!read_dump_string(ctx->imageStream, "manufacturer", current->entry.manufacturerLength, &remaining_payload,
&current->manufacturer))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "model", current->entry.modelLength, &remaining_payload,
&current->model))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "revision", current->entry.revisionLength, &remaining_payload,
&current->revision))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "firmware", current->entry.firmwareLength, &remaining_payload,
&current->firmware))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "serial", current->entry.serialLength, &remaining_payload,
&current->serial))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "software name", current->entry.softwareNameLength, &remaining_payload,
&current->softwareName))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "software version", current->entry.softwareVersionLength,
&remaining_payload, &current->softwareVersion))
goto parse_failure;
if(!read_dump_string(ctx->imageStream, "software operating system",
current->entry.softwareOperatingSystemLength, &remaining_payload,
&current->softwareOperatingSystem))
goto parse_failure;
const uint32_t extent_count = current->entry.extents;
if(extent_count == 0) continue;
const size_t extent_bytes = (size_t)extent_count * sizeof(DumpExtent);
if(extent_bytes / sizeof(DumpExtent) != extent_count || extent_bytes > remaining_payload)
{
TRACE("Extent array for entry %u exceeds remaining payload (%zu bytes requested, %u left)", e, extent_bytes,
remaining_payload);
goto parse_failure;
}
current->extents = (DumpExtent *)malloc(extent_bytes);
if(current->extents == NULL)
{
TRACE("Could not allocate %zu bytes for dump hardware entry %u extents", extent_bytes, e);
goto parse_failure;
}
const size_t extents_read = fread(current->extents, sizeof(DumpExtent), extent_count, ctx->imageStream);
if(extents_read != extent_count)
{
TRACE("Could not read %u dump hardware extents for entry %u (read %zu)", extent_count, e, extents_read);
goto parse_failure;
}
remaining_payload -= (uint32_t)extent_bytes;
qsort(current->extents, extent_count, sizeof(DumpExtent), compare_extents);
TRACE("Sorted %u extents for entry %u", extent_count, e);
}
reset_dump_hardware_context(ctx);
ctx->dump_hardware_entries_with_data = entries;
ctx->dump_hardware_header = header;
if(remaining_payload != 0)
{
TRACE("Dump hardware block parsing completed with %u trailing payload bytes", remaining_payload);
}
TRACE("Exiting process_dumphw_block()");
return;
parse_failure:
free_dump_hardware_entries_array(entries, processed_entry + 1);
free(entries);
reset_dump_hardware_context(ctx);
TRACE("Exiting process_dumphw_block()");
}
process_dumphw_block
void process_dumphw_block(aaruformat_context *ctx, const IndexEntry *entry)
Processes a dump hardware block from the image stream.
Definition dump.c:107
reset_dump_hardware_context
static void reset_dump_hardware_context(aaruformat_context *ctx)
Definition dump.c:54
free_dump_hardware_entries_array
static void free_dump_hardware_entries_array(DumpHardwareEntriesWithData *entries, uint16_t count)
Definition dump.c:36
read_dump_string
static bool read_dump_string(FILE *stream, const char *field_name, const uint32_t length, uint32_t *remaining, uint8_t **destination)
Definition dump.c:64
consts.h
Core public constants and compile‑time limits for the Aaru container format implementation.
AARUF_VERSION_V1
#define AARUF_VERSION_V1
First on‑disk version (C# implementation).
Definition consts.h:71
context.h
Central runtime context structures for libaaruformat (image state, caches, checksum buffers).
decls.h
aaruf_crc64_data
uint64_t aaruf_crc64_data(const uint8_t *data, uint32_t len)
Definition crc64.c:160
dump.h
Packed on-disk structures describing hardware and software used during image acquisition.
endian.h
bswap_64
#define bswap_64(x)
Definition endian.h:81
enums.h
DumpHardwareBlock
@ DumpHardwareBlock
Block containing an array of hardware used to create the image.
Definition enums.h:160
index.h
On‑disk index block header and entry structures (versions 1, 2 and 3).
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
AaruHeaderV2::imageMajorVersion
uint8_t imageMajorVersion
Container format major version.
Definition header.h:110
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
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::header
AaruHeaderV2 header
Parsed container header (v2).
Definition context.h:178
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::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
aaruformat_context::dump_hardware_header
DumpHardwareHeader dump_hardware_header
Dump hardware header.
Definition context.h:235