diff --git a/CMakeLists.txt b/CMakeLists.txt index e9ae7ab..db1e726 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,14 @@ set(AMDUAT_ASL_LOG_STORE_SRCS src/core/asl_log_store.c ) +set(AMDUAT_ASL_RECORD_SRCS + src/core/asl_record.c +) + +set(AMDUAT_ASL_COLLECTION_SRCS + src/core/asl_collection.c +) + set(AMDUAT_HASH_ASL1_SRCS src/near_core/hash/asl1.c src/near_core/hash/sha256.c @@ -215,6 +223,13 @@ target_compile_definitions(amduat_asl_pointer_fs_obj PRIVATE _POSIX_C_SOURCE=200 amduat_add_lib(asl_log_store SRCS ${AMDUAT_ASL_LOG_STORE_SRCS}) amduat_link(asl_log_store amduat_asl_pointer_fs amduat_asl amduat_enc amduat_util) + +amduat_add_lib(asl_record SRCS ${AMDUAT_ASL_RECORD_SRCS}) +amduat_link(asl_record amduat_asl amduat_enc amduat_util) + +amduat_add_lib(asl_collection SRCS ${AMDUAT_ASL_COLLECTION_SRCS}) +amduat_link(asl_collection amduat_asl_log_store amduat_asl_record amduat_asl_pointer_fs + amduat_asl amduat_enc amduat_util) amduat_add_lib(tgk_store_mem SRCS ${AMDUAT_TGK_STORE_MEM_SRCS}) amduat_link(tgk_store_mem amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util) @@ -268,7 +283,8 @@ target_include_directories(amduat_pel_cli ) target_link_libraries(amduat_pel_cli PRIVATE amduat_format amduat_pel amduat_asl_store_fs - amduat_asl_log_store amduat_asl_pointer_fs + amduat_asl_collection amduat_asl_record amduat_asl_log_store + amduat_asl_pointer_fs amduat_asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util ) diff --git a/include/amduat/asl/collection.h b/include/amduat/asl/collection.h new file mode 100644 index 0000000..3145e40 --- /dev/null +++ b/include/amduat/asl/collection.h @@ -0,0 +1,68 @@ +#ifndef AMDUAT_ASL_COLLECTION_H +#define AMDUAT_ASL_COLLECTION_H + +#include "amduat/asl/asl_pointer_fs.h" +#include "amduat/asl/core.h" +#include "amduat/asl/log_store.h" +#include "amduat/asl/record.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + AMDUAT_ASL_COLLECTION_OK = 0, + AMDUAT_ASL_COLLECTION_ERR_INVALID_NAME = 1, + AMDUAT_ASL_COLLECTION_ERR_IO = 2, + AMDUAT_ASL_COLLECTION_ERR_INTEGRITY = 3, + AMDUAT_ASL_COLLECTION_ERR_CAS_MISMATCH = 4 +} amduat_asl_collection_error_t; + +typedef struct { + amduat_asl_store_t *store; + amduat_asl_log_store_t log_store; + amduat_asl_pointer_store_t pointer_store; +} amduat_asl_collection_store_t; + +bool amduat_asl_collection_store_init( + amduat_asl_collection_store_t *collection_store, + const char *root_path, + amduat_asl_store_t *store); + +amduat_asl_collection_error_t amduat_asl_collection_append( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + amduat_reference_t record_ref, + uint16_t kind, + amduat_octets_t actor, + uint64_t *out_offset); + +amduat_asl_collection_error_t amduat_asl_collection_snapshot( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + uint64_t up_to_offset, + amduat_reference_t *out_snapshot_ref, + bool *out_swapped); + +amduat_asl_collection_error_t amduat_asl_collection_read( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + uint64_t from_offset, + size_t limit, + amduat_reference_t **out_record_refs, + size_t *out_len, + uint64_t *out_next_offset, + bool *out_end); + +void amduat_asl_collection_refs_free(amduat_reference_t *refs, + size_t refs_len); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUAT_ASL_COLLECTION_H */ diff --git a/include/amduat/asl/record.h b/include/amduat/asl/record.h new file mode 100644 index 0000000..89f96ca --- /dev/null +++ b/include/amduat/asl/record.h @@ -0,0 +1,46 @@ +#ifndef AMDUAT_ASL_RECORD_H +#define AMDUAT_ASL_RECORD_H + +#include "amduat/asl/core.h" +#include "amduat/asl/store.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { TYPE_TAG_ASL_RECORD_1 = 0x00000402u }; +enum { AMDUAT_TYPE_TAG_ASL_RECORD_1 = TYPE_TAG_ASL_RECORD_1 }; + +typedef struct { + amduat_octets_t schema; + amduat_octets_t payload; +} amduat_asl_record_t; + +bool amduat_asl_record_encode_v1(const amduat_asl_record_t *record, + amduat_octets_t *out_bytes); + +bool amduat_asl_record_decode_v1(amduat_octets_t bytes, + amduat_asl_record_t *out_record); + +void amduat_asl_record_free(amduat_asl_record_t *record); + +amduat_asl_store_error_t amduat_asl_record_store_put( + amduat_asl_store_t *store, + amduat_octets_t schema, + amduat_octets_t payload, + amduat_reference_t *out_ref); + +amduat_asl_store_error_t amduat_asl_record_store_get( + amduat_asl_store_t *store, + amduat_reference_t ref, + amduat_asl_record_t *out_record); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUAT_ASL_RECORD_H */ diff --git a/src/core/asl_collection.c b/src/core/asl_collection.c new file mode 100644 index 0000000..6a8b738 --- /dev/null +++ b/src/core/asl_collection.c @@ -0,0 +1,483 @@ +#include "amduat/asl/collection.h" + +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/util/log.h" + +#include +#include +#include + +enum { + AMDUAT_ASL_COLLECTION_MAGIC_LEN = 8, + AMDUAT_ASL_COLLECTION_VERSION = 1, + AMDUAT_ASL_COLLECTION_READ_BATCH = 1024u +}; + +static const uint8_t k_amduat_asl_collection_magic[ + AMDUAT_ASL_COLLECTION_MAGIC_LEN] = { + 'A', 'S', 'L', 'C', 'O', 'L', '1', '\0' +}; + +static void amduat_asl_collection_store_u32_le(uint8_t *out, uint32_t value) { + out[0] = (uint8_t)(value & 0xffu); + out[1] = (uint8_t)((value >> 8) & 0xffu); + out[2] = (uint8_t)((value >> 16) & 0xffu); + out[3] = (uint8_t)((value >> 24) & 0xffu); +} + +static void amduat_asl_collection_store_u64_le(uint8_t *out, uint64_t value) { + out[0] = (uint8_t)(value & 0xffu); + out[1] = (uint8_t)((value >> 8) & 0xffu); + out[2] = (uint8_t)((value >> 16) & 0xffu); + out[3] = (uint8_t)((value >> 24) & 0xffu); + out[4] = (uint8_t)((value >> 32) & 0xffu); + out[5] = (uint8_t)((value >> 40) & 0xffu); + out[6] = (uint8_t)((value >> 48) & 0xffu); + out[7] = (uint8_t)((value >> 56) & 0xffu); +} + +static bool amduat_asl_collection_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduat_asl_collection_build_log_name(const char *name, + char **out_name) { + size_t name_len; + size_t total_len; + char *buffer; + size_t offset; + + if (name == NULL || out_name == NULL) { + return false; + } + if (!amduat_asl_pointer_name_is_valid(name)) { + return false; + } + name_len = strlen(name); + total_len = 11u + name_len + 4u + 1u; + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + offset = 0u; + memcpy(buffer + offset, "collection/", 11u); + offset += 11u; + memcpy(buffer + offset, name, name_len); + offset += name_len; + memcpy(buffer + offset, "/log", 4u); + offset += 4u; + buffer[offset] = '\0'; + *out_name = buffer; + return true; +} + +static bool amduat_asl_collection_build_head_name(const char *name, + char **out_name) { + size_t name_len; + size_t total_len; + char *buffer; + size_t offset; + + if (name == NULL || out_name == NULL) { + return false; + } + if (!amduat_asl_pointer_name_is_valid(name)) { + return false; + } + name_len = strlen(name); + total_len = 11u + name_len + 5u + 1u; + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + offset = 0u; + memcpy(buffer + offset, "collection/", 11u); + offset += 11u; + memcpy(buffer + offset, name, name_len); + offset += name_len; + memcpy(buffer + offset, "/head", 5u); + offset += 5u; + buffer[offset] = '\0'; + *out_name = buffer; + return true; +} + +static bool amduat_asl_collection_encode_snapshot_payload( + uint64_t snapshot_offset, + const amduat_reference_t *refs, + size_t refs_len, + amduat_octets_t *out_payload) { + size_t total_len = 0u; + uint8_t *buffer; + size_t offset = 0u; + + if (out_payload == NULL) { + return false; + } + out_payload->data = NULL; + out_payload->len = 0u; + + if (refs_len > UINT32_MAX) { + return false; + } + + if (!amduat_asl_collection_add_size( + &total_len, AMDUAT_ASL_COLLECTION_MAGIC_LEN + 4u + 8u + 4u)) { + return false; + } + for (size_t i = 0u; i < refs_len; ++i) { + amduat_octets_t ref_bytes = amduat_octets(NULL, 0u); + if (!amduat_enc_asl1_core_encode_reference_v1(refs[i], &ref_bytes)) { + return false; + } + if (!amduat_asl_collection_add_size(&total_len, 4u + ref_bytes.len)) { + free((void *)ref_bytes.data); + return false; + } + free((void *)ref_bytes.data); + } + + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + memcpy(buffer + offset, k_amduat_asl_collection_magic, + AMDUAT_ASL_COLLECTION_MAGIC_LEN); + offset += AMDUAT_ASL_COLLECTION_MAGIC_LEN; + amduat_asl_collection_store_u32_le(buffer + offset, + AMDUAT_ASL_COLLECTION_VERSION); + offset += 4u; + amduat_asl_collection_store_u64_le(buffer + offset, snapshot_offset); + offset += 8u; + amduat_asl_collection_store_u32_le(buffer + offset, (uint32_t)refs_len); + offset += 4u; + + for (size_t i = 0u; i < refs_len; ++i) { + amduat_octets_t ref_bytes = amduat_octets(NULL, 0u); + if (!amduat_enc_asl1_core_encode_reference_v1(refs[i], &ref_bytes)) { + free(buffer); + return false; + } + amduat_asl_collection_store_u32_le(buffer + offset, + (uint32_t)ref_bytes.len); + offset += 4u; + memcpy(buffer + offset, ref_bytes.data, ref_bytes.len); + offset += ref_bytes.len; + free((void *)ref_bytes.data); + } + + if (offset != total_len) { + free(buffer); + return false; + } + *out_payload = amduat_octets(buffer, total_len); + return true; +} + +bool amduat_asl_collection_store_init( + amduat_asl_collection_store_t *collection_store, + const char *root_path, + amduat_asl_store_t *store) { + if (collection_store == NULL || root_path == NULL || store == NULL) { + return false; + } + collection_store->store = store; + if (!amduat_asl_pointer_store_init(&collection_store->pointer_store, + root_path)) { + return false; + } + if (!amduat_asl_log_store_init(&collection_store->log_store, root_path, + store, &collection_store->pointer_store)) { + return false; + } + return true; +} + +amduat_asl_collection_error_t amduat_asl_collection_append( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + amduat_reference_t record_ref, + uint16_t kind, + amduat_octets_t actor, + uint64_t *out_offset) { + char *log_name = NULL; + amduat_asl_log_entry_t entry; + amduat_asl_store_error_t err; + + if (collection_store == NULL || collection_store->store == NULL || + collection_name == NULL) { + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + if (!amduat_asl_pointer_name_is_valid(collection_name)) { + return AMDUAT_ASL_COLLECTION_ERR_INVALID_NAME; + } + if (!amduat_asl_collection_build_log_name(collection_name, &log_name)) { + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + + memset(&entry, 0, sizeof(entry)); + entry.kind = kind; + entry.payload_ref = record_ref; + entry.has_actor = actor.len != 0u; + entry.actor = actor; + + err = amduat_asl_log_append(&collection_store->log_store, log_name, + &entry, 1u, out_offset); + free(log_name); + if (err != AMDUAT_ASL_STORE_OK) { + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + return AMDUAT_ASL_COLLECTION_OK; +} + +amduat_asl_collection_error_t amduat_asl_collection_snapshot( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + uint64_t up_to_offset, + amduat_reference_t *out_snapshot_ref, + bool *out_swapped) { + char *log_name = NULL; + char *head_name = NULL; + amduat_reference_t *refs = NULL; + size_t refs_len = 0u; + uint64_t from = 0u; + bool end = false; + uint64_t next_offset = 0u; + amduat_asl_record_t snapshot_record; + amduat_octets_t payload = amduat_octets(NULL, 0u); + amduat_reference_t snapshot_ref; + amduat_asl_pointer_error_t ptr_err; + bool head_exists = false; + amduat_reference_t head_ref; + bool swapped = false; + + if (out_snapshot_ref != NULL) { + out_snapshot_ref->hash_id = 0u; + out_snapshot_ref->digest = amduat_octets(NULL, 0u); + } + if (out_swapped != NULL) { + *out_swapped = false; + } + if (collection_store == NULL || collection_store->store == NULL || + collection_name == NULL) { + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + if (!amduat_asl_pointer_name_is_valid(collection_name)) { + return AMDUAT_ASL_COLLECTION_ERR_INVALID_NAME; + } + + if (!amduat_asl_collection_build_log_name(collection_name, &log_name) || + !amduat_asl_collection_build_head_name(collection_name, &head_name)) { + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + + while (!end && (up_to_offset == UINT64_MAX || from <= up_to_offset)) { + size_t limit = AMDUAT_ASL_COLLECTION_READ_BATCH; + amduat_asl_log_entry_t *entries = NULL; + size_t entries_len = 0u; + amduat_asl_store_error_t err = amduat_asl_log_read( + &collection_store->log_store, log_name, from, limit, &entries, + &entries_len, &next_offset, &end); + if (err != AMDUAT_ASL_STORE_OK) { + free(log_name); + free(head_name); + amduat_asl_collection_refs_free(refs, refs_len); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + for (size_t i = 0u; i < entries_len; ++i) { + uint64_t offset = from + i; + if (up_to_offset != UINT64_MAX && offset > up_to_offset) { + end = true; + break; + } + amduat_reference_t *next = + (amduat_reference_t *)realloc( + refs, (refs_len + 1u) * sizeof(*refs)); + if (next == NULL) { + amduat_asl_log_entries_free(entries, entries_len); + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + refs = next; + refs[refs_len] = entries[i].payload_ref; + if (!amduat_octets_clone(entries[i].payload_ref.digest, + &refs[refs_len].digest)) { + amduat_asl_log_entries_free(entries, entries_len); + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + refs_len++; + } + amduat_asl_log_entries_free(entries, entries_len); + from = next_offset; + if (entries_len == 0u) { + break; + } + } + + { + uint64_t snapshot_offset = from; + if (up_to_offset != UINT64_MAX && + up_to_offset < UINT64_MAX && + snapshot_offset > up_to_offset + 1u) { + snapshot_offset = up_to_offset + 1u; + } + if (!amduat_asl_collection_encode_snapshot_payload( + snapshot_offset, refs, refs_len, &payload)) { + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + } + + if (payload.len == 0u || payload.data == NULL) { + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + + { + amduat_asl_store_error_t err = amduat_asl_record_store_put( + collection_store->store, + amduat_octets("collection/snapshot", + strlen("collection/snapshot")), + payload, + &snapshot_ref); + amduat_octets_free(&payload); + if (err != AMDUAT_ASL_STORE_OK) { + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + } + + ptr_err = amduat_asl_pointer_get(&collection_store->pointer_store, head_name, + &head_exists, &head_ref); + if (ptr_err != AMDUAT_ASL_POINTER_OK) { + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + ptr_err = amduat_asl_pointer_cas(&collection_store->pointer_store, head_name, + head_exists, + head_exists ? &head_ref : NULL, + &snapshot_ref, &swapped); + if (head_exists) { + amduat_reference_free(&head_ref); + } + if (ptr_err != AMDUAT_ASL_POINTER_OK) { + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + + if (out_swapped != NULL) { + *out_swapped = swapped; + } + if (out_snapshot_ref != NULL) { + *out_snapshot_ref = snapshot_ref; + } + if (!swapped) { + amduat_log(AMDUAT_LOG_DEBUG, "collection snapshot CAS mismatch"); + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_ERR_CAS_MISMATCH; + } + + amduat_asl_collection_refs_free(refs, refs_len); + free(log_name); + free(head_name); + return AMDUAT_ASL_COLLECTION_OK; +} + +amduat_asl_collection_error_t amduat_asl_collection_read( + amduat_asl_collection_store_t *collection_store, + const char *collection_name, + uint64_t from_offset, + size_t limit, + amduat_reference_t **out_record_refs, + size_t *out_len, + uint64_t *out_next_offset, + bool *out_end) { + char *log_name = NULL; + amduat_asl_log_entry_t *entries = NULL; + size_t entries_len = 0u; + amduat_asl_store_error_t err; + + if (out_record_refs == NULL || out_len == NULL || + out_next_offset == NULL || out_end == NULL) { + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + *out_record_refs = NULL; + *out_len = 0u; + + if (collection_store == NULL || collection_store->store == NULL || + collection_name == NULL) { + return AMDUAT_ASL_COLLECTION_ERR_INTEGRITY; + } + if (!amduat_asl_pointer_name_is_valid(collection_name)) { + return AMDUAT_ASL_COLLECTION_ERR_INVALID_NAME; + } + if (!amduat_asl_collection_build_log_name(collection_name, &log_name)) { + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + + err = amduat_asl_log_read(&collection_store->log_store, log_name, + from_offset, limit, &entries, &entries_len, + out_next_offset, out_end); + free(log_name); + if (err != AMDUAT_ASL_STORE_OK) { + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + + if (entries_len != 0u) { + amduat_reference_t *refs = + (amduat_reference_t *)calloc(entries_len, sizeof(*refs)); + if (refs == NULL) { + amduat_asl_log_entries_free(entries, entries_len); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + for (size_t i = 0u; i < entries_len; ++i) { + refs[i].hash_id = entries[i].payload_ref.hash_id; + if (!amduat_octets_clone(entries[i].payload_ref.digest, + &refs[i].digest)) { + amduat_asl_collection_refs_free(refs, i); + amduat_asl_log_entries_free(entries, entries_len); + return AMDUAT_ASL_COLLECTION_ERR_IO; + } + } + *out_record_refs = refs; + *out_len = entries_len; + } + + amduat_asl_log_entries_free(entries, entries_len); + return AMDUAT_ASL_COLLECTION_OK; +} + +void amduat_asl_collection_refs_free(amduat_reference_t *refs, + size_t refs_len) { + if (refs == NULL) { + return; + } + for (size_t i = 0u; i < refs_len; ++i) { + amduat_reference_free(&refs[i]); + } + free(refs); +} diff --git a/src/core/asl_record.c b/src/core/asl_record.c new file mode 100644 index 0000000..dcea006 --- /dev/null +++ b/src/core/asl_record.c @@ -0,0 +1,268 @@ +#include "amduat/asl/record.h" + +#include +#include + +enum { + AMDUAT_ASL_RECORD_MAGIC_LEN = 8, + AMDUAT_ASL_RECORD_VERSION = 1 +}; + +static const uint8_t k_amduat_asl_record_magic[AMDUAT_ASL_RECORD_MAGIC_LEN] = { + 'A', 'S', 'L', 'R', 'E', 'C', '1', '\0' +}; + +static void amduat_asl_record_store_u32_le(uint8_t *out, uint32_t value) { + out[0] = (uint8_t)(value & 0xffu); + out[1] = (uint8_t)((value >> 8) & 0xffu); + out[2] = (uint8_t)((value >> 16) & 0xffu); + out[3] = (uint8_t)((value >> 24) & 0xffu); +} + +static bool amduat_asl_record_read_u32_le(const uint8_t *data, + size_t len, + size_t *offset, + uint32_t *out) { + if (len - *offset < 4u) { + return false; + } + *out = (uint32_t)data[*offset] | + ((uint32_t)data[*offset + 1u] << 8) | + ((uint32_t)data[*offset + 2u] << 16) | + ((uint32_t)data[*offset + 3u] << 24); + *offset += 4u; + return true; +} + +static bool amduat_asl_record_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduat_asl_record_schema_is_ascii(amduat_octets_t schema) { + if (schema.len == 0u || schema.data == NULL) { + return false; + } + for (size_t i = 0u; i < schema.len; ++i) { + uint8_t c = schema.data[i]; + if (c < 0x20u || c > 0x7eu) { + return false; + } + } + return true; +} + +bool amduat_asl_record_encode_v1(const amduat_asl_record_t *record, + amduat_octets_t *out_bytes) { + uint8_t *buffer; + size_t total_len = 0u; + size_t offset = 0u; + uint32_t schema_len; + uint32_t payload_len; + uint8_t flags = 0u; + + if (out_bytes == NULL || record == NULL) { + return false; + } + out_bytes->data = NULL; + out_bytes->len = 0u; + + if (!amduat_asl_record_schema_is_ascii(record->schema)) { + return false; + } + if (record->payload.len != 0u && record->payload.data == NULL) { + return false; + } + if (record->schema.len > UINT32_MAX || record->payload.len > UINT32_MAX) { + return false; + } + schema_len = (uint32_t)record->schema.len; + payload_len = (uint32_t)record->payload.len; + + if (!amduat_asl_record_add_size(&total_len, + AMDUAT_ASL_RECORD_MAGIC_LEN + 4u) || + !amduat_asl_record_add_size(&total_len, 4u + schema_len) || + !amduat_asl_record_add_size(&total_len, 4u + payload_len) || + !amduat_asl_record_add_size(&total_len, 1u)) { + return false; + } + + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + memcpy(buffer + offset, k_amduat_asl_record_magic, + AMDUAT_ASL_RECORD_MAGIC_LEN); + offset += AMDUAT_ASL_RECORD_MAGIC_LEN; + amduat_asl_record_store_u32_le(buffer + offset, AMDUAT_ASL_RECORD_VERSION); + offset += 4u; + + amduat_asl_record_store_u32_le(buffer + offset, schema_len); + offset += 4u; + memcpy(buffer + offset, record->schema.data, schema_len); + offset += schema_len; + + amduat_asl_record_store_u32_le(buffer + offset, payload_len); + offset += 4u; + if (payload_len != 0u) { + memcpy(buffer + offset, record->payload.data, payload_len); + offset += payload_len; + } + + buffer[offset++] = flags; + + if (offset != total_len) { + free(buffer); + return false; + } + + out_bytes->data = buffer; + out_bytes->len = total_len; + return true; +} + +bool amduat_asl_record_decode_v1(amduat_octets_t bytes, + amduat_asl_record_t *out_record) { + size_t offset = 0u; + uint32_t version; + uint32_t schema_len; + uint32_t payload_len; + uint8_t flags; + uint8_t *schema_bytes; + uint8_t *payload_bytes; + + if (out_record == NULL) { + return false; + } + out_record->schema = amduat_octets(NULL, 0u); + out_record->payload = amduat_octets(NULL, 0u); + + if (bytes.len < AMDUAT_ASL_RECORD_MAGIC_LEN + 4u + 1u) { + return false; + } + if (bytes.data == NULL) { + return false; + } + if (memcmp(bytes.data, k_amduat_asl_record_magic, + AMDUAT_ASL_RECORD_MAGIC_LEN) != 0) { + return false; + } + offset += AMDUAT_ASL_RECORD_MAGIC_LEN; + if (!amduat_asl_record_read_u32_le(bytes.data, bytes.len, &offset, + &version) || + version != AMDUAT_ASL_RECORD_VERSION) { + return false; + } + if (!amduat_asl_record_read_u32_le(bytes.data, bytes.len, &offset, + &schema_len)) { + return false; + } + if (bytes.len - offset < schema_len) { + return false; + } + schema_bytes = NULL; + if (schema_len != 0u) { + schema_bytes = (uint8_t *)malloc(schema_len); + if (schema_bytes == NULL) { + return false; + } + memcpy(schema_bytes, bytes.data + offset, schema_len); + } + offset += schema_len; + + if (!amduat_asl_record_read_u32_le(bytes.data, bytes.len, &offset, + &payload_len)) { + free(schema_bytes); + return false; + } + if (bytes.len - offset < payload_len + 1u) { + free(schema_bytes); + return false; + } + payload_bytes = NULL; + if (payload_len != 0u) { + payload_bytes = (uint8_t *)malloc(payload_len); + if (payload_bytes == NULL) { + free(schema_bytes); + return false; + } + memcpy(payload_bytes, bytes.data + offset, payload_len); + } + offset += payload_len; + flags = bytes.data[offset++]; + (void)flags; + + if (offset != bytes.len) { + free(schema_bytes); + free(payload_bytes); + return false; + } + + out_record->schema = amduat_octets(schema_bytes, schema_len); + out_record->payload = amduat_octets(payload_bytes, payload_len); + return true; +} + +void amduat_asl_record_free(amduat_asl_record_t *record) { + if (record == NULL) { + return; + } + amduat_octets_free(&record->schema); + amduat_octets_free(&record->payload); +} + +amduat_asl_store_error_t amduat_asl_record_store_put( + amduat_asl_store_t *store, + amduat_octets_t schema, + amduat_octets_t payload, + amduat_reference_t *out_ref) { + amduat_asl_record_t record; + amduat_octets_t encoded = amduat_octets(NULL, 0u); + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + + if (store == NULL || out_ref == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + record.schema = schema; + record.payload = payload; + if (!amduat_asl_record_encode_v1(&record, &encoded)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + artifact = amduat_artifact_with_type( + encoded, amduat_type_tag(AMDUAT_TYPE_TAG_ASL_RECORD_1)); + err = amduat_asl_store_put(store, artifact, out_ref); + free((void *)encoded.data); + return err; +} + +amduat_asl_store_error_t amduat_asl_record_store_get( + amduat_asl_store_t *store, + amduat_reference_t ref, + amduat_asl_record_t *out_record) { + amduat_artifact_t artifact; + amduat_asl_store_error_t err; + + if (store == NULL || out_record == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + err = amduat_asl_store_get(store, ref, &artifact); + if (err != AMDUAT_ASL_STORE_OK) { + return err; + } + if (!artifact.has_type_tag || + artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_RECORD_1) { + amduat_artifact_free(&artifact); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (!amduat_asl_record_decode_v1(artifact.bytes, out_record)) { + amduat_artifact_free(&artifact); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + amduat_artifact_free(&artifact); + return AMDUAT_ASL_STORE_OK; +} diff --git a/src/tools/amduat_pel_cli.c b/src/tools/amduat_pel_cli.c index d934a49..95bd132 100644 --- a/src/tools/amduat_pel_cli.c +++ b/src/tools/amduat_pel_cli.c @@ -3459,6 +3459,461 @@ static int amduat_pel_cli_cmd_log( return AMDUAT_PEL_CLI_EXIT_USAGE; } +static int amduat_pel_cli_cmd_rec_put( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *schema_text = NULL; + const char *payload_arg = NULL; + uint8_t *payload_bytes = NULL; + size_t payload_len = 0u; + amduat_reference_t ref; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--schema") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --schema requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + schema_text = argv[++i]; + } else if (strcmp(argv[i], "--payload") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --payload requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + payload_arg = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (schema_text == NULL || payload_arg == NULL) { + fprintf(stderr, "error: --schema and --payload are required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (payload_arg[0] != '@' || payload_arg[1] == '\0') { + fprintf(stderr, "error: --payload must be @file\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_asl_read_path(payload_arg + 1, &payload_bytes, &payload_len)) { + fprintf(stderr, "error: failed to read payload file\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + if (!amduat_pel_cli_init_store(global->root, &store, &fs)) { + free(payload_bytes); + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + { + amduat_asl_store_error_t err = amduat_asl_record_store_put( + &store, + amduat_octets(schema_text, strlen(schema_text)), + amduat_octets(payload_bytes, payload_len), + &ref); + free(payload_bytes); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: record put failed: %s\n", + amduat_pel_cli_store_error_str(err)); + return amduat_pel_cli_map_store_error(err); + } + } + + fputs("ref=", stdout); + if (!amduat_format_ref_write_text(stdout, ref, global->ref_format)) { + fprintf(stderr, "error: failed to format ref\n"); + amduat_pel_cli_free_reference(&ref); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + amduat_pel_cli_free_reference(&ref); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_rec_get( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *ref_text = NULL; + amduat_reference_t ref; + amduat_asl_record_t record; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + bool stdin_used = false; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + ref_text = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (ref_text == NULL) { + fprintf(stderr, "error: --ref is required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_ref_from_text(ref_text, global->ref_format, + &stdin_used, &ref)) { + fprintf(stderr, "error: invalid --ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_init_store(global->root, &store, &fs)) { + amduat_pel_cli_free_reference(&ref); + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + { + amduat_asl_store_error_t err = amduat_asl_record_store_get( + &store, ref, &record); + if (err != AMDUAT_ASL_STORE_OK) { + amduat_pel_cli_free_reference(&ref); + fprintf(stderr, "error: record get failed: %s\n", + amduat_pel_cli_store_error_str(err)); + return amduat_pel_cli_map_store_error(err); + } + } + + printf("schema=%.*s\n", (int)record.schema.len, + (const char *)record.schema.data); + { + char *payload_hex = NULL; + if (!amduat_hex_encode_alloc(record.payload.data, record.payload.len, + &payload_hex)) { + amduat_asl_record_free(&record); + amduat_pel_cli_free_reference(&ref); + fprintf(stderr, "error: failed to encode payload\n"); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + printf("payload_hex=%s\n", payload_hex); + free(payload_hex); + } + + amduat_asl_record_free(&record); + amduat_pel_cli_free_reference(&ref); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_rec( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: rec requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "put") == 0) { + return amduat_pel_cli_cmd_rec_put(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "get") == 0) { + return amduat_pel_cli_cmd_rec_get(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown rec subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + +static int amduat_pel_cli_cmd_col_append( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *name = NULL; + const char *ref_text = NULL; + const char *actor_text = NULL; + unsigned long kind = 0u; + amduat_reference_t ref; + amduat_asl_collection_store_t collection_store; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + uint64_t offset = 0u; + bool stdin_used = false; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--name") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --name requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + name = argv[++i]; + } else if (strcmp(argv[i], "--record-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --record-ref requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + ref_text = argv[++i]; + } else if (strcmp(argv[i], "--kind") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --kind requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + kind = strtoul(argv[++i], NULL, 10); + } else if (strcmp(argv[i], "--actor") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --actor requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + actor_text = argv[++i]; + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (name == NULL || ref_text == NULL) { + fprintf(stderr, "error: --name and --record-ref are required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (kind > UINT16_MAX) { + fprintf(stderr, "error: --kind out of range\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_ref_from_text(ref_text, global->ref_format, + &stdin_used, &ref)) { + fprintf(stderr, "error: invalid --record-ref\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_init_store(global->root, &store, &fs) || + !amduat_asl_collection_store_init(&collection_store, global->root, + &store)) { + amduat_pel_cli_free_reference(&ref); + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + { + amduat_asl_collection_error_t err = amduat_asl_collection_append( + &collection_store, name, ref, (uint16_t)kind, + actor_text ? amduat_octets(actor_text, strlen(actor_text)) + : amduat_octets(NULL, 0u), + &offset); + amduat_pel_cli_free_reference(&ref); + if (err != AMDUAT_ASL_COLLECTION_OK) { + fprintf(stderr, "error: collection append failed\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + } + + printf("offset=%" PRIu64 "\n", offset); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_col_snapshot( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *name = NULL; + uint64_t up_to = UINT64_MAX; + amduat_reference_t snapshot_ref; + bool swapped = false; + amduat_asl_collection_store_t collection_store; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--name") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --name requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + name = argv[++i]; + } else if (strcmp(argv[i], "--up-to") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --up-to requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + up_to = (uint64_t)strtoull(argv[++i], NULL, 10); + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (name == NULL) { + fprintf(stderr, "error: --name is required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_init_store(global->root, &store, &fs) || + !amduat_asl_collection_store_init(&collection_store, global->root, + &store)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + { + amduat_asl_collection_error_t err = amduat_asl_collection_snapshot( + &collection_store, name, up_to, &snapshot_ref, &swapped); + if (err == AMDUAT_ASL_COLLECTION_ERR_CAS_MISMATCH) { + fprintf(stderr, "error: snapshot CAS mismatch\n"); + return 2; + } + if (err != AMDUAT_ASL_COLLECTION_OK) { + fprintf(stderr, "error: snapshot failed\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + } + + fputs("snapshot_ref=", stdout); + if (!amduat_format_ref_write_text(stdout, snapshot_ref, + global->ref_format)) { + fprintf(stderr, "error: failed to format ref\n"); + amduat_pel_cli_free_reference(&snapshot_ref); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + printf("\nswapped=%s\n", swapped ? "true" : "false"); + amduat_pel_cli_free_reference(&snapshot_ref); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_col_read( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + const char *name = NULL; + uint64_t from = 0u; + size_t limit = 0u; + amduat_reference_t *refs = NULL; + size_t refs_len = 0u; + uint64_t next_offset = 0u; + bool end = false; + amduat_asl_collection_store_t collection_store; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--name") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --name requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + name = argv[++i]; + } else if (strcmp(argv[i], "--from") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --from requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + from = (uint64_t)strtoull(argv[++i], NULL, 10); + } else if (strcmp(argv[i], "--limit") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --limit requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + limit = (size_t)strtoull(argv[++i], NULL, 10); + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (name == NULL || limit == 0u) { + fprintf(stderr, "error: --name and --limit are required\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (!amduat_pel_cli_init_store(global->root, &store, &fs) || + !amduat_asl_collection_store_init(&collection_store, global->root, + &store)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + { + amduat_asl_collection_error_t err = amduat_asl_collection_read( + &collection_store, name, from, limit, + &refs, &refs_len, &next_offset, &end); + if (err != AMDUAT_ASL_COLLECTION_OK) { + fprintf(stderr, "error: collection read failed\n"); + return AMDUAT_PEL_CLI_EXIT_IO; + } + } + + for (size_t iref = 0u; iref < refs_len; ++iref) { + printf("offset=%" PRIu64 " ref=", from + iref); + if (!amduat_format_ref_write_text(stdout, refs[iref], + global->ref_format)) { + fprintf(stderr, "error: failed to format ref\n"); + amduat_asl_collection_refs_free(refs, refs_len); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + } + printf("next=%" PRIu64 " end=%s\n", next_offset, end ? "true" : "false"); + amduat_asl_collection_refs_free(refs, refs_len); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_col( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: col requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "append") == 0) { + return amduat_pel_cli_cmd_col_append(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "snapshot") == 0) { + return amduat_pel_cli_cmd_col_snapshot(argc - 1, argv + 1, global); + } + if (strcmp(argv[0], "read") == 0) { + return amduat_pel_cli_cmd_col_read(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown col subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + static int amduat_pel_cli_cmd_help(int argc, char **argv) { (void)argc; (void)argv; @@ -3549,6 +4004,12 @@ int main(int argc, char **argv) { if (strcmp(command, "log") == 0) { return amduat_pel_cli_cmd_log(argc - i, argv + i, &global); } + if (strcmp(command, "rec") == 0) { + return amduat_pel_cli_cmd_rec(argc - i, argv + i, &global); + } + if (strcmp(command, "col") == 0) { + return amduat_pel_cli_cmd_col(argc - i, argv + i, &global); + } if (strcmp(command, "matcache") == 0) { return amduat_pel_cli_cmd_matcache(argc - i, argv + i, &global); }