|
libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
|
Implements image finalization and resource cleanup for libaaruformat. More...
#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <aaruformat.h>#include "internal.h"#include "log.h"Go to the source code of this file.
Functions | |
| static int32_t | write_cached_secondary_ddt (aaruformat_context *ctx) |
| Flush a cached secondary (child) DeDuplication Table (DDT) to the image. | |
| static int32_t | write_primary_ddt (aaruformat_context *ctx) |
| Write (flush) the multi-level primary DDT table header and data back to its file offset. | |
| static int32_t | write_single_level_ddt (aaruformat_context *ctx) |
| Serialize a single-level DDT (tableShift == 0) directly after its header. | |
| static int32_t | write_tape_ddt (aaruformat_context *ctx) |
| Converts tape DDT hash table to array format and writes it as a single-level DDT. | |
| static void | write_checksum_block (aaruformat_context *ctx) |
| Finalize any active checksum calculations and append a checksum block. | |
| static void | write_tracks_block (aaruformat_context *ctx) |
| Serialize the tracks metadata block and add it to the index. | |
| static void | write_mode2_subheaders_block (aaruformat_context *ctx) |
| Serialize a MODE 2 (XA) subheaders data block. | |
| static void | write_sector_prefix (aaruformat_context *ctx) |
| Serialize the optional CD sector prefix block. | |
| static void | write_sector_suffix (aaruformat_context *ctx) |
| Serialize the optional CD sector suffix block (EDC/ECC region capture). | |
| static void | write_sector_prefix_ddt (aaruformat_context *ctx) |
| Serialize the per-sector CD prefix status / index DeDuplication Table (DDT v2, prefix variant). | |
| static void | write_sector_suffix_ddt (aaruformat_context *ctx) |
| Serialize the per-sector CD suffix status / index DeDuplication Table (DDT v2, suffix variant). | |
| static void | write_sector_subchannel (const aaruformat_context *ctx) |
| Serialize the per-sector subchannel or tag data block. | |
| void | write_dvd_long_sector_blocks (aaruformat_context *ctx) |
| Serialize DVD long sector auxiliary data blocks to the image file. | |
| static void | write_dvd_title_key_decrypted_block (const aaruformat_context *ctx) |
| Serialize the DVD decrypted title key data block to the image file. | |
| static void | write_media_tags (const aaruformat_context *ctx) |
| Serialize all accumulated media tags to the image file. | |
| static void | write_tape_file_block (const aaruformat_context *ctx) |
| Serialize the tape file metadata block to the image file. | |
| static void | write_tape_partition_block (const aaruformat_context *ctx) |
| Serialize the tape partition metadata block to the image file. | |
| static void | write_geometry_block (const aaruformat_context *ctx) |
| Serialize the geometry metadata block to the image file. | |
| static void | write_metadata_block (aaruformat_context *ctx) |
| Serialize the metadata block containing image and media descriptive information. | |
| static void | write_dumphw_block (aaruformat_context *ctx) |
| Serialize the dump hardware block containing acquisition environment information. | |
| static void | write_cicm_block (const aaruformat_context *ctx) |
| Serialize the CICM XML metadata block to the image file. | |
| static void | write_aaru_json_block (const aaruformat_context *ctx) |
| Serialize the Aaru metadata JSON block to the image file. | |
| static int32_t | write_index_block (aaruformat_context *ctx) |
| Serialize the accumulated index entries at the end of the image and back-patch the header. | |
| int | aaruf_close (void *context) |
| Close an Aaru image context, flushing pending data structures and releasing resources. | |
Implements image finalization and resource cleanup for libaaruformat.
This translation unit contains the logic that flushes any remaining in-memory structures (deduplication tables, checksum information, track metadata, MODE 2 subheaders, sector prefix data and the global index) to the on-disk Aaru image when closing a context opened for writing. It also performs orderly teardown of dynamically allocated resources regardless of read or write mode.
Helper (static) functions perform discrete serialization steps; the public entry point is aaruf_close(). All write helpers assume that the caller has already validated the context magic and (for write mode) written the initial provisional header at offset 0. Functions return libaaruformat status codes (AARUF_STATUS_OK on success or an AARUF_ERROR_* constant) except for aaruf_close(), which returns 0 on success and -1 on failure while setting errno.
Definition in file close.c.
| int aaruf_close | ( | void * | context | ) |
Close an Aaru image context, flushing pending data structures and releasing resources.
Public API entry point used to finalize an image being written or simply dispose of a context opened for reading. For write-mode contexts (ctx->isWriting true) the function performs the following ordered steps:
Afterwards (or for read-mode contexts) all dynamically allocated buffers, arrays, hash tables and mapping structures are freed/unmapped. Media tags are removed from their hash table.
Error Handling:
| context | Opaque pointer returned by earlier open/create calls (must be an aaruformatContext). |
| 0 | All pending data flushed (if writing) and resources released successfully. |
| -1 | Invalid context pointer or initial header rewrite failure (errno = EINVAL or AARUF_ERROR_CANNOT_WRITE_HEADER). |
| AARUF_ERROR_CANNOT_WRITE_HEADER | A later write helper (e.g., index, DDT) failed and returned this code directly. |
| <other | negative libaaruformat code> Propagated from a write helper if future helpers add more error codes. |
Definition at line 3995 of file close.c.
References AARU_MAGIC, aaruf_close_current_block(), AARUF_ERROR_CANNOT_WRITE_HEADER, AARUF_STATUS_OK, aaruformat_context::checksums, aaruformat_context::cicm_block, mediaTagEntry::data, aaruformat_context::deduplicate, aaruformat_context::dump_hardware_entries_with_data, aaruformat_context::dump_hardware_header, aaruformat_context::ecc_cd_context, DumpHardwareHeader::entries, DumpHardwareEntriesWithData::extents, FATAL, DumpHardwareEntriesWithData::firmware, free_map(), aaruformat_context::header, aaruformat_context::imageStream, aaruformat_context::in_memory_ddt, aaruformat_context::index_entries, aaruformat_context::is_tape, aaruformat_context::is_writing, aaruformat_context::magic, DumpHardwareEntriesWithData::manufacturer, aaruformat_context::mapped_memory_ddt_size, aaruformat_context::mediaTags, aaruformat_context::metadata_block, aaruformat_context::mode2_subheaders, DumpHardwareEntriesWithData::model, aaruformat_context::readableSectorTags, DumpHardwareEntriesWithData::revision, aaruformat_context::sector_cpr_mai, aaruformat_context::sector_edc, aaruformat_context::sector_hash_map, aaruformat_context::sector_id, aaruformat_context::sector_ied, aaruformat_context::sector_prefix, aaruformat_context::sector_prefix_corrected, aaruformat_context::sector_prefix_ddt, aaruformat_context::sector_prefix_ddt2, aaruformat_context::sector_subchannel, aaruformat_context::sector_suffix, aaruformat_context::sector_suffix_corrected, aaruformat_context::sector_suffix_ddt, aaruformat_context::sector_suffix_ddt2, DumpHardwareEntriesWithData::serial, DumpHardwareEntriesWithData::softwareName, DumpHardwareEntriesWithData::softwareOperatingSystem, DumpHardwareEntriesWithData::softwareVersion, Checksums::spamsum, TRACE, aaruformat_context::track_entries, aaruformat_context::user_data_ddt, write_aaru_json_block(), write_cached_secondary_ddt(), write_checksum_block(), write_cicm_block(), write_dumphw_block(), write_dvd_long_sector_blocks(), write_dvd_title_key_decrypted_block(), write_geometry_block(), write_index_block(), write_media_tags(), write_metadata_block(), write_mode2_subheaders_block(), write_primary_ddt(), write_sector_prefix(), write_sector_prefix_ddt(), write_sector_subchannel(), write_sector_suffix(), write_sector_suffix_ddt(), write_single_level_ddt(), write_tape_ddt(), write_tape_file_block(), write_tape_partition_block(), write_tracks_block(), and aaruformat_context::writing_buffer.
Referenced by aaruf_open().
|
static |
Serialize the Aaru metadata JSON block to the image file.
This function writes an AaruMetadataJsonBlock containing embedded Aaru metadata JSON to the Aaru image file. The Aaru metadata JSON format is a structured, machine-readable representation of comprehensive image metadata including media information, imaging session details, hardware configuration, optical disc tracks and sessions, checksums, and preservation metadata. The JSON payload is stored in its original form without parsing, interpretation, or validation by the library, preserving the exact structure and content provided during image creation.
The Aaru JSON block is optional; if no Aaru JSON metadata has been populated (jsonBlock is NULL, length is zero, or identifier is not set to AaruMetadataJsonBlock), the function returns immediately without writing anything. This no-op behavior allows the close operation to proceed gracefully whether or not Aaru JSON metadata was included during image creation.
Block structure: The serialized block consists of:
The header contains:
Alignment and file positioning: Before writing the block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from ctx->userDataDdtHeader.blockAlignmentShift. This ensures the Aaru JSON block begins on a properly aligned offset for efficient I/O and compliance with the Aaru format specification.
Write sequence: The function performs a two-stage write operation:
Both writes must succeed for the block to be considered successfully written. If the header write fails, the payload write is skipped. Only if both writes succeed is an index entry added.
Index registration: After successfully writing both the header and JSON payload, an IndexEntry is appended to ctx->indexEntries with:
Error handling: Write errors (fwrite returning < 1) are silently ignored; no index entry is added if either write fails. Diagnostic TRACE logs report success or failure. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
No-op conditions:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->jsonBlockHeader contains the header with identifier and length fields. ctx->jsonBlock points to the actual UTF-8 encoded JSON data (may be NULL if no Aaru JSON metadata was provided). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 3812 of file close.c.
References AaruMetadataJsonBlock, DdtHeader2::blockAlignmentShift, IndexEntry::blockType, IndexEntry::dataType, AaruMetadataJsonBlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, aaruformat_context::json_block, aaruformat_context::json_block_header, AaruMetadataJsonBlockHeader::length, IndexEntry::offset, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Flush a cached secondary (child) DeDuplication Table (DDT) to the image.
When working with a multi-level DDT (i.e. primary table with tableShift > 0), a single secondary table may be cached in memory while sectors belonging to its range are written. This function serializes the currently cached secondary table (if any) at the end of the file, aligning the write position to the DDT block alignment, and updates the corresponding primary table entry to point to the new block-aligned location. The primary table itself is then re-written in-place (only its data array portion) to persist the updated pointer. The index is updated by removing any previous index entry for the same secondary table offset and inserting a new one for the freshly written table.
CRC64 is computed for the serialized table contents and stored in crc64; cmpCrc64 stores the checksum of compressed data or equals crc64 if compression is not applied or not effective.
On return the cached secondary table buffers and bookkeeping fields (cachedSecondaryDdtSmall, cachedSecondaryDdtBig, cachedDdtOffset) are cleared.
| ctx | Pointer to an initialized aaruformatContext in write mode. |
| AARUF_STATUS_OK | Success or no cached secondary DDT needed flushing. |
| AARUF_ERROR_CANNOT_WRITE_HEADER | Failed writing secondary table or rewriting primary table. |
Definition at line 77 of file close.c.
References aaruf_crc64_data(), aaruf_crc64_final(), aaruf_crc64_init(), aaruf_crc64_update(), AARUF_ERROR_CANNOT_WRITE_HEADER, AARUF_ERROR_NOT_ENOUGH_MEMORY, aaruf_lzma_encode_buffer(), AARUF_STATUS_OK, DdtHeader2::blockAlignmentShift, DdtHeader2::blocks, IndexEntry::blockType, aaruformat_context::cached_ddt_offset, aaruformat_context::cached_ddt_position, aaruformat_context::cached_secondary_ddt2, DdtHeader2::cmpCrc64, DdtHeader2::cmpLength, DdtHeader2::compression, aaruformat_context::compression_enabled, DdtHeader2::crc64, DdtHeader2::dataShift, IndexEntry::dataType, DeDuplicationTableSecondary, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, DdtHeader2::length, DdtHeader2::levels, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, DdtHeader2::previousLevelOffset, aaruformat_context::primary_ddt_offset, DdtHeader2::start, DdtHeader2::tableLevel, DdtHeader2::tableShift, TRACE, DdtHeader2::type, aaruformat_context::user_data_ddt2, aaruformat_context::user_data_ddt_header, and UserData.
Referenced by aaruf_close().
|
static |
Finalize any active checksum calculations and append a checksum block.
This routine completes pending hash contexts (MD5, SHA-1, SHA-256, SpamSum, BLAKE3), marks the presence flags in ctx->checksums, and if at least one checksum exists writes a ChecksumBlock at the end of the image (block-aligned). Individual ChecksumEntry records are serialized for each available algorithm and the block is indexed. Feature flags (e.g. AARU_FEATURE_RW_BLAKE3) are updated if required.
Memory ownership: for SpamSum a buffer is allocated here if a digest was being computed and is subsequently written without being freed inside this function (it will be freed during close). The BLAKE3 context is freed after finalization.
| ctx | Pointer to an initialized aaruformatContext in write mode. |
Definition at line 654 of file close.c.
References AARU_FEATURE_RW_BLAKE3, aaruf_md5_final(), aaruf_sha1_final(), aaruf_sha256_final(), aaruf_spamsum_final(), aaruf_spamsum_free(), Blake3, Checksums::blake3, aaruformat_context::blake3_context, DdtHeader2::blockAlignmentShift, IndexEntry::blockType, aaruformat_context::calculating_blake3, aaruformat_context::calculating_md5, aaruformat_context::calculating_sha1, aaruformat_context::calculating_sha256, aaruformat_context::calculating_spamsum, ChecksumBlock, aaruformat_context::checksums, IndexEntry::dataType, ChecksumHeader::entries, AaruHeaderV2::featureCompatible, FUZZY_MAX_RESULT, Checksums::hasBlake3, Checksums::hasMd5, Checksums::hasSha1, Checksums::hasSha256, Checksums::hasSpamSum, aaruformat_context::header, ChecksumHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, ChecksumEntry::length, ChecksumHeader::length, Md5, Checksums::md5, aaruformat_context::md5_context, MD5_DIGEST_LENGTH, IndexEntry::offset, Sha1, Checksums::sha1, aaruformat_context::sha1_context, SHA1_DIGEST_LENGTH, Sha256, Checksums::sha256, aaruformat_context::sha256_context, SHA256_DIGEST_LENGTH, SpamSum, Checksums::spamsum, aaruformat_context::spamsum_context, TRACE, ChecksumEntry::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the CICM XML metadata block to the image file.
This function writes a CicmBlock containing embedded CICM (Canary Islands Computer Museum) XML metadata to the Aaru image file. The CICM XML format is a standardized metadata schema used for documenting preservation and archival information about media and disk images. The XML payload is stored in its original form without parsing, interpretation, or validation by the library, preserving the exact structure and content provided during image creation.
The CICM block is optional; if no CICM metadata has been populated (cicmBlock is NULL, length is zero, or identifier is not set to CicmBlock), the function returns immediately without writing anything. This no-op behavior allows the close operation to proceed gracefully whether or not CICM metadata was included during image creation.
Block structure: The serialized block consists of:
The header contains:
Alignment and file positioning: Before writing the block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from ctx->userDataDdtHeader.blockAlignmentShift. This ensures the CICM block begins on a properly aligned offset for efficient I/O and compliance with the Aaru format specification.
Write sequence: The function performs a two-stage write operation:
Both writes must succeed for the block to be considered successfully written. If the header write fails, the payload write is skipped. Only if both writes succeed is an index entry added.
Index registration: After successfully writing both the header and XML payload, an IndexEntry is appended to ctx->indexEntries with:
Error handling: Write errors (fwrite returning < 1) are silently ignored; no index entry is added if either write fails. Diagnostic TRACE logs report success or failure. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
No-op conditions:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->cicmBlockHeader contains the header with identifier and length fields. ctx->cicmBlock points to the actual UTF-8 encoded XML data (may be NULL if no CICM metadata was provided). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 3675 of file close.c.
References DdtHeader2::blockAlignmentShift, IndexEntry::blockType, aaruformat_context::cicm_block, aaruformat_context::cicm_block_header, CicmBlock, IndexEntry::dataType, CicmMetadataBlock::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, CicmMetadataBlock::length, IndexEntry::offset, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the dump hardware block containing acquisition environment information.
This function writes a DumpHardwareBlock to the image file, documenting the hardware and software environments used to create the image. A dump hardware block records one or more "dump environments" – typically combinations of physical devices (drives, controllers, adapters) and the software stacks that performed the read operations. This metadata is essential for understanding the imaging context, validating acquisition integrity, reproducing imaging conditions, and supporting forensic or archival documentation requirements.
Each environment entry includes hardware identification (manufacturer, model, revision, firmware, serial number), software identification (name, version, operating system), and optional extent ranges that specify which logical sectors or units were contributed by that particular environment. This structure supports complex imaging scenarios where multiple devices or software configurations were used to create a composite image.
The dump hardware block is optional; if no dump hardware information has been populated (dumpHardwareEntriesWithData is NULL, entries count is zero, or identifier is not set to DumpHardwareBlock), the function returns immediately without writing anything. This no-op behavior allows the close operation to proceed gracefully whether or not dump hardware metadata was included during image creation.
Block structure: The serialized block consists of:
All strings are UTF-8 encoded and NOT null-terminated in the serialized block. String lengths are measured in bytes, not character counts.
Serialization process:
Alignment and file positioning: Before writing the block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from ctx->userDataDdtHeader.blockAlignmentShift. This ensures the dump hardware block begins on a properly aligned offset for efficient I/O and compliance with the Aaru format specification.
CRC64 calculation: The function calculates CRC64-ECMA over the payload portion of the buffer (everything after the DumpHardwareHeader) and stores it in the header before writing. This checksum allows verification of dump hardware block integrity when reading the image.
Index registration: After successfully writing the complete block, an IndexEntry is appended to ctx->indexEntries with:
Error handling: Memory allocation failures (calloc returning NULL) cause immediate return without writing. Bounds checking is performed during serialization; if calculated entry sizes exceed the allocated buffer, the buffer is freed and the function returns without writing. Write errors (fwrite returning < 1) are silently ignored; no index entry is added if the write fails. Diagnostic TRACE logs report success or failure. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
No-op conditions:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->dumpHardwareHeader contains the header with identifier, entry count, and total payload length. ctx->dumpHardwareEntriesWithData contains the array of dump hardware entries with their associated string data and extents (may be NULL if no dump hardware was added). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 3445 of file close.c.
References aaruf_crc64_data(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, DumpHardwareHeader::crc64, IndexEntry::dataType, aaruformat_context::dump_hardware_entries_with_data, aaruformat_context::dump_hardware_header, DumpHardwareBlock, DumpHardwareHeader::entries, DumpHardwareEntriesWithData::entry, DumpHardwareEntriesWithData::extents, DumpHardwareEntry::extents, FATAL, DumpHardwareEntriesWithData::firmware, DumpHardwareEntry::firmwareLength, DumpHardwareHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, DumpHardwareHeader::length, DumpHardwareEntriesWithData::manufacturer, DumpHardwareEntry::manufacturerLength, DumpHardwareEntriesWithData::model, DumpHardwareEntry::modelLength, IndexEntry::offset, DumpHardwareEntriesWithData::revision, DumpHardwareEntry::revisionLength, DumpHardwareEntriesWithData::serial, DumpHardwareEntry::serialLength, DumpHardwareEntriesWithData::softwareName, DumpHardwareEntry::softwareNameLength, DumpHardwareEntriesWithData::softwareOperatingSystem, DumpHardwareEntry::softwareOperatingSystemLength, DumpHardwareEntriesWithData::softwareVersion, DumpHardwareEntry::softwareVersionLength, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
| void write_dvd_long_sector_blocks | ( | aaruformat_context * | ctx | ) |
Serialize DVD long sector auxiliary data blocks to the image file.
This function writes four separate data blocks containing DVD-specific auxiliary information extracted from "long" DVD sectors. DVD long sectors contain additional fields beyond the 2048 bytes of user data, including sector identification, error detection, and copy protection information. When writing DVD images with long sector support, these auxiliary fields are stored separately from the main user data to optimize storage and enable selective access.
The function is only invoked if all four auxiliary buffers have been populated during image creation (sector_id, sector_ied, sector_cpr_mai, sector_edc). If any buffer is NULL, the function returns immediately without writing anything, allowing DVD images without long sector data to be created normally.
Four auxiliary data blocks written:
Block structure for each auxiliary block: Each block consists of:
The total number of sectors includes negative sectors (for lead-in), normal image sectors, and overflow sectors (for lead-out), calculated as: total_sectors = negative + Sectors + overflow
Write sequence for each block:
Alignment and file positioning: Before writing each block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from ctx->userDataDdtHeader.blockAlignmentShift. This ensures all blocks begin on properly aligned offsets for efficient I/O and compliance with the Aaru format specification.
Index registration: After successfully writing each block's header and data, an IndexEntry is appended to ctx->indexEntries with:
Error handling: Write errors (fwrite returning < 1) are silently ignored for individual blocks; no index entry is added if a write fails, but iteration continues to attempt writing remaining blocks. Diagnostic TRACE logs report success or failure for each block. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
No-op conditions: If any of the four auxiliary buffers is NULL, the function returns immediately without writing anything. This is an all-or-nothing operation - either all four blocks are written or none.
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->sector_id contains the ID fields from all DVD long sectors (may be NULL). ctx->sector_ied contains the IED fields from all DVD long sectors (may be NULL). ctx->sector_cpr_mai contains the CPR/MAI fields from all DVD long sectors (may be NULL). ctx->sector_edc contains the EDC fields from all DVD long sectors (may be NULL). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 1808 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, DvdSectorCprMai, DvdSectorEdc, DvdSectorId, DvdSectorIed, BlockHeader::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, aaruformat_context::sector_cpr_mai, aaruformat_context::sector_edc, aaruformat_context::sector_id, aaruformat_context::sector_ied, ImageInfo::Sectors, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the DVD decrypted title key data block to the image file.
This function writes a data block containing decrypted DVD title keys for all sectors in the image. DVD title keys are used in the Content Scrambling System (CSS) encryption scheme to decrypt sector data on encrypted DVDs. When imaging encrypted DVD media, if the decryption keys are available, they can be stored alongside the encrypted data to enable future decryption without requiring the original disc or authentication process.
The function is only invoked if the decrypted title key buffer has been populated during image creation (ctx->sector_decrypted_title_key != NULL). If the buffer is NULL, the function returns immediately without writing anything, allowing DVD images without decrypted title keys to be created normally. This is typical for non-encrypted DVDs or when keys were not available during imaging.
Data block structure:
Block write sequence:
Alignment and file positioning:
Before writing the block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from the blockAlignmentShift. This ensures that all structural blocks begin on properly aligned offsets for efficient I/O and compliance with the Aaru format specification.
Index registration:
After successful write, an IndexEntry is created and added to ctx->indexEntries using utarray_push_back(). This allows the block to be located efficiently during image reading via the index lookup mechanism. The index stores the exact file offset where the BlockHeader was written.
Title Key Format:
Each sector's title key is exactly 5 bytes. The keys are stored sequentially in sector order (negative sectors first, then user sectors, then overflow sectors). The corrected sector addressing scheme is used to index into the buffer:
| ctx | Pointer to an initialized aaruformatContext in write mode. |
Definition at line 2245 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, DvdSectorTitleKeyDecrypted, BlockHeader::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, aaruformat_context::sector_decrypted_title_key, ImageInfo::Sectors, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the geometry metadata block to the image file.
This function writes a GeometryBlockHeader containing legacy CHS (Cylinder-Head-Sector) style logical geometry metadata to the Aaru image file. The geometry block records the physical/logical layout of media that can be addressed using classical CHS parameters (cylinders, heads, sectors per track) common to legacy hard disk drives and some optical media formats.
The geometry information is optional; if no geometry metadata was previously set (detected by checking ctx->geometryBlock.identifier != GeometryBlock), the function returns immediately as a no-op. When present, the block is written at the end of the image file, aligned to the DDT block boundary specified by blockAlignmentShift, and an IndexEntry is appended to ctx->indexEntries so readers can locate it during image parsing.
Block layout:
Total logical sectors implied by the geometry: cylinders × heads × sectorsPerTrack. Sector size is not encoded in this block and must be derived from other metadata (e.g., from the media type or explicitly stored elsewhere in the image).
Alignment strategy:
Error handling:
Indexing:
Use cases:
Thread safety: This function is not thread-safe; it modifies shared ctx state (imageStream file position, indexEntries array) and must only be called during single-threaded finalization (within aaruf_close).
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. The geometryBlock field must be pre-populated if geometry metadata is desired. The imageStream must be open and writable. |
Definition at line 3026 of file close.c.
References DdtHeader2::blockAlignmentShift, IndexEntry::blockType, IndexEntry::dataType, aaruformat_context::geometry_block, GeometryBlock, GeometryBlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, IndexEntry::offset, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the accumulated index entries at the end of the image and back-patch the header.
All previously written structural blocks push their IndexEntry into ctx->indexEntries. This function collects them, writes an IndexHeader3 followed by each IndexEntry, computes CRC64 over the entries, and then updates the main AaruHeaderV2 (at offset 0) with the index offset. The index itself is aligned to the DDT block boundary. No previous index chaining is currently implemented (index_header.previous = 0).
| ctx | Pointer to an initialized aaruformatContext in write mode. |
| AARUF_STATUS_OK | Index written and header updated. |
| AARUF_ERROR_CANNOT_WRITE_HEADER | Failed writing index header, entries, or updating main header. |
Definition at line 3862 of file close.c.
References aaruf_crc64_final(), aaruf_crc64_init(), aaruf_crc64_update(), AARUF_ERROR_CANNOT_WRITE_HEADER, AARUF_STATUS_OK, DdtHeader2::blockAlignmentShift, IndexEntry::blockType, IndexHeader3::crc64, IndexEntry::dataType, IndexHeader3::entries, aaruformat_context::header, IndexHeader3::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, IndexBlock3, AaruHeaderV2::indexOffset, IndexEntry::offset, IndexHeader3::previous, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize all accumulated media tags to the image file.
Media tags represent arbitrary metadata or descriptor blobs associated with the entire medium (as opposed to per-sector or per-track metadata). Examples include proprietary drive firmware information, TOC descriptors, ATIP data, PMA/Lead-in content, or manufacturer-specific binary structures that do not fit the standard track or sector model. Each tag is identified by a numeric type field interpreted by upper layers or external tooling.
This function traverses the ctx->mediaTags hash table (keyed by tag type) using HASH_ITER and writes each tag as an independent DataBlock. Each block is:
After successfully writing a tag's header and data, an IndexEntry is appended to ctx->indexEntries with:
Alignment and file positioning: Before writing each tag, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from the blockAlignmentShift. This ensures that all structural blocks (including media tags) begin on properly aligned offsets for efficient I/O and compliance with the Aaru format specification.
Order of operations for each tag:
Error handling: Write errors (fwrite returning < 1) are silently ignored for individual tags; no index entry is added if a write fails, but iteration continues. Diagnostic TRACE logs report success or failure for each tag. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
Hash table iteration: The function uses HASH_ITER(hh, ctx->mediaTags, media_tag, tmp_media_tag) from uthash to safely iterate all entries. The tmp_media_tag parameter provides deletion-safe traversal, though this function does not delete entries (cleanup is handled during context teardown).
No-op conditions: If ctx->mediaTags is NULL (no tags were added during image creation), the function returns immediately without writing anything or modifying the index.
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->mediaTags contains the hash table of media tags to serialize (may be NULL if no tags exist). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 2409 of file close.c.
References aaruf_crc64_data(), aaruf_get_datatype_for_media_tag_type(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, mediaTagEntry::data, DataBlock, IndexEntry::dataType, BlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, mediaTagEntry::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, aaruformat_context::mediaTags, None, IndexEntry::offset, TRACE, BlockHeader::type, mediaTagEntry::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the metadata block containing image and media descriptive information.
This function writes a MetadataBlock containing human-readable and machine-readable metadata strings that describe the image creation context, the physical media being preserved, and the drive used for acquisition. The metadata block stores variable-length UTF-16LE strings for fields such as creator identification, user comments, media identification (title, manufacturer, model, serial number, barcode, part number), and drive identification (manufacturer, model, serial number, firmware revision). Each string is stored sequentially in a single contiguous buffer, with the MetadataBlockHeader recording both the offset (relative to the start of the buffer) and length of each field.
The metadata block is optional; if no metadata fields have been populated (all string pointers are NULL and sequence numbers are zero), the function returns immediately without writing anything. This no-op behavior is detected by checking that the identifier has not been explicitly set to MetadataBlock and all relevant imageInfo string fields are NULL.
Block structure: The serialized block consists of:
The total blockSize is computed as sizeof(MetadataBlockHeader) plus the sum of all populated string lengths (creatorLength, commentsLength, mediaTitleLength, etc.). Note that lengths are in bytes, not character counts.
Field packing order: Non-NULL strings from ctx->imageInfo are copied into the buffer in the following order:
As each field is copied, its offset (relative to the buffer start, which begins after the header) is recorded in the corresponding offset field of ctx->metadataBlockHeader, and the position pointer is advanced by the field's length.
Alignment and file positioning: Before writing the block, the file position is moved to EOF and then aligned forward to the next boundary satisfying (position & alignment_mask) == 0, where alignment_mask is derived from ctx->userDataDdtHeader.blockAlignmentShift. This ensures the metadata block begins on a properly aligned offset for efficient I/O and compliance with the Aaru format specification.
Index registration: After successfully writing the metadata block, an IndexEntry is appended to ctx->indexEntries with:
Memory management: The function allocates a temporary buffer (via calloc) sized to hold the entire block payload. If allocation fails, the function returns immediately without writing anything. The buffer is freed before the function returns, regardless of write success or failure.
Error handling: Write errors (fwrite returning < 1) are silently ignored; no index entry is added if the write fails, but the temporary buffer is still freed. Diagnostic TRACE logs report success or failure. The function does not propagate error codes; higher-level close logic must validate overall integrity if needed.
No-op conditions:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->metadataBlockHeader contains the header template with pre-populated field lengths and sequence numbers (if applicable). ctx->imageInfo contains pointers to the actual UTF-16LE string data (may be NULL for unpopulated fields). ctx->imageStream must be open and writable. ctx->indexEntries must be initialized (utarray) to accept new index entries. |
Definition at line 3162 of file close.c.
References DdtHeader2::blockAlignmentShift, MetadataBlockHeader::blockSize, IndexEntry::blockType, aaruformat_context::comments, MetadataBlockHeader::commentsLength, MetadataBlockHeader::commentsOffset, aaruformat_context::creator, MetadataBlockHeader::creatorLength, MetadataBlockHeader::creatorOffset, IndexEntry::dataType, aaruformat_context::drive_firmware_revision, aaruformat_context::drive_manufacturer, aaruformat_context::drive_model, aaruformat_context::drive_serial_number, MetadataBlockHeader::driveFirmwareRevisionLength, MetadataBlockHeader::driveFirmwareRevisionOffset, MetadataBlockHeader::driveManufacturerLength, MetadataBlockHeader::driveManufacturerOffset, MetadataBlockHeader::driveModelLength, MetadataBlockHeader::driveModelOffset, MetadataBlockHeader::driveSerialNumberLength, MetadataBlockHeader::driveSerialNumberOffset, MetadataBlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, MetadataBlockHeader::lastMediaSequence, aaruformat_context::media_barcode, aaruformat_context::media_manufacturer, aaruformat_context::media_model, aaruformat_context::media_part_number, aaruformat_context::media_serial_number, aaruformat_context::media_title, MetadataBlockHeader::mediaBarcodeLength, MetadataBlockHeader::mediaBarcodeOffset, MetadataBlockHeader::mediaManufacturerLength, MetadataBlockHeader::mediaManufacturerOffset, MetadataBlockHeader::mediaModelLength, MetadataBlockHeader::mediaModelOffset, MetadataBlockHeader::mediaPartNumberLength, MetadataBlockHeader::mediaPartNumberOffset, MetadataBlockHeader::mediaSequence, MetadataBlockHeader::mediaSerialNumberLength, MetadataBlockHeader::mediaSerialNumberOffset, MetadataBlockHeader::mediaTitleLength, MetadataBlockHeader::mediaTitleOffset, aaruformat_context::metadata_block_header, MetadataBlock, IndexEntry::offset, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize a MODE 2 (XA) subheaders data block.
When Compact Disc Mode 2 form sectors are present, optional 8-byte subheaders (one per logical sector including negative / overflow ranges) are stored in an in-memory buffer. This function writes that buffer as a DataBlock of type CompactDiscMode2Subheader with CRC64 (compression enabled if configured) and adds an IndexEntry.
| ctx | Pointer to an initialized aaruformatContext in write mode; ctx->mode2_subheaders must point to a buffer sized for the described sector span. |
Definition at line 850 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, BlockHeader::cmpCrc64, BlockHeader::cmpLength, CompactDiscMode2Subheader, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, BlockHeader::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, aaruformat_context::mode2_subheaders, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, ImageInfo::Sectors, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Write (flush) the multi-level primary DDT table header and data back to its file offset.
This function is applicable only when a multi-level DDT is in use (tableShift > 0). It updates the header fields (identifier, type, compression, CRC, lengths) and writes first the header and then the entire primary table data block at ctx->primaryDdtOffset. The function also pushes an IndexEntry for the primary DDT into the in-memory index array so that later write_index_block() will serialize it.
| ctx | Pointer to an initialized aaruformatContext in write mode. |
| AARUF_STATUS_OK | Success or nothing to do (no multi-level primary table present). |
| AARUF_ERROR_CANNOT_WRITE_HEADER | Failed writing header or primary table data. |
Definition at line 283 of file close.c.
References aaruf_crc64_final(), aaruf_crc64_init(), aaruf_crc64_update(), AARUF_ERROR_CANNOT_WRITE_HEADER, AARUF_STATUS_OK, IndexEntry::blockType, DdtHeader2::cmpCrc64, DdtHeader2::cmpLength, DdtHeader2::compression, DdtHeader2::crc64, IndexEntry::dataType, DeDuplicationTable2, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, DdtHeader2::length, None, IndexEntry::offset, aaruformat_context::primary_ddt_offset, DdtHeader2::tableShift, TRACE, DdtHeader2::type, aaruformat_context::user_data_ddt2, aaruformat_context::user_data_ddt_header, and UserData.
Referenced by aaruf_close().
|
static |
Serialize the optional CD sector prefix block.
The sector prefix corresponds to the leading bytes of a raw CD sector (synchronization pattern, address / header in MSF format and the mode byte) that precede the user data and any ECC/ECCP fields. It is unrelated to subchannel (P–W) data, which is handled separately. If prefix data was collected (ctx->sector_prefix != NULL), this writes a DataBlock of type CdSectorPrefix containing exactly the bytes accumulated up to sector_prefix_offset. The block is CRC64 protected, compressed if enabled, aligned to the DDT block boundary and indexed.
Typical raw Mode 1 / Mode 2 sector layout (2352 bytes total): 12-byte sync pattern (00 FF FF FF FF FF FF FF FF FF FF 00) 3-byte address (Minute, Second, Frame in BCD) 1-byte mode (e.g., 0x01, 0x02) ... user data ... ... ECC / EDC ... The stored prefix encompasses the first 16 bytes (sync + address + mode) or whatever subset was gathered by the writer.
| ctx | Pointer to an initialized aaruformatContext in write mode. |
Definition at line 966 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, CdSectorPrefix, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, BlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, None, IndexEntry::offset, aaruformat_context::sector_prefix, aaruformat_context::sector_prefix_offset, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the per-sector CD prefix status / index DeDuplication Table (DDT v2, prefix variant).
This DDT records for each logical sector (including negative and overflow ranges) an optional index into the stored 16‑byte prefix capture buffer plus a 4-bit status code. It is written only if at least one prefix status or captured prefix was recorded (i.e., the in-memory DDT array exists).
Encoding: Bits 63..61 : SectorStatus enum value (see enums.h, values already positioned for v2 layout). Bits 60..0 : Index of the 16-byte prefix chunk inside the CdSectorPrefix data block, or 0 when no external prefix bytes were stored (status applies to a generated/implicit prefix).
Notes:
Alignment: The table is block-aligned using the same blockAlignmentShift as user data DDTs. Indexing: An IndexEntry is appended on success so readers can locate and parse the table.
| ctx | Pointer to a valid aaruformatContext in write mode (must not be NULL). |
Definition at line 1206 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, DdtHeader2::blocks, IndexEntry::blockType, CdSectorPrefix, DdtHeader2::cmpCrc64, DdtHeader2::cmpLength, DdtHeader2::compression, aaruformat_context::compression_enabled, DdtHeader2::crc64, DdtHeader2::dataShift, IndexEntry::dataType, DeDuplicationTable2, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, DdtHeader2::length, DdtHeader2::levels, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, aaruformat_context::sector_prefix_ddt2, ImageInfo::Sectors, DdtHeader2::start, DdtHeader2::tableLevel, DdtHeader2::tableShift, TRACE, DdtHeader2::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the per-sector subchannel or tag data block.
This routine writes out the accumulated subchannel or tag metadata that accompanies each logical sector (including negative pregap and overflow ranges). The exact interpretation and size depend on the media type:
Optical Disc (CD) subchannel:
Apple block media tags:
The block size is computed as (applicable_sector_count) × (bytes_per_sector_for_media_type). Compression is conditionally applied based on media type and compression settings:
Media type validation: The function only proceeds if XmlMediaType is OpticalDisc or BlockMedia and (for block media) the specific MediaType matches one of the supported Apple or Priam variants. Any other media type causes an immediate silent return (logged at TRACE level).
Alignment & indexing: The block is aligned using the same alignment shift as the user data DDT. An IndexEntry (blockType = DataBlock, dataType = subchannel_block.type, offset = aligned file position) is appended to ctx->indexEntries on successful write, enabling readers to locate the subchannel or tag data.
Thread / reentrancy: This function is invoked once during finalization (aaruf_close) in a single-threaded context. No synchronization is performed.
Error handling: Write errors are logged but not explicitly propagated as return codes. If the write succeeds an index entry is added; if it fails no entry is added and diagnostics appear in TRACE logs. Higher level close logic determines overall success or failure.
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. ctx->sector_subchannel must point to a fully populated buffer sized appropriately for the media type and sector count. |
Definition at line 1508 of file close.c.
References aaruf_crc64_data(), aaruf_cst_transform(), aaruf_lzma_encode_buffer(), AppleFileWare, AppleProfile, AppleProfileTag, AppleSonyDS, AppleSonySS, AppleSonyTag, DdtHeader2::blockAlignmentShift, BlockMedia, IndexEntry::blockType, CdSectorSubchannel, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, BlockHeader::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, LzmaClauniaSubchannelTransform, ImageInfo::MediaType, ImageInfo::MetadataMediaType, DdtHeader2::negative, None, IndexEntry::offset, OpticalDisc, DdtHeader2::overflow, PriamDataTower, PriamDataTowerTag, aaruformat_context::sector_subchannel, ImageInfo::Sectors, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the optional CD sector suffix block (EDC/ECC region capture).
The sector suffix contains trailing integrity and redundancy bytes of a raw CD sector that follow the user data area. Depending on the mode this includes:
During writing, when an error or uncorrectable condition is detected for a sector's suffix, the 288-byte trailing portion (starting at offset 2064 for Mode 1 / Form 1 or 2348/2349 for other layouts) is copied into an in-memory expandable buffer pointed to by ctx->sector_suffix. The per-sector DDT entry encodes either an inlined status (OK / No CRC / etc.) or an index to the stored suffix (ctx->sector_suffix_offset / 288). This function serializes the accumulated suffix buffer as a DataBlock of type CdSectorSuffix if any suffix bytes were stored.
Layout considerations:
Indexing: An IndexEntry is appended so later readers can locate the suffix collection. Absence of this block implies no per-sector suffix captures were required (all suffixes considered OK).
Thread / reentrancy: This routine is called only once during finalization (aaruf_close) in a single-threaded context; no synchronization is performed.
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. |
Definition at line 1088 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, CdSectorSuffix, BlockHeader::cmpCrc64, BlockHeader::cmpLength, BlockHeader::compression, aaruformat_context::compression_enabled, BlockHeader::crc64, DataBlock, IndexEntry::dataType, BlockHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, BlockHeader::length, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, None, IndexEntry::offset, aaruformat_context::sector_suffix, aaruformat_context::sector_suffix_offset, TRACE, BlockHeader::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the per-sector CD suffix status / index DeDuplication Table (DDT v2, suffix variant).
This routine emits the DDT v2 table that maps each logical sector (including negative pregap and overflow ranges) to (a) a 4-bit SectorStatus code and (b) a 12-bit index pointing into the captured suffix data block (CdSectorSuffix). The suffix bytes (typically the 288-byte EDC/ECC region for Mode 1 or Mode 2 Form 1, or shorter EDC-only for Form 2) are stored separately by write_sector_suffix(). When a sector's suffix was captured because it differed from the expected generated values (e.g., uncorrectable, intentionally preserved corruption, or variant layout), the in-memory mini entry records the index of its 16 * 18 (288) byte chunk. If no suffix bytes were explicitly stored for a sector the index field is zero and only the status applies.
Encoding (DDT v2 semantics): Bits 63..61 : SectorStatus enumeration (already aligned for direct storage; no legacy masks used). Bits 60..0 : Index referencing a suffix unit of size 288 bytes (2^dataShift granularity), or 0 when the sector uses an implicit / regenerated suffix (no external data captured).
Characteristics & constraints:
Index integration: On success an IndexEntry (blockType = DeDuplicationTable2, dataType = CdSectorSuffix, offset = file position) is appended to ctx->indexEntries enabling later readers to locate and parse the suffix DDT.
Error handling & assumptions:
Preconditions:
| ctx | Active aaruformatContext being finalized. |
Definition at line 1350 of file close.c.
References aaruf_crc64_data(), aaruf_lzma_encode_buffer(), DdtHeader2::blockAlignmentShift, DdtHeader2::blocks, IndexEntry::blockType, CdSectorSuffix, DdtHeader2::cmpCrc64, DdtHeader2::cmpLength, DdtHeader2::compression, aaruformat_context::compression_enabled, DdtHeader2::crc64, DdtHeader2::dataShift, IndexEntry::dataType, DeDuplicationTable2, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::image_info, aaruformat_context::imageStream, aaruformat_context::index_entries, DdtHeader2::length, DdtHeader2::levels, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, DdtHeader2::negative, None, IndexEntry::offset, DdtHeader2::overflow, aaruformat_context::sector_suffix_ddt2, ImageInfo::Sectors, DdtHeader2::start, DdtHeader2::tableLevel, DdtHeader2::tableShift, TRACE, DdtHeader2::type, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize a single-level DDT (tableShift == 0) directly after its header.
For single-level DDT configurations the entire table of sector references is contiguous. This routine computes a CRC64 for the table, populates all header metadata, writes the header at ctx->primaryDdtOffset followed immediately by the table, and registers the block in the index.
| ctx | Pointer to an initialized aaruformatContext in write mode with tableShift == 0. |
| AARUF_STATUS_OK | Success or nothing to do (not single-level / buffers missing). |
| AARUF_ERROR_CANNOT_WRITE_HEADER | Failed writing header or table data. |
Definition at line 369 of file close.c.
References aaruf_crc64_data(), AARUF_ERROR_CANNOT_WRITE_HEADER, AARUF_ERROR_NOT_ENOUGH_MEMORY, aaruf_lzma_encode_buffer(), AARUF_STATUS_OK, DdtHeader2::blockAlignmentShift, IndexEntry::blockType, DdtHeader2::cmpCrc64, DdtHeader2::cmpLength, DdtHeader2::compression, aaruformat_context::compression_enabled, DdtHeader2::crc64, IndexEntry::dataType, DeDuplicationTable2, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, DdtHeader2::length, DdtHeader2::levels, Lzma, aaruformat_context::lzma_dict_size, LZMA_PROPERTIES_LENGTH, None, IndexEntry::offset, DdtHeader2::previousLevelOffset, DdtHeader2::tableLevel, DdtHeader2::tableShift, TRACE, DdtHeader2::type, aaruformat_context::user_data_ddt2, aaruformat_context::user_data_ddt_header, and UserData.
Referenced by aaruf_close(), and write_tape_ddt().
|
static |
Converts tape DDT hash table to array format and writes it as a single-level DDT.
This function is specifically designed for tape media images where sectors have been tracked using a sparse hash table (UTHASH) during write operations. It converts the hash-based tape DDT into a traditional array-based DDT structure suitable for serialization to disk. The function performs a complete transformation from the sparse hash representation to a dense array representation, then delegates the actual write operation to write_single_level_ddt().
The conversion process involves:
Hash Table to Array Conversion: The tape DDT hash table uses sector addresses as keys and DDT entries as values. This function creates a zero-initialized array of size (max_key + 1) and copies each hash entry to array[entry->key] = entry->value. Sectors not present in the hash table remain as zero entries in the array, which indicates SectorStatusNotDumped in the DDT format.
Memory Allocation: The function always uses BigDdtSizeType (32-bit entries) for tape DDTs, allocating (max_key + 1) * sizeof(uint32_t) bytes. This provides sufficient capacity for the 28-bit data + 4-bit status encoding used in tape DDT entries.
DDT Header Configuration: The userDataDdtHeader is configured for a single-level DDT v2 structure:
| ctx | Pointer to the aaruformat context. Must not be NULL and must be in write mode. The context must have is_tape set to true and tapeDdt hash table populated. The ctx->userDataDdtBig array will be allocated and populated by this function. The ctx->userDataDdtHeader will be initialized with DDT metadata. |
| AARUF_STATUS_OK | (0) Successfully converted and wrote the tape DDT. This occurs when:
|
| AARUF_STATUS_INVALID_CONTEXT | (-2) The context is not for tape media. This occurs when:
|
| AARUF_ERROR_NOT_ENOUGH_MEMORY | (-6) Memory allocation failed. This occurs when:
|
| AARUF_ERROR_CANNOT_WRITE_HEADER | (-8) Writing the DDT failed. This can occur when:
|
Definition at line 596 of file close.c.
References AARUF_ERROR_NOT_ENOUGH_MEMORY, AARUF_STATUS_INVALID_CONTEXT, DdtHeader2::blocks, DdtHeader2::cmpLength, DdtHeader2::compression, aaruformat_context::compression_enabled, DeDuplicationTable2, DdtHeader2::entries, DdtHeader2::identifier, aaruformat_context::is_tape, TapeDdtHashEntry::key, DdtHeader2::length, DdtHeader2::levels, Lzma, DdtHeader2::negative, None, DdtHeader2::overflow, DdtHeader2::previousLevelOffset, DdtHeader2::start, DdtHeader2::tableLevel, DdtHeader2::tableShift, aaruformat_context::tape_ddt, TRACE, DdtHeader2::type, aaruformat_context::user_data_ddt2, aaruformat_context::user_data_ddt_header, UserData, TapeDdtHashEntry::value, and write_single_level_ddt().
Referenced by aaruf_close().
|
static |
Serialize the tape file metadata block to the image file.
This function writes a TapeFileBlock containing the complete tape file structure metadata to the Aaru image file. The tape file block documents all logical files present on the tape medium, recording each file's partition number, file number, and block range (first and last block addresses). This metadata enables random access to specific files within the tape image and preserves the original tape's logical organization for archival purposes.
The tape file block is optional; if no tape file metadata has been populated (ctx->tapeFiles hash table is NULL or empty), the function returns immediately without writing anything. This no-op behavior allows the close operation to proceed gracefully whether or not tape file structure metadata was included during image creation.
Block Structure: The serialized block consists of:
Processing Flow:
Hash Table Iteration: The function uses UTHASH's HASH_ITER macro to safely traverse ctx->tapeFiles:
CRC64 Integrity Protection: A CRC64-ECMA checksum is computed over the complete array of TapeFileEntry structures using aaruf_crc64_data(). This checksum is stored in the TapeFileHeader and verified during image opening by process_tape_files_block() to detect corruption in the file table. The checksum covers only the entry data, not the header itself.
Alignment Strategy: Before writing, the file position is:
Write Sequence: The function performs a two-stage write operation:
Both writes must succeed for the index entry to be added. If either write fails, the block is incomplete but the function continues (no error propagation).
Indexing: On successful write, an IndexEntry is created and pushed to ctx->indexEntries:
This index entry enables process_tape_files_block() to quickly locate the tape file metadata during subsequent image opens without scanning the entire file.
Entry Order: The current implementation writes entries in hash table iteration order, which is non-deterministic and depends on the hash function and insertion sequence. For better compatibility and reproducibility, entries should ideally be sorted by:
Error Handling: The function handles errors gracefully without propagating them:
This opportunistic approach ensures that tape file metadata write failures do not prevent the image from being created, though the resulting image will lack file structure metadata.
Memory Management:
Thread Safety: This function is NOT thread-safe. It modifies shared ctx state (imageStream file position, indexEntries array) and must only be called during single-threaded finalization (within aaruf_close).
Use Cases:
Relationship to Other Functions:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. The tapeFiles hash table should be populated if tape file metadata exists. The imageStream must be open and writable. The indexEntries array must be initialized for adding the index entry. |
Definition at line 2669 of file close.c.
References aaruf_crc64_data(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, TapeFileHeader::crc64, IndexEntry::dataType, TapeFileHashEntry::fileEntry, TapeFileHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, TapeFileHeader::length, IndexEntry::offset, aaruformat_context::tape_files, TapeFileBlock, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the tape partition metadata block to the image file.
This function writes a TapePartitionBlock containing the complete tape partition structure metadata to the Aaru image file. The tape partition block documents all physical partitions present on the tape medium, recording each partition's partition number and block range (first and last block addresses). This metadata enables proper interpretation of tape file locations, validation of partition boundaries, and preservation of the original tape's physical organization for archival purposes.
The tape partition block is optional; if no tape partition metadata has been populated (ctx->tapePartitions hash table is NULL or empty), the function returns immediately without writing anything. This no-op behavior allows the close operation to proceed gracefully whether or not tape partition structure metadata was included during image creation.
Block Structure: The serialized block consists of:
Processing Flow:
Hash Table Iteration: The function uses UTHASH's HASH_ITER macro to safely traverse ctx->tapePartitions:
CRC64 Integrity Protection: A CRC64-ECMA checksum is computed over the complete array of TapePartitionEntry structures using aaruf_crc64_data(). This checksum is stored in the TapePartitionHeader and verified during image opening by process_tape_partitions_block() to detect corruption in the partition table. The checksum covers only the entry data, not the header itself.
Alignment Strategy: Before writing, the file position is:
Write Sequence: The function performs a two-stage write operation:
Both writes must succeed for the index entry to be added. If either write fails, the block is incomplete but the function continues (no error propagation).
Indexing: On successful write, an IndexEntry is created and pushed to ctx->indexEntries:
This index entry enables process_tape_partitions_block() to quickly locate the tape partition metadata during subsequent image opens without scanning the entire file.
Entry Order: The current implementation writes entries in hash table iteration order, which is non-deterministic and depends on the hash function and insertion sequence. For better compatibility and reproducibility, entries should ideally be sorted by partition number (ascending). However, the current implementation does not enforce this ordering.
Partition Block Ranges: Each partition entry defines an independent block address space:
Different partitions may have overlapping logical block numbers (e.g., partition 0 and partition 1 can both have blocks 0-1000). The partition metadata is essential for correctly interpreting tape file locations, as files reference partition numbers in their definitions.
Error Handling: The function handles errors gracefully without propagating them:
This opportunistic approach ensures that tape partition metadata write failures do not prevent the image from being created, though the resulting image will lack partition structure metadata.
Memory Management:
Thread Safety: This function is NOT thread-safe. It modifies shared ctx state (imageStream file position, indexEntries array) and must only be called during single-threaded finalization (within aaruf_close).
Use Cases:
Relationship to Other Functions:
Format Considerations:
| ctx | Pointer to an initialized aaruformatContext in write mode. Must not be NULL. The tapePartitions hash table should be populated if tape partition metadata exists. The imageStream must be open and writable. The indexEntries array must be initialized for adding the index entry. |
Definition at line 2901 of file close.c.
References aaruf_crc64_data(), DdtHeader2::blockAlignmentShift, IndexEntry::blockType, TapePartitionHeader::crc64, IndexEntry::dataType, TapePartitionHeader::identifier, aaruformat_context::imageStream, aaruformat_context::index_entries, TapePartitionHeader::length, IndexEntry::offset, TapePartitionHashEntry::partitionEntry, aaruformat_context::tape_partitions, TapePartitionBlock, TRACE, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().
|
static |
Serialize the tracks metadata block and add it to the index.
Writes a TracksHeader followed by the array of TrackEntry structures if any track entries are present (tracksHeader.entries > 0 and trackEntries not NULL). The block is aligned to the DDT block boundary and an IndexEntry is appended.
| ctx | Pointer to an initialized aaruformatContext in write mode. |
Definition at line 798 of file close.c.
References DdtHeader2::blockAlignmentShift, IndexEntry::blockType, IndexEntry::dataType, TracksHeader::entries, aaruformat_context::imageStream, aaruformat_context::index_entries, IndexEntry::offset, TRACE, aaruformat_context::track_entries, aaruformat_context::tracks_header, TracksBlock, and aaruformat_context::user_data_ddt_header.
Referenced by aaruf_close().