Loading...
Searching...
No Matches
ddt_v1.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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "errors.h"
#ifdef __linux__
#include <sys/mman.h>
#endif
#include "aaruformat.h"
#include "log.h"
int32_t process_ddt_v1(aaruformat_context *ctx, IndexEntry *entry, bool *found_user_data_ddt)
{
TRACE("Entering process_ddt_v1(%p, %p, %d)", ctx, entry, *found_user_data_ddt);
int pos = 0;
size_t read_bytes = 0;
DdtHeader ddt_header;
uint8_t *cmp_data = NULL;
uint32_t *cd_ddt = NULL;
uint8_t lzma_properties[LZMA_PROPERTIES_LENGTH];
size_t lzma_size = 0;
int error_no = 0;
// Check if the context and image stream are valid
if(ctx == NULL || ctx->imageStream == NULL)
{
FATAL("Invalid context or image stream.");
TRACE("Exiting process_ddt_v1() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
// Seek to block
TRACE("Seeking to DDT block at position %" PRIu64, entry->offset);
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...", entry->offset);
TRACE("Exiting process_ddt_v1() = AARUF_ERROR_CANNOT_READ_BLOCK");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
// Even if those two checks shall have been done before
TRACE("Reading DDT block header at position %" PRIu64, entry->offset);
read_bytes = fread(&ddt_header, 1, sizeof(DdtHeader), ctx->imageStream);
if(read_bytes != sizeof(DdtHeader))
{
FATAL("Could not read block header at %" PRIu64 "", entry->offset);
TRACE("Exiting process_ddt_v1() = AARUF_ERROR_CANNOT_READ_BLOCK");
return AARUF_ERROR_CANNOT_READ_BLOCK;
}
*found_user_data_ddt = true;
ctx->image_info.ImageSize += ddt_header.cmpLength;
if(entry->dataType == UserData)
{
ctx->image_info.Sectors = ddt_header.entries;
ctx->shift = ddt_header.shift;
ctx->ddt_version = 1;
// Check for DDT compression
switch(ddt_header.compression)
{
// TODO: Check CRC
case Lzma:
if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
{
FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
lzma_size = ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH;
cmp_data = (uint8_t *)malloc(lzma_size);
if(cmp_data == NULL)
{
TRACE("Cannot allocate memory for DDT, continuing...");
break;
}
ctx->user_data_ddt = (uint64_t *)malloc(ddt_header.length);
if(ctx->user_data_ddt == NULL)
{
TRACE("Cannot allocate memory for DDT, continuing...");
free(cmp_data);
break;
}
read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
if(read_bytes != LZMA_PROPERTIES_LENGTH)
{
TRACE("Could not read LZMA properties, continuing...");
free(cmp_data);
free(ctx->user_data_ddt);
ctx->user_data_ddt = NULL;
break;
}
read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
if(read_bytes != lzma_size)
{
TRACE("Could not read compressed block, continuing...");
free(cmp_data);
free(ctx->user_data_ddt);
ctx->user_data_ddt = NULL;
break;
}
read_bytes = ddt_header.length;
TRACE("Decompressing block of size %zu bytes", ddt_header.length);
error_no = aaruf_lzma_decode_buffer((uint8_t *)ctx->user_data_ddt, &read_bytes, cmp_data, &lzma_size,
lzma_properties, LZMA_PROPERTIES_LENGTH);
if(error_no != 0)
{
FATAL("Got error %d from LZMA, stopping...", error_no);
free(cmp_data);
free(ctx->user_data_ddt);
ctx->user_data_ddt = NULL;
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
if(read_bytes != ddt_header.length)
{
FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
free(cmp_data);
free(ctx->user_data_ddt);
ctx->user_data_ddt = NULL;
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
free(cmp_data);
cmp_data = NULL;
ctx->in_memory_ddt = true;
*found_user_data_ddt = true;
break;
// TODO: Check CRC
case None:
ctx->user_data_ddt = (uint64_t *)malloc(ddt_header.length);
if(ctx->user_data_ddt == NULL)
{
TRACE("Cannot allocate memory for DDT, continuing...");
break;
}
read_bytes = fread(ctx->user_data_ddt, 1, ddt_header.entries * sizeof(uint64_t), ctx->imageStream);
if(read_bytes != ddt_header.entries * sizeof(uint64_t))
{
free(ctx->user_data_ddt);
TRACE("Could not read deduplication table, continuing...");
break;
}
ctx->in_memory_ddt = true;
*found_user_data_ddt = true;
break;
default:
TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
*found_user_data_ddt = false;
break;
}
}
else if(entry->dataType == CdSectorPrefixCorrected || entry->dataType == CdSectorSuffixCorrected)
{
switch(ddt_header.compression)
{
// TODO: Check CRC
case Lzma:
if(ddt_header.cmpLength <= LZMA_PROPERTIES_LENGTH)
{
FATAL("Compressed DDT payload too small (%" PRIu64 ") for LZMA properties.", ddt_header.cmpLength);
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
lzma_size = ddt_header.cmpLength - LZMA_PROPERTIES_LENGTH;
cmp_data = (uint8_t *)malloc(lzma_size);
if(cmp_data == NULL)
{
TRACE("Cannot allocate memory for DDT, continuing...");
break;
}
cd_ddt = (uint32_t *)malloc(ddt_header.length);
if(cd_ddt == NULL)
{
TRACE("Cannot allocate memory for DDT, continuing...");
free(cmp_data);
break;
}
read_bytes = fread(lzma_properties, 1, LZMA_PROPERTIES_LENGTH, ctx->imageStream);
if(read_bytes != LZMA_PROPERTIES_LENGTH)
{
TRACE("Could not read LZMA properties, continuing...");
free(cmp_data);
free(cd_ddt);
break;
}
read_bytes = fread(cmp_data, 1, lzma_size, ctx->imageStream);
if(read_bytes != lzma_size)
{
TRACE("Could not read compressed block, continuing...");
free(cmp_data);
free(cd_ddt);
break;
}
read_bytes = ddt_header.length;
TRACE("Decompressing block of size %zu bytes", ddt_header.length);
error_no = aaruf_lzma_decode_buffer((uint8_t *)cd_ddt, &read_bytes, cmp_data, &lzma_size,
lzma_properties, LZMA_PROPERTIES_LENGTH);
if(error_no != 0)
{
FATAL("Got error %d from LZMA, stopping...", error_no);
free(cmp_data);
free(cd_ddt);
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
if(read_bytes != ddt_header.length)
{
FATAL("Error decompressing block, should be {0} bytes but got {1} bytes., stopping...");
free(cmp_data);
free(cd_ddt);
return AARUF_ERROR_CANNOT_DECOMPRESS_BLOCK;
}
free(cmp_data);
cmp_data = NULL;
if(entry->dataType == CdSectorPrefixCorrected)
ctx->sector_prefix_ddt = cd_ddt;
else if(entry->dataType == CdSectorSuffixCorrected)
ctx->sector_suffix_ddt = cd_ddt;
else
free(cd_ddt);
break;
// TODO: Check CRC
case None:
cd_ddt = (uint32_t *)malloc(ddt_header.entries * sizeof(uint32_t));
if(cd_ddt == NULL)
{
TRACE("Cannot allocate memory for deduplication table.");
break;
}
read_bytes = fread(cd_ddt, 1, ddt_header.entries * sizeof(uint32_t), ctx->imageStream);
if(read_bytes != ddt_header.entries * sizeof(uint32_t))
{
free(cd_ddt);
TRACE("Could not read deduplication table, continuing...");
break;
}
if(entry->dataType == CdSectorPrefixCorrected)
ctx->sector_prefix_ddt = cd_ddt;
else if(entry->dataType == CdSectorSuffixCorrected)
ctx->sector_suffix_ddt = cd_ddt;
else
free(cd_ddt);
break;
default:
TRACE("Found unknown compression type %d, continuing...", ddt_header.compression);
break;
}
}
TRACE("Exiting process_ddt_v1() = AARUF_STATUS_OK");
return AARUF_STATUS_OK;
}
int32_t decode_ddt_entry_v1(aaruformat_context *ctx, const uint64_t sector_address, uint64_t *offset,
uint64_t *block_offset, uint8_t *sector_status)
{
TRACE("Entering decode_ddt_entry_v1(%p, %" PRIu64 ", %p, %p, %p)", ctx, sector_address, offset, block_offset,
sector_status);
// Check if the context and image stream are valid
if(ctx == NULL || ctx->imageStream == NULL)
{
FATAL("Invalid context or image stream.");
TRACE("Exiting decode_ddt_entry_v1() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(ctx->user_data_ddt == NULL)
{
FATAL("User data DDT not loaded.");
TRACE("Exiting decode_ddt_entry_v1() = AARUF_ERROR_NOT_AARUFORMAT");
return AARUF_ERROR_NOT_AARUFORMAT;
}
if(ctx->shift >= 64)
{
FATAL("Invalid DDT shift value %u", ctx->shift);
TRACE("Exiting decode_ddt_entry_v1() = AARUF_ERROR_INCORRECT_DATA_SIZE");
return AARUF_ERROR_INCORRECT_DATA_SIZE;
}
const uint64_t ddt_entry = ctx->user_data_ddt[sector_address];
const uint64_t offset_mask64 = (UINT64_C(1) << ctx->shift) - UINT64_C(1);
*offset = ddt_entry & offset_mask64;
*block_offset = ddt_entry >> ctx->shift;
// Partially written image... as we can't know the real sector size just assume it's common :/
if(ddt_entry == 0)
*sector_status = SectorStatusNotDumped;
else
*sector_status = SectorStatusDumped;
TRACE("Exiting decode_ddt_entry_v1(%p, %" PRIu64 ", %llu, %llu, %d) = AARUF_STATUS_OK", ctx, sector_address,
*offset, *block_offset, *sector_status);
return AARUF_STATUS_OK;
}
aaruformat.h
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
decode_ddt_entry_v1
int32_t decode_ddt_entry_v1(aaruformat_context *ctx, const uint64_t sector_address, uint64_t *offset, uint64_t *block_offset, uint8_t *sector_status)
Decodes a DDT v1 entry for a given sector address.
Definition ddt_v1.c:405
process_ddt_v1
int32_t process_ddt_v1(aaruformat_context *ctx, IndexEntry *entry, bool *found_user_data_ddt)
Processes a DDT v1 block from the image stream.
Definition ddt_v1.c:85
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
SectorStatusDumped
@ SectorStatusDumped
Sector(s) successfully dumped without error.
Definition enums.h:237
SectorStatusNotDumped
@ SectorStatusNotDumped
Sector(s) not yet acquired during image dumping.
Definition enums.h:236
CdSectorPrefixCorrected
@ CdSectorPrefixCorrected
Compact Disc sector prefix (sync, header) corrected-only stored.
Definition enums.h:121
CdSectorSuffixCorrected
@ CdSectorSuffixCorrected
Compact Disc sector suffix (EDC, ECC P, ECC Q) corrected-only stored.
Definition enums.h:122
UserData
@ UserData
User (main) data.
Definition enums.h:46
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_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_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
log.h
FATAL
#define FATAL(fmt,...)
Definition log.h:40
TRACE
#define TRACE(fmt,...)
Definition log.h:25
DdtHeader
Header preceding a version 1 (flat) deduplication table body.
Definition ddt.h:66
DdtHeader::shift
uint8_t shift
Left shift applied to per-entry file offset component forming logicalEntryValue.
Definition ddt.h:70
DdtHeader::compression
uint16_t compression
Compression algorithm for the table body (CompressionType).
Definition ddt.h:69
DdtHeader::cmpLength
uint64_t cmpLength
Size in bytes of compressed entries payload.
Definition ddt.h:72
DdtHeader::length
uint64_t length
Size in bytes of uncompressed entries payload.
Definition ddt.h:73
DdtHeader::entries
uint64_t entries
Number of deduplication entries contained in (uncompressed) table.
Definition ddt.h:71
ImageInfo::ImageSize
uint64_t ImageSize
Size of the image payload in bytes (excludes headers/metadata).
Definition aaru.h:935
ImageInfo::Sectors
uint64_t Sectors
Total count of addressable logical sectors/blocks.
Definition aaru.h:936
IndexEntry
Single index entry describing a block's type, (optional) data classification, and file offset.
Definition index.h:109
IndexEntry::offset
uint64_t offset
Absolute byte offset in the image where the referenced block header begins.
Definition index.h:112
IndexEntry::dataType
uint16_t dataType
Data classification (value from DataType) or unused for untyped blocks.
Definition index.h:111
aaruformat_context
Master context representing an open or in‑creation Aaru image.
Definition context.h:175
aaruformat_context::user_data_ddt
uint64_t * user_data_ddt
Legacy flat DDT pointer (NULL when using v2 mini/big arrays).
Definition context.h:184
aaruformat_context::shift
uint8_t shift
Legacy overall shift (deprecated by data_shift/table_shift).
Definition context.h:198
aaruformat_context::sector_suffix_ddt
uint32_t * sector_suffix_ddt
Legacy CD sector suffix DDT.
Definition context.h:187
aaruformat_context::in_memory_ddt
bool in_memory_ddt
True if primary (and possibly secondary) DDT loaded.
Definition context.h:199
aaruformat_context::ddt_version
int ddt_version
DDT version in use (1=legacy, 2=v2 hierarchical).
Definition context.h:197
aaruformat_context::imageStream
FILE * imageStream
Underlying FILE* stream (binary mode).
Definition context.h:179
aaruformat_context::image_info
ImageInfo image_info
Exposed high-level image info summary.
Definition context.h:263
aaruformat_context::sector_prefix_ddt
uint32_t * sector_prefix_ddt
Legacy CD sector prefix DDT (deprecated by *2).
Definition context.h:186