diff --git a/CMakeLists.txt b/CMakeLists.txt index 64c6b38..50b0e45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,10 @@ set(AMDUAT_ASL_STORE_INDEX_FS_SRCS src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c ) +set(AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS + src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c +) + set(AMDUAT_TGK_STORE_MEM_SRCS src/adapters/tgk_store_mem/tgk_store_mem.c ) @@ -173,6 +177,10 @@ amduat_add_lib(asl_store_index_fs SRCS ${AMDUAT_ASL_STORE_INDEX_FS_SRCS}) amduat_link(asl_store_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util) target_compile_definitions(amduat_asl_store_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L) +amduat_add_lib(asl_derivation_index_fs SRCS ${AMDUAT_ASL_DERIVATION_INDEX_FS_SRCS}) +amduat_link(asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util) +target_compile_definitions(amduat_asl_derivation_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L) + 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) @@ -192,7 +200,7 @@ target_include_directories(amduat_asl_cli ) target_link_libraries(amduat_asl_cli PRIVATE amduat_format amduat_asl_store_fs amduat_asl_store_index_fs - amduat_asl amduat_enc + amduat_asl_derivation_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util ) set_target_properties(amduat_asl_cli PROPERTIES OUTPUT_NAME amduat-asl) @@ -431,6 +439,21 @@ target_link_libraries(amduat_test_asl_index_accel ) add_test(NAME asl_index_accel COMMAND amduat_test_asl_index_accel) +add_executable(amduat_test_asl_derivation_index_fs + tests/asl/test_asl_derivation_index_fs.c) +target_include_directories(amduat_test_asl_derivation_index_fs + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_compile_definitions(amduat_test_asl_derivation_index_fs + PRIVATE _POSIX_C_SOURCE=200809L +) +target_link_libraries(amduat_test_asl_derivation_index_fs + PRIVATE amduat_asl_derivation_index_fs +) +add_test(NAME asl_derivation_index_fs + COMMAND amduat_test_asl_derivation_index_fs) + add_executable(amduat_test_pel_program_dag_exec tests/pel/test_pel_program_dag_exec.c) target_include_directories(amduat_test_pel_program_dag_exec diff --git a/include/amduat/asl/asl_derivation_index_fs.h b/include/amduat/asl/asl_derivation_index_fs.h new file mode 100644 index 0000000..1535e03 --- /dev/null +++ b/include/amduat/asl/asl_derivation_index_fs.h @@ -0,0 +1,58 @@ +#ifndef AMDUAT_ASL_DERIVATION_INDEX_FS_H +#define AMDUAT_ASL_DERIVATION_INDEX_FS_H + +#include "amduat/asl/core.h" +#include "amduat/asl/store.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { AMDUAT_ASL_DERIVATION_INDEX_FS_ROOT_MAX = 1024 }; + +typedef struct { + amduat_octets_t sid; + amduat_reference_t program_ref; + amduat_reference_t *input_refs; + size_t input_refs_len; + bool has_params_ref; + amduat_reference_t params_ref; + bool has_exec_profile; + amduat_octets_t exec_profile; +} amduat_asl_derivation_record_t; + +typedef struct { + char root_path[AMDUAT_ASL_DERIVATION_INDEX_FS_ROOT_MAX]; +} amduat_asl_derivation_index_fs_t; + +bool amduat_asl_derivation_index_fs_init( + amduat_asl_derivation_index_fs_t *index, + const char *root_path); + +amduat_asl_store_error_t amduat_asl_derivation_index_fs_add( + amduat_asl_derivation_index_fs_t *index, + amduat_reference_t artifact_ref, + const amduat_asl_derivation_record_t *record); + +amduat_asl_store_error_t amduat_asl_derivation_index_fs_list( + amduat_asl_derivation_index_fs_t *index, + amduat_reference_t artifact_ref, + amduat_asl_derivation_record_t **out_records, + size_t *out_count); + +void amduat_asl_derivation_record_free( + amduat_asl_derivation_record_t *record); + +void amduat_asl_derivation_records_free( + amduat_asl_derivation_record_t *records, + size_t count); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* AMDUAT_ASL_DERIVATION_INDEX_FS_H */ diff --git a/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c new file mode 100644 index 0000000..3ce0c64 --- /dev/null +++ b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c @@ -0,0 +1,1029 @@ +#include "amduat/asl/asl_derivation_index_fs.h" + +#include "amduat/asl/ref_text.h" +#include "amduat/enc/asl1_core_codec.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN = 8, + AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN = 16, + AMDUAT_ASL_DERIVATION_INDEX_VERSION = 1 +}; + +static const uint8_t k_amduat_asl_derivation_index_magic[ + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN] = {'A', 'S', 'L', 'D', + 'R', 'V', '0', '1'}; + +typedef struct { + const uint8_t *data; + size_t len; + size_t offset; +} amduat_asl_derivation_cursor_t; + +static void amduat_asl_derivation_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_derivation_read_u32_le( + amduat_asl_derivation_cursor_t *cur, + uint32_t *out) { + const uint8_t *data; + + if (cur->len - cur->offset < 4u) { + return false; + } + data = cur->data + cur->offset; + *out = (uint32_t)data[0] | ((uint32_t)data[1] << 8) | + ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); + cur->offset += 4u; + return true; +} + +static bool amduat_asl_derivation_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduat_asl_derivation_join_path(const char *base, + const char *segment, + char **out_path) { + size_t base_len; + size_t segment_len; + bool needs_sep; + size_t total_len; + char *buffer; + size_t offset; + + if (base == NULL || segment == NULL || out_path == NULL) { + return false; + } + if (base[0] == '\0' || segment[0] == '\0') { + return false; + } + + base_len = strlen(base); + segment_len = strlen(segment); + needs_sep = base[base_len - 1u] != '/'; + total_len = base_len + (needs_sep ? 1u : 0u) + segment_len + 1u; + + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + offset = 0u; + memcpy(buffer + offset, base, base_len); + offset += base_len; + if (needs_sep) { + buffer[offset++] = '/'; + } + memcpy(buffer + offset, segment, segment_len); + offset += segment_len; + buffer[offset] = '\0'; + + *out_path = buffer; + return true; +} + +static bool amduat_asl_derivation_ensure_directory(const char *path) { + struct stat st; + + if (path == NULL || path[0] == '\0') { + return false; + } + if (stat(path, &st) == 0) { + return S_ISDIR(st.st_mode); + } + if (errno != ENOENT) { + return false; + } + if (mkdir(path, 0755) != 0) { + if (errno == EEXIST) { + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); + } + return false; + } + return true; +} + +static bool amduat_asl_derivation_build_derivations_path( + const char *root_path, + char **out_path) { + char *index_path; + char *derivations_path; + + if (!amduat_asl_derivation_join_path(root_path, "index", &index_path)) { + return false; + } + if (!amduat_asl_derivation_join_path(index_path, "derivations", + &derivations_path)) { + free(index_path); + return false; + } + free(index_path); + *out_path = derivations_path; + return true; +} + +static bool amduat_asl_derivation_build_record_path( + const char *root_path, + amduat_reference_t artifact_ref, + char **out_path) { + char *derivations_path = NULL; + char *by_artifact_path = NULL; + char *prefix_a_path = NULL; + char *prefix_b_path = NULL; + char *hex = NULL; + char prefix_a[3]; + char prefix_b[3]; + bool ok = false; + + if (out_path == NULL) { + return false; + } + *out_path = NULL; + + if (!amduat_asl_derivation_build_derivations_path(root_path, + &derivations_path)) { + return false; + } + if (!amduat_asl_derivation_join_path(derivations_path, "by_artifact", + &by_artifact_path)) { + goto cleanup; + } + if (!amduat_asl_ref_encode_hex(artifact_ref, &hex) || hex == NULL) { + goto cleanup; + } + + prefix_a[0] = hex[0] != '\0' ? hex[0] : '0'; + prefix_a[1] = hex[1] != '\0' ? hex[1] : '0'; + prefix_a[2] = '\0'; + prefix_b[0] = hex[2] != '\0' ? hex[2] : '0'; + prefix_b[1] = hex[3] != '\0' ? hex[3] : '0'; + prefix_b[2] = '\0'; + + if (!amduat_asl_derivation_join_path(by_artifact_path, prefix_a, + &prefix_a_path)) { + goto cleanup; + } + if (!amduat_asl_derivation_join_path(prefix_a_path, prefix_b, + &prefix_b_path)) { + goto cleanup; + } + if (!amduat_asl_derivation_join_path(prefix_b_path, hex, out_path)) { + goto cleanup; + } + ok = true; + +cleanup: + free(hex); + free(prefix_b_path); + free(prefix_a_path); + free(by_artifact_path); + free(derivations_path); + return ok; +} + +static bool amduat_asl_derivation_build_artifact_dir( + const char *root_path, + amduat_reference_t artifact_ref, + char **out_dir, + char **out_hex) { + char *derivations_path = NULL; + char *by_artifact_path = NULL; + char *prefix_a_path = NULL; + char *prefix_b_path = NULL; + char *hex = NULL; + char prefix_a[3]; + char prefix_b[3]; + bool ok = false; + + if (out_dir == NULL || out_hex == NULL) { + return false; + } + *out_dir = NULL; + *out_hex = NULL; + + if (!amduat_asl_derivation_build_derivations_path(root_path, + &derivations_path)) { + return false; + } + if (!amduat_asl_derivation_join_path(derivations_path, "by_artifact", + &by_artifact_path)) { + goto cleanup; + } + if (!amduat_asl_ref_encode_hex(artifact_ref, &hex) || hex == NULL) { + goto cleanup; + } + + prefix_a[0] = hex[0] != '\0' ? hex[0] : '0'; + prefix_a[1] = hex[1] != '\0' ? hex[1] : '0'; + prefix_a[2] = '\0'; + prefix_b[0] = hex[2] != '\0' ? hex[2] : '0'; + prefix_b[1] = hex[3] != '\0' ? hex[3] : '0'; + prefix_b[2] = '\0'; + + if (!amduat_asl_derivation_join_path(by_artifact_path, prefix_a, + &prefix_a_path)) { + goto cleanup; + } + if (!amduat_asl_derivation_join_path(prefix_a_path, prefix_b, + &prefix_b_path)) { + goto cleanup; + } + + *out_dir = prefix_b_path; + *out_hex = hex; + prefix_b_path = NULL; + hex = NULL; + ok = true; + +cleanup: + free(hex); + free(prefix_b_path); + free(prefix_a_path); + free(by_artifact_path); + free(derivations_path); + return ok; +} + +static amduat_asl_store_error_t amduat_asl_derivation_validate_record( + const amduat_asl_derivation_record_t *record) { + size_t i; + + if (record == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (record->sid.len == 0u || record->sid.data == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (record->program_ref.digest.len != 0u && + record->program_ref.digest.data == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (record->input_refs_len != 0u && record->input_refs == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + for (i = 0u; i < record->input_refs_len; ++i) { + if (record->input_refs[i].digest.len != 0u && + record->input_refs[i].digest.data == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } + if (record->has_params_ref && + record->params_ref.digest.len != 0u && + record->params_ref.digest.data == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (record->has_exec_profile && + record->exec_profile.len != 0u && + record->exec_profile.data == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_write_header(FILE *fp) { + uint8_t header[AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN]; + + memcpy(header, k_amduat_asl_derivation_index_magic, + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN); + amduat_asl_derivation_store_u32_le( + header + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN, + AMDUAT_ASL_DERIVATION_INDEX_VERSION); + amduat_asl_derivation_store_u32_le( + header + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN + 4u, 0u); + + if (fwrite(header, 1u, sizeof(header), fp) != sizeof(header)) { + return AMDUAT_ASL_STORE_ERR_IO; + } + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_validate_header(FILE *fp) { + uint8_t header[AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN]; + uint32_t version; + + if (fread(header, 1u, sizeof(header), fp) != sizeof(header)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (memcmp(header, k_amduat_asl_derivation_index_magic, + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN) != 0) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + version = (uint32_t)header[8] | ((uint32_t)header[9] << 8) | + ((uint32_t)header[10] << 16) | ((uint32_t)header[11] << 24); + if (version != AMDUAT_ASL_DERIVATION_INDEX_VERSION) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_open_records_file( + const char *path, + FILE **out_fp) { + struct stat st; + FILE *fp; + + if (out_fp == NULL) { + return AMDUAT_ASL_STORE_ERR_IO; + } + *out_fp = NULL; + + if (stat(path, &st) != 0) { + if (errno != ENOENT) { + return AMDUAT_ASL_STORE_ERR_IO; + } + fp = fopen(path, "wb"); + if (fp == NULL) { + return AMDUAT_ASL_STORE_ERR_IO; + } + if (amduat_asl_derivation_write_header(fp) != AMDUAT_ASL_STORE_OK) { + fclose(fp); + return AMDUAT_ASL_STORE_ERR_IO; + } + fclose(fp); + } else { + fp = fopen(path, "rb"); + if (fp == NULL) { + return AMDUAT_ASL_STORE_ERR_IO; + } + if (amduat_asl_derivation_validate_header(fp) != AMDUAT_ASL_STORE_OK) { + fclose(fp); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + fclose(fp); + } + + fp = fopen(path, "ab"); + if (fp == NULL) { + return AMDUAT_ASL_STORE_ERR_IO; + } + *out_fp = fp; + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_encode_ref( + amduat_reference_t ref, + amduat_octets_t *out_bytes) { + if (out_bytes == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (!amduat_enc_asl1_core_encode_reference_v1(ref, out_bytes)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_write_record( + FILE *fp, + const amduat_asl_derivation_record_t *record) { + amduat_octets_t program_bytes = amduat_octets(NULL, 0u); + amduat_octets_t params_bytes = amduat_octets(NULL, 0u); + amduat_octets_t *input_bytes = NULL; + uint8_t *buffer = NULL; + size_t total_len = 0u; + size_t offset = 0u; + uint32_t input_count; + size_t i; + + input_count = (uint32_t)record->input_refs_len; + if (record->input_refs_len > UINT32_MAX) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (amduat_asl_derivation_encode_ref(record->program_ref, + &program_bytes) != + AMDUAT_ASL_STORE_OK) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (record->has_params_ref) { + if (amduat_asl_derivation_encode_ref(record->params_ref, ¶ms_bytes) != + AMDUAT_ASL_STORE_OK) { + free((void *)program_bytes.data); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } + + if (record->input_refs_len != 0u) { + input_bytes = + (amduat_octets_t *)calloc(record->input_refs_len, sizeof(*input_bytes)); + if (input_bytes == NULL) { + free((void *)program_bytes.data); + free((void *)params_bytes.data); + return AMDUAT_ASL_STORE_ERR_IO; + } + for (i = 0u; i < record->input_refs_len; ++i) { + if (amduat_asl_derivation_encode_ref(record->input_refs[i], + &input_bytes[i]) != + AMDUAT_ASL_STORE_OK) { + for (size_t j = 0u; j < i; ++j) { + free((void *)input_bytes[j].data); + } + free(input_bytes); + free((void *)program_bytes.data); + free((void *)params_bytes.data); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } + } + + total_len = 0u; + if (!amduat_asl_derivation_add_size(&total_len, 4u + record->sid.len) || + !amduat_asl_derivation_add_size(&total_len, 4u + program_bytes.len) || + !amduat_asl_derivation_add_size(&total_len, 4u)) { + goto cleanup; + } + for (i = 0u; i < record->input_refs_len; ++i) { + if (!amduat_asl_derivation_add_size(&total_len, + 4u + input_bytes[i].len)) { + goto cleanup; + } + } + if (!amduat_asl_derivation_add_size(&total_len, 1u)) { + goto cleanup; + } + if (record->has_params_ref) { + if (!amduat_asl_derivation_add_size(&total_len, 4u + params_bytes.len)) { + goto cleanup; + } + } + if (!amduat_asl_derivation_add_size(&total_len, 1u)) { + goto cleanup; + } + if (record->has_exec_profile) { + if (!amduat_asl_derivation_add_size(&total_len, + 4u + record->exec_profile.len)) { + goto cleanup; + } + } + + if (total_len > UINT32_MAX) { + goto cleanup; + } + + buffer = (uint8_t *)malloc(total_len + 4u); + if (buffer == NULL) { + goto cleanup; + } + + amduat_asl_derivation_store_u32_le(buffer, (uint32_t)total_len); + offset = 4u; + + amduat_asl_derivation_store_u32_le(buffer + offset, + (uint32_t)record->sid.len); + offset += 4u; + memcpy(buffer + offset, record->sid.data, record->sid.len); + offset += record->sid.len; + + amduat_asl_derivation_store_u32_le(buffer + offset, + (uint32_t)program_bytes.len); + offset += 4u; + memcpy(buffer + offset, program_bytes.data, program_bytes.len); + offset += program_bytes.len; + + amduat_asl_derivation_store_u32_le(buffer + offset, input_count); + offset += 4u; + for (i = 0u; i < record->input_refs_len; ++i) { + amduat_asl_derivation_store_u32_le(buffer + offset, + (uint32_t)input_bytes[i].len); + offset += 4u; + memcpy(buffer + offset, input_bytes[i].data, input_bytes[i].len); + offset += input_bytes[i].len; + } + + buffer[offset++] = record->has_params_ref ? 1u : 0u; + if (record->has_params_ref) { + amduat_asl_derivation_store_u32_le(buffer + offset, + (uint32_t)params_bytes.len); + offset += 4u; + memcpy(buffer + offset, params_bytes.data, params_bytes.len); + offset += params_bytes.len; + } + + buffer[offset++] = record->has_exec_profile ? 1u : 0u; + if (record->has_exec_profile) { + amduat_asl_derivation_store_u32_le(buffer + offset, + (uint32_t)record->exec_profile.len); + offset += 4u; + memcpy(buffer + offset, record->exec_profile.data, + record->exec_profile.len); + offset += record->exec_profile.len; + } + + if (offset != total_len + 4u) { + free(buffer); + goto cleanup; + } + + if (fwrite(buffer, 1u, total_len + 4u, fp) != total_len + 4u) { + free(buffer); + goto cleanup; + } + free(buffer); + + for (i = 0u; i < record->input_refs_len; ++i) { + free((void *)input_bytes[i].data); + } + free(input_bytes); + free((void *)program_bytes.data); + free((void *)params_bytes.data); + return AMDUAT_ASL_STORE_OK; + +cleanup: + for (i = 0u; i < record->input_refs_len; ++i) { + if (input_bytes != NULL) { + free((void *)input_bytes[i].data); + } + } + free(input_bytes); + free((void *)program_bytes.data); + free((void *)params_bytes.data); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; +} + +bool amduat_asl_derivation_index_fs_init( + amduat_asl_derivation_index_fs_t *index, + const char *root_path) { + size_t len; + + if (index == NULL || root_path == NULL) { + return false; + } + len = strlen(root_path); + if (len == 0u || len >= AMDUAT_ASL_DERIVATION_INDEX_FS_ROOT_MAX) { + return false; + } + memcpy(index->root_path, root_path, len); + index->root_path[len] = '\0'; + return true; +} + +amduat_asl_store_error_t amduat_asl_derivation_index_fs_add( + amduat_asl_derivation_index_fs_t *index, + amduat_reference_t artifact_ref, + const amduat_asl_derivation_record_t *record) { + amduat_asl_store_error_t err; + char *path = NULL; + char *dir_a = NULL; + char *dir_b = NULL; + char *dir_c = NULL; + char *artifact_dir = NULL; + char *artifact_hex = NULL; + FILE *fp = NULL; + + if (index == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + err = amduat_asl_derivation_validate_record(record); + if (err != AMDUAT_ASL_STORE_OK) { + return err; + } + + if (!amduat_asl_derivation_build_artifact_dir(index->root_path, artifact_ref, + &artifact_dir, &artifact_hex)) { + return AMDUAT_ASL_STORE_ERR_IO; + } + if (!amduat_asl_derivation_join_path(artifact_dir, artifact_hex, &path)) { + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + + dir_a = strdup(path); + if (dir_a == NULL) { + free(path); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + dir_b = strrchr(dir_a, '/'); + if (dir_b == NULL) { + free(path); + free(dir_a); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + *dir_b = '\0'; + + dir_b = strdup(dir_a); + if (dir_b == NULL) { + free(path); + free(dir_a); + return AMDUAT_ASL_STORE_ERR_IO; + } + dir_c = strrchr(dir_b, '/'); + if (dir_c == NULL) { + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + *dir_c = '\0'; + + if (!amduat_asl_derivation_ensure_directory(artifact_dir) || + !amduat_asl_derivation_ensure_directory(dir_b) || + !amduat_asl_derivation_ensure_directory(dir_a)) { + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + + { + char *derivations_path = NULL; + char *by_artifact_path = NULL; + if (!amduat_asl_derivation_build_derivations_path(index->root_path, + &derivations_path)) { + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + if (!amduat_asl_derivation_ensure_directory(derivations_path)) { + free(derivations_path); + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + if (!amduat_asl_derivation_join_path(derivations_path, "by_artifact", + &by_artifact_path)) { + free(derivations_path); + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + if (!amduat_asl_derivation_ensure_directory(by_artifact_path)) { + free(by_artifact_path); + free(derivations_path); + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return AMDUAT_ASL_STORE_ERR_IO; + } + free(by_artifact_path); + free(derivations_path); + } + + err = amduat_asl_derivation_open_records_file(path, &fp); + if (err != AMDUAT_ASL_STORE_OK) { + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return err; + } + + err = amduat_asl_derivation_write_record(fp, record); + if (fclose(fp) != 0) { + err = AMDUAT_ASL_STORE_ERR_IO; + } + + free(path); + free(dir_a); + free(dir_b); + free(artifact_dir); + free(artifact_hex); + return err; +} + +static amduat_asl_store_error_t amduat_asl_derivation_decode_ref( + amduat_asl_derivation_cursor_t *cur, + amduat_reference_t *out_ref) { + uint32_t len; + amduat_octets_t bytes; + + if (!amduat_asl_derivation_read_u32_le(cur, &len)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (cur->len - cur->offset < len) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + bytes = amduat_octets(cur->data + cur->offset, len); + cur->offset += len; + if (!amduat_enc_asl1_core_decode_reference_v1(bytes, out_ref)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_derivation_decode_record( + amduat_asl_derivation_cursor_t *cur, + amduat_asl_derivation_record_t *out_record) { + uint32_t record_len; + size_t start_offset; + uint32_t sid_len; + uint32_t input_count; + uint32_t params_len; + uint32_t exec_len; + size_t i; + + memset(out_record, 0, sizeof(*out_record)); + out_record->input_refs = NULL; + out_record->input_refs_len = 0u; + + if (!amduat_asl_derivation_read_u32_le(cur, &record_len)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (cur->len - cur->offset < record_len) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + start_offset = cur->offset; + if (!amduat_asl_derivation_read_u32_le(cur, &sid_len)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (sid_len == 0u || cur->len - cur->offset < sid_len) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + out_record->sid = amduat_octets(NULL, 0u); + if (sid_len != 0u) { + uint8_t *sid_bytes = (uint8_t *)malloc(sid_len); + if (sid_bytes == NULL) { + return AMDUAT_ASL_STORE_ERR_IO; + } + memcpy(sid_bytes, cur->data + cur->offset, sid_len); + out_record->sid = amduat_octets(sid_bytes, sid_len); + } + cur->offset += sid_len; + + if (amduat_asl_derivation_decode_ref(cur, &out_record->program_ref) != + AMDUAT_ASL_STORE_OK) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (!amduat_asl_derivation_read_u32_le(cur, &input_count)) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (input_count != 0u) { + out_record->input_refs = (amduat_reference_t *)calloc( + input_count, sizeof(*out_record->input_refs)); + if (out_record->input_refs == NULL) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_IO; + } + out_record->input_refs_len = input_count; + } + for (i = 0u; i < input_count; ++i) { + if (amduat_asl_derivation_decode_ref(cur, + &out_record->input_refs[i]) != + AMDUAT_ASL_STORE_OK) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } + + if (cur->len - cur->offset < 1u) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + out_record->has_params_ref = cur->data[cur->offset++] != 0u; + if (out_record->has_params_ref) { + params_len = 0u; + if (!amduat_asl_derivation_read_u32_le(cur, ¶ms_len)) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (cur->len - cur->offset < params_len) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (!amduat_enc_asl1_core_decode_reference_v1( + amduat_octets(cur->data + cur->offset, params_len), + &out_record->params_ref)) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + cur->offset += params_len; + } + + if (cur->len - cur->offset < 1u) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + out_record->has_exec_profile = cur->data[cur->offset++] != 0u; + if (out_record->has_exec_profile) { + exec_len = 0u; + if (!amduat_asl_derivation_read_u32_le(cur, &exec_len)) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (cur->len - cur->offset < exec_len) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (exec_len != 0u) { + uint8_t *exec_bytes = (uint8_t *)malloc(exec_len); + if (exec_bytes == NULL) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_IO; + } + memcpy(exec_bytes, cur->data + cur->offset, exec_len); + out_record->exec_profile = amduat_octets(exec_bytes, exec_len); + } + cur->offset += exec_len; + } else { + out_record->exec_profile = amduat_octets(NULL, 0u); + } + + if (cur->offset - start_offset != record_len) { + amduat_asl_derivation_record_free(out_record); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + return AMDUAT_ASL_STORE_OK; +} + +amduat_asl_store_error_t amduat_asl_derivation_index_fs_list( + amduat_asl_derivation_index_fs_t *index, + amduat_reference_t artifact_ref, + amduat_asl_derivation_record_t **out_records, + size_t *out_count) { + char *path = NULL; + FILE *fp = NULL; + uint8_t *buffer = NULL; + size_t file_size = 0u; + size_t read_len; + amduat_asl_derivation_cursor_t cur; + amduat_asl_derivation_record_t *records = NULL; + size_t records_len = 0u; + size_t records_cap = 0u; + amduat_asl_store_error_t err; + + if (out_records == NULL || out_count == NULL || index == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + *out_records = NULL; + *out_count = 0u; + + if (!amduat_asl_derivation_build_record_path(index->root_path, artifact_ref, + &path)) { + return AMDUAT_ASL_STORE_ERR_IO; + } + + fp = fopen(path, "rb"); + if (fp == NULL) { + free(path); + return errno == ENOENT ? AMDUAT_ASL_STORE_ERR_NOT_FOUND + : AMDUAT_ASL_STORE_ERR_IO; + } + + if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + free(path); + return AMDUAT_ASL_STORE_ERR_IO; + } + { + long size = ftell(fp); + if (size < 0) { + fclose(fp); + free(path); + return AMDUAT_ASL_STORE_ERR_IO; + } + file_size = (size_t)size; + } + if (fseek(fp, 0, SEEK_SET) != 0) { + fclose(fp); + free(path); + return AMDUAT_ASL_STORE_ERR_IO; + } + + buffer = (uint8_t *)malloc(file_size); + if (buffer == NULL) { + fclose(fp); + free(path); + return AMDUAT_ASL_STORE_ERR_IO; + } + read_len = fread(buffer, 1u, file_size, fp); + fclose(fp); + free(path); + if (read_len != file_size) { + free(buffer); + return AMDUAT_ASL_STORE_ERR_IO; + } + + if (file_size < AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN || + memcmp(buffer, k_amduat_asl_derivation_index_magic, + AMDUAT_ASL_DERIVATION_INDEX_MAGIC_LEN) != 0) { + free(buffer); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + { + uint32_t version = (uint32_t)buffer[8] | ((uint32_t)buffer[9] << 8) | + ((uint32_t)buffer[10] << 16) | + ((uint32_t)buffer[11] << 24); + if (version != AMDUAT_ASL_DERIVATION_INDEX_VERSION) { + free(buffer); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } + cur.data = buffer; + cur.len = file_size; + cur.offset = AMDUAT_ASL_DERIVATION_INDEX_HEADER_LEN; + + while (cur.offset < cur.len) { + amduat_asl_derivation_record_t record; + if (records_len == records_cap) { + size_t next_cap = records_cap == 0u ? 4u : records_cap * 2u; + amduat_asl_derivation_record_t *next = + (amduat_asl_derivation_record_t *)realloc( + records, next_cap * sizeof(*records)); + if (next == NULL) { + err = AMDUAT_ASL_STORE_ERR_IO; + goto cleanup; + } + records = next; + records_cap = next_cap; + } + err = amduat_asl_derivation_decode_record(&cur, &record); + if (err != AMDUAT_ASL_STORE_OK) { + goto cleanup; + } + records[records_len++] = record; + } + + free(buffer); + *out_records = records; + *out_count = records_len; + return AMDUAT_ASL_STORE_OK; + +cleanup: + if (records != NULL) { + amduat_asl_derivation_records_free(records, records_len); + } + free(records); + free(buffer); + return err; +} + +void amduat_asl_derivation_record_free( + amduat_asl_derivation_record_t *record) { + size_t i; + + if (record == NULL) { + return; + } + amduat_octets_free(&record->sid); + amduat_reference_free(&record->program_ref); + if (record->input_refs != NULL) { + for (i = 0u; i < record->input_refs_len; ++i) { + amduat_reference_free(&record->input_refs[i]); + } + } + free(record->input_refs); + record->input_refs = NULL; + record->input_refs_len = 0u; + if (record->has_params_ref) { + amduat_reference_free(&record->params_ref); + } + record->has_params_ref = false; + if (record->has_exec_profile) { + amduat_octets_free(&record->exec_profile); + } + record->has_exec_profile = false; +} + +void amduat_asl_derivation_records_free( + amduat_asl_derivation_record_t *records, + size_t count) { + size_t i; + + if (records == NULL) { + return; + } + for (i = 0u; i < count; ++i) { + amduat_asl_derivation_record_free(&records[i]); + } +} diff --git a/src/tools/amduat_asl_cli.c b/src/tools/amduat_asl_cli.c index 4f059ee..f098d7a 100644 --- a/src/tools/amduat_asl_cli.c +++ b/src/tools/amduat_asl_cli.c @@ -5,6 +5,7 @@ #include "amduat/asl/parse.h" #include "amduat/asl/ref_io.h" #include "amduat/asl/ref_text.h" +#include "amduat/asl/asl_derivation_index_fs.h" #include "amduat/asl/asl_store_index_fs.h" #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" @@ -14,6 +15,7 @@ #include "amduat/format/parse.h" #include "amduat/format/ref.h" #include "amduat/hash/asl1.h" +#include "amduat/util/hex.h" #include #include #include @@ -96,6 +98,13 @@ static void amduat_asl_cli_print_usage(FILE *stream) { " [--output-format raw|artifact]\n" " [--expect-type-tag TAG] [--print-type-tag]\n" " [--quiet]\n" + " amduat-asl derivations add --artifact-ref REF --sid SID\n" + " --program-ref REF [--input-ref REF...]\n" + " [--params-ref REF] [--exec-profile PATH|-]\n" + " [--root PATH] [--ref-format hex|bytes]\n" + " [--sid-format hex|bytes]\n" + " amduat-asl derivations list --artifact-ref REF [--root PATH]\n" + " [--ref-format hex|bytes]\n" " amduat-asl log inspect [--root PATH]\n" " amduat-asl index init [--root PATH] [--store-id ID]\n" " [--profile PROFILE_ID|name]\n" @@ -113,7 +122,8 @@ static void amduat_asl_cli_print_usage(FILE *stream) { " --ref-format hex\n" "\n" "notes:\n" - " --ref-format bytes expects --ref as a path or -\n", + " --ref-format bytes expects refs as paths or -\n" + " derivations list outputs refs/sid in hex\n", AMDUAT_ASL_CLI_DEFAULT_ROOT); } @@ -126,6 +136,71 @@ static void amduat_asl_cli_free_reference(amduat_reference_t *ref) { ref->digest.len = 0u; } +static bool amduat_asl_cli_decode_ref_arg(amduat_format_ref_format_t fmt, + const char *arg, + amduat_reference_t *out_ref) { + uint8_t *ref_bytes; + size_t ref_len; + + if (arg == NULL || out_ref == NULL) { + return false; + } + memset(out_ref, 0, sizeof(*out_ref)); + + if (fmt == AMDUAT_FORMAT_REF_HEX) { + return amduat_asl_ref_decode_format( + fmt, amduat_octets(arg, strlen(arg)), out_ref); + } + + ref_bytes = NULL; + ref_len = 0u; + if (!amduat_asl_read_path(arg, &ref_bytes, &ref_len)) { + return false; + } + if (!amduat_asl_ref_decode_format( + fmt, amduat_octets(ref_bytes, ref_len), out_ref)) { + free(ref_bytes); + return false; + } + free(ref_bytes); + return true; +} + +static bool amduat_asl_cli_read_sid(const char *arg, + bool sid_is_hex, + amduat_octets_t *out_sid) { + uint8_t *bytes; + size_t len; + const char *text; + + if (arg == NULL || out_sid == NULL) { + return false; + } + *out_sid = amduat_octets(NULL, 0u); + + if (!sid_is_hex) { + bytes = NULL; + len = 0u; + if (!amduat_asl_read_path(arg, &bytes, &len)) { + return false; + } + *out_sid = amduat_octets(bytes, len); + return true; + } + + text = arg; + if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) { + text += 2; + } + bytes = NULL; + len = 0u; + if (!amduat_hex_decode_alloc(text, &bytes, &len)) { + return false; + } + *out_sid = amduat_octets(bytes, len); + return true; +} + static void amduat_asl_cli_print_store_meta( const amduat_asl_store_fs_config_t *cfg) { if (cfg == NULL) { @@ -1212,6 +1287,374 @@ static int amduat_asl_cli_cmd_get(int argc, char **argv) { return exit_code; } +static int amduat_asl_cli_cmd_derivations_add(int argc, char **argv) { + const char *root = AMDUAT_ASL_CLI_DEFAULT_ROOT; + const char *artifact_ref_arg = NULL; + const char *sid_arg = NULL; + const char *program_ref_arg = NULL; + const char *params_ref_arg = NULL; + const char *exec_profile_path = NULL; + amduat_format_ref_format_t ref_format = AMDUAT_FORMAT_REF_HEX; + bool sid_is_hex = true; + const char **input_args = NULL; + size_t input_len = 0u; + int exit_code = AMDUAT_ASL_CLI_EXIT_OK; + int i; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--artifact-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --artifact-ref requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + artifact_ref_arg = argv[++i]; + } else if (strcmp(argv[i], "--sid") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --sid requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + sid_arg = argv[++i]; + } else if (strcmp(argv[i], "--program-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program-ref requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + program_ref_arg = argv[++i]; + } else if (strcmp(argv[i], "--input-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-ref requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + const char *val = argv[++i]; + const char **next = + (const char **)realloc(input_args, + (input_len + 1u) * sizeof(*input_args)); + if (next == NULL) { + free(input_args); + fprintf(stderr, "error: out of memory\n"); + return AMDUAT_ASL_CLI_EXIT_IO; + } + input_args = next; + input_args[input_len++] = val; + } else if (strcmp(argv[i], "--params-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --params-ref requires a value\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + params_ref_arg = argv[++i]; + } else if (strcmp(argv[i], "--exec-profile") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --exec-profile requires a path or -\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + exec_profile_path = argv[++i]; + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + if (!amduat_format_ref_parse(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--sid-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --sid-format requires a value\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + const char *fmt = argv[++i]; + if (strcmp(fmt, "hex") == 0) { + sid_is_hex = true; + } else if (strcmp(fmt, "bytes") == 0) { + sid_is_hex = false; + } else { + fprintf(stderr, "error: invalid sid-format\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + free(input_args); + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + if (artifact_ref_arg == NULL || sid_arg == NULL || program_ref_arg == NULL) { + fprintf(stderr, + "error: --artifact-ref, --sid, and --program-ref are required\n"); + free(input_args); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + + { + amduat_asl_derivation_index_fs_t index; + amduat_asl_derivation_record_t record; + amduat_reference_t artifact_ref; + amduat_reference_t program_ref; + amduat_reference_t params_ref; + amduat_reference_t *inputs = NULL; + uint8_t *exec_bytes = NULL; + size_t exec_len = 0u; + size_t j; + + if (!amduat_asl_derivation_index_fs_init(&index, root)) { + fprintf(stderr, "error: invalid root path\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CONFIG; + goto cleanup_inputs; + } + + if (!amduat_asl_cli_decode_ref_arg(ref_format, artifact_ref_arg, + &artifact_ref)) { + fprintf(stderr, "error: invalid artifact ref\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + goto cleanup_inputs; + } + if (!amduat_asl_cli_decode_ref_arg(ref_format, program_ref_arg, + &program_ref)) { + fprintf(stderr, "error: invalid program ref\n"); + amduat_asl_cli_free_reference(&artifact_ref); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + goto cleanup_inputs; + } + + if (input_len != 0u) { + inputs = (amduat_reference_t *)calloc(input_len, sizeof(*inputs)); + if (inputs == NULL) { + fprintf(stderr, "error: out of memory\n"); + amduat_asl_cli_free_reference(&artifact_ref); + amduat_asl_cli_free_reference(&program_ref); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + goto cleanup_inputs; + } + for (j = 0u; j < input_len; ++j) { + if (!amduat_asl_cli_decode_ref_arg(ref_format, input_args[j], + &inputs[j])) { + fprintf(stderr, "error: invalid input ref\n"); + amduat_asl_cli_free_reference(&artifact_ref); + amduat_asl_cli_free_reference(&program_ref); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + goto cleanup_decoded; + } + } + } + + memset(&record, 0, sizeof(record)); + if (!amduat_asl_cli_read_sid(sid_arg, sid_is_hex, &record.sid)) { + fprintf(stderr, "error: invalid sid\n"); + amduat_asl_cli_free_reference(&artifact_ref); + amduat_asl_cli_free_reference(&program_ref); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + goto cleanup_decoded; + } + record.program_ref = program_ref; + record.input_refs = inputs; + record.input_refs_len = input_len; + + record.has_params_ref = false; + if (params_ref_arg != NULL) { + if (!amduat_asl_cli_decode_ref_arg(ref_format, params_ref_arg, + ¶ms_ref)) { + fprintf(stderr, "error: invalid params ref\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; + goto cleanup_record; + } + record.has_params_ref = true; + record.params_ref = params_ref; + } + + record.has_exec_profile = false; + if (exec_profile_path != NULL) { + if (!amduat_asl_read_path(exec_profile_path, &exec_bytes, &exec_len)) { + fprintf(stderr, "error: failed to read exec profile\n"); + exit_code = AMDUAT_ASL_CLI_EXIT_IO; + goto cleanup_record; + } + record.has_exec_profile = true; + record.exec_profile = amduat_octets(exec_bytes, exec_len); + } + + { + amduat_asl_store_error_t err = + amduat_asl_derivation_index_fs_add(&index, artifact_ref, &record); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: derivation add failed: %s\n", + amduat_asl_cli_store_error_str(err)); + exit_code = amduat_asl_cli_map_store_error(err); + } + } + +cleanup_record: + amduat_octets_free(&record.sid); + if (record.has_params_ref) { + amduat_asl_cli_free_reference(&record.params_ref); + } + if (record.has_exec_profile) { + free(exec_bytes); + } + for (j = 0u; j < input_len; ++j) { + amduat_asl_cli_free_reference(&inputs[j]); + } + amduat_asl_cli_free_reference(&program_ref); + amduat_asl_cli_free_reference(&artifact_ref); +cleanup_decoded: + free(inputs); +cleanup_inputs: + free(input_args); + } + + return exit_code; +} + +static int amduat_asl_cli_cmd_derivations_list(int argc, char **argv) { + const char *root = AMDUAT_ASL_CLI_DEFAULT_ROOT; + const char *artifact_ref_arg = NULL; + amduat_format_ref_format_t ref_format = AMDUAT_FORMAT_REF_HEX; + int i; + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + root = argv[++i]; + } else if (strcmp(argv[i], "--artifact-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --artifact-ref requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + artifact_ref_arg = argv[++i]; + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + if (!amduat_format_ref_parse(argv[++i], &ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_asl_cli_print_usage(stdout); + return AMDUAT_ASL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + } + + if (artifact_ref_arg == NULL) { + fprintf(stderr, "error: --artifact-ref is required\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + + { + amduat_asl_derivation_index_fs_t index; + amduat_reference_t artifact_ref; + amduat_asl_derivation_record_t *records = NULL; + size_t records_len = 0u; + size_t j; + amduat_asl_store_error_t err; + + if (!amduat_asl_derivation_index_fs_init(&index, root)) { + fprintf(stderr, "error: invalid root path\n"); + return AMDUAT_ASL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_cli_decode_ref_arg(ref_format, artifact_ref_arg, + &artifact_ref)) { + fprintf(stderr, "error: invalid artifact ref\n"); + return AMDUAT_ASL_CLI_EXIT_CODEC; + } + + err = amduat_asl_derivation_index_fs_list(&index, + artifact_ref, + &records, + &records_len); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: derivation list failed: %s\n", + amduat_asl_cli_store_error_str(err)); + amduat_asl_cli_free_reference(&artifact_ref); + return amduat_asl_cli_map_store_error(err); + } + + for (j = 0u; j < records_len; ++j) { + char *sid_hex = NULL; + char *prog_hex = NULL; + char *params_hex = NULL; + char *exec_hex = NULL; + size_t k; + + (void)amduat_hex_encode_alloc(records[j].sid.data, + records[j].sid.len, + &sid_hex); + (void)amduat_asl_ref_encode_hex(records[j].program_ref, &prog_hex); + if (records[j].has_params_ref) { + (void)amduat_asl_ref_encode_hex(records[j].params_ref, ¶ms_hex); + } + if (records[j].has_exec_profile) { + (void)amduat_hex_encode_alloc(records[j].exec_profile.data, + records[j].exec_profile.len, + &exec_hex); + } + + printf("sid=%s program=%s inputs=", + sid_hex == NULL ? "(null)" : sid_hex, + prog_hex == NULL ? "(null)" : prog_hex); + for (k = 0u; k < records[j].input_refs_len; ++k) { + char *input_hex = NULL; + (void)amduat_asl_ref_encode_hex(records[j].input_refs[k], &input_hex); + printf("%s%s", + k == 0u ? "" : ",", + input_hex == NULL ? "(null)" : input_hex); + free(input_hex); + } + printf(" params=%s exec_profile=%s\n", + params_hex == NULL ? "-" : params_hex, + exec_hex == NULL ? "-" : exec_hex); + + free(exec_hex); + free(params_hex); + free(prog_hex); + free(sid_hex); + } + + amduat_asl_derivation_records_free(records, records_len); + free(records); + amduat_asl_cli_free_reference(&artifact_ref); + } + + return AMDUAT_ASL_CLI_EXIT_OK; +} + +static int amduat_asl_cli_cmd_derivations(int argc, char **argv) { + if (argc < 1) { + fprintf(stderr, "error: derivations command requires a subcommand\n"); + return AMDUAT_ASL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "add") == 0) { + return amduat_asl_cli_cmd_derivations_add(argc - 1, argv + 1); + } + if (strcmp(argv[0], "list") == 0) { + return amduat_asl_cli_cmd_derivations_list(argc - 1, argv + 1); + } + fprintf(stderr, "error: unknown derivations command: %s\n", argv[0]); + return AMDUAT_ASL_CLI_EXIT_USAGE; +} + static int amduat_asl_cli_cmd_log(int argc, char **argv) { const char *root; uint8_t *log_bytes; @@ -1706,6 +2149,9 @@ int main(int argc, char **argv) { if (strcmp(argv[1], "get") == 0) { return amduat_asl_cli_cmd_get(argc - 2, argv + 2); } + if (strcmp(argv[1], "derivations") == 0) { + return amduat_asl_cli_cmd_derivations(argc - 2, argv + 2); + } if (strcmp(argv[1], "log") == 0) { return amduat_asl_cli_cmd_log(argc - 2, argv + 2); } diff --git a/tests/asl/test_asl_derivation_index_fs.c b/tests/asl/test_asl_derivation_index_fs.c new file mode 100644 index 0000000..6ff073d --- /dev/null +++ b/tests/asl/test_asl_derivation_index_fs.c @@ -0,0 +1,221 @@ +#include "amduat/asl/asl_derivation_index_fs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *make_temp_root(void) { + char *templ; + const char template_prefix[] = + "/tmp/amduat_test_asl_derivation_index_fs_XXXXXX"; + + templ = (char *)malloc(sizeof(template_prefix)); + if (templ == NULL) { + return NULL; + } + memcpy(templ, template_prefix, sizeof(template_prefix)); + if (mkdtemp(templ) == NULL) { + free(templ); + return NULL; + } + return templ; +} + +static bool remove_tree(const char *path) { + DIR *dir; + struct dirent *entry; + struct stat st; + + if (path == NULL) { + return false; + } + if (lstat(path, &st) != 0) { + return errno == ENOENT; + } + if (!S_ISDIR(st.st_mode)) { + return unlink(path) == 0; + } + + dir = opendir(path); + if (dir == NULL) { + return false; + } + while ((entry = readdir(dir)) != NULL) { + char child[2048]; + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) { + continue; + } + snprintf(child, sizeof(child), "%s/%s", path, entry->d_name); + if (!remove_tree(child)) { + closedir(dir); + return false; + } + } + closedir(dir); + return rmdir(path) == 0; +} + +static amduat_reference_t make_ref(uint8_t seed, size_t len) { + uint8_t *bytes = (uint8_t *)malloc(len); + if (bytes == NULL) { + return amduat_reference(0u, amduat_octets(NULL, 0u)); + } + for (size_t i = 0; i < len; ++i) { + bytes[i] = (uint8_t)(seed + i); + } + return amduat_reference(0x0001u, amduat_octets(bytes, len)); +} + +static int test_round_trip(void) { + amduat_asl_derivation_index_fs_t index; + amduat_asl_derivation_record_t record; + amduat_asl_derivation_record_t record2; + amduat_asl_derivation_record_t *records = NULL; + size_t records_len = 0u; + amduat_reference_t artifact_ref; + amduat_reference_t inputs[2]; + uint8_t sid_bytes[32]; + uint8_t exec_bytes[4] = {0x01, 0x02, 0x03, 0x04}; + char *root; + int exit_code = 1; + + root = make_temp_root(); + if (root == NULL) { + fprintf(stderr, "temp root failed\n"); + return 1; + } + + if (!amduat_asl_derivation_index_fs_init(&index, root)) { + fprintf(stderr, "index init failed\n"); + goto cleanup; + } + + artifact_ref = make_ref(0x10, 4); + record.program_ref = make_ref(0x20, 4); + inputs[0] = make_ref(0x30, 4); + inputs[1] = make_ref(0x40, 4); + memset(sid_bytes, 0xa5, sizeof(sid_bytes)); + record.sid = amduat_octets(sid_bytes, sizeof(sid_bytes)); + record.input_refs = inputs; + record.input_refs_len = 2u; + record.has_params_ref = true; + record.params_ref = make_ref(0x50, 4); + record.has_exec_profile = true; + record.exec_profile = amduat_octets(exec_bytes, sizeof(exec_bytes)); + + record2 = record; + record2.sid = amduat_octets(sid_bytes, sizeof(sid_bytes)); + record2.has_params_ref = false; + record2.has_exec_profile = false; + + if (amduat_asl_derivation_index_fs_add(&index, artifact_ref, &record) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "add record failed\n"); + goto cleanup_records; + } + if (amduat_asl_derivation_index_fs_add(&index, artifact_ref, &record2) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "add record2 failed\n"); + goto cleanup_records; + } + + if (amduat_asl_derivation_index_fs_list(&index, + artifact_ref, + &records, + &records_len) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "list failed\n"); + goto cleanup_records; + } + if (records_len != 2u) { + fprintf(stderr, "record count mismatch\n"); + goto cleanup_records_list; + } + if (records[0].input_refs_len != 2u || + records[0].has_params_ref != true || + records[0].has_exec_profile != true) { + fprintf(stderr, "record fields mismatch\n"); + goto cleanup_records_list; + } + if (records[1].has_params_ref != false || + records[1].has_exec_profile != false) { + fprintf(stderr, "record2 flags mismatch\n"); + goto cleanup_records_list; + } + + exit_code = 0; + +cleanup_records_list: + amduat_asl_derivation_records_free(records, records_len); + free(records); +cleanup_records: + amduat_reference_free(&artifact_ref); + amduat_reference_free(&record.program_ref); + amduat_reference_free(&record.params_ref); + amduat_reference_free(&inputs[0]); + amduat_reference_free(&inputs[1]); +cleanup: + if (!remove_tree(root)) { + fprintf(stderr, "cleanup failed\n"); + exit_code = 1; + } + free(root); + return exit_code; +} + +static int test_not_found(void) { + amduat_asl_derivation_index_fs_t index; + amduat_reference_t artifact_ref; + amduat_asl_derivation_record_t *records = NULL; + size_t records_len = 0u; + char *root; + int exit_code = 1; + + root = make_temp_root(); + if (root == NULL) { + fprintf(stderr, "temp root failed\n"); + return 1; + } + + if (!amduat_asl_derivation_index_fs_init(&index, root)) { + fprintf(stderr, "index init failed\n"); + goto cleanup; + } + + artifact_ref = make_ref(0x70, 4); + if (amduat_asl_derivation_index_fs_list(&index, + artifact_ref, + &records, + &records_len) != + AMDUAT_ASL_STORE_ERR_NOT_FOUND) { + fprintf(stderr, "expected not found\n"); + amduat_reference_free(&artifact_ref); + goto cleanup; + } + amduat_reference_free(&artifact_ref); + + exit_code = 0; + +cleanup: + if (!remove_tree(root)) { + fprintf(stderr, "cleanup failed\n"); + exit_code = 1; + } + free(root); + return exit_code; +} + +int main(void) { + if (test_round_trip() != 0) { + return 1; + } + return test_not_found(); +}