466 lines
14 KiB
C
466 lines
14 KiB
C
|
|
#ifndef _POSIX_C_SOURCE
|
||
|
|
#define _POSIX_C_SOURCE 200809L
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#include "amduatd_space_doctor.h"
|
||
|
|
|
||
|
|
#include "amduat/asl/asl_store_fs_meta.h"
|
||
|
|
#include "amduat/asl/core.h"
|
||
|
|
#include "amduat/asl/none.h"
|
||
|
|
#include "amduat/asl/record.h"
|
||
|
|
#include "amduat/enc/asl1_core_codec.h"
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
static char *amduatd_test_make_temp_dir(void) {
|
||
|
|
char tmpl[] = "/tmp/amduatd-doctor-XXXXXX";
|
||
|
|
char *dir = mkdtemp(tmpl);
|
||
|
|
size_t len;
|
||
|
|
char *copy;
|
||
|
|
if (dir == NULL) {
|
||
|
|
perror("mkdtemp");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
len = strlen(dir);
|
||
|
|
copy = (char *)malloc(len + 1u);
|
||
|
|
if (copy == NULL) {
|
||
|
|
fprintf(stderr, "failed to allocate temp dir copy\n");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
memcpy(copy, dir, len + 1u);
|
||
|
|
return copy;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool amduatd_check_status(const amduatd_space_doctor_report_t *report,
|
||
|
|
const char *name,
|
||
|
|
amduatd_space_doctor_status_t expected) {
|
||
|
|
if (report == NULL || name == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
for (size_t i = 0u; i < report->checks_len; ++i) {
|
||
|
|
if (strcmp(report->checks[i].name, name) == 0) {
|
||
|
|
return report->checks[i].status == expected;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
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_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 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 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_build_edge_index_state_payload(
|
||
|
|
amduat_reference_t graph_ref,
|
||
|
|
amduat_octets_t *out_payload) {
|
||
|
|
static const uint8_t k_magic[8] = {
|
||
|
|
'A', 'S', 'L', 'E', 'I', 'X', '1', '\0'
|
||
|
|
};
|
||
|
|
amduat_octets_t ref_bytes = amduat_octets(NULL, 0u);
|
||
|
|
uint8_t *payload = NULL;
|
||
|
|
size_t payload_len = 0u;
|
||
|
|
size_t offset = 0u;
|
||
|
|
|
||
|
|
if (out_payload == NULL) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
*out_payload = amduat_octets(NULL, 0u);
|
||
|
|
if (!amduat_enc_asl1_core_encode_reference_v1(graph_ref, &ref_bytes)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (ref_bytes.len > UINT32_MAX) {
|
||
|
|
free((void *)ref_bytes.data);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
payload_len = 8u + 4u + 8u + 4u + ref_bytes.len;
|
||
|
|
payload = (uint8_t *)malloc(payload_len);
|
||
|
|
if (payload == NULL) {
|
||
|
|
free((void *)ref_bytes.data);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
memcpy(payload + offset, k_magic, 8u);
|
||
|
|
offset += 8u;
|
||
|
|
amduatd_store_u32_le(payload + offset, 1u);
|
||
|
|
offset += 4u;
|
||
|
|
amduatd_store_u64_le(payload + offset, 0u);
|
||
|
|
offset += 8u;
|
||
|
|
amduatd_store_u32_le(payload + offset, (uint32_t)ref_bytes.len);
|
||
|
|
offset += 4u;
|
||
|
|
memcpy(payload + offset, ref_bytes.data, ref_bytes.len);
|
||
|
|
offset += ref_bytes.len;
|
||
|
|
free((void *)ref_bytes.data);
|
||
|
|
*out_payload = amduat_octets(payload, payload_len);
|
||
|
|
return offset == payload_len;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int amduatd_test_empty_store(void) {
|
||
|
|
char *root = amduatd_test_make_temp_dir();
|
||
|
|
amduat_asl_store_fs_config_t cfg;
|
||
|
|
amduatd_store_ctx_t store_ctx;
|
||
|
|
amduat_asl_store_t store;
|
||
|
|
amduat_asl_pointer_store_t pointer_store;
|
||
|
|
amduatd_space_t space;
|
||
|
|
amduatd_cfg_t dcfg;
|
||
|
|
amduatd_fed_cfg_t fed_cfg;
|
||
|
|
amduatd_space_doctor_report_t report;
|
||
|
|
|
||
|
|
if (root == NULL) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&cfg, 0, sizeof(cfg));
|
||
|
|
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
|
||
|
|
fprintf(stderr, "failed to init store root\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&store_ctx, 0, sizeof(store_ctx));
|
||
|
|
memset(&store, 0, sizeof(store));
|
||
|
|
if (!amduatd_store_init(&store,
|
||
|
|
&cfg,
|
||
|
|
&store_ctx,
|
||
|
|
root,
|
||
|
|
AMDUATD_STORE_BACKEND_FS)) {
|
||
|
|
fprintf(stderr, "failed to init fs backend\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
|
||
|
|
fprintf(stderr, "failed to init pointer store\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_space_init(&space, NULL, false)) {
|
||
|
|
fprintf(stderr, "failed to init space\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
memset(&dcfg, 0, sizeof(dcfg));
|
||
|
|
dcfg.space = space;
|
||
|
|
memset(&fed_cfg, 0, sizeof(fed_cfg));
|
||
|
|
|
||
|
|
if (!amduatd_space_doctor_run(&store,
|
||
|
|
&pointer_store,
|
||
|
|
&dcfg.space,
|
||
|
|
&dcfg,
|
||
|
|
&fed_cfg,
|
||
|
|
&report)) {
|
||
|
|
fprintf(stderr, "doctor run failed\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (report.backend != AMDUATD_STORE_BACKEND_FS) {
|
||
|
|
fprintf(stderr, "expected fs backend\n");
|
||
|
|
amduatd_space_doctor_report_free(&report);
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_check_status(&report,
|
||
|
|
"pointer_name_validation",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edges_index_head",
|
||
|
|
AMDUATD_DOCTOR_WARN) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edges_collection_snapshot_head",
|
||
|
|
AMDUATD_DOCTOR_WARN) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edges_collection_log_head",
|
||
|
|
AMDUATD_DOCTOR_WARN) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"index_current_state",
|
||
|
|
AMDUATD_DOCTOR_SKIPPED) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"index_log_scan",
|
||
|
|
AMDUATD_DOCTOR_SKIPPED)) {
|
||
|
|
fprintf(stderr, "unexpected doctor status for empty store\n");
|
||
|
|
amduatd_space_doctor_report_free(&report);
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
amduatd_space_doctor_report_free(&report);
|
||
|
|
free(root);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int amduatd_test_minimal_fixture(void) {
|
||
|
|
char *root = amduatd_test_make_temp_dir();
|
||
|
|
amduat_asl_store_fs_config_t cfg;
|
||
|
|
amduatd_store_ctx_t store_ctx;
|
||
|
|
amduat_asl_store_t store;
|
||
|
|
amduat_asl_pointer_store_t pointer_store;
|
||
|
|
amduatd_space_t space;
|
||
|
|
amduatd_cfg_t dcfg;
|
||
|
|
amduatd_fed_cfg_t fed_cfg;
|
||
|
|
amduatd_space_doctor_report_t report;
|
||
|
|
amduat_reference_t none_ref;
|
||
|
|
amduat_reference_t graph_ref;
|
||
|
|
amduat_reference_t edge_index_ref;
|
||
|
|
amduat_octets_t collection_name = amduat_octets(NULL, 0u);
|
||
|
|
amduat_octets_t index_head_name = amduat_octets(NULL, 0u);
|
||
|
|
char *collection_head = NULL;
|
||
|
|
char *collection_log_head = NULL;
|
||
|
|
amduat_octets_t payload = amduat_octets(NULL, 0u);
|
||
|
|
bool swapped = false;
|
||
|
|
|
||
|
|
if (root == NULL) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&cfg, 0, sizeof(cfg));
|
||
|
|
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
|
||
|
|
fprintf(stderr, "failed to init store root\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset(&store_ctx, 0, sizeof(store_ctx));
|
||
|
|
memset(&store, 0, sizeof(store));
|
||
|
|
if (!amduatd_store_init(&store,
|
||
|
|
&cfg,
|
||
|
|
&store_ctx,
|
||
|
|
root,
|
||
|
|
AMDUATD_STORE_BACKEND_FS)) {
|
||
|
|
fprintf(stderr, "failed to init fs backend\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
|
||
|
|
fprintf(stderr, "failed to init pointer store\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_space_init(&space, NULL, false)) {
|
||
|
|
fprintf(stderr, "failed to init space\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
memset(&dcfg, 0, sizeof(dcfg));
|
||
|
|
dcfg.space = space;
|
||
|
|
memset(&fed_cfg, 0, sizeof(fed_cfg));
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_artifact_t none_artifact;
|
||
|
|
if (!amduat_asl_none_artifact(&none_artifact)) {
|
||
|
|
fprintf(stderr, "failed to build none artifact\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (amduat_asl_store_put(&store, none_artifact, &none_ref) !=
|
||
|
|
AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "failed to store none artifact\n");
|
||
|
|
amduat_artifact_free(&none_artifact);
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
amduat_artifact_free(&none_artifact);
|
||
|
|
}
|
||
|
|
|
||
|
|
{
|
||
|
|
amduat_artifact_t graph_artifact =
|
||
|
|
amduat_artifact(amduat_octets("graph", 5u));
|
||
|
|
if (amduat_asl_store_put(&store, graph_artifact, &graph_ref) !=
|
||
|
|
AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "failed to store graph artifact\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_build_edge_index_state_payload(graph_ref, &payload)) {
|
||
|
|
fprintf(stderr, "failed to encode edge index state\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (amduat_asl_record_store_put(&store,
|
||
|
|
amduat_octets("tgk/edge_index_state", 20u),
|
||
|
|
payload,
|
||
|
|
&edge_index_ref) != AMDUAT_ASL_STORE_OK) {
|
||
|
|
fprintf(stderr, "failed to store edge index record\n");
|
||
|
|
free((void *)payload.data);
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
free((void *)payload.data);
|
||
|
|
|
||
|
|
if (!amduatd_space_edges_collection_name(&dcfg.space, &collection_name) ||
|
||
|
|
!amduatd_space_edges_index_head_name(&dcfg.space, &index_head_name)) {
|
||
|
|
fprintf(stderr, "failed to build scoped names\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_build_collection_head_name((const char *)collection_name.data,
|
||
|
|
&collection_head) ||
|
||
|
|
!amduatd_build_collection_log_head_name(
|
||
|
|
(const char *)collection_name.data, &collection_log_head)) {
|
||
|
|
fprintf(stderr, "failed to build collection heads\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (amduat_asl_pointer_cas(&pointer_store,
|
||
|
|
(const char *)index_head_name.data,
|
||
|
|
false,
|
||
|
|
NULL,
|
||
|
|
&edge_index_ref,
|
||
|
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
|
||
|
|
amduat_asl_pointer_cas(&pointer_store,
|
||
|
|
collection_head,
|
||
|
|
false,
|
||
|
|
NULL,
|
||
|
|
&none_ref,
|
||
|
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
|
||
|
|
amduat_asl_pointer_cas(&pointer_store,
|
||
|
|
collection_log_head,
|
||
|
|
false,
|
||
|
|
NULL,
|
||
|
|
&none_ref,
|
||
|
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped) {
|
||
|
|
fprintf(stderr, "failed to seed pointers\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_space_doctor_run(&store,
|
||
|
|
&pointer_store,
|
||
|
|
&dcfg.space,
|
||
|
|
&dcfg,
|
||
|
|
&fed_cfg,
|
||
|
|
&report)) {
|
||
|
|
fprintf(stderr, "doctor run failed\n");
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!amduatd_check_status(&report,
|
||
|
|
"edges_index_head",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edges_collection_snapshot_head",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edges_collection_log_head",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edge_index_state_parse",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"edge_index_graph_ref",
|
||
|
|
AMDUATD_DOCTOR_OK) ||
|
||
|
|
!amduatd_check_status(&report,
|
||
|
|
"index_current_state",
|
||
|
|
AMDUATD_DOCTOR_SKIPPED)) {
|
||
|
|
fprintf(stderr, "unexpected doctor status for fixture\n");
|
||
|
|
amduatd_space_doctor_report_free(&report);
|
||
|
|
free(root);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
amduatd_space_doctor_report_free(&report);
|
||
|
|
free((void *)collection_name.data);
|
||
|
|
free((void *)index_head_name.data);
|
||
|
|
free(collection_head);
|
||
|
|
free(collection_log_head);
|
||
|
|
free(root);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
int main(void) {
|
||
|
|
if (amduatd_test_empty_store() != 0) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
if (amduatd_test_minimal_fixture() != 0) {
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|