4249 lines
137 KiB
C
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(¶ms_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, ¶ms_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(¶ms_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(®istry_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(®istry_input_ref, 0, sizeof(registry_input_ref));
|
|
memset(&schema_output_ref, 0, sizeof(schema_output_ref));
|
|
memset(®istry_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",
|
|
®istry_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(®istry_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, ®istry_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(®istry_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, ®istry_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],
|
|
®istry_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(®istry_concept_ref);
|
|
amduat_reference_free(&identity_program_ref);
|
|
amduat_reference_free(&schema_input_ref);
|
|
amduat_reference_free(®istry_input_ref);
|
|
amduat_reference_free(&schema_output_ref);
|
|
amduat_reference_free(®istry_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);
|
|
}
|