#include "amduat/asl/asl_store_index_fs.h" #include "amduat/asl/index_bloom.h" #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" #include "amduat/enc/asl_core_index.h" #include "amduat/hash/asl1.h" #include #include #include #include #include #include #include #include #include #include #include static bool join_path(const char *base, const char *segment, char **out_path) { size_t base_len; size_t seg_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); seg_len = strlen(segment); needs_sep = base[base_len - 1u] != '/'; total_len = base_len + (needs_sep ? 1u : 0u) + seg_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, seg_len); offset += seg_len; buffer[offset] = '\0'; *out_path = buffer; return true; } static bool remove_tree(const char *path) { struct stat st; DIR *dir; struct dirent *entry; 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 = NULL; if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } if (!join_path(path, entry->d_name, &child)) { closedir(dir); return false; } if (!remove_tree(child)) { free(child); closedir(dir); return false; } free(child); } if (closedir(dir) != 0) { return false; } return rmdir(path) == 0; } static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) { FILE *fp; long size; uint8_t *buffer; size_t read_len; if (path == NULL || out_bytes == NULL || out_len == NULL) { return false; } *out_bytes = NULL; *out_len = 0u; fp = fopen(path, "rb"); if (fp == NULL) { return false; } if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return false; } size = ftell(fp); if (size < 0) { fclose(fp); return false; } if (fseek(fp, 0, SEEK_SET) != 0) { fclose(fp); return false; } buffer = (uint8_t *)malloc((size_t)size); if (buffer == NULL) { fclose(fp); return false; } read_len = fread(buffer, 1u, (size_t)size, fp); fclose(fp); if (read_len != (size_t)size) { free(buffer); return false; } *out_bytes = buffer; *out_len = (size_t)size; return true; } static bool build_segment_path(const char *root, uint64_t segment_id, char **out_path) { int needed; char *buffer; if (root == NULL || out_path == NULL) { return false; } needed = snprintf(NULL, 0, "%s/index/segments/segment-%016" PRIx64 ".asl", root, segment_id); if (needed <= 0) { return false; } buffer = (char *)malloc((size_t)needed + 1u); if (buffer == NULL) { return false; } snprintf(buffer, (size_t)needed + 1u, "%s/index/segments/segment-%016" PRIx64 ".asl", root, segment_id); *out_path = buffer; return true; } static char *make_temp_root(void) { char *templ; const char template_prefix[] = "/tmp/amduat_test_asl_store_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 int test_round_trip(void) { amduat_asl_store_config_t config; amduat_asl_store_index_fs_t fs; amduat_asl_store_t store; amduat_asl_store_error_t err; amduat_asl_index_state_t state; amduat_asl_index_state_t current_state; amduat_artifact_t artifact; amduat_artifact_t loaded; amduat_reference_t ref; uint8_t payload[6]; char *root; int exit_code = 1; root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); return 1; } config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; if (!amduat_asl_store_index_fs_init(&fs, config, root)) { fprintf(stderr, "index fs init failed\n"); goto cleanup; } amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs); memset(payload, 0x5a, sizeof(payload)); artifact = amduat_artifact(amduat_octets(payload, sizeof(payload))); ref = amduat_reference(0u, amduat_octets(NULL, 0u)); err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state); if (err != AMDUAT_ASL_STORE_OK) { fprintf(stderr, "put_indexed failed: %d\n", err); goto cleanup; } if (!amduat_asl_index_current_state(&store, ¤t_state)) { fprintf(stderr, "current_state failed\n"); goto cleanup; } if (current_state.log_position != state.log_position) { fprintf(stderr, "log position mismatch\n"); goto cleanup; } loaded = amduat_artifact(amduat_octets(NULL, 0u)); err = amduat_asl_store_get_indexed(&store, ref, state, &loaded); if (err != AMDUAT_ASL_STORE_OK) { fprintf(stderr, "get_indexed failed: %d\n", err); goto cleanup; } if (!amduat_artifact_eq(artifact, loaded)) { fprintf(stderr, "artifact mismatch\n"); amduat_artifact_free(&loaded); goto cleanup; } amduat_artifact_free(&loaded); if (state.log_position > 0u) { amduat_asl_index_state_t cutoff = state; cutoff.log_position = state.log_position - 1u; loaded = amduat_artifact(amduat_octets(NULL, 0u)); err = amduat_asl_store_get_indexed(&store, ref, cutoff, &loaded); if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) { fprintf(stderr, "cutoff lookup expected not found: %d\n", err); amduat_artifact_free(&loaded); goto cleanup; } } { char *segment_path = NULL; uint8_t *segment_bytes = NULL; size_t segment_len = 0u; amduat_asl_core_index_segment_t segment; bool bloom_nonzero = false; size_t i; memset(&segment, 0, sizeof(segment)); if (!build_segment_path(root, 1u, &segment_path)) { fprintf(stderr, "segment path build failed\n"); goto cleanup; } if (!read_file(segment_path, &segment_bytes, &segment_len)) { fprintf(stderr, "segment read failed\n"); free(segment_path); goto cleanup; } free(segment_path); if (!amduat_enc_asl_core_index_decode_v1( amduat_octets(segment_bytes, segment_len), &segment)) { fprintf(stderr, "segment decode failed\n"); free(segment_bytes); goto cleanup; } free(segment_bytes); if (segment.bloom.len != AMDUAT_ASL_INDEX_BLOOM_BYTES) { fprintf(stderr, "segment bloom size mismatch\n"); amduat_enc_asl_core_index_free(&segment); goto cleanup; } if (!amduat_asl_index_bloom_maybe_contains(segment.bloom, ref.hash_id, ref.digest)) { fprintf(stderr, "segment bloom missing digest\n"); amduat_enc_asl_core_index_free(&segment); goto cleanup; } for (i = 0u; i < segment.bloom.len; ++i) { if (segment.bloom.data[i] != 0u) { bloom_nonzero = true; break; } } if (!bloom_nonzero) { fprintf(stderr, "segment bloom was empty\n"); amduat_enc_asl_core_index_free(&segment); goto cleanup; } amduat_enc_asl_core_index_free(&segment); } exit_code = 0; cleanup: amduat_reference_free(&ref); if (!remove_tree(root)) { fprintf(stderr, "cleanup failed\n"); exit_code = 1; } free(root); return exit_code; } int main(void) { return test_round_trip(); }