diff --git a/include/aaruformat/decls.h b/include/aaruformat/decls.h index 019485c..e0b0edf 100644 --- a/include/aaruformat/decls.h +++ b/include/aaruformat/decls.h @@ -160,6 +160,19 @@ AARU_EXPORT int32_t AARU_CALL aaruf_set_drive_manufacturer(void *context, const AARU_EXPORT int32_t AARU_CALL aaruf_set_drive_model(void *context, const uint8_t *data, int32_t length); 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_media_sequence(const void *context, int32_t *sequence, int32_t *last_sequence); +AARU_EXPORT int32_t AARU_CALL aaruf_get_creator(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_comments(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_title(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_manufacturer(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_model(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_serial_number(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_barcode(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_media_part_number(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_drive_manufacturer(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_drive_model(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_drive_serial_number(const void *context, uint8_t *buffer, int32_t *length); +AARU_EXPORT int32_t AARU_CALL aaruf_get_drive_firmware_revision(const void *context, uint8_t *buffer, 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 int32_t AARU_CALL aaruf_set_aaru_json_metadata(void *context, uint8_t *data, size_t length); diff --git a/include/aaruformat/errors.h b/include/aaruformat/errors.h index 4827f4d..a891515 100644 --- a/include/aaruformat/errors.h +++ b/include/aaruformat/errors.h @@ -66,6 +66,7 @@ #define AARUF_ERROR_INVALID_TAG (-27) ///< Invalid or unsupported media or sector tag format. #define AARUF_ERROR_TAPE_FILE_NOT_FOUND (-28) ///< Requested tape file number not present in image. #define AARUF_ERROR_TAPE_PARTITION_NOT_FOUND (-29) ///< Requested tape partition not present in image. +#define AARUF_ERROR_METADATA_NOT_PRESENT (-30) ///< Requested metadata not present in image. /** @} */ /** \name Non-fatal sector status codes (non-negative) diff --git a/src/metadata.c b/src/metadata.c index 82e0dda..cffb8c9 100644 --- a/src/metadata.c +++ b/src/metadata.c @@ -16,6 +16,7 @@ * License along with this library; if not, see . */ +#include #include #include "aaruformat.h" @@ -1892,7 +1893,7 @@ int32_t aaruf_set_drive_firmware_revision(void *context, const uint8_t *data, co * - The CICM 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: + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer is insufficient. This occurs when: * - The input *length is less than ctx->cicmBlockHeader.length * - The *length parameter is updated to contain the required buffer size * - No data is copied to the buffer @@ -2039,7 +2040,7 @@ int32_t aaruf_get_cicm_metadata(const void *context, uint8_t *buffer, size_t *le * - 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: + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) 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 @@ -2300,8 +2301,925 @@ int32_t aaruf_set_aaru_json_metadata(void *context, uint8_t *data, size_t length if(ctx->jsonBlock != NULL) free(ctx->jsonBlock); ctx->jsonBlock = copy; ctx->jsonBlockHeader.identifier = AaruMetadataJsonBlock; - ctx->jsonBlockHeader.length = length; + ctx->jsonBlockHeader.length = (uint32_t)length; TRACE("Exiting aaruf_set_aaru_json_metadata(%p, %p, %d) = AARUF_STATUS_OK", context, data, length); return AARUF_STATUS_OK; } + +/** + * @brief Retrieves the media sequence metadata for multi-volume image sets. + * + * Reads the media sequence fields stored in the MetadataBlock header and returns the current media + * number together with the final media number for the complete set. This information indicates the + * position of the imaged medium within a multi-volume collection (for example, "disc 2 of 5"). The + * function operates entirely on in-memory structures populated during aaruf_open(); no additional + * disk I/O is performed. + * + * @param context Pointer to an initialized aaruformat context opened for reading or writing. + * @param sequence Pointer that receives the current media sequence number (typically 1-based). + * @param last_sequence Pointer that receives the total number of media in the set. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Metadata was present and the output parameters were populated. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The provided context pointer is NULL or not an + * aaruformat context (magic mismatch). + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) The MetadataBlock was not present in the image, + * making sequence data unavailable. + * + * @note For standalone media, both sequence and last_sequence are commonly set to 1. Some creators + * may also set them to 0 to indicate the absence of sequence semantics; callers should handle + * either pattern gracefully. + * + * @note The function does not validate logical consistency (e.g., whether sequence <= last_sequence); + * it simply returns the values stored in the image header. + */ +int32_t aaruf_get_media_sequence(const void *context, int32_t *sequence, int32_t *last_sequence) +{ + TRACE("Entering aaruf_get_media_sequence(%p, %d, %d)", context, *sequence, *last_sequence); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_set_media_sequence() = 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_set_media_sequence() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_set_media_sequence() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + *sequence = ctx->metadataBlockHeader.mediaSequence; + *last_sequence = ctx->metadataBlockHeader.lastMediaSequence; + + TRACE("Exiting aaruf_set_media_sequence(%p, %d, %d) = AARUF_STATUS_OK", context, *sequence, *last_sequence); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the recorded creator (operator) name from the MetadataBlock. + * + * Copies the UTF-16LE encoded creator string that identifies the person or operator who created the + * image. The function supports the common two-call pattern: the caller first determines the required + * buffer size by passing a buffer that may be NULL or too small, then allocates sufficient memory and + * calls again to obtain the actual data. On success the buffer contains an opaque UTF-16LE string of + * length *length bytes (not null-terminated). + * + * @param context Pointer to a valid aaruformat context opened for reading or writing. + * @param buffer Pointer to the destination buffer that will receive the creator string. May be NULL + * to query the required size. + * @param length Pointer to an int32_t that on input specifies the size of @p buffer in bytes and on + * output receives the actual length of the creator metadata. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) The creator string was copied successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context is NULL or not an aaruformat context. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) Creator metadata has not been recorded in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer was insufficient; *length contains + * the required size and no data was copied. + * + * @note The returned data is UTF-16LE encoded and may contain embedded null characters. Callers + * should treat it as an opaque byte array unless they explicitly handle UTF-16LE strings. + * + * @note The function does not allocate memory. Callers are responsible for ensuring @p buffer is + * large enough before requesting the data. + */ +int32_t aaruf_get_creator(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_creator(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_creator() = 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_creator() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.Creator == NULL || + ctx->metadataBlockHeader.creatorLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_creator() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.creatorLength) + { + *length = ctx->metadataBlockHeader.creatorLength; + + TRACE("Exiting aaruf_get_creator() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.Creator, ctx->metadataBlockHeader.creatorLength); + *length = ctx->metadataBlockHeader.creatorLength; + + TRACE("Exiting aaruf_get_creator(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the user comments or notes stored in the MetadataBlock. + * + * Provides access to the UTF-16LE encoded comments associated with the image. Comments are often + * used for provenance notes, imaging details, or curator remarks. The function follows the same + * two-call buffer sizing pattern used by other metadata retrieval APIs: the caller may probe the + * required size before allocating memory. + * + * @param context Pointer to a valid aaruformat context opened with aaruf_open() or aaruf_create(). + * @param buffer Destination buffer that receives the comments data. May be NULL when probing size. + * @param length Pointer to an int32_t. On input it contains the size of @p buffer in bytes; on output + * it is updated with the actual comments length. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Comments were available and copied successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid or not a libaaruformat context. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No comments metadata exists in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The supplied buffer was too small. *length is updated with + * the required size and no data is copied. + * + * @note Comments are stored exactly as provided during image creation and may include multi-line text + * or other control characters. No validation or normalization is applied by the library. + */ +int32_t aaruf_get_comments(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_comments(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_comments() = 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_comments() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.Comments == NULL || + ctx->metadataBlockHeader.commentsLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_comments() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.commentsLength) + { + *length = ctx->metadataBlockHeader.commentsLength; + + TRACE("Exiting aaruf_get_comments() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.Comments, ctx->metadataBlockHeader.commentsLength); + *length = ctx->metadataBlockHeader.commentsLength; + + TRACE("Exiting aaruf_get_comments(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the media title or label captured during image creation. + * + * Returns the UTF-16LE encoded media title string, representing markings or labels present on the + * original physical media. This function allows applications to present or archive the media title + * alongside the image, preserving contextual information from the physical artifact. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the UTF-16LE title string. May be NULL when querying size. + * @param length Pointer to an int32_t that on input holds the buffer capacity in bytes and on output + * receives the actual title length. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) The media title was available and copied to @p buffer. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No media title metadata exists. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The supplied buffer was insufficient. + * + * @note Titles may contain international characters, control codes, or mixed casing. The library does + * not attempt to sanitize or interpret the string. + */ +int32_t aaruf_get_media_title(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_title(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_title() = 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_media_title() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaTitle == NULL || + ctx->metadataBlockHeader.mediaTitleLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_title() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaTitleLength) + { + *length = ctx->metadataBlockHeader.mediaTitleLength; + + TRACE("Exiting aaruf_get_media_title() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaTitle, ctx->metadataBlockHeader.mediaTitleLength); + *length = ctx->metadataBlockHeader.mediaTitleLength; + + TRACE("Exiting aaruf_get_media_title(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the recorded media manufacturer name. + * + * Provides access to the UTF-16LE encoded manufacturer metadata that identifies the company which + * produced the physical medium. This information is taken from the MetadataBlock and mirrors the + * value previously stored via aaruf_set_media_manufacturer(). + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Buffer that receives the manufacturer string. May be NULL when probing size. + * @param length Pointer to an int32_t specifying the buffer size on input and receiving the actual + * manufacturer string length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Manufacturer metadata was available and copied. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) The image lacks manufacturer metadata. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer was too small; *length indicates size. + * + * @note Values may include trailing spaces or vendor-specific capitalization. Treat the returned data + * as authoritative and avoid trimming unless required by the consuming application. + */ +int32_t aaruf_get_media_manufacturer(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_manufacturer(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_manufacturer() = 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_media_manufacturer() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaManufacturer == NULL || + ctx->metadataBlockHeader.mediaManufacturerLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_manufacturer() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaManufacturerLength) + { + *length = ctx->metadataBlockHeader.mediaManufacturerLength; + + TRACE("Exiting aaruf_get_media_manufacturer() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaManufacturer, ctx->metadataBlockHeader.mediaManufacturerLength); + *length = ctx->metadataBlockHeader.mediaManufacturerLength; + + TRACE("Exiting aaruf_get_media_manufacturer(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the media model or product designation metadata. + * + * Returns the UTF-16LE encoded model name that specifies the exact product variant of the physical + * medium. The function mirrors the set counterpart and is useful for accurately documenting media + * specifications during preservation workflows. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the model string; may be NULL when querying required size. + * @param length Pointer to an int32_t that on input contains the buffer capacity in bytes and on + * output is updated with the actual metadata length. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Model metadata was successfully copied. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No media model metadata is present in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The caller-provided buffer is too small. + * + * @note Model strings often contain performance ratings (e.g., "16x", "LTO-7"). The data is opaque and + * should be handled without modification unless necessary. + */ +int32_t aaruf_get_media_model(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_model(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_model() = 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_media_model() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaModel == NULL || + ctx->metadataBlockHeader.mediaModelLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_model() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaModelLength) + { + *length = ctx->metadataBlockHeader.mediaModelLength; + + TRACE("Exiting aaruf_get_media_model() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaModel, ctx->metadataBlockHeader.mediaModelLength); + *length = ctx->metadataBlockHeader.mediaModelLength; + + TRACE("Exiting aaruf_get_media_model(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the media serial number recorded in the image metadata. + * + * Copies the UTF-16LE encoded serial number identifying the specific physical medium. Serial numbers + * are particularly important for forensic tracking and archival provenance, enabling correlation + * between the physical item and its digital representation. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the serial number. May be NULL to determine required size. + * @param length Pointer to an int32_t that on input holds the buffer size and on output receives the + * actual serial number length. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Serial number metadata was copied into @p buffer. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No serial number metadata was stored in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer was too small. + * + * @note Serial numbers may contain spaces, hyphens, or alphanumeric characters. The library does not + * normalize or validate these strings. + */ +int32_t aaruf_get_media_serial_number(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_serial_number(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_serial_number() = 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_media_serial_number() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaSerialNumber == NULL || + ctx->metadataBlockHeader.mediaSerialNumberLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_serial_number() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaSerialNumberLength) + { + *length = ctx->metadataBlockHeader.mediaSerialNumberLength; + + TRACE("Exiting aaruf_get_media_serial_number() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaSerialNumber, ctx->metadataBlockHeader.mediaSerialNumberLength); + *length = ctx->metadataBlockHeader.mediaSerialNumberLength; + + TRACE("Exiting aaruf_get_media_serial_number(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the barcode assigned to the physical media or its packaging. + * + * Returns the UTF-16LE encoded barcode string that was captured when the image was created. Barcodes + * are commonly used in institutional workflows for inventory tracking and automated retrieval. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Buffer that receives the barcode string; may be NULL when probing size requirements. + * @param length Pointer to an int32_t specifying the buffer size on input and receiving the actual + * barcode length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Barcode metadata was present and copied successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No barcode metadata exists in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The supplied buffer was too small. + * + * @note Barcode values can be strict alphanumeric codes (e.g., LTO cartridge IDs) or full strings from + * custom labeling systems. Preserve the returned string exactly for catalog interoperability. + */ +int32_t aaruf_get_media_barcode(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_barcode(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_barcode() = 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_media_barcode() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaBarcode == NULL || + ctx->metadataBlockHeader.mediaBarcodeLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_barcode() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaBarcodeLength) + { + *length = ctx->metadataBlockHeader.mediaBarcodeLength; + + TRACE("Exiting aaruf_get_media_barcode() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaBarcode, ctx->metadataBlockHeader.mediaBarcodeLength); + *length = ctx->metadataBlockHeader.mediaBarcodeLength; + + TRACE("Exiting aaruf_get_media_barcode(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the media part number recorded in the MetadataBlock. + * + * Provides access to the UTF-16LE encoded part number identifying the precise catalog or ordering + * code for the physical medium. Part numbers help archivists procure exact replacements and document + * specific media variants used during acquisition. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the part number string. May be NULL while querying size. + * @param length Pointer to an int32_t that supplies the buffer size on input and is updated with the + * actual part number length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Part number metadata was returned successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No part number metadata exists. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer was insufficient; *length contains the + * required size. + * + * @note Part numbers may include manufacturer-specific formatting such as hyphens or suffix letters. + * The library stores and returns the data verbatim. + */ +int32_t aaruf_get_media_part_number(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_media_part_number(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_media_part_number() = 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_media_part_number() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.MediaPartNumber == NULL || + ctx->metadataBlockHeader.mediaPartNumberLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_media_part_number() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.mediaPartNumberLength) + { + *length = ctx->metadataBlockHeader.mediaPartNumberLength; + + TRACE("Exiting aaruf_get_media_part_number() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.MediaPartNumber, ctx->metadataBlockHeader.mediaPartNumberLength); + *length = ctx->metadataBlockHeader.mediaPartNumberLength; + + TRACE("Exiting aaruf_get_media_part_number(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the drive manufacturer metadata captured during imaging. + * + * Copies the UTF-16LE encoded manufacturer name of the device used to read or write the medium. This + * information documents the hardware involved in the imaging process, which is crucial for forensic + * reporting and reproducibility studies. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the manufacturer string. May be NULL when querying required + * length. + * @param length Pointer to an int32_t specifying the buffer size on input and receiving the actual + * metadata length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Drive manufacturer metadata was copied successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) The image lacks drive manufacturer metadata. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer is too small; *length holds the + * required size for a subsequent call. + * + * @note The returned manufacturer string corresponds to the value recorded by aaruf_set_drive_manufacturer() + * and may include branding or OEM designations. + */ +int32_t aaruf_get_drive_manufacturer(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_drive_manufacturer(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_drive_manufacturer() = 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_drive_manufacturer() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.DriveManufacturer == NULL || + ctx->metadataBlockHeader.driveManufacturerLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_drive_manufacturer() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.driveManufacturerLength) + { + *length = ctx->metadataBlockHeader.driveManufacturerLength; + + TRACE("Exiting aaruf_get_drive_manufacturer() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.DriveManufacturer, ctx->metadataBlockHeader.driveManufacturerLength); + *length = ctx->metadataBlockHeader.driveManufacturerLength; + + TRACE("Exiting aaruf_get_drive_manufacturer(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the device model information for the imaging drive. + * + * Returns the UTF-16LE encoded model identifier for the drive used during acquisition. The model + * metadata provides finer granularity than the manufacturer name, enabling detailed documentation of + * imaging hardware capabilities and behavior. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Buffer that receives the model string; may be NULL while probing required capacity. + * @param length Pointer to an int32_t indicating buffer size on input and receiving the metadata length + * on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Drive model metadata was available and copied. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No drive model metadata exists in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The supplied buffer was insufficient; *length is updated. + * + * @note Model strings can include firmware suffixes, interface hints, or OEM variations. Consume the + * data verbatim to maintain accurate provenance records. + */ +int32_t aaruf_get_drive_model(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_drive_model(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_drive_model() = 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_drive_model() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.DriveModel == NULL || + ctx->metadataBlockHeader.driveModelLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_drive_model() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.driveModelLength) + { + *length = ctx->metadataBlockHeader.driveModelLength; + + TRACE("Exiting aaruf_get_drive_model() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.DriveModel, ctx->metadataBlockHeader.driveModelLength); + *length = ctx->metadataBlockHeader.driveModelLength; + + TRACE("Exiting aaruf_get_drive_model(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the imaging drive's serial number metadata. + * + * Copies the UTF-16LE encoded serial number reported for the drive used during the imaging session. + * This metadata enables correlation between images and specific hardware units for forensic chain of + * custody or quality assurance workflows. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the serial number; may be NULL when querying size. + * @param length Pointer to an int32_t carrying the buffer size on input and receiving the actual + * serial number length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Drive serial number metadata was copied to @p buffer. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No drive serial number metadata is available. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The provided buffer was insufficient. + * + * @note Serial numbers are stored exactly as returned by the imaging hardware and may include leading + * zeros or spacing that should be preserved. + */ +int32_t aaruf_get_drive_serial_number(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_drive_serial_number(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_drive_serial_number() = 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_drive_serial_number() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.DriveSerialNumber == NULL || + ctx->metadataBlockHeader.driveSerialNumberLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_drive_serial_number() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.driveSerialNumberLength) + { + *length = ctx->metadataBlockHeader.driveSerialNumberLength; + + TRACE("Exiting aaruf_get_drive_serial_number() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.DriveSerialNumber, ctx->metadataBlockHeader.driveSerialNumberLength); + *length = ctx->metadataBlockHeader.driveSerialNumberLength; + + TRACE("Exiting aaruf_get_drive_serial_number(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +} + +/** + * @brief Retrieves the firmware revision metadata for the imaging drive. + * + * Returns the UTF-16LE encoded firmware revision string that was captured when the image was created. + * Firmware information is critical for reproducing imaging environments and diagnosing drive-specific + * behavior or bugs. + * + * @param context Pointer to a valid aaruformat context. + * @param buffer Destination buffer for the firmware revision string. May be NULL when probing size. + * @param length Pointer to an int32_t that specifies the buffer capacity in bytes on input and is + * updated with the actual metadata length on output. + * + * @return Returns one of the following status codes: + * @retval AARUF_STATUS_OK (0) Firmware revision metadata was present and copied successfully. + * @retval AARUF_ERROR_NOT_AARUFORMAT (-1) The context pointer is invalid. + * @retval AARUF_ERROR_METADATA_NOT_PRESENT (-30) No firmware metadata exists in the image. + * @retval AARUF_ERROR_BUFFER_TOO_SMALL (-10) The supplied buffer was too small; *length is updated. + * + * @note Firmware revision formats vary between manufacturers (e.g., numeric, alphanumeric, dot-separated). + * The library stores the data verbatim without attempting normalization. + */ +int32_t aaruf_get_drive_firmware_revision(const void *context, uint8_t *buffer, int32_t *length) +{ + TRACE("Entering aaruf_get_drive_firmware_revision(%p, %p, %d)", context, buffer, *length); + + // Check context is correct AaruFormat context + if(context == NULL) + { + FATAL("Invalid context"); + + TRACE("Exiting aaruf_get_drive_firmware_revision() = 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_drive_firmware_revision() = AARUF_ERROR_NOT_AARUFORMAT"); + return AARUF_ERROR_NOT_AARUFORMAT; + } + + if(ctx->metadataBlockHeader.identifier != MetadataBlock || ctx->imageInfo.DriveFirmwareRevision == NULL || + ctx->metadataBlockHeader.driveFirmwareRevisionLength == 0) + { + FATAL("No metadata block present"); + + TRACE("Exiting aaruf_get_drive_firmware_revision() = AARUF_ERROR_METADATA_NOT_PRESENT"); + return AARUF_ERROR_METADATA_NOT_PRESENT; + } + + if(buffer == NULL || *length < ctx->metadataBlockHeader.driveFirmwareRevisionLength) + { + *length = ctx->metadataBlockHeader.driveFirmwareRevisionLength; + + TRACE("Exiting aaruf_get_drive_firmware_revision() = AARUF_ERROR_BUFFER_TOO_SMALL"); + return AARUF_ERROR_BUFFER_TOO_SMALL; + } + + // Copy opaque UTF-16LE string + memcpy(buffer, ctx->imageInfo.DriveFirmwareRevision, ctx->metadataBlockHeader.driveFirmwareRevisionLength); + *length = ctx->metadataBlockHeader.driveFirmwareRevisionLength; + + TRACE("Exiting aaruf_get_drive_firmware_revision(%p, %p, %d) = AARUF_STATUS_OK", context, buffer, *length); + return AARUF_STATUS_OK; +}