diff --git a/include/aaruformat/decls.h b/include/aaruformat/decls.h index eefa125..706205f 100644 --- a/include/aaruformat/decls.h +++ b/include/aaruformat/decls.h @@ -158,6 +158,7 @@ AARU_EXPORT int32_t AARU_CALL aaruf_set_drive_model(void *context, const uint8_t AARU_EXPORT int32_t AARU_CALL aaruf_set_drive_serial_number(void *context, const uint8_t *data, int32_t length); AARU_EXPORT int32_t AARU_CALL aaruf_set_drive_firmware_revision(void *context, const uint8_t *data, int32_t length); AARU_EXPORT int32_t AARU_CALL aaruf_get_cicm_metadata(const void *context, uint8_t *buffer, size_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_aaru_json_metadata(const void *context, uint8_t *buffer, size_t *length); AARU_EXPORT spamsum_ctx *AARU_CALL aaruf_spamsum_init(void); AARU_EXPORT int AARU_CALL aaruf_spamsum_update(spamsum_ctx *ctx, const uint8_t *data, uint32_t len); diff --git a/src/metadata.c b/src/metadata.c index 999abf9..8206d9c 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -1988,4 +1988,160 @@ int32_t aaruf_get_cicm_metadata(const void *context, uint8_t *buffer, size_t *le TRACE("CICM XML metadata read successfully, length %u", *length); TRACE("Exiting aaruf_get_cicm_metadata(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the embedded Aaru metadata JSON from the image. + * + * Aaru metadata JSON is a structured metadata format that provides machine-readable, comprehensive + * information about the image, media, imaging session details, hardware configuration, optical disc + * tracks and sessions, checksums, and preservation metadata. This function extracts the raw JSON + * payload that was embedded in the AaruFormat image during creation. The JSON data is preserved in + * its original form without parsing or interpretation by the library, allowing callers to process + * the structured metadata using standard JSON parsing libraries. + * + * This function supports a two-call pattern for buffer size determination: + * 1. First call with a buffer that may be too small returns AARUF_ERROR_BUFFER_TOO_SMALL + * and sets *length to the required size + * 2. Second call with a properly sized buffer retrieves the actual data + * + * Alternatively, if the caller already knows the buffer is large enough, a single call + * will succeed and populate the buffer with the Aaru JSON data. + * + * @param context Pointer to the aaruformat context (must be a valid, opened image context). + * @param buffer Pointer to a buffer that will receive the Aaru metadata JSON. Must be large + * enough to hold the entire JSON payload (at least *length bytes on input). + * The buffer will contain raw UTF-8 encoded JSON data on success. + * @param length Pointer to a size_t that serves dual purpose: + * - On input: size of the provided buffer in bytes + * - On output: actual size of the Aaru metadata JSON in bytes + * If the function returns AARUF_ERROR_BUFFER_TOO_SMALL, this will be updated + * to contain the required buffer size for a subsequent successful call. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Successfully retrieved Aaru metadata JSON. This is returned when: + * - The context is valid and properly initialized + * - The Aaru JSON block is present in the image (identifier == AaruMetadataJsonBlock) + * - The provided buffer is large enough (>= required length) + * - The Aaru JSON data is successfully copied to the buffer + * - The *length parameter is set to the actual size of the JSON data + * + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context is invalid. This occurs when: + * - The context parameter is NULL + * - The context magic number doesn't match AARU_MAGIC (invalid context type) + * - The context was not properly initialized by aaruf_open() or aaruf_create() + * + * @retval AARUF_ERROR_CANNOT_READ_BLOCK (-6) The Aaru JSON block is not present. This occurs when: + * - The image was created without Aaru metadata JSON + * - ctx->jsonBlock is NULL (no data loaded) + * - ctx->jsonBlockHeader.length is 0 (empty metadata) + * - ctx->jsonBlockHeader.identifier doesn't equal AaruMetadataJsonBlock + * - The Aaru JSON block was not found during image opening + * - The *length output parameter is set to 0 to indicate no data available + * + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-12) The provided buffer is insufficient. This occurs when: + * - The input *length is less than ctx->jsonBlockHeader.length + * - The *length parameter is updated to contain the required buffer size + * - No data is copied to the buffer + * - The caller should allocate a larger buffer and call again + * + * @note Aaru JSON Format and Encoding: + * - The JSON payload is stored in UTF-8 encoding + * - The payload may or may not be null-terminated + * - The library treats the JSON as opaque binary data + * - No JSON parsing, interpretation, or validation is performed by libaaruformat + * - JSON schema validation and parsing are the caller's responsibility + * + * @note Aaru Metadata JSON Purpose: + * - Provides machine-readable structured metadata using modern JSON format + * - Includes comprehensive information about media, sessions, tracks, and checksums + * - Enables programmatic access to metadata without XML parsing overhead + * - Documents imaging session details, hardware configuration, and preservation data + * - Used by Aaru and compatible tools for metadata exchange and analysis + * - Complements or serves as alternative to CICM XML metadata + * + * @note Buffer Size Handling: + * - First call with insufficient buffer returns required size in *length + * - Caller allocates properly sized buffer based on returned length + * - Second call with adequate buffer retrieves the actual JSON data + * - Single call succeeds if buffer is already large enough + * + * @note Data Availability: + * - Aaru JSON blocks are optional in AaruFormat images + * - Not all images will contain Aaru metadata JSON + * - The presence of JSON data depends on how the image was created + * - Check return value to handle missing metadata gracefully + * - Images may contain CICM XML, Aaru JSON, both, or neither + * + * @note Distinction from CICM XML: + * - CICM XML follows the Canary Islands Computer Museum schema (older format) + * - Aaru JSON follows the Aaru-specific metadata schema (newer format) + * - Both can coexist in the same image file + * - Aaru JSON may provide more detailed or different metadata than CICM XML + * - Different tools and workflows may prefer one format over the other + * + * @warning This function reads from the in-memory Aaru JSON block loaded during aaruf_open(). + * It does not perform file I/O operations. The entire JSON is kept in memory + * for the lifetime of the context. + * + * @warning The buffer parameter must be valid and large enough to hold the JSON data. + * Passing a buffer smaller than the required size will result in + * AARUF_ERROR_BUFFER_TOO_SMALL with no partial data copied. + * + * @warning This function does not validate JSON syntax or schema. Corrupted JSON data will + * be retrieved successfully and errors will only be detected when attempting to parse. + * + * @see AaruMetadataJsonBlockHeader for the on-disk structure definition. + * @see aaruf_get_cicm_metadata() for retrieving CICM XML metadata. + * @see process_aaru_metadata_json_block() for the loading process during image opening. + */ +int32_t aaruf_get_aaru_json_metadata(const void *context, uint8_t *buffer, size_t *length) +{ + TRACE("Entering aaruf_get_aaru_json_metadata(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_aaru_json_metadata() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + const aaruformatContext *ctx = context; + + // Not a libaaruformat context + if(ctx->magic != AARU_MAGIC) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_aaru_json_metadata() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->jsonBlock == NULL || ctx->jsonBlockHeader.length == 0 || + ctx->jsonBlockHeader.identifier != AaruMetadataJsonBlock) + { + TRACE("No Aaru metadata JSON present"); + *length = 0; + + TRACE("Exiting aaruf_get_aaru_json_metadata() = AARUF_ERROR_CANNOT_READ_BLOCK"); + return AARUF_ERROR_CANNOT_READ_BLOCK; + } + + if(*length < ctx->jsonBlockHeader.length) + { + TRACE("Buffer too small for Aaru metadata JSON, required %u bytes", ctx->jsonBlockHeader.length); + *length = ctx->jsonBlockHeader.length; + + TRACE("Exiting aaruf_get_aaru_json_metadata() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + *length = ctx->jsonBlockHeader.length; + memcpy(buffer, ctx->jsonBlock, ctx->jsonBlockHeader.length); + + TRACE("Aaru metadata JSON read successfully, length %u", *length); + TRACE("Exiting aaruf_get_aaru_json_metadata(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; } \ No newline at end of file