Add space sync status endpoint with cursor peer discovery
This commit is contained in:
parent
d74884b442
commit
67c837be3c
|
|
@ -30,7 +30,7 @@ set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c
|
||||||
src/amduatd_fed.c src/amduatd_fed_cursor.c
|
src/amduatd_fed.c src/amduatd_fed_cursor.c
|
||||||
src/amduatd_fed_pull_plan.c src/amduatd_fed_push_plan.c
|
src/amduatd_fed_pull_plan.c src/amduatd_fed_push_plan.c
|
||||||
src/amduatd_fed_pull_apply.c src/amduatd_fed_push_apply.c
|
src/amduatd_fed_pull_apply.c src/amduatd_fed_push_apply.c
|
||||||
src/amduatd_space_doctor.c)
|
src/amduatd_space_doctor.c src/amduatd_space_roots.c)
|
||||||
if(AMDUATD_ENABLE_UI)
|
if(AMDUATD_ENABLE_UI)
|
||||||
list(APPEND amduatd_sources src/amduatd_ui.c)
|
list(APPEND amduatd_sources src/amduatd_ui.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
@ -286,3 +286,47 @@ target_link_libraries(amduatd_test_space_doctor
|
||||||
)
|
)
|
||||||
|
|
||||||
add_test(NAME amduatd_space_doctor COMMAND amduatd_test_space_doctor)
|
add_test(NAME amduatd_space_doctor COMMAND amduatd_test_space_doctor)
|
||||||
|
|
||||||
|
add_executable(amduatd_test_space_roots
|
||||||
|
tests/test_amduatd_space_roots.c
|
||||||
|
src/amduatd_space_roots.c
|
||||||
|
src/amduatd_space.c
|
||||||
|
src/amduatd_fed_cursor.c
|
||||||
|
src/amduatd_store.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(amduatd_test_space_roots
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(amduatd_test_space_roots
|
||||||
|
PRIVATE amduat_asl_store_fs amduat_asl_pointer_fs amduat_asl_record
|
||||||
|
amduat_asl_store_index_fs amduat_asl amduat_enc amduat_util
|
||||||
|
amduat_hash_asl1
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME amduatd_space_roots COMMAND amduatd_test_space_roots)
|
||||||
|
|
||||||
|
add_executable(amduatd_test_space_sync_status
|
||||||
|
tests/test_amduatd_space_sync_status.c
|
||||||
|
src/amduatd_space_roots.c
|
||||||
|
src/amduatd_space.c
|
||||||
|
src/amduatd_fed_cursor.c
|
||||||
|
src/amduatd_store.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(amduatd_test_space_sync_status
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(amduatd_test_space_sync_status
|
||||||
|
PRIVATE amduat_asl_store_fs amduat_asl_pointer_fs amduat_asl_record
|
||||||
|
amduat_asl_store_index_fs amduat_asl amduat_enc amduat_util
|
||||||
|
amduat_hash_asl1
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME amduatd_space_sync_status COMMAND amduatd_test_space_sync_status)
|
||||||
|
|
|
||||||
29
README.md
29
README.md
|
|
@ -199,6 +199,35 @@ Run the daemon with derivation indexing enabled:
|
||||||
./build/amduatd --root .amduat-asl --sock amduatd.sock --enable-derivation-index
|
./build/amduatd --root .amduat-asl --sock amduatd.sock --enable-derivation-index
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Space roots (GC)
|
||||||
|
|
||||||
|
`/v1/space/roots` enumerates the pointer heads that must be treated as GC roots
|
||||||
|
for the effective space, including federation cursor heads.
|
||||||
|
|
||||||
|
GC root sets MUST include federation cursors to avoid trimming artifacts still
|
||||||
|
reachable via replication state. Use the roots listing to build your root set
|
||||||
|
before running GC tooling.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --unix-socket amduatd.sock \
|
||||||
|
'http://localhost/v1/space/roots' \
|
||||||
|
-H 'X-Amduat-Space: demo'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Space sync status
|
||||||
|
|
||||||
|
`/v1/space/sync/status` is a read-only summary of federation readiness and
|
||||||
|
per-peer cursor positions for the effective space. Peers are discovered from
|
||||||
|
cursor head pointers (pull and push) and returned in deterministic order.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --unix-socket amduatd.sock \
|
||||||
|
'http://localhost/v1/space/sync/status' \
|
||||||
|
-H 'X-Amduat-Space: demo'
|
||||||
|
```
|
||||||
|
|
||||||
To fail `/v1/pel/run` if the derivation index write fails:
|
To fail `/v1/pel/run` if the derivation index write fails:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
{"method": "GET", "path": "/v1/meta"},
|
{"method": "GET", "path": "/v1/meta"},
|
||||||
{"method": "HEAD", "path": "/v1/meta"},
|
{"method": "HEAD", "path": "/v1/meta"},
|
||||||
{"method": "GET", "path": "/v1/contract"},
|
{"method": "GET", "path": "/v1/contract"},
|
||||||
|
{"method": "GET", "path": "/v1/space/doctor"},
|
||||||
|
{"method": "GET", "path": "/v1/space/roots"},
|
||||||
|
{"method": "GET", "path": "/v1/space/sync/status"},
|
||||||
{"method": "POST", "path": "/v1/capabilities"},
|
{"method": "POST", "path": "/v1/capabilities"},
|
||||||
{"method": "GET", "path": "/v1/cap/resolve"},
|
{"method": "GET", "path": "/v1/cap/resolve"},
|
||||||
{"method": "GET", "path": "/v1/fed/records"},
|
{"method": "GET", "path": "/v1/fed/records"},
|
||||||
|
|
|
||||||
404
src/amduatd.c
404
src/amduatd.c
|
|
@ -49,6 +49,7 @@
|
||||||
#include "amduatd_store.h"
|
#include "amduatd_store.h"
|
||||||
#include "amduatd_derivation_index.h"
|
#include "amduatd_derivation_index.h"
|
||||||
#include "amduatd_space_doctor.h"
|
#include "amduatd_space_doctor.h"
|
||||||
|
#include "amduatd_space_roots.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
@ -122,6 +123,8 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"{\"method\":\"HEAD\",\"path\":\"/v1/meta\"},"
|
"{\"method\":\"HEAD\",\"path\":\"/v1/meta\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/contract\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/contract\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/space/doctor\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/space/doctor\"},"
|
||||||
|
"{\"method\":\"GET\",\"path\":\"/v1/space/roots\"},"
|
||||||
|
"{\"method\":\"GET\",\"path\":\"/v1/space/sync/status\"},"
|
||||||
"{\"method\":\"POST\",\"path\":\"/v1/capabilities\"},"
|
"{\"method\":\"POST\",\"path\":\"/v1/capabilities\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/cap/resolve\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/cap/resolve\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/fed/records\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/fed/records\"},"
|
||||||
|
|
@ -865,6 +868,380 @@ static bool amduatd_handle_get_space_doctor(int fd,
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amduatd_handle_get_space_roots(int fd,
|
||||||
|
const amduatd_http_req_t *req,
|
||||||
|
const amduatd_cfg_t *dcfg,
|
||||||
|
const amduatd_caps_t *caps,
|
||||||
|
const char *root_path) {
|
||||||
|
amduat_asl_pointer_store_t pointer_store;
|
||||||
|
amduatd_space_roots_list_t roots;
|
||||||
|
amduatd_strbuf_t b;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&roots, 0, sizeof(roots));
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
|
||||||
|
if (req == NULL || dcfg == NULL || root_path == NULL) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"internal error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caps != NULL && caps->enabled && req->x_capability[0] != '\0') {
|
||||||
|
const char *reason = NULL;
|
||||||
|
if (!amduatd_caps_check_space(caps, dcfg, req, &reason)) {
|
||||||
|
if (reason != NULL && strcmp(reason, "wrong-space") == 0) {
|
||||||
|
return amduatd_send_json_error(fd, 403, "Forbidden",
|
||||||
|
"space not permitted by capability");
|
||||||
|
}
|
||||||
|
return amduatd_send_json_error(fd, 403, "Forbidden",
|
||||||
|
"invalid capability");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"pointer store error");
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_list(root_path,
|
||||||
|
&pointer_store,
|
||||||
|
req->effective_space,
|
||||||
|
&roots)) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"root enumeration failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
if (req->effective_space != NULL && req->effective_space->enabled &&
|
||||||
|
req->effective_space->space_id.data != NULL) {
|
||||||
|
const char *space_id = (const char *)req->effective_space->space_id.data;
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"scoped\",") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"space_id\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, space_id) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"unscoped\",") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"space_id\":null")) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "},\"roots\":[")) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0u; i < roots.len; ++i) {
|
||||||
|
const char *pointer_name = roots.names[i];
|
||||||
|
bool exists = false;
|
||||||
|
amduat_reference_t ref;
|
||||||
|
amduat_asl_pointer_error_t perr;
|
||||||
|
char *ref_hex = NULL;
|
||||||
|
|
||||||
|
memset(&ref, 0, sizeof(ref));
|
||||||
|
if (i != 0u) {
|
||||||
|
if (!amduatd_strbuf_append_char(&b, ',')) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
perr = amduat_asl_pointer_get(&pointer_store,
|
||||||
|
pointer_name,
|
||||||
|
&exists,
|
||||||
|
&ref);
|
||||||
|
if (perr != AMDUAT_ASL_POINTER_OK) {
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"pointer_name\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, pointer_name) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\",\"head_ref_present\":") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, exists ? "true" : "false")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
if (exists) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"head_ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, ref_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "}")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ref_hex);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "]}\n")) {
|
||||||
|
goto roots_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
|
||||||
|
|
||||||
|
roots_cleanup:
|
||||||
|
if (!ok) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
|
||||||
|
}
|
||||||
|
amduatd_space_roots_list_free(&roots);
|
||||||
|
amduatd_strbuf_free(&b);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_sync_status_append_cursor(
|
||||||
|
amduatd_strbuf_t *b,
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
const char *peer_key,
|
||||||
|
bool push) {
|
||||||
|
amduatd_fed_cursor_record_t cursor;
|
||||||
|
amduat_reference_t ref;
|
||||||
|
amduatd_fed_cursor_status_t status;
|
||||||
|
bool present = false;
|
||||||
|
const char *error = NULL;
|
||||||
|
char *ref_hex = NULL;
|
||||||
|
|
||||||
|
amduatd_fed_cursor_record_init(&cursor);
|
||||||
|
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||||
|
|
||||||
|
status = push
|
||||||
|
? amduatd_fed_push_cursor_get(store,
|
||||||
|
pointer_store,
|
||||||
|
effective_space,
|
||||||
|
peer_key,
|
||||||
|
&cursor,
|
||||||
|
&ref)
|
||||||
|
: amduatd_fed_cursor_get(store,
|
||||||
|
pointer_store,
|
||||||
|
effective_space,
|
||||||
|
peer_key,
|
||||||
|
&cursor,
|
||||||
|
&ref);
|
||||||
|
|
||||||
|
if (status == AMDUATD_FED_CURSOR_OK) {
|
||||||
|
present = true;
|
||||||
|
} else if (status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) {
|
||||||
|
present = false;
|
||||||
|
} else if (status == AMDUATD_FED_CURSOR_ERR_CODEC) {
|
||||||
|
present = false;
|
||||||
|
error = "codec";
|
||||||
|
} else {
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (present && cursor.has_record_ref) {
|
||||||
|
if (!amduat_asl_ref_encode_hex(cursor.last_record_ref, &ref_hex)) {
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(b, "{\"present\":") ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, present ? "true" : "false")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (error != NULL) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(b, ",\"error\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, error) ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, "\"")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (present && cursor.has_logseq) {
|
||||||
|
char tmp[32];
|
||||||
|
int n = snprintf(tmp, sizeof(tmp), "%llu",
|
||||||
|
(unsigned long long)cursor.last_logseq);
|
||||||
|
if (n <= 0 || (size_t)n >= sizeof(tmp)) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(b, ",\"last_logseq\":") ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, tmp)) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (present && ref_hex != NULL) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(b, ",\"ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, ref_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(b, "\"")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(b, "}")) {
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ref_hex);
|
||||||
|
amduatd_fed_cursor_record_free(&cursor);
|
||||||
|
amduat_reference_free(&ref);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_handle_get_space_sync_status(
|
||||||
|
int fd,
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
const amduatd_http_req_t *req,
|
||||||
|
const amduatd_cfg_t *dcfg,
|
||||||
|
const amduatd_fed_cfg_t *fed_cfg,
|
||||||
|
const amduatd_caps_t *caps,
|
||||||
|
const char *root_path,
|
||||||
|
amduatd_store_backend_t store_backend) {
|
||||||
|
amduat_asl_pointer_store_t pointer_store;
|
||||||
|
amduatd_space_roots_list_t peers;
|
||||||
|
amduatd_strbuf_t b;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&peers, 0, sizeof(peers));
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
|
||||||
|
if (store == NULL || req == NULL || dcfg == NULL || fed_cfg == NULL ||
|
||||||
|
root_path == NULL) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"internal error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caps != NULL && caps->enabled && req->x_capability[0] != '\0') {
|
||||||
|
const char *reason = NULL;
|
||||||
|
if (!amduatd_caps_check_space(caps, dcfg, req, &reason)) {
|
||||||
|
if (reason != NULL && strcmp(reason, "wrong-space") == 0) {
|
||||||
|
return amduatd_send_json_error(fd, 403, "Forbidden",
|
||||||
|
"space not permitted by capability");
|
||||||
|
}
|
||||||
|
return amduatd_send_json_error(fd, 403, "Forbidden",
|
||||||
|
"invalid capability");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"pointer store error");
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_list_cursor_peers(root_path,
|
||||||
|
req->effective_space,
|
||||||
|
&peers)) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"cursor scan failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (req->effective_space != NULL && req->effective_space->enabled &&
|
||||||
|
req->effective_space->space_id.data != NULL) {
|
||||||
|
const char *space_id = (const char *)req->effective_space->space_id.data;
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"scoped\",") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"space_id\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, space_id) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"unscoped\",") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"space_id\":null")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "},\"store_backend\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, amduatd_store_backend_name(store_backend)) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\",\"federation\":{")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"enabled\":") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, fed_cfg->enabled ? "true" : "false") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, ",\"transport\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, amduatd_fed_transport_name(
|
||||||
|
fed_cfg->transport_kind)) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"},\"peers\":[")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0u; i < peers.len; ++i) {
|
||||||
|
const char *peer_key = peers.names[i];
|
||||||
|
if (i != 0u) {
|
||||||
|
if (!amduatd_strbuf_append_char(&b, ',')) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"peer_key\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, peer_key) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\",\"pull_cursor\":")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_sync_status_append_cursor(&b,
|
||||||
|
store,
|
||||||
|
&pointer_store,
|
||||||
|
req->effective_space,
|
||||||
|
peer_key,
|
||||||
|
false)) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"push_cursor\":")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_sync_status_append_cursor(&b,
|
||||||
|
store,
|
||||||
|
&pointer_store,
|
||||||
|
req->effective_space,
|
||||||
|
peer_key,
|
||||||
|
true)) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "}")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "]}\n")) {
|
||||||
|
goto sync_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
|
||||||
|
|
||||||
|
sync_cleanup:
|
||||||
|
if (!ok) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
|
||||||
|
}
|
||||||
|
amduatd_space_roots_list_free(&peers);
|
||||||
|
amduatd_strbuf_free(&b);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_seed_api_contract(amduat_asl_store_t *store,
|
static bool amduatd_seed_api_contract(amduat_asl_store_t *store,
|
||||||
const amduat_asl_store_fs_config_t *cfg,
|
const amduat_asl_store_fs_config_t *cfg,
|
||||||
amduat_reference_t *out_ref) {
|
amduat_reference_t *out_ref) {
|
||||||
|
|
@ -6512,7 +6889,8 @@ static bool amduatd_handle_conn(int fd,
|
||||||
const amduat_fed_coord_t *coord,
|
const amduat_fed_coord_t *coord,
|
||||||
const amduatd_allowlist_t *allowlist,
|
const amduatd_allowlist_t *allowlist,
|
||||||
amduatd_caps_t *caps,
|
amduatd_caps_t *caps,
|
||||||
const char *root_path) {
|
const char *root_path,
|
||||||
|
amduatd_store_backend_t store_backend) {
|
||||||
amduatd_http_req_t req;
|
amduatd_http_req_t req;
|
||||||
char no_query[1024];
|
char no_query[1024];
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
|
@ -6619,6 +6997,27 @@ static bool amduatd_handle_conn(int fd,
|
||||||
root_path);
|
root_path);
|
||||||
goto conn_cleanup;
|
goto conn_cleanup;
|
||||||
}
|
}
|
||||||
|
if (strcmp(req.method, "GET") == 0 &&
|
||||||
|
strcmp(no_query, "/v1/space/roots") == 0) {
|
||||||
|
ok = amduatd_handle_get_space_roots(fd,
|
||||||
|
&req,
|
||||||
|
effective_cfg,
|
||||||
|
caps,
|
||||||
|
root_path);
|
||||||
|
goto conn_cleanup;
|
||||||
|
}
|
||||||
|
if (strcmp(req.method, "GET") == 0 &&
|
||||||
|
strcmp(no_query, "/v1/space/sync/status") == 0) {
|
||||||
|
ok = amduatd_handle_get_space_sync_status(fd,
|
||||||
|
store,
|
||||||
|
&req,
|
||||||
|
effective_cfg,
|
||||||
|
fed_cfg,
|
||||||
|
caps,
|
||||||
|
root_path,
|
||||||
|
store_backend);
|
||||||
|
goto conn_cleanup;
|
||||||
|
}
|
||||||
if (strcmp(req.method, "GET") == 0 &&
|
if (strcmp(req.method, "GET") == 0 &&
|
||||||
strcmp(no_query, "/v1/fed/records") == 0) {
|
strcmp(no_query, "/v1/fed/records") == 0) {
|
||||||
ok = amduatd_handle_get_fed_records(fd, store, fed_cfg, &req, root_path);
|
ok = amduatd_handle_get_fed_records(fd, store, fed_cfg, &req, root_path);
|
||||||
|
|
@ -7171,7 +7570,8 @@ int main(int argc, char **argv) {
|
||||||
fed_coord,
|
fed_coord,
|
||||||
&allowlist,
|
&allowlist,
|
||||||
&caps,
|
&caps,
|
||||||
root);
|
root,
|
||||||
|
store_backend);
|
||||||
(void)close(cfd);
|
(void)close(cfd);
|
||||||
|
|
||||||
if (dcfg.edges_refresh_ms != 0u) {
|
if (dcfg.edges_refresh_ms != 0u) {
|
||||||
|
|
|
||||||
603
src/amduatd_space_roots.c
Normal file
603
src/amduatd_space_roots.c
Normal file
|
|
@ -0,0 +1,603 @@
|
||||||
|
#include "amduatd_space_roots.h"
|
||||||
|
|
||||||
|
#include "amduat/asl/asl_pointer_fs.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_join_path(const char *base,
|
||||||
|
const char *segment,
|
||||||
|
char **out_path) {
|
||||||
|
size_t base_len;
|
||||||
|
size_t seg_len;
|
||||||
|
bool needs_sep;
|
||||||
|
size_t total_len;
|
||||||
|
char *buffer;
|
||||||
|
size_t offset = 0u;
|
||||||
|
|
||||||
|
if (base == NULL || segment == NULL || out_path == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (base[0] == '\0') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
base_len = strlen(base);
|
||||||
|
seg_len = strlen(segment);
|
||||||
|
needs_sep = base[base_len - 1u] != '/';
|
||||||
|
if (seg_len > SIZE_MAX - base_len - 2u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
|
||||||
|
buffer = (char *)malloc(total_len);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer + offset, base, base_len);
|
||||||
|
offset += base_len;
|
||||||
|
if (needs_sep) {
|
||||||
|
buffer[offset++] = '/';
|
||||||
|
}
|
||||||
|
if (seg_len != 0u) {
|
||||||
|
memcpy(buffer + offset, segment, seg_len);
|
||||||
|
offset += seg_len;
|
||||||
|
}
|
||||||
|
buffer[offset] = '\0';
|
||||||
|
|
||||||
|
*out_path = buffer;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_segment_valid(const char *segment) {
|
||||||
|
size_t len;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (segment == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
len = strlen(segment);
|
||||||
|
if (len == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (len == 1u && segment[0] == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (len == 2u && segment[0] == '.' && segment[1] == '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0u; i < len; ++i) {
|
||||||
|
unsigned char c = (unsigned char)segment[i];
|
||||||
|
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_list_reserve(
|
||||||
|
amduatd_space_roots_list_t *list,
|
||||||
|
size_t extra) {
|
||||||
|
size_t need;
|
||||||
|
size_t next_cap;
|
||||||
|
char **next;
|
||||||
|
|
||||||
|
if (list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (extra > (SIZE_MAX - list->len)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
need = list->len + extra;
|
||||||
|
if (need <= list->cap) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
next_cap = list->cap != 0u ? list->cap : 8u;
|
||||||
|
while (next_cap < need) {
|
||||||
|
if (next_cap > (SIZE_MAX / 2u)) {
|
||||||
|
next_cap = need;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next_cap *= 2u;
|
||||||
|
}
|
||||||
|
next = (char **)realloc(list->names, next_cap * sizeof(*next));
|
||||||
|
if (next == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
list->names = next;
|
||||||
|
list->cap = next_cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_list_add(amduatd_space_roots_list_t *list,
|
||||||
|
const char *name) {
|
||||||
|
size_t len;
|
||||||
|
char *copy;
|
||||||
|
|
||||||
|
if (list == NULL || name == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduat_asl_pointer_name_is_valid(name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
len = strlen(name);
|
||||||
|
if (len > SIZE_MAX - 1u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_list_reserve(list, 1u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
copy = (char *)malloc(len + 1u);
|
||||||
|
if (copy == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(copy, name, len);
|
||||||
|
copy[len] = '\0';
|
||||||
|
list->names[list->len++] = copy;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduatd_space_roots_list_cmp(const void *a, const void *b) {
|
||||||
|
const char *const *lhs = (const char *const *)a;
|
||||||
|
const char *const *rhs = (const char *const *)b;
|
||||||
|
if (lhs == NULL || rhs == NULL || *lhs == NULL || *rhs == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return strcmp(*lhs, *rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void amduatd_space_roots_list_sort_dedupe(
|
||||||
|
amduatd_space_roots_list_t *list) {
|
||||||
|
size_t out = 0u;
|
||||||
|
if (list == NULL || list->len == 0u) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qsort(list->names, list->len, sizeof(*list->names),
|
||||||
|
amduatd_space_roots_list_cmp);
|
||||||
|
for (size_t i = 0u; i < list->len; ++i) {
|
||||||
|
if (out != 0u && strcmp(list->names[i], list->names[out - 1u]) == 0) {
|
||||||
|
free(list->names[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
list->names[out++] = list->names[i];
|
||||||
|
}
|
||||||
|
list->len = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_collect_dir(
|
||||||
|
const char *dir_path,
|
||||||
|
const char *rel_name,
|
||||||
|
amduatd_space_roots_list_t *list) {
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *entry;
|
||||||
|
|
||||||
|
if (dir_path == NULL || rel_name == NULL || list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = opendir(dir_path);
|
||||||
|
if (dir == NULL) {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
const char *name = entry->d_name;
|
||||||
|
char *child_path = NULL;
|
||||||
|
struct stat st;
|
||||||
|
bool is_dir = false;
|
||||||
|
bool is_file = false;
|
||||||
|
|
||||||
|
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_segment_valid(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_join_path(dir_path, name, &child_path)) {
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (stat(child_path, &st) != 0) {
|
||||||
|
free(child_path);
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
is_dir = S_ISDIR(st.st_mode);
|
||||||
|
is_file = S_ISREG(st.st_mode);
|
||||||
|
if (is_dir) {
|
||||||
|
char *next_rel = NULL;
|
||||||
|
size_t rel_len = strlen(rel_name);
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
size_t total_len = rel_len + 1u + name_len + 1u;
|
||||||
|
if (rel_len == 0u) {
|
||||||
|
total_len = name_len + 1u;
|
||||||
|
}
|
||||||
|
next_rel = (char *)malloc(total_len);
|
||||||
|
if (next_rel == NULL) {
|
||||||
|
free(child_path);
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (rel_len != 0u) {
|
||||||
|
memcpy(next_rel, rel_name, rel_len);
|
||||||
|
next_rel[rel_len] = '/';
|
||||||
|
memcpy(next_rel + rel_len + 1u, name, name_len);
|
||||||
|
next_rel[rel_len + 1u + name_len] = '\0';
|
||||||
|
} else {
|
||||||
|
memcpy(next_rel, name, name_len);
|
||||||
|
next_rel[name_len] = '\0';
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_collect_dir(child_path, next_rel, list)) {
|
||||||
|
free(next_rel);
|
||||||
|
free(child_path);
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(next_rel);
|
||||||
|
} else if (is_file && strcmp(name, "head") == 0) {
|
||||||
|
if (rel_name[0] != '\0') {
|
||||||
|
if (!amduatd_space_roots_list_add(list, rel_name)) {
|
||||||
|
free(child_path);
|
||||||
|
closedir(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(child_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_collect_cursor_prefix(
|
||||||
|
const char *pointers_root,
|
||||||
|
const char *prefix_name,
|
||||||
|
amduatd_space_roots_list_t *list) {
|
||||||
|
char *prefix_path = NULL;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (pointers_root == NULL || prefix_name == NULL || list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduat_asl_pointer_name_is_valid(prefix_name)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_join_path(pointers_root, prefix_name,
|
||||||
|
&prefix_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ok = amduatd_space_roots_collect_dir(prefix_path, prefix_name, list);
|
||||||
|
free(prefix_path);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_append_cursor_heads(
|
||||||
|
const char *store_root,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
bool push,
|
||||||
|
amduatd_space_roots_list_t *list) {
|
||||||
|
char *pointers_root = NULL;
|
||||||
|
amduat_octets_t prefix = amduat_octets(NULL, 0u);
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (store_root == NULL || list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_scope_name(effective_space,
|
||||||
|
push ? "fed/push_cursor" : "fed/cursor",
|
||||||
|
&prefix)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_join_path(store_root, "pointers",
|
||||||
|
&pointers_root)) {
|
||||||
|
amduat_octets_free(&prefix);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ok = amduatd_space_roots_collect_cursor_prefix(
|
||||||
|
pointers_root, (const char *)prefix.data, list);
|
||||||
|
free(pointers_root);
|
||||||
|
amduat_octets_free(&prefix);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_cursor_peer_from_name(
|
||||||
|
const char *prefix,
|
||||||
|
const char *pointer_name,
|
||||||
|
char **out_peer) {
|
||||||
|
const char suffix[] = "/head";
|
||||||
|
size_t prefix_len;
|
||||||
|
size_t name_len;
|
||||||
|
size_t suffix_len = sizeof(suffix) - 1u;
|
||||||
|
size_t peer_len;
|
||||||
|
char *peer;
|
||||||
|
|
||||||
|
if (prefix == NULL || pointer_name == NULL || out_peer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out_peer = NULL;
|
||||||
|
prefix_len = strlen(prefix);
|
||||||
|
name_len = strlen(pointer_name);
|
||||||
|
if (name_len <= prefix_len + 1u + suffix_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strncmp(pointer_name, prefix, prefix_len) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (pointer_name[prefix_len] != '/') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (strcmp(pointer_name + name_len - suffix_len, suffix) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
peer_len = name_len - prefix_len - 1u - suffix_len;
|
||||||
|
if (peer_len == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
peer = (char *)malloc(peer_len + 1u);
|
||||||
|
if (peer == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(peer, pointer_name + prefix_len + 1u, peer_len);
|
||||||
|
peer[peer_len] = '\0';
|
||||||
|
*out_peer = peer;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_space_roots_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_space_roots_build_collection_log_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 = 4u + 11u + name_len + 4u + 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list(
|
||||||
|
const char *store_root,
|
||||||
|
const amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduatd_space_roots_list_t *out_list) {
|
||||||
|
amduat_octets_t edges_collection = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t edges_index_head = amduat_octets(NULL, 0u);
|
||||||
|
char *collection_head = NULL;
|
||||||
|
char *collection_log_head = NULL;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
if (out_list != NULL) {
|
||||||
|
memset(out_list, 0, sizeof(*out_list));
|
||||||
|
}
|
||||||
|
if (store_root == NULL || pointer_store == NULL || out_list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
(void)pointer_store;
|
||||||
|
|
||||||
|
if (!amduatd_space_edges_collection_name(effective_space,
|
||||||
|
&edges_collection) ||
|
||||||
|
!amduatd_space_edges_index_head_name(effective_space,
|
||||||
|
&edges_index_head)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_build_collection_head_name(
|
||||||
|
(const char *)edges_collection.data, &collection_head) ||
|
||||||
|
!amduatd_space_roots_build_collection_log_head_name(
|
||||||
|
(const char *)edges_collection.data, &collection_log_head)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_list_add(out_list,
|
||||||
|
(const char *)edges_index_head.data) ||
|
||||||
|
!amduatd_space_roots_list_add(out_list, collection_head) ||
|
||||||
|
!amduatd_space_roots_list_add(out_list, collection_log_head)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_append_cursor_heads(store_root,
|
||||||
|
effective_space,
|
||||||
|
false,
|
||||||
|
out_list) ||
|
||||||
|
!amduatd_space_roots_append_cursor_heads(store_root,
|
||||||
|
effective_space,
|
||||||
|
true,
|
||||||
|
out_list)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduatd_space_roots_list_sort_dedupe(out_list);
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
free((void *)edges_collection.data);
|
||||||
|
free((void *)edges_index_head.data);
|
||||||
|
free(collection_head);
|
||||||
|
free(collection_log_head);
|
||||||
|
if (!ok) {
|
||||||
|
amduatd_space_roots_list_free(out_list);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list_cursor_heads(
|
||||||
|
const char *store_root,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
bool push,
|
||||||
|
amduatd_space_roots_list_t *out_list) {
|
||||||
|
bool ok = false;
|
||||||
|
if (out_list != NULL) {
|
||||||
|
memset(out_list, 0, sizeof(*out_list));
|
||||||
|
}
|
||||||
|
if (store_root == NULL || out_list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_append_cursor_heads(store_root,
|
||||||
|
effective_space,
|
||||||
|
push,
|
||||||
|
out_list)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
amduatd_space_roots_list_sort_dedupe(out_list);
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (!ok) {
|
||||||
|
amduatd_space_roots_list_free(out_list);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list_cursor_peers(
|
||||||
|
const char *store_root,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduatd_space_roots_list_t *out_list) {
|
||||||
|
amduatd_space_roots_list_t pull_heads;
|
||||||
|
amduatd_space_roots_list_t push_heads;
|
||||||
|
amduat_octets_t pull_prefix = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t push_prefix = amduat_octets(NULL, 0u);
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&pull_heads, 0, sizeof(pull_heads));
|
||||||
|
memset(&push_heads, 0, sizeof(push_heads));
|
||||||
|
if (out_list != NULL) {
|
||||||
|
memset(out_list, 0, sizeof(*out_list));
|
||||||
|
}
|
||||||
|
if (store_root == NULL || out_list == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_scope_name(effective_space,
|
||||||
|
"fed/cursor",
|
||||||
|
&pull_prefix) ||
|
||||||
|
!amduatd_space_scope_name(effective_space,
|
||||||
|
"fed/push_cursor",
|
||||||
|
&push_prefix)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_roots_list_cursor_heads(store_root,
|
||||||
|
effective_space,
|
||||||
|
false,
|
||||||
|
&pull_heads) ||
|
||||||
|
!amduatd_space_roots_list_cursor_heads(store_root,
|
||||||
|
effective_space,
|
||||||
|
true,
|
||||||
|
&push_heads)) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0u; i < pull_heads.len; ++i) {
|
||||||
|
char *peer = NULL;
|
||||||
|
if (amduatd_space_roots_cursor_peer_from_name(
|
||||||
|
(const char *)pull_prefix.data,
|
||||||
|
pull_heads.names[i],
|
||||||
|
&peer)) {
|
||||||
|
if (!amduatd_space_roots_list_add(out_list, peer)) {
|
||||||
|
free(peer);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
free(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0u; i < push_heads.len; ++i) {
|
||||||
|
char *peer = NULL;
|
||||||
|
if (amduatd_space_roots_cursor_peer_from_name(
|
||||||
|
(const char *)push_prefix.data,
|
||||||
|
push_heads.names[i],
|
||||||
|
&peer)) {
|
||||||
|
if (!amduatd_space_roots_list_add(out_list, peer)) {
|
||||||
|
free(peer);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
free(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amduatd_space_roots_list_sort_dedupe(out_list);
|
||||||
|
ok = true;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
amduatd_space_roots_list_free(&pull_heads);
|
||||||
|
amduatd_space_roots_list_free(&push_heads);
|
||||||
|
amduat_octets_free(&pull_prefix);
|
||||||
|
amduat_octets_free(&push_prefix);
|
||||||
|
if (!ok) {
|
||||||
|
amduatd_space_roots_list_free(out_list);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amduatd_space_roots_list_free(amduatd_space_roots_list_t *list) {
|
||||||
|
if (list == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0u; i < list->len; ++i) {
|
||||||
|
free(list->names[i]);
|
||||||
|
}
|
||||||
|
free(list->names);
|
||||||
|
memset(list, 0, sizeof(*list));
|
||||||
|
}
|
||||||
44
src/amduatd_space_roots.h
Normal file
44
src/amduatd_space_roots.h
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#ifndef AMDUATD_SPACE_ROOTS_H
|
||||||
|
#define AMDUATD_SPACE_ROOTS_H
|
||||||
|
|
||||||
|
#include "amduatd_space.h"
|
||||||
|
|
||||||
|
#include "amduat/asl/asl_pointer_fs.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char **names;
|
||||||
|
size_t len;
|
||||||
|
size_t cap;
|
||||||
|
} amduatd_space_roots_list_t;
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list(
|
||||||
|
const char *store_root,
|
||||||
|
const amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduatd_space_roots_list_t *out_list);
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list_cursor_heads(
|
||||||
|
const char *store_root,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
bool push,
|
||||||
|
amduatd_space_roots_list_t *out_list);
|
||||||
|
|
||||||
|
bool amduatd_space_roots_list_cursor_peers(
|
||||||
|
const char *store_root,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduatd_space_roots_list_t *out_list);
|
||||||
|
|
||||||
|
void amduatd_space_roots_list_free(amduatd_space_roots_list_t *list);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* AMDUATD_SPACE_ROOTS_H */
|
||||||
325
tests/test_amduatd_space_roots.c
Normal file
325
tests/test_amduatd_space_roots.c
Normal file
|
|
@ -0,0 +1,325 @@
|
||||||
|
#ifndef _POSIX_C_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "amduatd_space_roots.h"
|
||||||
|
|
||||||
|
#include "amduatd_fed_cursor.h"
|
||||||
|
#include "amduatd_space.h"
|
||||||
|
#include "amduatd_store.h"
|
||||||
|
|
||||||
|
#include "amduat/asl/asl_store_fs_meta.h"
|
||||||
|
#include "amduat/asl/asl_pointer_fs.h"
|
||||||
|
#include "amduat/asl/none.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static char *amduatd_test_make_temp_dir(void) {
|
||||||
|
char tmpl[] = "/tmp/amduatd-roots-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_list_contains(const amduatd_space_roots_list_t *list,
|
||||||
|
const char *name) {
|
||||||
|
if (list == NULL || name == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0u; i < list->len; ++i) {
|
||||||
|
if (strcmp(list->names[i], name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_list_sorted(const amduatd_space_roots_list_t *list) {
|
||||||
|
if (list == NULL || list->len < 2u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (size_t i = 1u; i < list->len; ++i) {
|
||||||
|
if (strcmp(list->names[i - 1u], list->names[i]) > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 = 4u + 11u + name_len + 4u + 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 int amduatd_test_empty_store(void) {
|
||||||
|
char *root = amduatd_test_make_temp_dir();
|
||||||
|
amduat_asl_store_fs_config_t cfg;
|
||||||
|
amduat_asl_pointer_store_t pointer_store;
|
||||||
|
amduatd_space_t space;
|
||||||
|
amduatd_space_roots_list_t list;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_list(root, &pointer_store, &space, &list)) {
|
||||||
|
fprintf(stderr, "roots list failed\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.len != 3u ||
|
||||||
|
!amduatd_list_contains(&list, "daemon/edges/index/head") ||
|
||||||
|
!amduatd_list_contains(&list, "collection/daemon/edges/head") ||
|
||||||
|
!amduatd_list_contains(&list, "log/collection/daemon/edges/log/head") ||
|
||||||
|
!amduatd_list_sorted(&list)) {
|
||||||
|
fprintf(stderr, "unexpected empty-store roots list\n");
|
||||||
|
amduatd_space_roots_list_free(&list);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduatd_space_roots_list_free(&list);
|
||||||
|
free(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduatd_test_cursor_roots(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;
|
||||||
|
amduat_artifact_t none_artifact;
|
||||||
|
amduat_reference_t none_ref;
|
||||||
|
amduat_octets_t cursor_name = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t push_name = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t edges_collection = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t edges_index_head = amduat_octets(NULL, 0u);
|
||||||
|
char *collection_head = NULL;
|
||||||
|
char *collection_log_head = NULL;
|
||||||
|
amduatd_space_roots_list_t list;
|
||||||
|
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 store\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, "alpha", false)) {
|
||||||
|
fprintf(stderr, "failed to init space\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!amduatd_fed_cursor_pointer_name(&space, "peer-a", &cursor_name) ||
|
||||||
|
!amduatd_fed_push_cursor_pointer_name(&space, "peer-b", &push_name)) {
|
||||||
|
fprintf(stderr, "failed to build cursor names\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amduat_asl_pointer_cas(&pointer_store,
|
||||||
|
(const char *)cursor_name.data,
|
||||||
|
false,
|
||||||
|
NULL,
|
||||||
|
&none_ref,
|
||||||
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
|
||||||
|
amduat_asl_pointer_cas(&pointer_store,
|
||||||
|
(const char *)push_name.data,
|
||||||
|
false,
|
||||||
|
NULL,
|
||||||
|
&none_ref,
|
||||||
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped) {
|
||||||
|
fprintf(stderr, "failed to seed cursor pointers\n");
|
||||||
|
amduat_octets_free(&cursor_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_list(root, &pointer_store, &space, &list)) {
|
||||||
|
fprintf(stderr, "roots list failed\n");
|
||||||
|
amduat_octets_free(&cursor_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_edges_collection_name(&space, &edges_collection) ||
|
||||||
|
!amduatd_space_edges_index_head_name(&space, &edges_index_head) ||
|
||||||
|
!amduatd_build_collection_head_name(
|
||||||
|
(const char *)edges_collection.data, &collection_head) ||
|
||||||
|
!amduatd_build_collection_log_head_name(
|
||||||
|
(const char *)edges_collection.data, &collection_log_head)) {
|
||||||
|
fprintf(stderr, "failed to build static root names\n");
|
||||||
|
amduat_octets_free(&cursor_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
amduatd_space_roots_list_free(&list);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.len != 5u ||
|
||||||
|
!amduatd_list_contains(&list, (const char *)cursor_name.data) ||
|
||||||
|
!amduatd_list_contains(&list, (const char *)push_name.data) ||
|
||||||
|
!amduatd_list_contains(&list, (const char *)edges_index_head.data) ||
|
||||||
|
!amduatd_list_contains(&list, collection_head) ||
|
||||||
|
!amduatd_list_contains(&list, collection_log_head) ||
|
||||||
|
!amduatd_list_sorted(&list)) {
|
||||||
|
fprintf(stderr, "unexpected cursor roots list\n");
|
||||||
|
amduat_octets_free(&cursor_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
amduat_octets_free(&edges_collection);
|
||||||
|
amduat_octets_free(&edges_index_head);
|
||||||
|
free(collection_head);
|
||||||
|
free(collection_log_head);
|
||||||
|
amduatd_space_roots_list_free(&list);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduat_octets_free(&cursor_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
amduat_octets_free(&edges_collection);
|
||||||
|
amduat_octets_free(&edges_index_head);
|
||||||
|
free(collection_head);
|
||||||
|
free(collection_log_head);
|
||||||
|
amduatd_space_roots_list_free(&list);
|
||||||
|
free(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (amduatd_test_empty_store() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (amduatd_test_cursor_roots() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
228
tests/test_amduatd_space_sync_status.c
Normal file
228
tests/test_amduatd_space_sync_status.c
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
#ifndef _POSIX_C_SOURCE
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "amduatd_space_roots.h"
|
||||||
|
|
||||||
|
#include "amduatd_fed_cursor.h"
|
||||||
|
#include "amduatd_space.h"
|
||||||
|
#include "amduatd_store.h"
|
||||||
|
|
||||||
|
#include "amduat/asl/asl_pointer_fs.h"
|
||||||
|
#include "amduat/asl/asl_store_fs_meta.h"
|
||||||
|
#include "amduat/asl/none.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static char *amduatd_test_make_temp_dir(void) {
|
||||||
|
char tmpl[] = "/tmp/amduatd-sync-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_list_contains(const amduatd_space_roots_list_t *list,
|
||||||
|
const char *name) {
|
||||||
|
if (list == NULL || name == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0u; i < list->len; ++i) {
|
||||||
|
if (strcmp(list->names[i], name) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_list_sorted(const amduatd_space_roots_list_t *list) {
|
||||||
|
if (list == NULL || list->len < 2u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (size_t i = 1u; i < list->len; ++i) {
|
||||||
|
if (strcmp(list->names[i - 1u], list->names[i]) > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduatd_test_empty_peers(void) {
|
||||||
|
char *root = amduatd_test_make_temp_dir();
|
||||||
|
amduat_asl_store_fs_config_t cfg;
|
||||||
|
amduat_asl_pointer_store_t pointer_store;
|
||||||
|
amduatd_space_t space;
|
||||||
|
amduatd_space_roots_list_t peers;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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, "alpha", false)) {
|
||||||
|
fprintf(stderr, "failed to init space\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_list_cursor_peers(root, &space, &peers)) {
|
||||||
|
fprintf(stderr, "cursor peer list failed\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peers.len != 0u) {
|
||||||
|
fprintf(stderr, "expected empty peers list\n");
|
||||||
|
amduatd_space_roots_list_free(&peers);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduatd_space_roots_list_free(&peers);
|
||||||
|
free(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int amduatd_test_peer_discovery(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;
|
||||||
|
amduat_artifact_t none_artifact;
|
||||||
|
amduat_reference_t none_ref;
|
||||||
|
amduat_octets_t pull_name = amduat_octets(NULL, 0u);
|
||||||
|
amduat_octets_t push_name = amduat_octets(NULL, 0u);
|
||||||
|
amduatd_space_roots_list_t peers;
|
||||||
|
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 store\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, "alpha", false)) {
|
||||||
|
fprintf(stderr, "failed to init space\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (!amduatd_fed_cursor_pointer_name(&space, "1", &pull_name) ||
|
||||||
|
!amduatd_fed_push_cursor_pointer_name(&space, "2", &push_name)) {
|
||||||
|
fprintf(stderr, "failed to build cursor names\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amduat_asl_pointer_cas(&pointer_store,
|
||||||
|
(const char *)pull_name.data,
|
||||||
|
false,
|
||||||
|
NULL,
|
||||||
|
&none_ref,
|
||||||
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
|
||||||
|
amduat_asl_pointer_cas(&pointer_store,
|
||||||
|
(const char *)push_name.data,
|
||||||
|
false,
|
||||||
|
NULL,
|
||||||
|
&none_ref,
|
||||||
|
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped) {
|
||||||
|
fprintf(stderr, "failed to seed cursor pointers\n");
|
||||||
|
amduat_octets_free(&pull_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_roots_list_cursor_peers(root, &space, &peers)) {
|
||||||
|
fprintf(stderr, "cursor peer list failed\n");
|
||||||
|
amduat_octets_free(&pull_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peers.len != 2u ||
|
||||||
|
!amduatd_list_contains(&peers, "1") ||
|
||||||
|
!amduatd_list_contains(&peers, "2") ||
|
||||||
|
!amduatd_list_sorted(&peers)) {
|
||||||
|
fprintf(stderr, "unexpected peers list\n");
|
||||||
|
amduatd_space_roots_list_free(&peers);
|
||||||
|
amduat_octets_free(&pull_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
amduatd_space_roots_list_free(&peers);
|
||||||
|
amduat_octets_free(&pull_name);
|
||||||
|
amduat_octets_free(&push_name);
|
||||||
|
free(root);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
if (amduatd_test_empty_peers() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (amduatd_test_peer_discovery() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue