amduat-api/src/amduatd_concepts.c
2026-01-24 15:49:47 +01:00

4249 lines
137 KiB
C

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define _POSIX_C_SOURCE 200809L
#include "amduatd_concepts.h"
#include "amduatd_http.h"
#include "amduatd_caps.h"
#include "amduat/asl/artifact_io.h"
#include "amduat/asl/collection_view.h"
#include "amduat/asl/log_store.h"
#include "amduat/asl/none.h"
#include "amduat/asl/record.h"
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/ref_derive.h"
#include "amduat/asl/ref_text.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/enc/fer1_receipt.h"
#include "amduat/enc/pel1_result.h"
#include "amduat/enc/pel_program_dag.h"
#include "amduat/enc/tgk1_edge.h"
#include "amduat/fer/receipt.h"
#include "amduat/format/pel.h"
#include "amduat/pel/program_dag.h"
#include "amduat/pel/program_dag_desc.h"
#include "amduat/pel/run.h"
#include "amduat/util/log.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct amduatd_strbuf {
char *data;
size_t len;
size_t cap;
} amduatd_strbuf_t;
static const char *const AMDUATD_EDGES_FILE = ".amduatd.edges";
static const char *const AMDUATD_EDGE_COLLECTION = "daemon/edges";
static const uint32_t AMDUATD_EDGE_VIEW_BATCH = 1024u;
static const uint32_t AMDUATD_EDGE_REFRESH_BATCH = 512u;
static const uint16_t AMDUATD_EDGE_COLLECTION_KIND = 1u;
static void amduatd_edge_entry_free(amduatd_edge_entry_t *entry) {
if (entry == NULL) {
return;
}
amduat_reference_free(&entry->record_ref);
amduat_reference_free(&entry->src_ref);
amduat_reference_free(&entry->dst_ref);
free(entry->rel);
entry->rel = NULL;
}
static void amduatd_edge_list_clear(amduatd_edge_list_t *list) {
if (list == NULL) {
return;
}
for (size_t i = 0; i < list->len; ++i) {
amduatd_edge_entry_free(&list->items[i]);
}
free(list->items);
list->items = NULL;
list->len = 0;
list->cap = 0;
}
static bool amduatd_edge_list_push(amduatd_edge_list_t *list,
const amduatd_edge_entry_t *entry) {
amduatd_edge_entry_t cloned;
amduatd_edge_entry_t *next;
if (list == NULL || entry == NULL) {
return false;
}
memset(&cloned, 0, sizeof(cloned));
if (!amduat_reference_clone(entry->record_ref, &cloned.record_ref) ||
!amduat_reference_clone(entry->src_ref, &cloned.src_ref) ||
!amduat_reference_clone(entry->dst_ref, &cloned.dst_ref)) {
amduatd_edge_entry_free(&cloned);
return false;
}
if (entry->rel != NULL) {
cloned.rel = strdup(entry->rel);
if (cloned.rel == NULL) {
amduatd_edge_entry_free(&cloned);
return false;
}
}
if (list->len == list->cap) {
size_t next_cap = list->cap != 0 ? list->cap * 2u : 64u;
next = (amduatd_edge_entry_t *)realloc(list->items,
next_cap * sizeof(*list->items));
if (next == NULL) {
amduatd_edge_entry_free(&cloned);
return false;
}
list->items = next;
list->cap = next_cap;
}
list->items[list->len++] = cloned;
return true;
}
void amduatd_concepts_free(amduatd_concepts_t *c) {
if (c == NULL) {
return;
}
amduat_reference_free(&c->rel_aliases_ref);
amduat_reference_free(&c->rel_materializes_ref);
amduat_reference_free(&c->rel_represents_ref);
amduat_reference_free(&c->rel_requires_key_ref);
amduat_reference_free(&c->rel_within_domain_ref);
amduat_reference_free(&c->rel_computed_by_ref);
amduat_reference_free(&c->rel_has_provenance_ref);
amduatd_edge_list_clear(&c->edges);
free(c->edge_collection_name);
c->edge_collection_name = NULL;
}
enum {
AMDUATD_EDGE_MAGIC_LEN = 8,
AMDUATD_EDGE_VERSION = 1
};
static const uint8_t k_amduatd_edge_magic[AMDUATD_EDGE_MAGIC_LEN] = {
'A', 'S', 'L', 'E', 'D', 'G', '1', '\0'
};
static const char *const AMDUATD_EDGE_SCHEMA = "tgk/edge";
static const char *const AMDUATD_EDGE_INDEX_SCHEMA = "tgk/edge_index_state";
enum {
AMDUATD_EDGE_INDEX_MAGIC_LEN = 8,
AMDUATD_EDGE_INDEX_VERSION = 1
};
static const uint8_t k_amduatd_edge_index_magic[AMDUATD_EDGE_INDEX_MAGIC_LEN] = {
'A', 'S', 'L', 'E', 'I', 'X', '1', '\0'
};
enum {
AMDUATD_EDGE_GRAPH_MAGIC_LEN = 8,
AMDUATD_EDGE_GRAPH_VERSION = 1
};
static const uint8_t k_amduatd_edge_graph_magic[AMDUATD_EDGE_GRAPH_MAGIC_LEN] = {
'A', 'S', 'L', 'E', 'G', 'R', '1', '\0'
};
enum { AMDUAT_TYPE_TAG_TGK_EDGE_GRAPH_1 = 0x00000202u };
static const char *const AMDUATD_REL_ALIAS = "alias";
static const char *const AMDUATD_REL_MATERIALIZES = "materializes";
static const char *const AMDUATD_REL_REPRESENTS = "represents";
static const char *const AMDUATD_REL_REQUIRES_KEY = "requires_key";
static const char *const AMDUATD_REL_WITHIN_DOMAIN = "within_domain";
static const char *const AMDUATD_REL_COMPUTED_BY = "computed_by";
static const char *const AMDUATD_REL_HAS_PROVENANCE = "has_provenance";
static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store,
const char *name,
amduat_reference_t *out_ref);
static bool amduatd_concepts_put_edge(amduat_asl_store_t *store,
amduatd_concepts_t *c,
amduat_reference_t from,
amduat_reference_t to,
amduat_reference_t relation_concept_ref,
amduat_octets_t actor,
amduat_reference_t *out_edge_ref);
static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store,
const amduatd_concepts_t *c,
const char *name,
amduat_reference_t *out_concept_ref);
static bool amduatd_concepts_refresh_edges_internal(
amduat_asl_store_t *store,
amduatd_concepts_t *c,
const amduatd_space_t *space,
size_t max_new_entries);
static void amduatd_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 amduatd_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 void amduatd_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 amduatd_read_u64_le(const uint8_t *data,
size_t len,
size_t *offset,
uint64_t *out) {
if (len - *offset < 8u) {
return false;
}
*out = (uint64_t)data[*offset] |
((uint64_t)data[*offset + 1u] << 8) |
((uint64_t)data[*offset + 2u] << 16) |
((uint64_t)data[*offset + 3u] << 24) |
((uint64_t)data[*offset + 4u] << 32) |
((uint64_t)data[*offset + 5u] << 40) |
((uint64_t)data[*offset + 6u] << 48) |
((uint64_t)data[*offset + 7u] << 56);
*offset += 8u;
return true;
}
static bool amduatd_add_size(size_t *acc, size_t add) {
if (*acc > SIZE_MAX - add) {
return false;
}
*acc += add;
return true;
}
typedef struct {
uint64_t indexed_up_to_offset;
bool has_graph_ref;
amduat_reference_t graph_ref;
} amduatd_edge_index_state_t;
static void amduatd_edge_index_state_free(amduatd_edge_index_state_t *state) {
if (state == NULL) {
return;
}
if (state->has_graph_ref) {
amduat_reference_free(&state->graph_ref);
}
memset(state, 0, sizeof(*state));
}
static bool amduatd_edge_index_state_encode(
const amduatd_edge_index_state_t *state,
amduat_octets_t *out_payload) {
amduat_octets_t graph_bytes = amduat_octets(NULL, 0u);
uint8_t *payload = NULL;
size_t payload_len = 0u;
size_t offset = 0u;
uint32_t ref_len = 0u;
if (out_payload == NULL) {
return false;
}
*out_payload = amduat_octets(NULL, 0u);
if (state == NULL) {
return false;
}
if (state->has_graph_ref) {
if (!amduat_enc_asl1_core_encode_reference_v1(state->graph_ref,
&graph_bytes)) {
return false;
}
if (graph_bytes.len > UINT32_MAX) {
free((void *)graph_bytes.data);
return false;
}
ref_len = (uint32_t)graph_bytes.len;
}
payload_len = AMDUATD_EDGE_INDEX_MAGIC_LEN + 4u + 8u + 4u + ref_len;
payload = (uint8_t *)malloc(payload_len);
if (payload == NULL) {
free((void *)graph_bytes.data);
return false;
}
memcpy(payload + offset, k_amduatd_edge_index_magic,
AMDUATD_EDGE_INDEX_MAGIC_LEN);
offset += AMDUATD_EDGE_INDEX_MAGIC_LEN;
amduatd_store_u32_le(payload + offset, AMDUATD_EDGE_INDEX_VERSION);
offset += 4u;
amduatd_store_u64_le(payload + offset, state->indexed_up_to_offset);
offset += 8u;
amduatd_store_u32_le(payload + offset, ref_len);
offset += 4u;
if (ref_len != 0u) {
memcpy(payload + offset, graph_bytes.data, ref_len);
offset += ref_len;
}
free((void *)graph_bytes.data);
*out_payload = amduat_octets(payload, payload_len);
return offset == payload_len;
}
static bool amduatd_edge_index_state_decode(
amduat_octets_t payload,
amduatd_edge_index_state_t *out_state) {
size_t offset = 0u;
uint32_t version = 0u;
uint32_t ref_len = 0u;
amduat_octets_t ref_bytes;
if (out_state == NULL) {
return false;
}
memset(out_state, 0, sizeof(*out_state));
if (payload.data == NULL ||
payload.len < AMDUATD_EDGE_INDEX_MAGIC_LEN + 4u + 8u + 4u) {
return false;
}
if (memcmp(payload.data, k_amduatd_edge_index_magic,
AMDUATD_EDGE_INDEX_MAGIC_LEN) != 0) {
return false;
}
offset += AMDUATD_EDGE_INDEX_MAGIC_LEN;
if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &version) ||
version != AMDUATD_EDGE_INDEX_VERSION) {
return false;
}
if (!amduatd_read_u64_le(payload.data, payload.len, &offset,
&out_state->indexed_up_to_offset) ||
!amduatd_read_u32_le(payload.data, payload.len, &offset, &ref_len)) {
return false;
}
if (payload.len - offset < ref_len) {
return false;
}
if (ref_len != 0u) {
ref_bytes = amduat_octets(payload.data + offset, ref_len);
if (!amduat_enc_asl1_core_decode_reference_v1(ref_bytes,
&out_state->graph_ref)) {
amduatd_edge_index_state_free(out_state);
return false;
}
out_state->has_graph_ref = true;
offset += ref_len;
}
return offset == payload.len;
}
static bool amduatd_is_ascii(amduat_octets_t bytes) {
if (bytes.len == 0u || bytes.data == NULL) {
return false;
}
for (size_t i = 0u; i < bytes.len; ++i) {
uint8_t c = bytes.data[i];
if (c < 0x20u || c > 0x7eu) {
return false;
}
}
return true;
}
static bool amduatd_edge_payload_encode(amduat_reference_t src,
amduat_reference_t dst,
const char *rel,
amduat_octets_t *out_payload) {
amduat_octets_t src_bytes = amduat_octets(NULL, 0u);
amduat_octets_t dst_bytes = amduat_octets(NULL, 0u);
size_t total_len = 0u;
size_t offset = 0u;
uint8_t *buffer = NULL;
uint32_t rel_len = 0u;
if (out_payload == NULL || rel == NULL) {
return false;
}
out_payload->data = NULL;
out_payload->len = 0u;
{
size_t rel_len_full = strlen(rel);
if (rel_len_full > UINT32_MAX) {
return false;
}
rel_len = (uint32_t)rel_len_full;
}
if (rel_len == 0u) {
return false;
}
if (!amduatd_is_ascii(amduat_octets((const uint8_t *)rel, rel_len))) {
return false;
}
if (!amduat_enc_asl1_core_encode_reference_v1(src, &src_bytes) ||
!amduat_enc_asl1_core_encode_reference_v1(dst, &dst_bytes)) {
free((void *)src_bytes.data);
free((void *)dst_bytes.data);
return false;
}
if (src_bytes.len > UINT32_MAX || dst_bytes.len > UINT32_MAX) {
free((void *)src_bytes.data);
free((void *)dst_bytes.data);
return false;
}
if (!amduatd_add_size(&total_len,
AMDUATD_EDGE_MAGIC_LEN + 4u + 4u + src_bytes.len) ||
!amduatd_add_size(&total_len, 4u + dst_bytes.len) ||
!amduatd_add_size(&total_len, 4u + rel_len) ||
!amduatd_add_size(&total_len, 1u)) {
free((void *)src_bytes.data);
free((void *)dst_bytes.data);
return false;
}
buffer = (uint8_t *)malloc(total_len);
if (buffer == NULL) {
free((void *)src_bytes.data);
free((void *)dst_bytes.data);
return false;
}
memcpy(buffer + offset, k_amduatd_edge_magic, AMDUATD_EDGE_MAGIC_LEN);
offset += AMDUATD_EDGE_MAGIC_LEN;
amduatd_store_u32_le(buffer + offset, AMDUATD_EDGE_VERSION);
offset += 4u;
amduatd_store_u32_le(buffer + offset, (uint32_t)src_bytes.len);
offset += 4u;
memcpy(buffer + offset, src_bytes.data, src_bytes.len);
offset += src_bytes.len;
amduatd_store_u32_le(buffer + offset, (uint32_t)dst_bytes.len);
offset += 4u;
memcpy(buffer + offset, dst_bytes.data, dst_bytes.len);
offset += dst_bytes.len;
amduatd_store_u32_le(buffer + offset, rel_len);
offset += 4u;
memcpy(buffer + offset, rel, rel_len);
offset += rel_len;
buffer[offset++] = 0u;
free((void *)src_bytes.data);
free((void *)dst_bytes.data);
if (offset != total_len) {
free(buffer);
return false;
}
out_payload->data = buffer;
out_payload->len = total_len;
return true;
}
static bool amduatd_edge_payload_decode(amduat_octets_t payload,
amduat_reference_t *out_src,
amduat_reference_t *out_dst,
char **out_rel) {
size_t offset = 0u;
uint32_t version = 0u;
uint32_t src_len = 0u;
uint32_t dst_len = 0u;
uint32_t rel_len = 0u;
uint8_t flags = 0u;
amduat_octets_t src_bytes;
amduat_octets_t dst_bytes;
char *rel = NULL;
if (out_src == NULL || out_dst == NULL || out_rel == NULL) {
return false;
}
*out_src = amduat_reference(0u, amduat_octets(NULL, 0u));
*out_dst = amduat_reference(0u, amduat_octets(NULL, 0u));
*out_rel = NULL;
if (payload.len < AMDUATD_EDGE_MAGIC_LEN + 4u + 1u ||
payload.data == NULL) {
return false;
}
if (memcmp(payload.data, k_amduatd_edge_magic,
AMDUATD_EDGE_MAGIC_LEN) != 0) {
return false;
}
offset += AMDUATD_EDGE_MAGIC_LEN;
if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &version) ||
version != AMDUATD_EDGE_VERSION) {
return false;
}
if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &src_len)) {
return false;
}
if (payload.len - offset < src_len) {
return false;
}
src_bytes = amduat_octets(payload.data + offset, src_len);
offset += src_len;
if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &dst_len)) {
return false;
}
if (payload.len - offset < dst_len) {
return false;
}
dst_bytes = amduat_octets(payload.data + offset, dst_len);
offset += dst_len;
if (!amduatd_read_u32_le(payload.data, payload.len, &offset, &rel_len)) {
return false;
}
if (payload.len - offset < rel_len + 1u) {
return false;
}
if (!amduatd_is_ascii(amduat_octets(payload.data + offset, rel_len))) {
return false;
}
rel = (char *)malloc(rel_len + 1u);
if (rel == NULL) {
return false;
}
memcpy(rel, payload.data + offset, rel_len);
rel[rel_len] = '\0';
offset += rel_len;
flags = payload.data[offset++];
(void)flags;
if (offset != payload.len) {
free(rel);
return false;
}
if (!amduat_enc_asl1_core_decode_reference_v1(src_bytes, out_src) ||
!amduat_enc_asl1_core_decode_reference_v1(dst_bytes, out_dst)) {
amduat_reference_free(out_src);
amduat_reference_free(out_dst);
free(rel);
return false;
}
*out_rel = rel;
return true;
}
typedef struct {
amduat_octets_t record_bytes;
amduat_octets_t src_bytes;
amduat_octets_t dst_bytes;
} amduatd_edge_graph_encoded_t;
static void amduatd_edge_graph_encoded_free(
amduatd_edge_graph_encoded_t *encoded,
size_t len) {
if (encoded == NULL) {
return;
}
for (size_t i = 0u; i < len; ++i) {
amduat_octets_free(&encoded[i].record_bytes);
amduat_octets_free(&encoded[i].src_bytes);
amduat_octets_free(&encoded[i].dst_bytes);
}
free(encoded);
}
static bool amduatd_edge_graph_encode(const amduatd_edge_list_t *edges,
amduat_octets_t *out_bytes) {
amduatd_edge_graph_encoded_t *encoded = NULL;
size_t total_len = 0u;
uint8_t *buffer = NULL;
size_t offset = 0u;
uint32_t edge_count = 0u;
if (out_bytes == NULL) {
return false;
}
*out_bytes = amduat_octets(NULL, 0u);
if (edges == NULL) {
return false;
}
if (edges->len > UINT32_MAX) {
return false;
}
edge_count = (uint32_t)edges->len;
if (edge_count != 0u) {
encoded = (amduatd_edge_graph_encoded_t *)calloc(edge_count,
sizeof(*encoded));
if (encoded == NULL) {
return false;
}
}
if (!amduatd_add_size(&total_len,
AMDUATD_EDGE_GRAPH_MAGIC_LEN + 4u + 4u)) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
for (uint32_t i = 0u; i < edge_count; ++i) {
const amduatd_edge_entry_t *entry = &edges->items[i];
size_t rel_len = 0u;
if (entry->rel == NULL) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
rel_len = strlen(entry->rel);
if (rel_len > UINT32_MAX) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
if (!amduat_enc_asl1_core_encode_reference_v1(entry->record_ref,
&encoded[i].record_bytes) ||
!amduat_enc_asl1_core_encode_reference_v1(entry->src_ref,
&encoded[i].src_bytes) ||
!amduat_enc_asl1_core_encode_reference_v1(entry->dst_ref,
&encoded[i].dst_bytes)) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
if (encoded[i].record_bytes.len > UINT32_MAX ||
encoded[i].src_bytes.len > UINT32_MAX ||
encoded[i].dst_bytes.len > UINT32_MAX) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
if (!amduatd_add_size(&total_len, 4u + rel_len) ||
!amduatd_add_size(&total_len, 4u + encoded[i].record_bytes.len) ||
!amduatd_add_size(&total_len, 4u + encoded[i].src_bytes.len) ||
!amduatd_add_size(&total_len, 4u + encoded[i].dst_bytes.len)) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
}
buffer = (uint8_t *)malloc(total_len);
if (buffer == NULL) {
amduatd_edge_graph_encoded_free(encoded, edge_count);
return false;
}
memcpy(buffer + offset, k_amduatd_edge_graph_magic,
AMDUATD_EDGE_GRAPH_MAGIC_LEN);
offset += AMDUATD_EDGE_GRAPH_MAGIC_LEN;
amduatd_store_u32_le(buffer + offset, AMDUATD_EDGE_GRAPH_VERSION);
offset += 4u;
amduatd_store_u32_le(buffer + offset, edge_count);
offset += 4u;
for (uint32_t i = 0u; i < edge_count; ++i) {
const amduatd_edge_entry_t *entry = &edges->items[i];
uint32_t rel_len = (uint32_t)strlen(entry->rel);
amduatd_store_u32_le(buffer + offset, rel_len);
offset += 4u;
memcpy(buffer + offset, entry->rel, rel_len);
offset += rel_len;
amduatd_store_u32_le(buffer + offset,
(uint32_t)encoded[i].record_bytes.len);
offset += 4u;
memcpy(buffer + offset, encoded[i].record_bytes.data,
encoded[i].record_bytes.len);
offset += encoded[i].record_bytes.len;
amduatd_store_u32_le(buffer + offset,
(uint32_t)encoded[i].src_bytes.len);
offset += 4u;
memcpy(buffer + offset, encoded[i].src_bytes.data,
encoded[i].src_bytes.len);
offset += encoded[i].src_bytes.len;
amduatd_store_u32_le(buffer + offset,
(uint32_t)encoded[i].dst_bytes.len);
offset += 4u;
memcpy(buffer + offset, encoded[i].dst_bytes.data,
encoded[i].dst_bytes.len);
offset += encoded[i].dst_bytes.len;
}
amduatd_edge_graph_encoded_free(encoded, edge_count);
if (offset != total_len) {
free(buffer);
return false;
}
*out_bytes = amduat_octets(buffer, total_len);
return true;
}
static bool amduatd_edge_graph_decode(amduat_octets_t bytes,
amduatd_edge_list_t *out_edges) {
size_t offset = 0u;
uint32_t version = 0u;
uint32_t edge_count = 0u;
amduatd_edge_list_t parsed;
if (out_edges == NULL) {
return false;
}
memset(&parsed, 0, sizeof(parsed));
if (bytes.data == NULL ||
bytes.len < AMDUATD_EDGE_GRAPH_MAGIC_LEN + 4u + 4u) {
return false;
}
if (memcmp(bytes.data, k_amduatd_edge_graph_magic,
AMDUATD_EDGE_GRAPH_MAGIC_LEN) != 0) {
return false;
}
offset += AMDUATD_EDGE_GRAPH_MAGIC_LEN;
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &version) ||
version != AMDUATD_EDGE_GRAPH_VERSION) {
return false;
}
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &edge_count)) {
return false;
}
for (uint32_t i = 0u; i < edge_count; ++i) {
uint32_t rel_len = 0u;
uint32_t record_len = 0u;
uint32_t src_len = 0u;
uint32_t dst_len = 0u;
amduat_octets_t record_bytes;
amduat_octets_t src_bytes;
amduat_octets_t dst_bytes;
amduatd_edge_entry_t entry;
char *rel = NULL;
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &rel_len) ||
bytes.len - offset < rel_len) {
goto decode_fail;
}
if (rel_len == 0u ||
!amduatd_is_ascii(amduat_octets(bytes.data + offset, rel_len))) {
goto decode_fail;
}
rel = (char *)malloc(rel_len + 1u);
if (rel == NULL) {
goto decode_fail;
}
memcpy(rel, bytes.data + offset, rel_len);
rel[rel_len] = '\0';
offset += rel_len;
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &record_len) ||
bytes.len - offset < record_len) {
free(rel);
goto decode_fail;
}
record_bytes = amduat_octets(bytes.data + offset, record_len);
offset += record_len;
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &src_len) ||
bytes.len - offset < src_len) {
free(rel);
goto decode_fail;
}
src_bytes = amduat_octets(bytes.data + offset, src_len);
offset += src_len;
if (!amduatd_read_u32_le(bytes.data, bytes.len, &offset, &dst_len) ||
bytes.len - offset < dst_len) {
free(rel);
goto decode_fail;
}
dst_bytes = amduat_octets(bytes.data + offset, dst_len);
offset += dst_len;
memset(&entry, 0, sizeof(entry));
if (!amduat_enc_asl1_core_decode_reference_v1(record_bytes,
&entry.record_ref) ||
!amduat_enc_asl1_core_decode_reference_v1(src_bytes,
&entry.src_ref) ||
!amduat_enc_asl1_core_decode_reference_v1(dst_bytes,
&entry.dst_ref)) {
free(rel);
amduatd_edge_entry_free(&entry);
goto decode_fail;
}
entry.rel = rel;
if (!amduatd_edge_list_push(&parsed, &entry)) {
amduatd_edge_entry_free(&entry);
goto decode_fail;
}
amduatd_edge_entry_free(&entry);
}
if (offset != bytes.len) {
goto decode_fail;
}
amduatd_edge_list_clear(out_edges);
*out_edges = parsed;
return true;
decode_fail:
amduatd_edge_list_clear(&parsed);
return false;
}
static const char *amduatd_relation_name_for_ref(
const amduatd_concepts_t *c,
amduat_reference_t ref) {
if (c == NULL) {
return NULL;
}
if (amduat_reference_eq(ref, c->rel_aliases_ref)) {
return AMDUATD_REL_ALIAS;
}
if (amduat_reference_eq(ref, c->rel_materializes_ref)) {
return AMDUATD_REL_MATERIALIZES;
}
if (amduat_reference_eq(ref, c->rel_represents_ref)) {
return AMDUATD_REL_REPRESENTS;
}
if (amduat_reference_eq(ref, c->rel_requires_key_ref)) {
return AMDUATD_REL_REQUIRES_KEY;
}
if (amduat_reference_eq(ref, c->rel_within_domain_ref)) {
return AMDUATD_REL_WITHIN_DOMAIN;
}
if (amduat_reference_eq(ref, c->rel_computed_by_ref)) {
return AMDUATD_REL_COMPUTED_BY;
}
if (amduat_reference_eq(ref, c->rel_has_provenance_ref)) {
return AMDUATD_REL_HAS_PROVENANCE;
}
return NULL;
}
static bool amduatd_build_collection_head_name(const char *name,
char **out_name) {
size_t name_len;
size_t total_len;
char *buffer;
size_t offset = 0u;
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;
}
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 amduatd_build_collection_log_name(const char *name,
char **out_name) {
size_t name_len;
size_t total_len;
char *buffer;
size_t offset = 0u;
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;
}
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 amduatd_build_collection_log_head_name(const char *name,
char **out_name) {
size_t name_len;
size_t log_name_len;
size_t total_len;
char *buffer;
size_t offset = 0u;
if (name == NULL || out_name == NULL) {
return false;
}
if (!amduat_asl_pointer_name_is_valid(name)) {
return false;
}
name_len = strlen(name);
log_name_len = 11u + name_len + 4u;
total_len = 4u + log_name_len + 5u + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
memcpy(buffer + offset, "log/", 4u);
offset += 4u;
memcpy(buffer + offset, "collection/", 11u);
offset += 11u;
memcpy(buffer + offset, name, name_len);
offset += name_len;
memcpy(buffer + offset, "/log", 4u);
offset += 4u;
memcpy(buffer + offset, "/head", 5u);
offset += 5u;
buffer[offset] = '\0';
*out_name = buffer;
return true;
}
static bool amduatd_get_none_ref(amduat_asl_store_t *store,
amduat_reference_t *out_ref) {
amduat_artifact_t none_artifact;
if (store == NULL || out_ref == NULL) {
return false;
}
if (!amduat_asl_none_artifact(&none_artifact)) {
return false;
}
if (amduat_asl_store_put(store, none_artifact, out_ref) !=
AMDUAT_ASL_STORE_OK) {
amduat_artifact_free(&none_artifact);
return false;
}
amduat_artifact_free(&none_artifact);
return true;
}
static bool amduatd_put_view_params(amduat_asl_store_t *store,
uint64_t from_offset,
uint32_t limit,
amduat_reference_t *out_ref) {
uint8_t payload[12];
if (store == NULL || out_ref == NULL) {
return false;
}
payload[0] = (uint8_t)(from_offset & 0xffu);
payload[1] = (uint8_t)((from_offset >> 8) & 0xffu);
payload[2] = (uint8_t)((from_offset >> 16) & 0xffu);
payload[3] = (uint8_t)((from_offset >> 24) & 0xffu);
payload[4] = (uint8_t)((from_offset >> 32) & 0xffu);
payload[5] = (uint8_t)((from_offset >> 40) & 0xffu);
payload[6] = (uint8_t)((from_offset >> 48) & 0xffu);
payload[7] = (uint8_t)((from_offset >> 56) & 0xffu);
payload[8] = (uint8_t)(limit & 0xffu);
payload[9] = (uint8_t)((limit >> 8) & 0xffu);
payload[10] = (uint8_t)((limit >> 16) & 0xffu);
payload[11] = (uint8_t)((limit >> 24) & 0xffu);
if (amduat_asl_record_store_put(
store,
amduat_octets("pel/params", strlen("pel/params")),
amduat_octets(payload, sizeof(payload)),
out_ref) != AMDUAT_ASL_STORE_OK) {
return false;
}
return true;
}
static void amduatd_program_free(amduat_pel_program_t *program) {
if (program == NULL || program->nodes == NULL) {
return;
}
for (size_t i = 0u; i < program->nodes_len; ++i) {
free(program->nodes[i].inputs);
}
free(program->nodes);
free(program->roots);
program->nodes = NULL;
program->roots = NULL;
program->nodes_len = 0u;
program->roots_len = 0u;
}
static bool amduatd_build_collection_view_program(
amduat_pel_program_t *out_program) {
static const char k_op_snapshot[] = "collection.snapshot_decode_v1";
static const char k_op_read_range[] = "log.read_range_v1";
static const char k_op_merge[] = "collection.merge_refs_v1";
amduat_pel_node_t *nodes;
amduat_pel_root_ref_t *roots;
if (out_program == NULL) {
return false;
}
memset(out_program, 0, sizeof(*out_program));
nodes = (amduat_pel_node_t *)calloc(3u, sizeof(*nodes));
roots = (amduat_pel_root_ref_t *)calloc(1u, sizeof(*roots));
if (nodes == NULL || roots == NULL) {
free(nodes);
free(roots);
return false;
}
nodes[0].id = 1u;
nodes[0].op.name = amduat_octets(k_op_snapshot, strlen(k_op_snapshot));
nodes[0].op.version = 1u;
nodes[0].inputs_len = 1u;
nodes[0].inputs =
(amduat_pel_dag_input_t *)calloc(1u, sizeof(*nodes[0].inputs));
if (nodes[0].inputs == NULL) {
free(nodes);
free(roots);
return false;
}
nodes[0].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[0].inputs[0].value.external.input_index = 0u;
nodes[0].params = amduat_octets(NULL, 0u);
nodes[1].id = 2u;
nodes[1].op.name = amduat_octets(k_op_read_range, strlen(k_op_read_range));
nodes[1].op.version = 1u;
nodes[1].inputs_len = 3u;
nodes[1].inputs =
(amduat_pel_dag_input_t *)calloc(3u, sizeof(*nodes[1].inputs));
if (nodes[1].inputs == NULL) {
free(nodes[0].inputs);
free(nodes);
free(roots);
return false;
}
nodes[1].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[1].inputs[0].value.external.input_index = 1u;
nodes[1].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[1].inputs[1].value.external.input_index = 2u;
nodes[1].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[1].inputs[2].value.external.input_index = 3u;
nodes[1].params = amduat_octets(NULL, 0u);
nodes[2].id = 3u;
nodes[2].op.name = amduat_octets(k_op_merge, strlen(k_op_merge));
nodes[2].op.version = 1u;
nodes[2].inputs_len = 4u;
nodes[2].inputs =
(amduat_pel_dag_input_t *)calloc(4u, sizeof(*nodes[2].inputs));
if (nodes[2].inputs == NULL) {
free(nodes[1].inputs);
free(nodes[0].inputs);
free(nodes);
free(roots);
return false;
}
nodes[2].inputs[0].kind = AMDUAT_PEL_DAG_INPUT_NODE;
nodes[2].inputs[0].value.node.node_id = 1u;
nodes[2].inputs[0].value.node.output_index = 0u;
nodes[2].inputs[1].kind = AMDUAT_PEL_DAG_INPUT_NODE;
nodes[2].inputs[1].value.node.node_id = 2u;
nodes[2].inputs[1].value.node.output_index = 0u;
nodes[2].inputs[2].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[2].inputs[2].value.external.input_index = 2u;
nodes[2].inputs[3].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
nodes[2].inputs[3].value.external.input_index = 3u;
nodes[2].params = amduat_octets(NULL, 0u);
roots[0].node_id = 3u;
roots[0].output_index = 0u;
out_program->nodes = nodes;
out_program->nodes_len = 3u;
out_program->roots = roots;
out_program->roots_len = 1u;
return true;
}
static bool amduatd_collection_view(amduat_asl_store_t *store,
const char *root_path,
const char *name,
uint64_t from,
uint32_t limit,
amduat_asl_collection_view_t *out_view) {
amduat_asl_pointer_store_t pointer_store;
amduat_reference_t snapshot_ref;
amduat_reference_t log_head_ref;
amduat_reference_t none_ref;
amduat_reference_t params_ref;
amduat_reference_t program_ref;
amduat_reference_t input_refs[4];
amduat_pel_program_t program;
amduat_octets_t program_bytes = amduat_octets(NULL, 0u);
amduat_pel_run_result_t run_result;
amduat_artifact_t view_artifact;
amduat_asl_collection_view_t view;
amduat_type_tag_t expected_type_tag;
amduat_asl_encoding_profile_id_t expected_profile;
char *snapshot_name = NULL;
char *log_head_name = NULL;
bool snapshot_exists = false;
bool log_head_exists = false;
bool ok = false;
if (out_view == NULL) {
return false;
}
memset(out_view, 0, sizeof(*out_view));
memset(&snapshot_ref, 0, sizeof(snapshot_ref));
memset(&log_head_ref, 0, sizeof(log_head_ref));
memset(&none_ref, 0, sizeof(none_ref));
memset(&params_ref, 0, sizeof(params_ref));
memset(&program_ref, 0, sizeof(program_ref));
memset(&run_result, 0, sizeof(run_result));
memset(&view, 0, sizeof(view));
if (store == NULL || root_path == NULL || name == NULL) {
return false;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
return false;
}
if (!amduatd_get_none_ref(store, &none_ref)) {
return false;
}
if (!amduatd_build_collection_head_name(name, &snapshot_name) ||
!amduatd_build_collection_log_head_name(name, &log_head_name)) {
goto view_cleanup;
}
if (amduat_asl_pointer_get(&pointer_store, snapshot_name,
&snapshot_exists, &snapshot_ref) !=
AMDUAT_ASL_POINTER_OK) {
goto view_cleanup;
}
if (!snapshot_exists &&
!amduat_reference_clone(none_ref, &snapshot_ref)) {
goto view_cleanup;
}
if (amduat_asl_pointer_get(&pointer_store, log_head_name,
&log_head_exists, &log_head_ref) !=
AMDUAT_ASL_POINTER_OK) {
goto view_cleanup;
}
if (!log_head_exists &&
!amduat_reference_clone(none_ref, &log_head_ref)) {
goto view_cleanup;
}
if (!amduatd_put_view_params(store, from, limit, &params_ref)) {
goto view_cleanup;
}
if (!amduatd_build_collection_view_program(&program)) {
goto view_cleanup;
}
if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) {
amduatd_program_free(&program);
goto view_cleanup;
}
amduatd_program_free(&program);
if (!amduat_pel_program_dag_desc_get_program_binding(&expected_type_tag,
&expected_profile)) {
goto view_cleanup;
}
if (expected_profile != AMDUAT_PEL_ENC_PROGRAM_DAG_V1) {
goto view_cleanup;
}
{
amduat_artifact_t program_artifact =
amduat_artifact_with_type(program_bytes, expected_type_tag);
if (amduat_asl_store_put(store, program_artifact, &program_ref) !=
AMDUAT_ASL_STORE_OK) {
goto view_cleanup;
}
}
input_refs[0] = snapshot_ref;
input_refs[1] = log_head_ref;
input_refs[2] = params_ref;
input_refs[3] = params_ref;
if (!amduat_pel_surf_run_with_result(store,
amduat_pel_program_dag_scheme_ref(),
program_ref,
input_refs,
4u,
true,
params_ref,
&run_result)) {
goto view_cleanup;
}
if (!run_result.has_result_value ||
run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK ||
run_result.output_refs_len != 1u) {
goto view_cleanup;
}
memset(&view_artifact, 0, sizeof(view_artifact));
if (amduat_asl_store_get(store, run_result.output_refs[0],
&view_artifact) != AMDUAT_ASL_STORE_OK) {
goto view_cleanup;
}
if (!view_artifact.has_type_tag ||
view_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 ||
!amduat_asl_collection_view_decode_v1(view_artifact.bytes, &view)) {
amduat_artifact_free(&view_artifact);
goto view_cleanup;
}
amduat_artifact_free(&view_artifact);
*out_view = view;
memset(&view, 0, sizeof(view));
ok = true;
view_cleanup:
if (run_result.has_result_value) {
amduat_enc_pel1_result_free(&run_result.result_value);
}
if (run_result.output_refs != NULL) {
amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len);
}
amduat_pel_surf_free_ref(&run_result.result_ref);
amduat_reference_free(&snapshot_ref);
amduat_reference_free(&log_head_ref);
amduat_reference_free(&none_ref);
amduat_reference_free(&params_ref);
amduat_reference_free(&program_ref);
amduat_asl_collection_view_free(&view);
free((void *)program_bytes.data);
free(snapshot_name);
free(log_head_name);
return ok;
}
static bool amduatd_read_file(const char *path, uint8_t **out, size_t *out_len) {
uint8_t *buf = NULL;
size_t cap = 0;
size_t len = 0;
FILE *f;
if (out != NULL) {
*out = NULL;
}
if (out_len != NULL) {
*out_len = 0;
}
if (path == NULL || out == NULL || out_len == NULL) {
return false;
}
f = fopen(path, "rb");
if (f == NULL) {
return false;
}
for (;;) {
size_t n;
if (len == cap) {
size_t next_cap = cap != 0 ? cap * 2u : 4096u;
uint8_t *next = (uint8_t *)realloc(buf, next_cap);
if (next == NULL) {
free(buf);
fclose(f);
return false;
}
buf = next;
cap = next_cap;
}
n = fread(buf + len, 1, cap - len, f);
len += n;
if (n == 0) {
if (ferror(f)) {
free(buf);
fclose(f);
return false;
}
break;
}
}
fclose(f);
*out = buf;
*out_len = len;
return true;
}
static bool amduatd_build_prefixed_bytes(const char *prefix,
const char *text,
uint8_t **out,
size_t *out_len) {
size_t p_len;
size_t t_len;
size_t len;
uint8_t *buf;
if (out != NULL) {
*out = NULL;
}
if (out_len != NULL) {
*out_len = 0;
}
if (prefix == NULL || text == NULL || out == NULL || out_len == NULL) {
return false;
}
p_len = strlen(prefix);
t_len = strlen(text);
if (p_len > SIZE_MAX - 1u || t_len > SIZE_MAX - (p_len + 1u)) {
return false;
}
len = p_len + 1u + t_len;
buf = (uint8_t *)malloc(len);
if (buf == NULL) {
return false;
}
memcpy(buf, prefix, p_len);
buf[p_len] = 0;
memcpy(buf + p_len + 1u, text, t_len);
*out = buf;
*out_len = len;
return true;
}
static bool amduatd_concepts_seed_relation(amduat_asl_store_t *store,
const amduatd_space_t *space,
const char *relation_name,
amduat_reference_t *out_ref) {
uint8_t *bytes = NULL;
size_t bytes_len = 0;
amduat_artifact_t artifact;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
bool ok = false;
if (out_ref == NULL) {
return false;
}
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
if (store == NULL || relation_name == NULL) {
return false;
}
if (!amduatd_space_scope_name(space, relation_name, &scoped_bytes)) {
return false;
}
scoped_name = (char *)scoped_bytes.data;
if (!amduatd_build_prefixed_bytes("AMDUATD/RELATION/1", scoped_name, &bytes,
&bytes_len)) {
goto seed_cleanup;
}
artifact = amduat_artifact(amduat_octets(bytes, bytes_len));
if (!amduat_asl_ref_derive(artifact,
store->config.encoding_profile_id,
store->config.hash_id,
out_ref,
NULL)) {
goto seed_cleanup;
}
(void)amduat_asl_store_put(store, artifact, out_ref);
ok = true;
seed_cleanup:
free(bytes);
free(scoped_name);
return ok;
}
static bool amduatd_concepts_ensure_alias(amduatd_concepts_t *c,
amduat_asl_store_t *store,
const amduatd_space_t *space,
const char *alias_name,
amduat_reference_t concept_ref) {
amduat_reference_t existing;
amduat_reference_t name_ref;
amduat_reference_t edge_ref;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
bool ok = false;
memset(&existing, 0, sizeof(existing));
memset(&name_ref, 0, sizeof(name_ref));
memset(&edge_ref, 0, sizeof(edge_ref));
if (c == NULL || store == NULL || alias_name == NULL) {
return false;
}
if (!amduatd_space_scope_name(space, alias_name, &scoped_bytes)) {
amduat_log(AMDUAT_LOG_ERROR,
"ensure alias: scope failed for %s", alias_name);
return false;
}
scoped_name = (char *)scoped_bytes.data;
if (amduatd_concepts_lookup_alias(store, c, scoped_name, &existing)) {
amduat_reference_free(&existing);
ok = true;
goto alias_cleanup;
}
if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"ensure alias: name artifact failed for %s", scoped_name);
goto alias_cleanup;
}
if (!amduatd_concepts_put_edge(store,
c,
name_ref,
concept_ref,
c->rel_aliases_ref,
amduat_octets(NULL, 0u),
&edge_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"ensure alias: put edge failed for %s", scoped_name);
goto alias_cleanup;
}
ok = true;
alias_cleanup:
amduat_reference_free(&name_ref);
amduat_reference_free(&edge_ref);
free(scoped_name);
return ok;
}
static bool amduatd_concepts_load_edges(amduatd_concepts_t *c,
amduat_asl_store_t *store,
uint64_t *out_up_to_offset) {
amduat_asl_collection_view_t view;
uint64_t from = 0u;
uint64_t computed_up_to = 0u;
bool ok = false;
if (c == NULL || store == NULL) {
return false;
}
if (out_up_to_offset != NULL) {
*out_up_to_offset = 0u;
}
amduatd_edge_list_clear(&c->edges);
while (true) {
memset(&view, 0, sizeof(view));
if (!amduatd_collection_view(store,
c->root_path,
c->edge_collection_name,
from,
AMDUATD_EDGE_VIEW_BATCH,
&view)) {
return false;
}
for (size_t i = 0u; i < view.refs_len; ++i) {
amduat_reference_t record_ref = view.refs[i];
amduat_asl_record_t record;
amduat_reference_t src_ref;
amduat_reference_t dst_ref;
char *rel = NULL;
amduatd_edge_entry_t entry;
amduat_asl_store_error_t err;
bool schema_ok = false;
memset(&record, 0, sizeof(record));
err = amduat_asl_record_store_get(store, record_ref, &record);
if (err != AMDUAT_ASL_STORE_OK) {
continue;
}
if (record.schema.len == strlen(AMDUATD_EDGE_SCHEMA) &&
memcmp(record.schema.data, AMDUATD_EDGE_SCHEMA,
record.schema.len) == 0) {
schema_ok = true;
}
if (!schema_ok ||
!amduatd_edge_payload_decode(record.payload,
&src_ref,
&dst_ref,
&rel)) {
amduat_asl_record_free(&record);
continue;
}
amduat_asl_record_free(&record);
memset(&entry, 0, sizeof(entry));
if (!amduat_reference_clone(record_ref, &entry.record_ref)) {
amduat_reference_free(&src_ref);
amduat_reference_free(&dst_ref);
free(rel);
goto load_cleanup;
}
entry.src_ref = src_ref;
entry.dst_ref = dst_ref;
entry.rel = rel;
if (!amduatd_edge_list_push(&c->edges, &entry)) {
amduatd_edge_entry_free(&entry);
goto load_cleanup;
}
amduatd_edge_entry_free(&entry);
}
if (view.refs_len == 0u || view.computed_up_to_offset <= from) {
computed_up_to = view.computed_up_to_offset;
break;
}
from = view.computed_up_to_offset;
computed_up_to = view.computed_up_to_offset;
amduat_asl_collection_view_free(&view);
}
ok = true;
load_cleanup:
amduat_asl_collection_view_free(&view);
if (ok && out_up_to_offset != NULL) {
*out_up_to_offset = computed_up_to;
}
return ok;
}
static bool amduatd_concepts_append_edge_record(amduatd_concepts_t *c,
amduat_asl_store_t *store,
amduat_reference_t src,
amduat_reference_t dst,
const char *rel,
amduat_octets_t actor,
amduat_reference_t *out_ref) {
amduat_octets_t payload = amduat_octets(NULL, 0u);
amduat_reference_t record_ref;
amduatd_edge_entry_t entry;
uint64_t offset = 0u;
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (c == NULL || store == NULL || rel == NULL) {
return false;
}
if (!amduatd_edge_payload_encode(src, dst, rel, &payload)) {
return false;
}
if (amduat_asl_record_store_put(store,
amduat_octets(AMDUATD_EDGE_SCHEMA,
strlen(AMDUATD_EDGE_SCHEMA)),
payload,
&record_ref) != AMDUAT_ASL_STORE_OK) {
free((void *)payload.data);
return false;
}
free((void *)payload.data);
{
amduat_asl_collection_error_t append_err =
amduat_asl_collection_append(&c->edge_collection,
c->edge_collection_name,
record_ref,
AMDUATD_EDGE_COLLECTION_KIND,
actor,
&offset);
if (append_err != AMDUAT_ASL_COLLECTION_OK) {
if (c->edge_collection_name != NULL &&
!amduat_asl_pointer_name_is_valid(c->edge_collection_name)) {
amduat_log(AMDUAT_LOG_ERROR, "edge collection name invalid: %s",
c->edge_collection_name);
}
if (c->edge_collection_name != NULL) {
size_t name_len = strlen(c->edge_collection_name);
size_t total_len = 11u + name_len + 4u + 1u;
char *log_name = (char *)malloc(total_len);
if (log_name != NULL) {
size_t pos = 0u;
memcpy(log_name + pos, "collection/", 11u);
pos += 11u;
memcpy(log_name + pos, c->edge_collection_name, name_len);
pos += name_len;
memcpy(log_name + pos, "/log", 4u);
pos += 4u;
log_name[pos] = '\0';
if (!amduat_asl_pointer_name_is_valid(log_name)) {
amduat_log(AMDUAT_LOG_ERROR, "edge log name invalid: %s", log_name);
}
{
size_t head_len = 4u + pos + 5u + 1u;
char *head_name = (char *)malloc(head_len);
if (head_name != NULL) {
size_t hpos = 0u;
memcpy(head_name + hpos, "log/", 4u);
hpos += 4u;
memcpy(head_name + hpos, log_name, pos);
hpos += pos;
memcpy(head_name + hpos, "/head", 5u);
hpos += 5u;
head_name[hpos] = '\0';
if (!amduat_asl_pointer_name_is_valid(head_name)) {
amduat_log(AMDUAT_LOG_ERROR,
"edge log head name invalid: %s", head_name);
} else {
bool head_exists = false;
amduat_reference_t head_ref;
memset(&head_ref, 0, sizeof(head_ref));
amduat_asl_pointer_error_t ptr_err =
amduat_asl_pointer_get(&c->edge_collection.pointer_store,
head_name,
&head_exists,
&head_ref);
if (ptr_err != AMDUAT_ASL_POINTER_OK) {
amduat_log(AMDUAT_LOG_ERROR,
"edge log head pointer get failed: %d", ptr_err);
}
amduat_reference_free(&head_ref);
}
free(head_name);
}
}
free(log_name);
}
}
amduat_log(AMDUAT_LOG_ERROR, "edge append failed for %s (err=%d)",
c->edge_collection_name != NULL ? c->edge_collection_name
: "?",
(int)append_err);
amduat_reference_free(&record_ref);
return false;
}
}
memset(&entry, 0, sizeof(entry));
entry.record_ref = record_ref;
entry.src_ref = src;
entry.dst_ref = dst;
entry.rel = (char *)rel;
if (!amduatd_edge_list_push(&c->edges, &entry)) {
amduat_reference_free(&record_ref);
return false;
}
if (offset != 0u && (offset % 256u) == 0u) {
amduat_reference_t snapshot_ref;
bool swapped = false;
memset(&snapshot_ref, 0, sizeof(snapshot_ref));
(void)amduat_asl_collection_snapshot(&c->edge_collection,
c->edge_collection_name,
offset,
&snapshot_ref,
&swapped);
amduat_reference_free(&snapshot_ref);
}
if (out_ref != NULL) {
*out_ref = record_ref;
} else {
amduat_reference_free(&record_ref);
}
return true;
}
static bool amduatd_concepts_migrate_edges(amduatd_concepts_t *c,
amduat_asl_store_t *store) {
uint8_t *file_bytes = NULL;
size_t file_len = 0;
bool ok = true;
if (c == NULL || store == NULL) {
return false;
}
if (!amduatd_read_file(c->edges_path, &file_bytes, &file_len)) {
return true;
}
{
char *text = (char *)file_bytes;
char *line = text;
char *end = text + file_len;
while (line < end) {
char *nl = memchr(line, '\n', (size_t)(end - line));
size_t n = nl != NULL ? (size_t)(nl - line) : (size_t)(end - line);
char *tmp;
amduat_reference_t ref;
bool ok_ref;
while (n != 0 && (line[n - 1] == '\r' || line[n - 1] == ' ' ||
line[n - 1] == '\t')) {
n--;
}
while (n != 0 && (*line == ' ' || *line == '\t')) {
line++;
n--;
}
if (n != 0) {
tmp = (char *)malloc(n + 1u);
if (tmp == NULL) {
ok = false;
break;
}
memcpy(tmp, line, n);
tmp[n] = '\0';
memset(&ref, 0, sizeof(ref));
ok_ref = amduat_asl_ref_decode_hex(tmp, &ref);
free(tmp);
if (ok_ref) {
amduat_artifact_t artifact;
amduat_tgk_edge_body_t edge;
const char *rel_name = NULL;
memset(&artifact, 0, sizeof(artifact));
if (amduat_asl_store_get(store, ref, &artifact) ==
AMDUAT_ASL_STORE_OK &&
artifact.has_type_tag &&
artifact.type_tag.tag_id == AMDUAT_TYPE_TAG_TGK1_EDGE_V1) {
memset(&edge, 0, sizeof(edge));
if (amduat_enc_tgk1_edge_decode_v1(artifact.bytes, &edge)) {
if (edge.from_len == 1 && edge.to_len == 1) {
rel_name = amduatd_relation_name_for_ref(c, edge.payload);
}
if (rel_name != NULL) {
if (!amduatd_concepts_append_edge_record(c,
store,
edge.from[0],
edge.to[0],
rel_name,
amduat_octets(NULL, 0u),
NULL)) {
ok = false;
}
}
amduat_enc_tgk1_edge_free(&edge);
}
amduat_asl_artifact_free(&artifact);
} else if (artifact.bytes.data != NULL) {
amduat_asl_artifact_free(&artifact);
}
amduat_reference_free(&ref);
if (!ok) {
break;
}
}
}
if (nl == NULL) {
break;
}
line = nl + 1;
}
}
free(file_bytes);
if (ok) {
char migrated_path[1100];
(void)snprintf(migrated_path,
sizeof(migrated_path),
"%s.migrated",
c->edges_path);
(void)rename(c->edges_path, migrated_path);
}
return ok;
}
static bool amduatd_concepts_migrate_unscoped_edges(
amduatd_concepts_t *c,
amduat_asl_store_t *store,
const char *unscoped_collection) {
amduat_asl_collection_view_t view;
uint64_t from = 0u;
bool ok = false;
if (c == NULL || store == NULL || unscoped_collection == NULL) {
return false;
}
while (true) {
memset(&view, 0, sizeof(view));
if (!amduatd_collection_view(store,
c->root_path,
unscoped_collection,
from,
AMDUATD_EDGE_VIEW_BATCH,
&view)) {
return false;
}
for (size_t i = 0u; i < view.refs_len; ++i) {
uint64_t offset = 0u;
if (amduat_asl_collection_append(&c->edge_collection,
c->edge_collection_name,
view.refs[i],
AMDUATD_EDGE_COLLECTION_KIND,
amduat_octets(NULL, 0u),
&offset) != AMDUAT_ASL_COLLECTION_OK) {
goto migrate_cleanup;
}
if (offset != 0u && (offset % 256u) == 0u) {
amduat_reference_t snapshot_ref;
bool swapped = false;
memset(&snapshot_ref, 0, sizeof(snapshot_ref));
(void)amduat_asl_collection_snapshot(&c->edge_collection,
c->edge_collection_name,
offset,
&snapshot_ref,
&swapped);
amduat_reference_free(&snapshot_ref);
}
}
if (view.refs_len == 0u || view.computed_up_to_offset <= from) {
break;
}
from = view.computed_up_to_offset;
amduat_asl_collection_view_free(&view);
}
ok = true;
migrate_cleanup:
amduat_asl_collection_view_free(&view);
return ok;
}
static bool amduatd_concepts_edge_index_pointer_name(
const amduatd_space_t *space,
amduat_octets_t *out_pointer_name) {
return amduatd_space_edges_index_head_name(space, out_pointer_name);
}
static bool amduatd_concepts_put_edge_graph(amduat_asl_store_t *store,
const amduatd_edge_list_t *edges,
amduat_reference_t *out_ref) {
amduat_octets_t graph_bytes = amduat_octets(NULL, 0u);
amduat_artifact_t artifact;
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || edges == NULL || out_ref == NULL) {
return false;
}
if (!amduatd_edge_graph_encode(edges, &graph_bytes)) {
return false;
}
artifact = amduat_artifact_with_type(
graph_bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK_EDGE_GRAPH_1));
if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) {
free((void *)graph_bytes.data);
return false;
}
free((void *)graph_bytes.data);
return true;
}
static bool amduatd_concepts_load_edge_graph(amduat_asl_store_t *store,
amduat_reference_t graph_ref,
amduatd_edge_list_t *out_edges) {
amduat_artifact_t artifact;
if (store == NULL || out_edges == NULL) {
return false;
}
memset(&artifact, 0, sizeof(artifact));
if (amduat_asl_store_get(store, graph_ref, &artifact) != AMDUAT_ASL_STORE_OK) {
return false;
}
if (!artifact.has_type_tag ||
artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_TGK_EDGE_GRAPH_1) {
amduat_asl_artifact_free(&artifact);
return false;
}
if (!amduatd_edge_graph_decode(artifact.bytes, out_edges)) {
amduat_asl_artifact_free(&artifact);
return false;
}
amduat_asl_artifact_free(&artifact);
return true;
}
static bool amduatd_concepts_put_edge_index_state(
amduat_asl_store_t *store,
const amduatd_edge_index_state_t *state,
amduat_reference_t *out_ref) {
amduat_octets_t payload = amduat_octets(NULL, 0u);
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || state == NULL || out_ref == NULL) {
return false;
}
if (!amduatd_edge_index_state_encode(state, &payload)) {
return false;
}
if (amduat_asl_record_store_put(
store,
amduat_octets(AMDUATD_EDGE_INDEX_SCHEMA,
strlen(AMDUATD_EDGE_INDEX_SCHEMA)),
payload,
out_ref) != AMDUAT_ASL_STORE_OK) {
free((void *)payload.data);
return false;
}
free((void *)payload.data);
return true;
}
static bool amduatd_concepts_load_edge_index_state(
amduatd_concepts_t *c,
amduat_asl_store_t *store,
const amduatd_space_t *space,
amduatd_edge_index_state_t *out_state,
amduat_reference_t *out_state_ref,
bool *out_exists) {
amduat_octets_t pointer_name = amduat_octets(NULL, 0u);
bool exists = false;
amduat_reference_t state_ref;
amduat_asl_record_t record;
amduat_asl_store_error_t err;
bool ok = false;
if (out_exists != NULL) {
*out_exists = false;
}
if (out_state_ref != NULL) {
*out_state_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (c == NULL || store == NULL || out_state == NULL || out_exists == NULL) {
return false;
}
memset(out_state, 0, sizeof(*out_state));
if (!amduatd_concepts_edge_index_pointer_name(space, &pointer_name)) {
return false;
}
if (amduat_asl_pointer_get(&c->edge_collection.pointer_store,
(const char *)pointer_name.data,
&exists,
&state_ref) != AMDUAT_ASL_POINTER_OK) {
free((void *)pointer_name.data);
return false;
}
free((void *)pointer_name.data);
if (!exists) {
*out_exists = false;
return true;
}
memset(&record, 0, sizeof(record));
err = amduat_asl_record_store_get(store, state_ref, &record);
if (err != AMDUAT_ASL_STORE_OK) {
amduat_reference_free(&state_ref);
return false;
}
if (record.schema.len != strlen(AMDUATD_EDGE_INDEX_SCHEMA) ||
memcmp(record.schema.data, AMDUATD_EDGE_INDEX_SCHEMA,
record.schema.len) != 0) {
amduat_asl_record_free(&record);
amduat_reference_free(&state_ref);
return false;
}
ok = amduatd_edge_index_state_decode(record.payload, out_state);
amduat_asl_record_free(&record);
if (!ok) {
amduat_reference_free(&state_ref);
amduatd_edge_index_state_free(out_state);
return false;
}
*out_exists = true;
if (out_state_ref != NULL) {
*out_state_ref = state_ref;
} else {
amduat_reference_free(&state_ref);
}
return true;
}
static bool amduatd_concepts_write_edge_index_state(
amduatd_concepts_t *c,
amduat_asl_store_t *store,
const amduatd_space_t *space,
uint64_t indexed_up_to_offset,
const amduatd_edge_list_t *edges,
bool expected_exists,
const amduat_reference_t *expected_ref,
bool *out_swapped) {
amduat_reference_t graph_ref;
amduat_reference_t state_ref;
amduatd_edge_index_state_t state;
amduat_octets_t pointer_name = amduat_octets(NULL, 0u);
bool swapped = false;
bool ok = false;
if (out_swapped != NULL) {
*out_swapped = false;
}
if (c == NULL || store == NULL || edges == NULL) {
return false;
}
if (expected_exists && expected_ref == NULL) {
return false;
}
memset(&graph_ref, 0, sizeof(graph_ref));
memset(&state_ref, 0, sizeof(state_ref));
memset(&state, 0, sizeof(state));
state.indexed_up_to_offset = indexed_up_to_offset;
if (!amduatd_concepts_put_edge_graph(store, edges, &graph_ref)) {
return false;
}
state.has_graph_ref = true;
state.graph_ref = graph_ref;
if (!amduatd_concepts_put_edge_index_state(store, &state, &state_ref)) {
amduat_reference_free(&graph_ref);
return false;
}
if (!amduatd_concepts_edge_index_pointer_name(space, &pointer_name)) {
amduat_reference_free(&graph_ref);
amduat_reference_free(&state_ref);
return false;
}
if (amduat_asl_pointer_cas(&c->edge_collection.pointer_store,
(const char *)pointer_name.data,
expected_exists,
expected_ref,
&state_ref,
&swapped) != AMDUAT_ASL_POINTER_OK) {
goto cleanup;
}
ok = true;
cleanup:
if (out_swapped != NULL) {
*out_swapped = swapped;
}
free((void *)pointer_name.data);
amduat_reference_free(&graph_ref);
amduat_reference_free(&state_ref);
return ok;
}
static bool amduatd_concepts_refresh_edges_internal(
amduat_asl_store_t *store,
amduatd_concepts_t *c,
const amduatd_space_t *space,
size_t max_new_entries) {
amduatd_edge_index_state_t state;
amduat_reference_t state_ref;
bool state_exists = false;
bool ok = false;
if (store == NULL || c == NULL) {
return false;
}
if (max_new_entries == 0u) {
max_new_entries = AMDUATD_EDGE_REFRESH_BATCH;
}
for (int attempt = 0; attempt < 2; ++attempt) {
amduat_asl_log_entry_t *entries = NULL;
size_t entries_len = 0u;
uint64_t next_offset = 0u;
bool end = false;
char *log_name = NULL;
bool swapped = false;
size_t applied = 0u;
memset(&state, 0, sizeof(state));
memset(&state_ref, 0, sizeof(state_ref));
if (!amduatd_concepts_load_edge_index_state(
c, store, space, &state, &state_ref, &state_exists)) {
return false;
}
if (!amduatd_build_collection_log_name(c->edge_collection_name, &log_name)) {
amduatd_edge_index_state_free(&state);
amduat_reference_free(&state_ref);
return false;
}
if (amduat_asl_log_read(&c->edge_collection.log_store,
log_name,
state_exists ? state.indexed_up_to_offset : 0u,
max_new_entries,
&entries,
&entries_len,
&next_offset,
&end) != AMDUAT_ASL_STORE_OK) {
free(log_name);
amduatd_edge_index_state_free(&state);
amduat_reference_free(&state_ref);
return false;
}
free(log_name);
(void)end;
if (entries_len == 0u) {
amduat_asl_log_entries_free(entries, entries_len);
amduatd_edge_index_state_free(&state);
amduat_reference_free(&state_ref);
return false;
}
for (size_t i = 0u; i < entries_len; ++i) {
amduat_reference_t record_ref;
amduat_asl_record_t record;
amduat_reference_t src_ref;
amduat_reference_t dst_ref;
char *rel = NULL;
amduatd_edge_entry_t entry;
bool schema_ok = false;
if (entries[i].kind != AMDUATD_EDGE_COLLECTION_KIND) {
continue;
}
memset(&record_ref, 0, sizeof(record_ref));
if (!amduat_reference_clone(entries[i].payload_ref, &record_ref)) {
continue;
}
memset(&record, 0, sizeof(record));
if (amduat_asl_record_store_get(store, record_ref, &record) !=
AMDUAT_ASL_STORE_OK) {
amduat_reference_free(&record_ref);
continue;
}
if (record.schema.len == strlen(AMDUATD_EDGE_SCHEMA) &&
memcmp(record.schema.data, AMDUATD_EDGE_SCHEMA,
record.schema.len) == 0) {
schema_ok = true;
}
if (!schema_ok ||
!amduatd_edge_payload_decode(record.payload,
&src_ref,
&dst_ref,
&rel)) {
amduat_asl_record_free(&record);
amduat_reference_free(&record_ref);
continue;
}
amduat_asl_record_free(&record);
memset(&entry, 0, sizeof(entry));
entry.record_ref = record_ref;
entry.src_ref = src_ref;
entry.dst_ref = dst_ref;
entry.rel = rel;
if (amduatd_edge_list_push(&c->edges, &entry)) {
applied++;
}
amduatd_edge_entry_free(&entry);
}
ok = amduatd_concepts_write_edge_index_state(
c,
store,
space,
next_offset,
&c->edges,
state_exists,
state_exists ? &state_ref : NULL,
&swapped);
amduat_asl_log_entries_free(entries, entries_len);
amduatd_edge_index_state_free(&state);
amduat_reference_free(&state_ref);
if (!ok) {
return false;
}
if (swapped) {
return applied != 0u;
}
if (attempt == 0) {
amduatd_edge_list_t refreshed;
amduatd_edge_index_state_t latest;
amduat_reference_t latest_ref;
bool latest_exists = false;
memset(&refreshed, 0, sizeof(refreshed));
memset(&latest, 0, sizeof(latest));
memset(&latest_ref, 0, sizeof(latest_ref));
if (!amduatd_concepts_load_edge_index_state(
c, store, space, &latest, &latest_ref, &latest_exists)) {
return false;
}
if (latest_exists && latest.has_graph_ref &&
amduatd_concepts_load_edge_graph(store, latest.graph_ref,
&refreshed)) {
amduatd_edge_list_clear(&c->edges);
c->edges = refreshed;
} else {
amduatd_edge_list_clear(&refreshed);
}
amduatd_edge_index_state_free(&latest);
amduat_reference_free(&latest_ref);
}
}
return false;
}
bool amduatd_concepts_init(amduatd_concepts_t *c,
amduat_asl_store_t *store,
const amduatd_space_t *space,
const char *root_path,
bool enable_migrations) {
amduat_octets_t scoped_collection = amduat_octets(NULL, 0u);
amduat_octets_t index_head_name = amduat_octets(NULL, 0u);
amduat_reference_t index_head_ref;
bool index_head_exists = false;
uint64_t indexed_up_to_offset = 0u;
bool used_index = false;
if (c == NULL || store == NULL || root_path == NULL) {
return false;
}
memset(c, 0, sizeof(*c));
memset(&index_head_ref, 0, sizeof(index_head_ref));
c->root_path = root_path;
(void)snprintf(c->edges_path, sizeof(c->edges_path), "%s/%s", root_path,
AMDUATD_EDGES_FILE);
if (!amduatd_space_edges_collection_name(space, &scoped_collection)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: scope edges failed");
return false;
}
c->edge_collection_name = (char *)scoped_collection.data;
if (c->edge_collection_name == NULL) {
return false;
}
if (!amduat_asl_collection_store_init(&c->edge_collection, root_path,
store)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: collection store init failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "aliases",
&c->rel_aliases_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed aliases failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.aliases",
c->rel_aliases_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.aliases failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "materializesAs",
&c->rel_materializes_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed materializesAs failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.materializes_as",
c->rel_materializes_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: ensure ms.materializes_as failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "represents",
&c->rel_represents_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed represents failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.represents",
c->rel_represents_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: ensure ms.represents failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "requiresKey",
&c->rel_requires_key_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed requiresKey failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.requires_key",
c->rel_requires_key_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: ensure ms.requires_key failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "withinDomain",
&c->rel_within_domain_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed withinDomain failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.within_domain",
c->rel_within_domain_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: ensure ms.within_domain failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "computedBy",
&c->rel_computed_by_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed computedBy failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.computed_by",
c->rel_computed_by_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: ensure ms.computed_by failed");
return false;
}
if (!amduatd_concepts_seed_relation(store, space, "hasProvenance",
&c->rel_has_provenance_ref)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: seed hasProvenance failed");
return false;
}
if (!amduatd_concepts_ensure_alias(c, store, space, "ms.has_provenance",
c->rel_has_provenance_ref)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: ensure ms.has_provenance failed");
return false;
}
if (amduatd_concepts_edge_index_pointer_name(space, &index_head_name)) {
(void)amduat_asl_pointer_get(&c->edge_collection.pointer_store,
(const char *)index_head_name.data,
&index_head_exists,
&index_head_ref);
free((void *)index_head_name.data);
}
{
amduatd_edge_index_state_t state;
amduat_reference_t state_ref;
bool state_exists = false;
bool have_index = false;
memset(&state, 0, sizeof(state));
memset(&state_ref, 0, sizeof(state_ref));
if (amduatd_concepts_load_edge_index_state(c, store, space,
&state, &state_ref,
&state_exists) &&
state_exists && state.has_graph_ref &&
amduatd_concepts_load_edge_graph(store, state.graph_ref, &c->edges)) {
indexed_up_to_offset = state.indexed_up_to_offset;
have_index = true;
}
amduatd_edge_index_state_free(&state);
amduat_reference_free(&state_ref);
if (have_index) {
used_index = true;
(void)amduatd_concepts_refresh_edges_internal(
store, c, space, AMDUATD_EDGE_REFRESH_BATCH);
}
}
if (!used_index) {
if (!amduatd_concepts_load_edges(c, store, &indexed_up_to_offset)) {
amduat_log(AMDUAT_LOG_ERROR, "concepts init: load edges failed");
amduat_reference_free(&index_head_ref);
return false;
}
if (enable_migrations && c->edges.len == 0u) {
if (space != NULL && space->enabled) {
if (amduatd_space_should_migrate_unscoped_edges(space)) {
if (!amduatd_concepts_migrate_unscoped_edges(
c,
store,
AMDUATD_EDGE_COLLECTION)) {
amduat_reference_free(&index_head_ref);
return false;
}
if (!amduatd_concepts_load_edges(c, store, &indexed_up_to_offset)) {
amduat_reference_free(&index_head_ref);
return false;
}
}
} else {
if (!amduatd_concepts_migrate_edges(c, store)) {
amduat_reference_free(&index_head_ref);
return false;
}
if (!amduatd_concepts_load_edges(c, store, &indexed_up_to_offset)) {
amduat_reference_free(&index_head_ref);
return false;
}
}
}
{
bool swapped = false;
if (!amduatd_concepts_write_edge_index_state(
c,
store,
space,
indexed_up_to_offset,
&c->edges,
index_head_exists,
index_head_exists ? &index_head_ref : NULL,
&swapped)) {
amduat_log(AMDUAT_LOG_ERROR,
"concepts init: write edge index failed");
}
}
}
amduat_reference_free(&index_head_ref);
return true;
}
bool amduatd_concepts_refresh_edges(amduatd_ctx_t *ctx,
size_t max_new_entries) {
const amduatd_space_t *space = NULL;
if (ctx == NULL || ctx->store == NULL || ctx->concepts == NULL) {
return false;
}
if (ctx->daemon_cfg != NULL) {
space = &ctx->daemon_cfg->space;
}
return amduatd_concepts_refresh_edges_internal(ctx->store,
ctx->concepts,
space,
max_new_entries);
}
static bool amduatd_concepts_derive_name_ref(
amduat_asl_store_t *store,
const char *name,
amduat_reference_t *out_ref) {
uint8_t *bytes = NULL;
size_t bytes_len = 0;
amduat_artifact_t artifact;
if (out_ref == NULL) {
return false;
}
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
if (store == NULL || name == NULL) {
return false;
}
if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes,
&bytes_len)) {
return false;
}
artifact = amduat_artifact(amduat_octets(bytes, bytes_len));
if (!amduat_asl_ref_derive(artifact,
store->config.encoding_profile_id,
store->config.hash_id,
out_ref,
NULL)) {
free(bytes);
return false;
}
free(bytes);
return true;
}
static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store,
const char *name,
amduat_reference_t *out_ref) {
uint8_t *bytes = NULL;
size_t bytes_len = 0;
amduat_artifact_t artifact;
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || name == NULL || out_ref == NULL) {
return false;
}
if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes,
&bytes_len)) {
return false;
}
artifact = amduat_artifact(amduat_octets(bytes, bytes_len));
if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) {
free(bytes);
return false;
}
free(bytes);
return true;
}
static bool amduatd_concepts_put_concept_id(amduat_asl_store_t *store,
amduat_reference_t *out_ref) {
uint8_t rnd[16];
uint8_t *bytes = NULL;
size_t bytes_len = 0;
amduat_artifact_t artifact;
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || out_ref == NULL) {
return false;
}
if (!amduatd_read_urandom(rnd, sizeof(rnd))) {
return false;
}
bytes_len = strlen("AMDUATD/CONCEPT-ID/1") + 1u + sizeof(rnd);
bytes = (uint8_t *)malloc(bytes_len);
if (bytes == NULL) {
return false;
}
memcpy(bytes, "AMDUATD/CONCEPT-ID/1", strlen("AMDUATD/CONCEPT-ID/1"));
bytes[strlen("AMDUATD/CONCEPT-ID/1")] = 0;
memcpy(bytes + strlen("AMDUATD/CONCEPT-ID/1") + 1u, rnd, sizeof(rnd));
artifact = amduat_artifact(amduat_octets(bytes, bytes_len));
if (!amduat_asl_ref_derive(artifact,
store->config.encoding_profile_id,
store->config.hash_id,
out_ref,
NULL)) {
free(bytes);
return false;
}
if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) {
free(bytes);
return false;
}
free(bytes);
return true;
}
static bool amduatd_concepts_put_edge(amduat_asl_store_t *store,
amduatd_concepts_t *c,
amduat_reference_t from,
amduat_reference_t to,
amduat_reference_t relation_concept_ref,
amduat_octets_t actor,
amduat_reference_t *out_edge_ref) {
const char *rel_name = NULL;
if (out_edge_ref != NULL) {
*out_edge_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || c == NULL || out_edge_ref == NULL) {
return false;
}
rel_name = amduatd_relation_name_for_ref(c, relation_concept_ref);
if (rel_name == NULL) {
return false;
}
return amduatd_concepts_append_edge_record(c,
store,
from,
to,
rel_name,
actor,
out_edge_ref);
}
static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store,
const amduatd_concepts_t *c,
const char *name,
amduat_reference_t *out_concept_ref) {
amduat_reference_t name_ref;
size_t i;
if (out_concept_ref != NULL) {
*out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || c == NULL || name == NULL || out_concept_ref == NULL) {
return false;
}
if (!amduatd_concepts_derive_name_ref(store, name, &name_ref)) {
return false;
}
for (i = c->edges.len; i > 0; --i) {
const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u];
if (entry->rel == NULL) {
continue;
}
if (strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) {
continue;
}
if (amduat_reference_eq(entry->src_ref, name_ref)) {
amduat_reference_clone(entry->dst_ref, out_concept_ref);
amduat_reference_free(&name_ref);
return true;
}
}
amduat_reference_free(&name_ref);
return false;
}
static bool amduatd_concepts_resolve_latest(amduat_asl_store_t *store,
const amduatd_concepts_t *c,
amduat_reference_t concept_ref,
amduat_reference_t *out_ref) {
size_t i;
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || c == NULL || out_ref == NULL) {
return false;
}
for (i = c->edges.len; i > 0; --i) {
const amduatd_edge_entry_t *entry = &c->edges.items[i - 1u];
if (entry->rel == NULL) {
continue;
}
if (strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) {
continue;
}
if (amduat_reference_eq(entry->src_ref, concept_ref)) {
amduat_reference_clone(entry->dst_ref, out_ref);
return true;
}
}
return false;
}
static bool amduatd_parse_name_artifact(amduat_artifact_t artifact,
const amduatd_cfg_t *dcfg,
char *out,
size_t cap) {
const uint8_t *bytes;
size_t len;
const char *prefix = "AMDUATD/NAME/1";
size_t prefix_len;
size_t i;
size_t name_len;
char scoped[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
amduat_octets_t unscoped = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
if (out != NULL && cap != 0) {
out[0] = '\0';
}
if (out == NULL || cap == 0) {
return false;
}
if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) {
return false;
}
bytes = artifact.bytes.data;
len = artifact.bytes.len;
prefix_len = strlen(prefix);
if (len < prefix_len + 1u) {
return false;
}
if (memcmp(bytes, prefix, prefix_len) != 0 || bytes[prefix_len] != 0) {
return false;
}
for (i = prefix_len + 1u; i < len; ++i) {
if (bytes[i] == 0) {
return false;
}
}
name_len = len - (prefix_len + 1u);
if (name_len == 0 || name_len >= sizeof(scoped)) {
return false;
}
memcpy(scoped, bytes + prefix_len + 1u, name_len);
scoped[name_len] = '\0';
if (!amduatd_space_unscoped_name(space, scoped, &unscoped)) {
return false;
}
if (unscoped.len >= cap) {
free((void *)unscoped.data);
return false;
}
if (unscoped.len != 0u) {
memcpy(out, unscoped.data, unscoped.len);
}
out[unscoped.len] = '\0';
free((void *)unscoped.data);
return true;
}
static bool amduatd_strbuf_reserve(amduatd_strbuf_t *b, size_t extra);
static bool amduatd_strbuf_append(amduatd_strbuf_t *b,
const char *data,
size_t n);
static void amduatd_strbuf_free(amduatd_strbuf_t *b);
static bool amduatd_strbuf_append_cstr(amduatd_strbuf_t *b, const char *s);
static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c);
static bool amduatd_handle_get_concepts(int fd,
amduat_asl_store_t *store,
const amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg) {
amduatd_strbuf_t b;
bool first = true;
size_t i;
if (store == NULL || concepts == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
memset(&b, 0, sizeof(b));
if (!amduatd_strbuf_append_cstr(&b, "{\"concepts\":[")) {
amduatd_strbuf_free(&b);
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
for (i = 0; i < concepts->edges.len; ++i) {
const amduatd_edge_entry_t *entry = &concepts->edges.items[i];
amduat_artifact_t artifact;
amduat_asl_store_error_t err;
char name[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
char *concept_hex = NULL;
if (entry->rel == NULL ||
strcmp(entry->rel, AMDUATD_REL_ALIAS) != 0) {
continue;
}
memset(&artifact, 0, sizeof(artifact));
err = amduat_asl_store_get(store, entry->src_ref, &artifact);
if (err != AMDUAT_ASL_STORE_OK ||
!amduatd_parse_name_artifact(artifact, dcfg, name, sizeof(name))) {
amduat_asl_artifact_free(&artifact);
continue;
}
amduat_asl_artifact_free(&artifact);
if (!amduat_asl_ref_encode_hex(entry->dst_ref, &concept_hex)) {
continue;
}
if (!first) {
(void)amduatd_strbuf_append_char(&b, ',');
}
first = false;
(void)amduatd_strbuf_append_cstr(&b, "{\"name\":\"");
(void)amduatd_strbuf_append_cstr(&b, name);
(void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\"");
(void)amduatd_strbuf_append_cstr(&b, concept_hex);
(void)amduatd_strbuf_append_cstr(&b, "\"}");
free(concept_hex);
}
if (!amduatd_strbuf_append_cstr(&b, "]}\n")) {
amduatd_strbuf_free(&b);
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
{
bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
amduatd_strbuf_free(&b);
return ok;
}
}
static bool amduatd_append_relation_entry(amduatd_strbuf_t *b,
bool *first,
const char *name,
amduat_reference_t ref) {
char *hex = NULL;
if (b == NULL || first == NULL || name == NULL) {
return false;
}
if (!amduat_asl_ref_encode_hex(ref, &hex)) {
return false;
}
if (!*first) {
(void)amduatd_strbuf_append_char(b, ',');
}
*first = false;
(void)amduatd_strbuf_append_cstr(b, "{\"name\":\"");
(void)amduatd_strbuf_append_cstr(b, name);
(void)amduatd_strbuf_append_cstr(b, "\",\"concept_ref\":\"");
(void)amduatd_strbuf_append_cstr(b, hex);
(void)amduatd_strbuf_append_cstr(b, "\"}");
free(hex);
return true;
}
static bool amduatd_handle_get_relations(int fd,
const amduatd_concepts_t *concepts) {
amduatd_strbuf_t b;
bool first = true;
if (concepts == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
memset(&b, 0, sizeof(b));
if (!amduatd_strbuf_append_cstr(&b, "{\"relations\":[")) {
amduatd_strbuf_free(&b);
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
if (!amduatd_append_relation_entry(&b, &first, "ms.aliases",
concepts->rel_aliases_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.materializes_as",
concepts->rel_materializes_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.represents",
concepts->rel_represents_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.requires_key",
concepts->rel_requires_key_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.within_domain",
concepts->rel_within_domain_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.computed_by",
concepts->rel_computed_by_ref) ||
!amduatd_append_relation_entry(&b, &first, "ms.has_provenance",
concepts->rel_has_provenance_ref)) {
amduatd_strbuf_free(&b);
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
(void)amduatd_strbuf_append_cstr(&b, "]}\n");
{
bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
amduatd_strbuf_free(&b);
return ok;
}
}
static bool amduatd_handle_get_concept(int fd,
amduat_asl_store_t *store,
const amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const char *name) {
amduat_reference_t concept_ref;
amduat_reference_t latest_ref;
amduatd_strbuf_t b;
char *concept_hex = NULL;
char *latest_hex = NULL;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
bool have_latest = false;
size_t i;
size_t version_count = 0;
memset(&concept_ref, 0, sizeof(concept_ref));
memset(&latest_ref, 0, sizeof(latest_ref));
memset(&b, 0, sizeof(b));
if (store == NULL || concepts == NULL || name == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (!amduatd_space_scope_name(space, name, &scoped_bytes)) {
return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name");
}
scoped_name = (char *)scoped_bytes.data;
amduatd_space_log_mapping(space, name, scoped_name);
if (!amduatd_concepts_lookup_alias(store, concepts, scoped_name,
&concept_ref)) {
free(scoped_name);
return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept");
}
free(scoped_name);
if (amduatd_concepts_resolve_latest(store, concepts, concept_ref,
&latest_ref)) {
have_latest = true;
}
if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) {
amduat_reference_free(&concept_ref);
amduat_reference_free(&latest_ref);
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"encode error");
}
if (have_latest) {
if (!amduat_asl_ref_encode_hex(latest_ref, &latest_hex)) {
free(concept_hex);
amduat_reference_free(&concept_ref);
amduat_reference_free(&latest_ref);
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"encode error");
}
}
if (!amduatd_strbuf_append_cstr(&b, "{")) {
free(concept_hex);
free(latest_hex);
amduat_reference_free(&concept_ref);
amduat_reference_free(&latest_ref);
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
(void)amduatd_strbuf_append_cstr(&b, "\"name\":\"");
(void)amduatd_strbuf_append_cstr(&b, name);
(void)amduatd_strbuf_append_cstr(&b, "\",\"concept_ref\":\"");
(void)amduatd_strbuf_append_cstr(&b, concept_hex);
(void)amduatd_strbuf_append_cstr(&b, "\",\"latest_ref\":");
if (latest_hex != NULL) {
(void)amduatd_strbuf_append_cstr(&b, "\"");
(void)amduatd_strbuf_append_cstr(&b, latest_hex);
(void)amduatd_strbuf_append_cstr(&b, "\"");
} else {
(void)amduatd_strbuf_append_cstr(&b, "null");
}
(void)amduatd_strbuf_append_cstr(&b, ",\"versions\":[");
for (i = 0; i < concepts->edges.len; ++i) {
const amduatd_edge_entry_t *entry = &concepts->edges.items[i];
char *edge_hex = NULL;
char *ref_hex = NULL;
if (entry->rel == NULL ||
strcmp(entry->rel, AMDUATD_REL_MATERIALIZES) != 0) {
continue;
}
if (!amduat_reference_eq(entry->src_ref, concept_ref)) {
continue;
}
if (!amduat_asl_ref_encode_hex(entry->record_ref, &edge_hex) ||
!amduat_asl_ref_encode_hex(entry->dst_ref, &ref_hex)) {
free(edge_hex);
free(ref_hex);
continue;
}
if (version_count != 0) {
(void)amduatd_strbuf_append_char(&b, ',');
}
version_count++;
(void)amduatd_strbuf_append_cstr(&b, "{\"edge_ref\":\"");
(void)amduatd_strbuf_append_cstr(&b, edge_hex);
(void)amduatd_strbuf_append_cstr(&b, "\",\"ref\":\"");
(void)amduatd_strbuf_append_cstr(&b, ref_hex);
(void)amduatd_strbuf_append_cstr(&b, "\"}");
free(edge_hex);
free(ref_hex);
if (version_count >= 64u) {
break;
}
}
(void)amduatd_strbuf_append_cstr(&b, "]}\n");
free(concept_hex);
free(latest_hex);
amduat_reference_free(&concept_ref);
amduat_reference_free(&latest_ref);
{
bool ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
amduatd_strbuf_free(&b);
return ok;
}
}
static bool amduatd_decode_ref_hex_str(const char *s,
size_t len,
amduat_reference_t *out_ref) {
char *tmp = NULL;
bool ok;
if (out_ref == NULL) {
return false;
}
memset(out_ref, 0, sizeof(*out_ref));
if (!amduatd_copy_json_str(s, len, &tmp)) {
return false;
}
ok = amduat_asl_ref_decode_hex(tmp, out_ref);
free(tmp);
return ok;
}
amduatd_ref_status_t amduatd_decode_ref_or_name_latest(
amduat_asl_store_t *store,
const amduat_asl_store_fs_config_t *cfg,
const amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const char *s,
size_t len,
amduat_reference_t *out_ref) {
amduat_reference_t concept_ref;
char *tmp = NULL;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
(void)cfg;
if (out_ref != NULL) {
memset(out_ref, 0, sizeof(*out_ref));
}
if (store == NULL || cfg == NULL || concepts == NULL || s == NULL ||
out_ref == NULL) {
return AMDUATD_REF_ERR_INTERNAL;
}
if (amduatd_decode_ref_hex_str(s, len, out_ref)) {
return AMDUATD_REF_OK;
}
if (!amduatd_copy_json_str(s, len, &tmp)) {
return AMDUATD_REF_ERR_INTERNAL;
}
if (!amduatd_space_scope_name(space, tmp, &scoped_bytes)) {
free(tmp);
return AMDUATD_REF_ERR_INVALID;
}
scoped_name = (char *)scoped_bytes.data;
amduatd_space_log_mapping(space, tmp, scoped_name);
memset(&concept_ref, 0, sizeof(concept_ref));
if (!amduatd_concepts_lookup_alias(store,
concepts,
scoped_name,
&concept_ref)) {
free(scoped_name);
free(tmp);
return AMDUATD_REF_ERR_NOT_FOUND;
}
free(scoped_name);
free(tmp);
if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, out_ref)) {
amduat_reference_free(&concept_ref);
return AMDUATD_REF_ERR_NOT_FOUND;
}
amduat_reference_free(&concept_ref);
return AMDUATD_REF_OK;
}
static bool amduatd_seed_concept_if_missing(
amduat_asl_store_t *store,
amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const char *name,
amduat_reference_t *out_concept_ref) {
amduat_reference_t name_ref;
amduat_reference_t concept_ref;
amduat_reference_t edge_ref;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
bool ok = false;
if (out_concept_ref != NULL) {
*out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || concepts == NULL || name == NULL ||
out_concept_ref == NULL) {
return false;
}
memset(&name_ref, 0, sizeof(name_ref));
memset(&concept_ref, 0, sizeof(concept_ref));
memset(&edge_ref, 0, sizeof(edge_ref));
if (!amduatd_space_scope_name(space, name, &scoped_bytes)) {
return false;
}
scoped_name = (char *)scoped_bytes.data;
if (amduatd_concepts_lookup_alias(store, concepts, scoped_name,
out_concept_ref)) {
ok = true;
goto seed_cleanup;
}
if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) {
goto seed_cleanup;
}
if (!amduatd_concepts_put_concept_id(store, &concept_ref)) {
goto seed_cleanup;
}
if (!amduatd_concepts_put_edge(store,
concepts,
name_ref,
concept_ref,
concepts->rel_aliases_ref,
amduat_octets(NULL, 0u),
&edge_ref)) {
goto seed_cleanup;
}
*out_concept_ref = concept_ref;
ok = true;
goto seed_cleanup;
seed_cleanup:
if (!ok) {
amduat_reference_free(&concept_ref);
}
amduat_reference_free(&name_ref);
amduat_reference_free(&edge_ref);
free(scoped_name);
return ok;
}
static bool amduatd_seed_store_artifact(amduat_asl_store_t *store,
amduat_artifact_t artifact,
amduat_reference_t *out_ref) {
if (out_ref != NULL) {
*out_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || out_ref == NULL) {
return false;
}
if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) {
return false;
}
return true;
}
static bool amduatd_seed_pel_identity_program(
amduat_asl_store_t *store,
amduat_reference_t *out_program_ref) {
amduat_pel_program_t program;
amduat_pel_node_t node;
amduat_pel_dag_input_t inputs[1];
amduat_pel_root_ref_t root;
amduat_octets_t program_bytes;
amduat_artifact_t artifact;
if (out_program_ref != NULL) {
*out_program_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
}
if (store == NULL || out_program_ref == NULL) {
return false;
}
memset(&program, 0, sizeof(program));
memset(&node, 0, sizeof(node));
memset(inputs, 0, sizeof(inputs));
memset(&root, 0, sizeof(root));
program_bytes = amduat_octets(NULL, 0u);
inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL;
inputs[0].value.external.input_index = 0;
node.id = 1;
node.op.name = amduat_octets("pel.bytes.concat",
strlen("pel.bytes.concat"));
node.op.version = 1;
node.inputs = inputs;
node.inputs_len = 1;
node.params = amduat_octets(NULL, 0u);
root.node_id = 1;
root.output_index = 0;
program.nodes = &node;
program.nodes_len = 1;
program.roots = &root;
program.roots_len = 1;
if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) {
return false;
}
artifact = amduat_artifact_with_type(program_bytes,
amduat_type_tag(0x00000101u));
if (!amduatd_seed_store_artifact(store, artifact, out_program_ref)) {
free((void *)program_bytes.data);
return false;
}
free((void *)program_bytes.data);
return true;
}
static bool amduatd_seed_materializes_if_missing(
amduat_asl_store_t *store,
amduatd_concepts_t *concepts,
amduat_reference_t concept_ref,
amduat_reference_t target_ref) {
amduat_reference_t latest;
amduat_reference_t edge_ref;
memset(&latest, 0, sizeof(latest));
if (amduatd_concepts_resolve_latest(store, concepts, concept_ref, &latest)) {
amduat_reference_free(&latest);
return true;
}
if (!amduatd_concepts_put_edge(store,
concepts,
concept_ref,
target_ref,
concepts->rel_materializes_ref,
amduat_octets(NULL, 0u),
&edge_ref)) {
return false;
}
amduat_reference_free(&edge_ref);
return true;
}
static bool amduatd_bytes_contains(const uint8_t *data,
size_t len,
const char *needle) {
size_t needle_len;
if (data == NULL || needle == NULL) {
return false;
}
needle_len = strlen(needle);
if (needle_len == 0 || needle_len > len) {
return false;
}
for (size_t i = 0; i + needle_len <= len; ++i) {
if (memcmp(data + i, needle, needle_len) == 0) {
return true;
}
}
return false;
}
typedef struct {
char *key_hex;
char *type_hex;
char *key_text;
char *value_hex;
} amduatd_seed_entry_t;
static int amduatd_seed_entry_cmp(const void *a, const void *b) {
const amduatd_seed_entry_t *x = (const amduatd_seed_entry_t *)a;
const amduatd_seed_entry_t *y = (const amduatd_seed_entry_t *)b;
if (x == NULL || y == NULL) {
return 0;
}
return strcmp(x->key_hex != NULL ? x->key_hex : "",
y->key_hex != NULL ? y->key_hex : "");
}
bool amduatd_seed_ms_ui_state(amduat_asl_store_t *store,
amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg) {
amduat_reference_t type_string_ref;
amduat_reference_t type_ref_ref;
amduat_reference_t key_title_ref;
amduat_reference_t key_status_ref;
amduat_reference_t key_latest_ref;
amduat_reference_t schema_concept_ref;
amduat_reference_t registry_concept_ref;
amduat_reference_t identity_program_ref;
amduat_reference_t schema_input_ref;
amduat_reference_t registry_input_ref;
amduat_reference_t schema_output_ref;
amduat_reference_t registry_output_ref;
amduat_reference_t seed_manifest_ref;
amduat_reference_t seed_environment_ref;
amduat_reference_t seed_executor_ref;
amduat_reference_t receipt_ref;
amduat_pel_run_result_t run_result;
amduatd_seed_entry_t fields[3];
amduatd_seed_entry_t entries[3];
amduatd_strbuf_t b;
amduat_artifact_t artifact;
char *hello_hex = NULL;
bool hello_hex_owned = false;
size_t i;
bool ok = false;
memset(&type_string_ref, 0, sizeof(type_string_ref));
memset(&type_ref_ref, 0, sizeof(type_ref_ref));
memset(&key_title_ref, 0, sizeof(key_title_ref));
memset(&key_status_ref, 0, sizeof(key_status_ref));
memset(&key_latest_ref, 0, sizeof(key_latest_ref));
memset(&schema_concept_ref, 0, sizeof(schema_concept_ref));
memset(&registry_concept_ref, 0, sizeof(registry_concept_ref));
memset(&identity_program_ref, 0, sizeof(identity_program_ref));
memset(&schema_input_ref, 0, sizeof(schema_input_ref));
memset(&registry_input_ref, 0, sizeof(registry_input_ref));
memset(&schema_output_ref, 0, sizeof(schema_output_ref));
memset(&registry_output_ref, 0, sizeof(registry_output_ref));
memset(&seed_manifest_ref, 0, sizeof(seed_manifest_ref));
memset(&seed_environment_ref, 0, sizeof(seed_environment_ref));
memset(&seed_executor_ref, 0, sizeof(seed_executor_ref));
memset(&receipt_ref, 0, sizeof(receipt_ref));
memset(&run_result, 0, sizeof(run_result));
memset(fields, 0, sizeof(fields));
memset(entries, 0, sizeof(entries));
memset(&b, 0, sizeof(b));
memset(&artifact, 0, sizeof(artifact));
if (!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.type.string",
&type_string_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.type.ref",
&type_ref_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.ui.field.title",
&key_title_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.ui.field.status",
&key_status_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.ui.field.latest_ref",
&key_latest_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.ui.state.schema.v1",
&schema_concept_ref) ||
!amduatd_seed_concept_if_missing(store, concepts, dcfg,
"ms.ui.state.registry.v1",
&registry_concept_ref)) {
goto seed_cleanup;
}
if (!amduatd_seed_pel_identity_program(store, &identity_program_ref)) {
goto seed_cleanup;
}
{
amduat_reference_t latest_schema;
amduat_reference_t latest_registry;
bool have_schema = false;
bool have_registry = false;
bool schema_ok = false;
bool registry_ok = false;
memset(&latest_schema, 0, sizeof(latest_schema));
memset(&latest_registry, 0, sizeof(latest_registry));
have_schema = amduatd_concepts_resolve_latest(store,
concepts,
schema_concept_ref,
&latest_schema);
have_registry = amduatd_concepts_resolve_latest(store,
concepts,
registry_concept_ref,
&latest_registry);
if (have_schema && have_registry) {
amduat_artifact_t schema_artifact;
amduat_artifact_t registry_artifact;
amduat_asl_store_error_t err;
memset(&schema_artifact, 0, sizeof(schema_artifact));
memset(&registry_artifact, 0, sizeof(registry_artifact));
err = amduat_asl_store_get(store, latest_schema, &schema_artifact);
if (err == AMDUAT_ASL_STORE_OK &&
schema_artifact.bytes.len != 0 &&
schema_artifact.bytes.data != NULL &&
amduatd_bytes_contains(schema_artifact.bytes.data,
schema_artifact.bytes.len,
"\"key_text\"")) {
schema_ok = true;
}
err = amduat_asl_store_get(store, latest_registry, &registry_artifact);
if (err == AMDUAT_ASL_STORE_OK &&
registry_artifact.bytes.len != 0 &&
registry_artifact.bytes.data != NULL &&
amduatd_bytes_contains(registry_artifact.bytes.data,
registry_artifact.bytes.len,
"\"value_text\"")) {
registry_ok = true;
}
amduat_asl_artifact_free(&schema_artifact);
amduat_asl_artifact_free(&registry_artifact);
}
amduat_reference_free(&latest_schema);
amduat_reference_free(&latest_registry);
if (have_schema && have_registry && schema_ok && registry_ok) {
ok = true;
goto seed_cleanup;
}
}
{
const char *payload = "{\"seed\":\"manifest\"}";
artifact = amduat_artifact(amduat_octets(payload, strlen(payload)));
if (!amduatd_seed_store_artifact(store, artifact, &seed_manifest_ref)) {
goto seed_cleanup;
}
}
{
const char *payload = "{\"seed\":\"environment\"}";
artifact = amduat_artifact(amduat_octets(payload, strlen(payload)));
if (!amduatd_seed_store_artifact(store, artifact, &seed_environment_ref)) {
goto seed_cleanup;
}
}
{
const char *payload = "{\"seed\":\"executor\"}";
artifact = amduat_artifact(amduat_octets(payload, strlen(payload)));
if (!amduatd_seed_store_artifact(store, artifact, &seed_executor_ref)) {
goto seed_cleanup;
}
}
{
amduat_reference_t hello_ref;
amduat_reference_t title_ref;
amduat_reference_t status_ref;
const char *payload = "hello";
memset(&hello_ref, 0, sizeof(hello_ref));
memset(&title_ref, 0, sizeof(title_ref));
memset(&status_ref, 0, sizeof(status_ref));
artifact = amduat_artifact(amduat_octets(payload, strlen(payload)));
if (!amduatd_seed_store_artifact(store, artifact, &hello_ref)) {
goto seed_cleanup;
}
if (!amduat_asl_ref_encode_hex(hello_ref, &hello_hex)) {
goto seed_cleanup;
}
hello_hex_owned = true;
artifact = amduat_artifact(amduat_octets("Amduat UI",
strlen("Amduat UI")));
if (!amduatd_seed_store_artifact(store, artifact, &title_ref)) {
if (hello_hex_owned) {
free(hello_hex);
hello_hex = NULL;
hello_hex_owned = false;
}
goto seed_cleanup;
}
artifact = amduat_artifact(amduat_octets("ready", strlen("ready")));
if (!amduatd_seed_store_artifact(store, artifact, &status_ref)) {
if (hello_hex_owned) {
free(hello_hex);
hello_hex = NULL;
hello_hex_owned = false;
}
goto seed_cleanup;
}
fields[0].key_hex = NULL;
fields[1].key_hex = NULL;
fields[2].key_hex = NULL;
if (!amduat_asl_ref_encode_hex(key_title_ref, &fields[0].key_hex) ||
!amduat_asl_ref_encode_hex(key_status_ref, &fields[1].key_hex) ||
!amduat_asl_ref_encode_hex(key_latest_ref, &fields[2].key_hex)) {
goto seed_cleanup;
}
fields[0].type_hex = strdup("ms.type.string");
fields[1].type_hex = strdup("ms.type.string");
fields[2].type_hex = strdup("ms.type.ref");
if (fields[0].type_hex == NULL || fields[1].type_hex == NULL ||
fields[2].type_hex == NULL) {
goto seed_cleanup;
}
fields[0].key_text = strdup("title");
fields[1].key_text = strdup("status");
fields[2].key_text = strdup("latest_ref");
if (fields[0].key_text == NULL || fields[1].key_text == NULL ||
fields[2].key_text == NULL) {
goto seed_cleanup;
}
entries[0].key_hex = fields[0].key_hex;
entries[1].key_hex = fields[1].key_hex;
entries[2].key_hex = fields[2].key_hex;
entries[0].value_hex = strdup("Amduat UI");
entries[1].value_hex = strdup("ready");
entries[2].value_hex = hello_hex;
hello_hex = NULL;
hello_hex_owned = false;
if (entries[0].value_hex == NULL || entries[1].value_hex == NULL ||
entries[2].value_hex == NULL) {
goto seed_cleanup;
}
qsort(fields, 3, sizeof(fields[0]), amduatd_seed_entry_cmp);
qsort(entries, 3, sizeof(entries[0]), amduatd_seed_entry_cmp);
if (!amduatd_strbuf_append_cstr(&b,
"{\"schema_version\":1,\"fields\":[")) {
goto seed_cleanup;
}
for (i = 0; i < 3; ++i) {
if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) {
goto seed_cleanup;
}
if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") ||
!amduatd_strbuf_append_cstr(&b, fields[i].key_hex) ||
!amduatd_strbuf_append_cstr(&b, "\",\"key_text\":\"") ||
!amduatd_strbuf_append_cstr(&b, fields[i].key_text) ||
!amduatd_strbuf_append_cstr(&b, "\",\"type_ref\":\"") ||
!amduatd_strbuf_append_cstr(&b, fields[i].type_hex) ||
!amduatd_strbuf_append_cstr(&b, "\"}")) {
goto seed_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "]}")) {
goto seed_cleanup;
}
artifact = amduat_artifact(amduat_octets(b.data, b.len));
if (!amduatd_seed_store_artifact(store, artifact, &schema_input_ref)) {
goto seed_cleanup;
}
amduatd_strbuf_free(&b);
memset(&b, 0, sizeof(b));
if (!amduatd_strbuf_append_cstr(&b,
"{\"registry_version\":1,\"entries\":[")) {
goto seed_cleanup;
}
for (i = 0; i < 3; ++i) {
if (i != 0 && !amduatd_strbuf_append_char(&b, ',')) {
goto seed_cleanup;
}
if (!amduatd_strbuf_append_cstr(&b, "{\"key_ref\":\"") ||
!amduatd_strbuf_append_cstr(&b, entries[i].key_hex) ||
!amduatd_strbuf_append_cstr(&b,
i == 2 ? "\",\"value_ref\":\""
: "\",\"value_text\":\"") ||
!amduatd_strbuf_append_cstr(&b, entries[i].value_hex) ||
!amduatd_strbuf_append_cstr(&b, "\"}")) {
goto seed_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "]}")) {
goto seed_cleanup;
}
artifact = amduat_artifact(amduat_octets(b.data, b.len));
if (!amduatd_seed_store_artifact(store, artifact, &registry_input_ref)) {
goto seed_cleanup;
}
}
{
amduat_reference_t inputs[1];
amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref();
amduat_artifact_t receipt_artifact;
amduat_reference_t edge_ref;
amduat_octets_t evaluator_id = amduat_octets("amduatd-seed",
strlen("amduatd-seed"));
inputs[0] = schema_input_ref;
if (!amduat_pel_surf_run_with_result(store,
scheme_ref,
identity_program_ref,
inputs,
1,
false,
amduat_reference(0u,
amduat_octets(NULL, 0u)),
&run_result)) {
goto seed_cleanup;
}
if (!run_result.has_result_value || run_result.output_refs_len != 1) {
goto seed_cleanup;
}
if (!amduat_reference_clone(run_result.output_refs[0],
&schema_output_ref)) {
goto seed_cleanup;
}
if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value,
seed_manifest_ref,
seed_environment_ref,
evaluator_id,
seed_executor_ref,
false,
amduat_reference(0u,
amduat_octets(NULL, 0u)),
amduat_octets(NULL, 0u),
0,
0,
&receipt_artifact)) {
goto seed_cleanup;
}
if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) {
amduat_asl_artifact_free(&receipt_artifact);
goto seed_cleanup;
}
amduat_asl_artifact_free(&receipt_artifact);
if (!amduatd_concepts_put_edge(store,
concepts,
schema_output_ref,
receipt_ref,
concepts->rel_has_provenance_ref,
amduat_octets(NULL, 0u),
&edge_ref)) {
goto seed_cleanup;
}
amduat_reference_free(&edge_ref);
amduat_reference_free(&receipt_ref);
amduat_enc_pel1_result_free(&run_result.result_value);
amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len);
amduat_pel_surf_free_ref(&run_result.result_ref);
memset(&run_result, 0, sizeof(run_result));
}
{
amduat_reference_t inputs[1];
amduat_reference_t scheme_ref = amduat_pel_program_dag_scheme_ref();
amduat_artifact_t receipt_artifact;
amduat_reference_t edge_ref;
amduat_octets_t evaluator_id = amduat_octets("amduatd-seed",
strlen("amduatd-seed"));
inputs[0] = registry_input_ref;
if (!amduat_pel_surf_run_with_result(store,
scheme_ref,
identity_program_ref,
inputs,
1,
false,
amduat_reference(0u,
amduat_octets(NULL, 0u)),
&run_result)) {
goto seed_cleanup;
}
if (!run_result.has_result_value || run_result.output_refs_len != 1) {
goto seed_cleanup;
}
if (!amduat_reference_clone(run_result.output_refs[0],
&registry_output_ref)) {
goto seed_cleanup;
}
if (!amduat_fer1_receipt_from_pel_result(&run_result.result_value,
seed_manifest_ref,
seed_environment_ref,
evaluator_id,
seed_executor_ref,
false,
amduat_reference(0u,
amduat_octets(NULL, 0u)),
amduat_octets(NULL, 0u),
0,
0,
&receipt_artifact)) {
goto seed_cleanup;
}
if (!amduatd_seed_store_artifact(store, receipt_artifact, &receipt_ref)) {
amduat_asl_artifact_free(&receipt_artifact);
goto seed_cleanup;
}
amduat_asl_artifact_free(&receipt_artifact);
if (!amduatd_concepts_put_edge(store,
concepts,
registry_output_ref,
receipt_ref,
concepts->rel_has_provenance_ref,
amduat_octets(NULL, 0u),
&edge_ref)) {
goto seed_cleanup;
}
amduat_reference_free(&edge_ref);
amduat_reference_free(&receipt_ref);
amduat_enc_pel1_result_free(&run_result.result_value);
amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len);
amduat_pel_surf_free_ref(&run_result.result_ref);
memset(&run_result, 0, sizeof(run_result));
}
if (!amduatd_seed_materializes_if_missing(store, concepts,
schema_concept_ref,
schema_output_ref) ||
!amduatd_seed_materializes_if_missing(store, concepts,
registry_concept_ref,
registry_output_ref)) {
goto seed_cleanup;
}
ok = true;
seed_cleanup:
if (run_result.has_result_value) {
amduat_enc_pel1_result_free(&run_result.result_value);
}
if (run_result.output_refs != NULL) {
amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len);
}
amduat_pel_surf_free_ref(&run_result.result_ref);
amduat_reference_free(&type_string_ref);
amduat_reference_free(&type_ref_ref);
amduat_reference_free(&key_title_ref);
amduat_reference_free(&key_status_ref);
amduat_reference_free(&key_latest_ref);
amduat_reference_free(&schema_concept_ref);
amduat_reference_free(&registry_concept_ref);
amduat_reference_free(&identity_program_ref);
amduat_reference_free(&schema_input_ref);
amduat_reference_free(&registry_input_ref);
amduat_reference_free(&schema_output_ref);
amduat_reference_free(&registry_output_ref);
amduat_reference_free(&seed_manifest_ref);
amduat_reference_free(&seed_environment_ref);
amduat_reference_free(&seed_executor_ref);
for (i = 0; i < 3; ++i) {
free(fields[i].key_hex);
free(fields[i].type_hex);
free(fields[i].key_text);
free(entries[i].value_hex);
}
amduatd_strbuf_free(&b);
if (hello_hex_owned) {
free(hello_hex);
}
return ok;
}
static bool amduatd_handle_post_concepts(int fd,
amduat_asl_store_t *store,
amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const amduatd_http_req_t *req) {
uint8_t *body = NULL;
const char *p = NULL;
const char *end = NULL;
char *name = NULL;
bool have_name = false;
bool have_ref = false;
amduat_reference_t target_ref;
amduat_reference_t name_ref;
amduat_reference_t concept_ref;
amduat_reference_t edge_ref;
char *concept_hex = NULL;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
char json[4096];
bool ok = false;
amduat_octets_t actor = amduat_octets(NULL, 0u);
memset(&target_ref, 0, sizeof(target_ref));
memset(&name_ref, 0, sizeof(name_ref));
memset(&concept_ref, 0, sizeof(concept_ref));
memset(&edge_ref, 0, sizeof(edge_ref));
if (store == NULL || concepts == NULL || req == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (req->has_actor) {
actor = req->actor;
}
if (req->content_length == 0) {
return amduatd_send_json_error(fd, 400, "Bad Request", "missing body");
}
if (req->content_length > (256u * 1024u)) {
return amduatd_send_json_error(fd, 413, "Payload Too Large",
"payload too large");
}
body = (uint8_t *)malloc(req->content_length);
if (body == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
}
if (!amduatd_read_exact(fd, body, req->content_length)) {
free(body);
return false;
}
p = (const char *)body;
end = (const char *)body + req->content_length;
if (!amduatd_json_expect(&p, end, '{')) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto concepts_cleanup;
}
for (;;) {
const char *key = NULL;
size_t key_len = 0;
const char *sv = NULL;
size_t sv_len = 0;
const char *cur = NULL;
cur = amduatd_json_skip_ws(p, end);
if (cur < end && *cur == '}') {
p = cur + 1;
break;
}
if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) ||
!amduatd_json_expect(&p, end, ':')) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto concepts_cleanup;
}
if (key_len == strlen("name") && memcmp(key, "name", key_len) == 0) {
if (have_name || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) ||
!amduatd_copy_json_str(sv, sv_len, &name)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name");
goto concepts_cleanup;
}
have_name = true;
} else if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) {
if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) ||
!amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref");
goto concepts_cleanup;
}
have_ref = true;
} else {
if (!amduatd_json_skip_value(&p, end, 0)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto concepts_cleanup;
}
}
cur = amduatd_json_skip_ws(p, end);
if (cur < end && *cur == ',') {
p = cur + 1;
continue;
}
if (cur < end && *cur == '}') {
p = cur + 1;
break;
}
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto concepts_cleanup;
}
if (!have_name) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name");
goto concepts_cleanup;
}
if (!amduatd_space_scope_name(space, name, &scoped_bytes)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name");
goto concepts_cleanup;
}
scoped_name = (char *)scoped_bytes.data;
amduatd_space_log_mapping(space, name, scoped_name);
if (!amduatd_concepts_put_name_artifact(store, scoped_name, &name_ref)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"store error");
goto concepts_cleanup;
}
{
amduat_reference_t existing;
memset(&existing, 0, sizeof(existing));
if (amduatd_concepts_lookup_alias(store,
concepts,
scoped_name,
&existing)) {
amduat_reference_free(&existing);
ok = amduatd_send_json_error(fd, 409, "Conflict", "name exists");
goto concepts_cleanup;
}
}
if (!amduatd_concepts_put_concept_id(store, &concept_ref)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"store error");
goto concepts_cleanup;
}
if (!amduatd_concepts_put_edge(store,
concepts,
name_ref,
concept_ref,
concepts->rel_aliases_ref,
actor,
&edge_ref)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"store error");
goto concepts_cleanup;
}
if (have_ref) {
if (!amduatd_concepts_put_edge(store,
concepts,
concept_ref,
target_ref,
concepts->rel_materializes_ref,
actor,
&edge_ref)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"store error");
goto concepts_cleanup;
}
}
if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"encode error");
goto concepts_cleanup;
}
{
int n = snprintf(json,
sizeof(json),
"{"
"\"name\":\"%s\","
"\"concept_ref\":\"%s\""
"}\n",
name,
concept_hex);
free(concept_hex);
if (n <= 0 || (size_t)n >= sizeof(json)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
goto concepts_cleanup;
}
ok = amduatd_http_send_json(fd, 200, "OK", json, false);
}
concepts_cleanup:
free(body);
free(name);
free(scoped_name);
amduat_reference_free(&target_ref);
amduat_reference_free(&name_ref);
amduat_reference_free(&concept_ref);
amduat_reference_free(&edge_ref);
return ok;
}
static bool amduatd_handle_post_concept_publish(int fd,
amduat_asl_store_t *store,
amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const char *name,
const amduatd_http_req_t *req) {
uint8_t *body = NULL;
const char *p = NULL;
const char *end = NULL;
amduat_reference_t target_ref;
amduat_reference_t concept_ref;
amduat_reference_t edge_ref;
bool have_ref = false;
bool ok = false;
amduat_octets_t actor = amduat_octets(NULL, 0u);
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
memset(&target_ref, 0, sizeof(target_ref));
memset(&concept_ref, 0, sizeof(concept_ref));
memset(&edge_ref, 0, sizeof(edge_ref));
if (store == NULL || concepts == NULL || name == NULL || req == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (req->has_actor) {
actor = req->actor;
}
if (!amduatd_space_scope_name(space, name, &scoped_bytes)) {
return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name");
}
scoped_name = (char *)scoped_bytes.data;
amduatd_space_log_mapping(space, name, scoped_name);
if (req->content_length == 0) {
free(scoped_name);
return amduatd_send_json_error(fd, 400, "Bad Request", "missing body");
}
if (req->content_length > (256u * 1024u)) {
free(scoped_name);
return amduatd_send_json_error(fd, 413, "Payload Too Large",
"payload too large");
}
if (!amduatd_concepts_lookup_alias(store,
concepts,
scoped_name,
&concept_ref)) {
free(scoped_name);
return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept");
}
free(scoped_name);
body = (uint8_t *)malloc(req->content_length);
if (body == NULL) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom");
goto publish_cleanup;
}
if (!amduatd_read_exact(fd, body, req->content_length)) {
free(body);
amduat_reference_free(&concept_ref);
return false;
}
p = (const char *)body;
end = (const char *)body + req->content_length;
if (!amduatd_json_expect(&p, end, '{')) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto publish_cleanup;
}
for (;;) {
const char *key = NULL;
size_t key_len = 0;
const char *sv = NULL;
size_t sv_len = 0;
const char *cur = NULL;
cur = amduatd_json_skip_ws(p, end);
if (cur < end && *cur == '}') {
p = cur + 1;
break;
}
if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) ||
!amduatd_json_expect(&p, end, ':')) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto publish_cleanup;
}
if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) {
if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) ||
!amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref");
goto publish_cleanup;
}
have_ref = true;
} else {
if (!amduatd_json_skip_value(&p, end, 0)) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto publish_cleanup;
}
}
cur = amduatd_json_skip_ws(p, end);
if (cur < end && *cur == ',') {
p = cur + 1;
continue;
}
if (cur < end && *cur == '}') {
p = cur + 1;
break;
}
ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json");
goto publish_cleanup;
}
if (!have_ref) {
ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing ref");
goto publish_cleanup;
}
if (!amduatd_concepts_put_edge(store,
concepts,
concept_ref,
target_ref,
concepts->rel_materializes_ref,
actor,
&edge_ref)) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error",
"store error");
goto publish_cleanup;
}
ok = amduatd_http_send_json(fd, 200, "OK", "{\"ok\":true}\n", false);
publish_cleanup:
free(body);
amduat_reference_free(&target_ref);
amduat_reference_free(&concept_ref);
amduat_reference_free(&edge_ref);
return ok;
}
static bool amduatd_handle_get_resolve(int fd,
amduat_asl_store_t *store,
const amduatd_concepts_t *concepts,
const amduatd_cfg_t *dcfg,
const char *name) {
amduat_reference_t concept_ref;
amduat_reference_t latest_ref;
char *hex = NULL;
char json[2048];
int n;
char *scoped_name = NULL;
amduat_octets_t scoped_bytes = amduat_octets(NULL, 0u);
const amduatd_space_t *space = dcfg != NULL ? &dcfg->space : NULL;
memset(&concept_ref, 0, sizeof(concept_ref));
memset(&latest_ref, 0, sizeof(latest_ref));
if (store == NULL || concepts == NULL || name == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (!amduatd_space_scope_name(space, name, &scoped_bytes)) {
return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name");
}
scoped_name = (char *)scoped_bytes.data;
amduatd_space_log_mapping(space, name, scoped_name);
if (!amduatd_concepts_lookup_alias(store, concepts, scoped_name,
&concept_ref)) {
free(scoped_name);
return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept");
}
free(scoped_name);
if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref,
&latest_ref)) {
amduat_reference_free(&concept_ref);
return amduatd_send_json_error(fd, 404, "Not Found", "no versions");
}
amduat_reference_free(&concept_ref);
if (!amduat_asl_ref_encode_hex(latest_ref, &hex)) {
amduat_reference_free(&latest_ref);
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"encode error");
}
amduat_reference_free(&latest_ref);
n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex);
free(hex);
if (n <= 0 || (size_t)n >= sizeof(json)) {
return amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
}
return amduatd_http_send_json(fd, 200, "OK", json, false);
}
bool amduatd_concepts_can_handle(const amduatd_http_req_t *req) {
char no_query[1024];
if (req == NULL) {
return false;
}
amduatd_path_without_query(req->path, no_query, sizeof(no_query));
if (strcmp(req->method, "POST") == 0 &&
strcmp(no_query, "/v1/concepts") == 0) {
return true;
}
if (strcmp(req->method, "GET") == 0 &&
strcmp(no_query, "/v1/concepts") == 0) {
return true;
}
if (strcmp(req->method, "GET") == 0 &&
strcmp(no_query, "/v1/relations") == 0) {
return true;
}
if (strcmp(req->method, "GET") == 0 &&
strncmp(no_query, "/v1/concepts/", 13) == 0) {
return true;
}
if (strcmp(req->method, "POST") == 0 &&
strncmp(no_query, "/v1/concepts/", 13) == 0 &&
strstr(no_query, "/publish") != NULL) {
return true;
}
if (strcmp(req->method, "GET") == 0 &&
strncmp(no_query, "/v1/resolve/", 12) == 0) {
return true;
}
return false;
}
bool amduatd_concepts_handle(amduatd_ctx_t *ctx,
const amduatd_http_req_t *req,
amduatd_http_resp_t *resp) {
char no_query[1024];
if (ctx == NULL || req == NULL || resp == NULL) {
return false;
}
amduatd_path_without_query(req->path, no_query, sizeof(no_query));
if (strcmp(req->method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) {
resp->ok = amduatd_handle_post_concepts(resp->fd,
ctx->store,
ctx->concepts,
ctx->daemon_cfg,
req);
return true;
}
if (strcmp(req->method, "GET") == 0 && strcmp(no_query, "/v1/concepts") == 0) {
resp->ok = amduatd_handle_get_concepts(resp->fd,
ctx->store,
ctx->concepts,
ctx->daemon_cfg);
return true;
}
if (strcmp(req->method, "GET") == 0 && strcmp(no_query, "/v1/relations") == 0) {
resp->ok = amduatd_handle_get_relations(resp->fd, ctx->concepts);
return true;
}
if (strcmp(req->method, "GET") == 0 && strncmp(no_query, "/v1/concepts/", 13) == 0) {
const char *name = no_query + 13;
if (name[0] == '\0') {
resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request",
"missing name");
return true;
}
resp->ok = amduatd_handle_get_concept(resp->fd,
ctx->store,
ctx->concepts,
ctx->daemon_cfg,
name);
return true;
}
if (strcmp(req->method, "POST") == 0 &&
strncmp(no_query, "/v1/concepts/", 13) == 0 &&
strstr(no_query, "/publish") != NULL) {
char name[256];
char *slash;
if (!amduatd_path_extract_name(no_query, "/v1/concepts/", name,
sizeof(name))) {
resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request",
"invalid path");
return true;
}
slash = strstr(name, "/publish");
if (slash == NULL || strcmp(slash, "/publish") != 0) {
resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request",
"invalid path");
return true;
}
*slash = '\0';
resp->ok = amduatd_handle_post_concept_publish(resp->fd,
ctx->store,
ctx->concepts,
ctx->daemon_cfg,
name,
req);
return true;
}
if (strcmp(req->method, "GET") == 0 && strncmp(no_query, "/v1/resolve/", 12) == 0) {
const char *name = no_query + 12;
if (name[0] == '\0') {
resp->ok = amduatd_send_json_error(resp->fd, 400, "Bad Request",
"missing name");
return true;
}
resp->ok = amduatd_handle_get_resolve(resp->fd,
ctx->store,
ctx->concepts,
ctx->daemon_cfg,
name);
return true;
}
return false;
}
static void amduatd_strbuf_free(amduatd_strbuf_t *b) {
if (b == NULL) {
return;
}
free(b->data);
b->data = NULL;
b->len = 0u;
b->cap = 0u;
}
static bool amduatd_strbuf_reserve(amduatd_strbuf_t *b, size_t extra) {
char *next = NULL;
size_t need = 0u;
size_t next_cap;
if (b == NULL) {
return false;
}
if (b->len > SIZE_MAX - extra) {
return false;
}
need = b->len + extra;
if (need <= b->cap) {
return true;
}
next_cap = b->cap != 0u ? b->cap * 2u : 128u;
if (next_cap < need) {
next_cap = need;
}
next = (char *)realloc(b->data, next_cap);
if (next == NULL) {
return false;
}
b->data = next;
b->cap = next_cap;
return true;
}
static bool amduatd_strbuf_append(amduatd_strbuf_t *b,
const char *data,
size_t n) {
if (b == NULL || (data == NULL && n != 0u)) {
return false;
}
if (!amduatd_strbuf_reserve(b, n + 1u)) {
return false;
}
if (n != 0u) {
memcpy(b->data + b->len, data, n);
b->len += n;
}
b->data[b->len] = '\0';
return true;
}
static bool amduatd_strbuf_append_cstr(amduatd_strbuf_t *b, const char *s) {
return amduatd_strbuf_append(b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u);
}
static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c) {
return amduatd_strbuf_append(b, &c, 1u);
}