Add CAS-safe PUT /v1/space/manifest with tests and docs
This commit is contained in:
parent
704ea18a32
commit
8eea0cb69b
23
README.md
23
README.md
|
|
@ -259,6 +259,29 @@ curl --unix-socket amduatd.sock \
|
||||||
-H 'X-Amduat-Space: demo'
|
-H 'X-Amduat-Space: demo'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Create the manifest (only if no head exists yet):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --unix-socket amduatd.sock \
|
||||||
|
-X PUT 'http://localhost/v1/space/manifest' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-H 'X-Amduat-Space: demo' \
|
||||||
|
--data-binary '{"version":1,"mounts":[]}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Update with optimistic concurrency:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl --unix-socket amduatd.sock \
|
||||||
|
-X PUT 'http://localhost/v1/space/manifest' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-H 'X-Amduat-Space: demo' \
|
||||||
|
-H 'If-Match: <manifest_ref>' \
|
||||||
|
--data-binary @manifest.json
|
||||||
|
```
|
||||||
|
|
||||||
|
`If-Match` can be replaced with `?expected_ref=<manifest_ref>` if needed.
|
||||||
|
|
||||||
To fail `/v1/pel/run` if the derivation index write fails:
|
To fail `/v1/pel/run` if the derivation index write fails:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
{"method": "GET", "path": "/v1/space/doctor"},
|
{"method": "GET", "path": "/v1/space/doctor"},
|
||||||
{"method": "GET", "path": "/v1/space/roots"},
|
{"method": "GET", "path": "/v1/space/roots"},
|
||||||
{"method": "GET", "path": "/v1/space/manifest"},
|
{"method": "GET", "path": "/v1/space/manifest"},
|
||||||
|
{"method": "PUT", "path": "/v1/space/manifest"},
|
||||||
{"method": "GET", "path": "/v1/space/sync/status"},
|
{"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"},
|
||||||
|
|
@ -227,6 +228,17 @@
|
||||||
"manifest_ref": {"type": "string"},
|
"manifest_ref": {"type": "string"},
|
||||||
"manifest": {"$ref": "#/schemas/space_manifest"}
|
"manifest": {"$ref": "#/schemas/space_manifest"}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"space_manifest_put_response": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["effective_space", "manifest_ref", "updated", "manifest"],
|
||||||
|
"properties": {
|
||||||
|
"effective_space": {"type": "object"},
|
||||||
|
"manifest_ref": {"type": "string"},
|
||||||
|
"updated": {"type": "boolean"},
|
||||||
|
"previous_ref": {"type": "string"},
|
||||||
|
"manifest": {"$ref": "#/schemas/space_manifest"}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
360
src/amduatd.c
360
src/amduatd.c
|
|
@ -115,6 +115,7 @@ static const char *const AMDUATD_DEFAULT_SOCK = "amduatd.sock";
|
||||||
static const uint64_t AMDUATD_FED_TICK_MS = 1000u;
|
static const uint64_t AMDUATD_FED_TICK_MS = 1000u;
|
||||||
static const size_t AMDUATD_FED_INGEST_MAX_BYTES = 8u * 1024u * 1024u;
|
static const size_t AMDUATD_FED_INGEST_MAX_BYTES = 8u * 1024u * 1024u;
|
||||||
static const size_t AMDUATD_REF_TEXT_MAX = 256u;
|
static const size_t AMDUATD_REF_TEXT_MAX = 256u;
|
||||||
|
static const size_t AMDUATD_SPACE_MANIFEST_MAX_BYTES = 64u * 1024u;
|
||||||
static const char k_amduatd_contract_v1_json[] =
|
static const char k_amduatd_contract_v1_json[] =
|
||||||
"{"
|
"{"
|
||||||
"\"contract\":\"AMDUATD/API/1\","
|
"\"contract\":\"AMDUATD/API/1\","
|
||||||
|
|
@ -127,6 +128,7 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/space/doctor\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/space/doctor\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/space/roots\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/space/roots\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/space/manifest\"},"
|
"{\"method\":\"GET\",\"path\":\"/v1/space/manifest\"},"
|
||||||
|
"{\"method\":\"PUT\",\"path\":\"/v1/space/manifest\"},"
|
||||||
"{\"method\":\"GET\",\"path\":\"/v1/space/sync/status\"},"
|
"{\"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\"},"
|
||||||
|
|
@ -319,6 +321,17 @@ static const char k_amduatd_contract_v1_json[] =
|
||||||
"\"manifest_ref\":{\"type\":\"string\"},"
|
"\"manifest_ref\":{\"type\":\"string\"},"
|
||||||
"\"manifest\":{\"$ref\":\"#/schemas/space_manifest\"}"
|
"\"manifest\":{\"$ref\":\"#/schemas/space_manifest\"}"
|
||||||
"}"
|
"}"
|
||||||
|
"},"
|
||||||
|
"\"space_manifest_put_response\":{"
|
||||||
|
"\"type\":\"object\","
|
||||||
|
"\"required\":[\"effective_space\",\"manifest_ref\",\"updated\",\"manifest\"],"
|
||||||
|
"\"properties\":{"
|
||||||
|
"\"effective_space\":{\"type\":\"object\"},"
|
||||||
|
"\"manifest_ref\":{\"type\":\"string\"},"
|
||||||
|
"\"updated\":{\"type\":\"boolean\"},"
|
||||||
|
"\"previous_ref\":{\"type\":\"string\"},"
|
||||||
|
"\"manifest\":{\"$ref\":\"#/schemas/space_manifest\"}"
|
||||||
|
"}"
|
||||||
"}"
|
"}"
|
||||||
"}"
|
"}"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
|
@ -678,6 +691,47 @@ static bool amduatd_decode_ref_hex_str(const char *s,
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amduatd_trim_header_value(const char *value,
|
||||||
|
char *out,
|
||||||
|
size_t cap) {
|
||||||
|
const char *start;
|
||||||
|
const char *end;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (out != NULL && cap != 0u) {
|
||||||
|
out[0] = '\0';
|
||||||
|
}
|
||||||
|
if (value == NULL || out == NULL || cap == 0u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = value;
|
||||||
|
while (*start == ' ' || *start == '\t') {
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
end = start + strlen(start);
|
||||||
|
while (end > start && (end[-1] == ' ' || end[-1] == '\t')) {
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
if (end <= start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (*start == '"' && end > start + 1 && end[-1] == '"') {
|
||||||
|
start++;
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
if (end <= start) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
len = (size_t)(end - start);
|
||||||
|
if (len >= cap) {
|
||||||
|
len = cap - 1u;
|
||||||
|
}
|
||||||
|
memcpy(out, start, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_parse_type_tag_hex(const char *text,
|
static bool amduatd_parse_type_tag_hex(const char *text,
|
||||||
bool *out_has_type_tag,
|
bool *out_has_type_tag,
|
||||||
amduat_type_tag_t *out_type_tag) {
|
amduat_type_tag_t *out_type_tag) {
|
||||||
|
|
@ -1036,6 +1090,65 @@ roots_cleanup:
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amduatd_send_manifest_conflict(
|
||||||
|
int fd,
|
||||||
|
const amduat_reference_t *expected_ref,
|
||||||
|
const amduat_reference_t *current_ref,
|
||||||
|
bool current_present) {
|
||||||
|
amduatd_strbuf_t b;
|
||||||
|
char *expected_hex = NULL;
|
||||||
|
char *current_hex = NULL;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
|
||||||
|
if (expected_ref != NULL) {
|
||||||
|
if (!amduat_asl_ref_encode_hex(*expected_ref, &expected_hex)) {
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"encode error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_present && current_ref != NULL) {
|
||||||
|
if (!amduat_asl_ref_encode_hex(*current_ref, ¤t_hex)) {
|
||||||
|
free(expected_hex);
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"encode error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"error\":\"manifest conflict\"")) {
|
||||||
|
goto manifest_conflict_cleanup;
|
||||||
|
}
|
||||||
|
if (expected_hex != NULL) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"expected_ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, expected_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
goto manifest_conflict_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (current_hex != NULL) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"current_ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, current_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
goto manifest_conflict_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "}\n")) {
|
||||||
|
goto manifest_conflict_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = amduatd_http_send_json(fd, 409, "Conflict", b.data, false);
|
||||||
|
|
||||||
|
manifest_conflict_cleanup:
|
||||||
|
amduatd_strbuf_free(&b);
|
||||||
|
free(expected_hex);
|
||||||
|
free(current_hex);
|
||||||
|
if (!ok) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_handle_get_space_manifest(
|
static bool amduatd_handle_get_space_manifest(
|
||||||
int fd,
|
int fd,
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
|
|
@ -1194,6 +1307,243 @@ manifest_cleanup:
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool amduatd_handle_put_space_manifest(
|
||||||
|
int fd,
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
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_manifest_t manifest;
|
||||||
|
amduat_reference_t new_ref;
|
||||||
|
amduat_reference_t expected_ref;
|
||||||
|
amduatd_space_manifest_status_t status;
|
||||||
|
amduatd_strbuf_t b;
|
||||||
|
uint8_t *body = NULL;
|
||||||
|
char expected_param[AMDUATD_REF_TEXT_MAX];
|
||||||
|
char if_match_value[AMDUATD_REF_TEXT_MAX];
|
||||||
|
const char *expected_text = NULL;
|
||||||
|
bool have_expected = false;
|
||||||
|
char *manifest_ref_hex = NULL;
|
||||||
|
char *previous_ref_hex = NULL;
|
||||||
|
char *manifest_json = NULL;
|
||||||
|
size_t manifest_json_len = 0u;
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
memset(&new_ref, 0, sizeof(new_ref));
|
||||||
|
memset(&expected_ref, 0, sizeof(expected_ref));
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
|
||||||
|
if (store == NULL || 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 (req->content_length == 0u) {
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request", "missing body");
|
||||||
|
}
|
||||||
|
if (req->content_length > AMDUATD_SPACE_MANIFEST_MAX_BYTES) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req->if_match[0] != '\0') {
|
||||||
|
if (!amduatd_trim_header_value(req->if_match,
|
||||||
|
if_match_value,
|
||||||
|
sizeof(if_match_value)) ||
|
||||||
|
if_match_value[0] == '\0' ||
|
||||||
|
strcmp(if_match_value, "*") == 0) {
|
||||||
|
free(body);
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid If-Match");
|
||||||
|
}
|
||||||
|
expected_text = if_match_value;
|
||||||
|
}
|
||||||
|
if (amduatd_query_param(req->path,
|
||||||
|
"expected_ref",
|
||||||
|
expected_param,
|
||||||
|
sizeof(expected_param)) != NULL &&
|
||||||
|
expected_param[0] != '\0') {
|
||||||
|
if (expected_text != NULL &&
|
||||||
|
strcmp(expected_text, expected_param) != 0) {
|
||||||
|
free(body);
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"conflicting expected_ref");
|
||||||
|
}
|
||||||
|
expected_text = expected_param;
|
||||||
|
}
|
||||||
|
if (expected_text != NULL) {
|
||||||
|
if (!amduat_asl_ref_decode_hex(expected_text, &expected_ref)) {
|
||||||
|
free(body);
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid expected_ref");
|
||||||
|
}
|
||||||
|
have_expected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
|
||||||
|
free(body);
|
||||||
|
if (have_expected) {
|
||||||
|
amduat_reference_free(&expected_ref);
|
||||||
|
}
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"pointer store error");
|
||||||
|
}
|
||||||
|
|
||||||
|
status = amduatd_space_manifest_put(
|
||||||
|
store,
|
||||||
|
&pointer_store,
|
||||||
|
req->effective_space,
|
||||||
|
amduat_octets(body, req->content_length),
|
||||||
|
have_expected ? &expected_ref : NULL,
|
||||||
|
&new_ref,
|
||||||
|
&manifest);
|
||||||
|
free(body);
|
||||||
|
body = NULL;
|
||||||
|
|
||||||
|
if (status == AMDUATD_SPACE_MANIFEST_ERR_CODEC) {
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&new_ref);
|
||||||
|
if (have_expected) {
|
||||||
|
amduat_reference_free(&expected_ref);
|
||||||
|
}
|
||||||
|
return amduatd_send_json_error(fd, 400, "Bad Request",
|
||||||
|
"invalid manifest");
|
||||||
|
}
|
||||||
|
if (status == AMDUATD_SPACE_MANIFEST_ERR_CONFLICT) {
|
||||||
|
amduat_octets_t pointer_name = amduat_octets(NULL, 0u);
|
||||||
|
amduat_reference_t current_ref;
|
||||||
|
bool current_present = false;
|
||||||
|
amduat_asl_pointer_error_t perr;
|
||||||
|
|
||||||
|
memset(¤t_ref, 0, sizeof(current_ref));
|
||||||
|
if (amduatd_space_scope_name(req->effective_space,
|
||||||
|
"manifest/head",
|
||||||
|
&pointer_name)) {
|
||||||
|
perr = amduat_asl_pointer_get(&pointer_store,
|
||||||
|
(const char *)pointer_name.data,
|
||||||
|
¤t_present,
|
||||||
|
¤t_ref);
|
||||||
|
amduat_octets_free(&pointer_name);
|
||||||
|
if (perr != AMDUAT_ASL_POINTER_OK) {
|
||||||
|
current_present = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&new_ref);
|
||||||
|
ok = amduatd_send_manifest_conflict(
|
||||||
|
fd,
|
||||||
|
have_expected ? &expected_ref : NULL,
|
||||||
|
current_present ? ¤t_ref : NULL,
|
||||||
|
current_present);
|
||||||
|
if (current_present) {
|
||||||
|
amduat_reference_free(¤t_ref);
|
||||||
|
}
|
||||||
|
if (have_expected) {
|
||||||
|
amduat_reference_free(&expected_ref);
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
if (status != AMDUATD_SPACE_MANIFEST_OK) {
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&new_ref);
|
||||||
|
if (have_expected) {
|
||||||
|
amduat_reference_free(&expected_ref);
|
||||||
|
}
|
||||||
|
return amduatd_send_json_error(fd, 500, "Internal Server Error",
|
||||||
|
"manifest update failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduat_asl_ref_encode_hex(new_ref, &manifest_ref_hex)) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
if (have_expected &&
|
||||||
|
!amduat_asl_ref_encode_hex(expected_ref, &previous_ref_hex)) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
if (!amduatd_space_manifest_encode_json(&manifest,
|
||||||
|
&manifest_json,
|
||||||
|
&manifest_json_len)) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) {
|
||||||
|
goto manifest_put_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 manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"unscoped\",") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"space_id\":null")) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, "},\"manifest_ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, manifest_ref_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\",\"updated\":true")) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
if (previous_ref_hex != NULL) {
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"previous_ref\":\"") ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, previous_ref_hex) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "\"")) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_strbuf_append_cstr(&b, ",\"manifest\":") ||
|
||||||
|
!amduatd_strbuf_append(&b, manifest_json, manifest_json_len) ||
|
||||||
|
!amduatd_strbuf_append_cstr(&b, "}\n")) {
|
||||||
|
goto manifest_put_cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
|
||||||
|
|
||||||
|
manifest_put_cleanup:
|
||||||
|
if (!ok) {
|
||||||
|
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
|
||||||
|
}
|
||||||
|
amduatd_strbuf_free(&b);
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&new_ref);
|
||||||
|
if (have_expected) {
|
||||||
|
amduat_reference_free(&expected_ref);
|
||||||
|
}
|
||||||
|
free(manifest_ref_hex);
|
||||||
|
free(previous_ref_hex);
|
||||||
|
free(manifest_json);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_sync_status_append_cursor(
|
static bool amduatd_sync_status_append_cursor(
|
||||||
amduatd_strbuf_t *b,
|
amduatd_strbuf_t *b,
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
|
|
@ -7812,6 +8162,16 @@ static bool amduatd_handle_conn(int fd,
|
||||||
root_path);
|
root_path);
|
||||||
goto conn_cleanup;
|
goto conn_cleanup;
|
||||||
}
|
}
|
||||||
|
if (strcmp(req.method, "PUT") == 0 &&
|
||||||
|
strcmp(no_query, "/v1/space/manifest") == 0) {
|
||||||
|
ok = amduatd_handle_put_space_manifest(fd,
|
||||||
|
store,
|
||||||
|
&req,
|
||||||
|
effective_cfg,
|
||||||
|
caps,
|
||||||
|
root_path);
|
||||||
|
goto conn_cleanup;
|
||||||
|
}
|
||||||
if (strcmp(req.method, "GET") == 0 &&
|
if (strcmp(req.method, "GET") == 0 &&
|
||||||
strcmp(no_query, "/v1/space/sync/status") == 0) {
|
strcmp(no_query, "/v1/space/sync/status") == 0) {
|
||||||
ok = amduatd_handle_get_space_sync_status(fd,
|
ok = amduatd_handle_get_space_sync_status(fd,
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,12 @@ bool amduatd_http_parse_request(int fd, amduatd_http_req_t *out_req) {
|
||||||
v++;
|
v++;
|
||||||
}
|
}
|
||||||
strncpy(out_req->accept, v, sizeof(out_req->accept) - 1);
|
strncpy(out_req->accept, v, sizeof(out_req->accept) - 1);
|
||||||
|
} else if (strncasecmp(line, "If-Match:", 9) == 0) {
|
||||||
|
const char *v = line + 9;
|
||||||
|
while (*v == ' ' || *v == '\t') {
|
||||||
|
v++;
|
||||||
|
}
|
||||||
|
strncpy(out_req->if_match, v, sizeof(out_req->if_match) - 1);
|
||||||
} else if (strncasecmp(line, "X-Amduat-Type-Tag:", 18) == 0) {
|
} else if (strncasecmp(line, "X-Amduat-Type-Tag:", 18) == 0) {
|
||||||
const char *v = line + 18;
|
const char *v = line + 18;
|
||||||
while (*v == ' ' || *v == '\t') {
|
while (*v == ' ' || *v == '\t') {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ typedef struct {
|
||||||
char path[1024];
|
char path[1024];
|
||||||
char content_type[128];
|
char content_type[128];
|
||||||
char accept[128];
|
char accept[128];
|
||||||
|
char if_match[256];
|
||||||
char x_type_tag[64];
|
char x_type_tag[64];
|
||||||
char x_capability[2048];
|
char x_capability[2048];
|
||||||
char x_space[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
|
char x_space[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,92 @@
|
||||||
#include "amduatd_http.h"
|
#include "amduatd_http.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *data;
|
||||||
|
size_t len;
|
||||||
|
size_t cap;
|
||||||
|
} amduatd_manifest_buf_t;
|
||||||
|
|
||||||
|
static void amduatd_manifest_buf_free(amduatd_manifest_buf_t *b) {
|
||||||
|
if (b == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(b->data);
|
||||||
|
b->data = NULL;
|
||||||
|
b->len = 0;
|
||||||
|
b->cap = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_manifest_buf_reserve(amduatd_manifest_buf_t *b,
|
||||||
|
size_t extra) {
|
||||||
|
size_t need;
|
||||||
|
size_t next_cap;
|
||||||
|
char *next;
|
||||||
|
|
||||||
|
if (b == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (extra > (SIZE_MAX - b->len)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
need = b->len + extra;
|
||||||
|
if (need <= b->cap) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
next_cap = b->cap != 0u ? b->cap : 256u;
|
||||||
|
while (next_cap < need) {
|
||||||
|
if (next_cap > (SIZE_MAX / 2u)) {
|
||||||
|
next_cap = need;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next_cap *= 2u;
|
||||||
|
}
|
||||||
|
next = (char *)realloc(b->data, next_cap);
|
||||||
|
if (next == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
b->data = next;
|
||||||
|
b->cap = next_cap;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_manifest_buf_append(amduatd_manifest_buf_t *b,
|
||||||
|
const char *s,
|
||||||
|
size_t n) {
|
||||||
|
if (b == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (n == 0u) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (s == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_manifest_buf_reserve(b, n + 1u)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(b->data + b->len, s, n);
|
||||||
|
b->len += n;
|
||||||
|
b->data[b->len] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_manifest_buf_append_cstr(amduatd_manifest_buf_t *b,
|
||||||
|
const char *s) {
|
||||||
|
return amduatd_manifest_buf_append(
|
||||||
|
b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool amduatd_manifest_buf_append_char(amduatd_manifest_buf_t *b,
|
||||||
|
char c) {
|
||||||
|
return amduatd_manifest_buf_append(b, &c, 1u);
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_space_manifest_decode_ref(const char *s,
|
static bool amduatd_space_manifest_decode_ref(const char *s,
|
||||||
size_t len,
|
size_t len,
|
||||||
amduat_reference_t *out_ref) {
|
amduat_reference_t *out_ref) {
|
||||||
|
|
@ -271,6 +353,96 @@ static bool amduatd_space_manifest_parse_mount(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool amduatd_space_manifest_encode_json(const amduatd_space_manifest_t *manifest,
|
||||||
|
char **out_json,
|
||||||
|
size_t *out_len) {
|
||||||
|
amduatd_manifest_buf_t b;
|
||||||
|
|
||||||
|
if (out_json != NULL) {
|
||||||
|
*out_json = NULL;
|
||||||
|
}
|
||||||
|
if (out_len != NULL) {
|
||||||
|
*out_len = 0u;
|
||||||
|
}
|
||||||
|
if (manifest == NULL || out_json == NULL || out_len == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&b, 0, sizeof(b));
|
||||||
|
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, "{\"version\":")) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char tmp[32];
|
||||||
|
int n = snprintf(tmp, sizeof(tmp), "%u", manifest->version);
|
||||||
|
if (n <= 0 || (size_t)n >= sizeof(tmp)) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, tmp) ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, ",\"mounts\":[")) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0u; i < manifest->mounts_len; ++i) {
|
||||||
|
const amduatd_space_manifest_mount_t *mount = &manifest->mounts[i];
|
||||||
|
char *root_ref_hex = NULL;
|
||||||
|
|
||||||
|
if (i != 0u) {
|
||||||
|
if (!amduatd_manifest_buf_append_char(&b, ',')) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, "{\"name\":\"") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, mount->name) ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, "\",\"peer_key\":\"") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, mount->peer_key) ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, "\",\"space_id\":\"") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, mount->space_id) ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, "\",\"mode\":\"") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(
|
||||||
|
&b,
|
||||||
|
mount->mode == AMDUATD_SPACE_MANIFEST_MOUNT_PINNED ? "pinned"
|
||||||
|
: "track") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, "\"")) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mount->mode == AMDUATD_SPACE_MANIFEST_MOUNT_PINNED) {
|
||||||
|
if (!amduat_asl_ref_encode_hex(mount->pinned_root_ref, &root_ref_hex)) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, ",\"pinned_root_ref\":\"") ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, root_ref_hex) ||
|
||||||
|
!amduatd_manifest_buf_append_cstr(&b, "\"")) {
|
||||||
|
free(root_ref_hex);
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
free(root_ref_hex);
|
||||||
|
}
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, "}")) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_manifest_buf_append_cstr(&b, "]}")) {
|
||||||
|
amduatd_manifest_buf_free(&b);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_json = b.data;
|
||||||
|
*out_len = b.len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool amduatd_space_manifest_parse(amduat_octets_t payload,
|
static bool amduatd_space_manifest_parse(amduat_octets_t payload,
|
||||||
amduatd_space_manifest_t *manifest) {
|
amduatd_space_manifest_t *manifest) {
|
||||||
const char *p = NULL;
|
const char *p = NULL;
|
||||||
|
|
@ -397,6 +569,99 @@ static bool amduatd_space_manifest_parse(amduat_octets_t payload,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amduatd_space_manifest_status_t amduatd_space_manifest_put(
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduat_octets_t payload,
|
||||||
|
const amduat_reference_t *expected_ref,
|
||||||
|
amduat_reference_t *out_new_ref,
|
||||||
|
amduatd_space_manifest_t *out_manifest) {
|
||||||
|
amduat_octets_t pointer_name = amduat_octets(NULL, 0u);
|
||||||
|
amduat_reference_t record_ref;
|
||||||
|
amduat_asl_store_error_t store_err;
|
||||||
|
amduat_asl_pointer_error_t perr;
|
||||||
|
bool swapped = false;
|
||||||
|
char *encoded = NULL;
|
||||||
|
size_t encoded_len = 0u;
|
||||||
|
amduatd_space_manifest_t local_manifest;
|
||||||
|
amduatd_space_manifest_t *manifest = out_manifest != NULL ? out_manifest
|
||||||
|
: &local_manifest;
|
||||||
|
|
||||||
|
if (out_new_ref != NULL) {
|
||||||
|
*out_new_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||||
|
}
|
||||||
|
if (manifest != NULL) {
|
||||||
|
memset(manifest, 0, sizeof(*manifest));
|
||||||
|
}
|
||||||
|
if (store == NULL || pointer_store == NULL || manifest == NULL) {
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_manifest_parse(payload, manifest)) {
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_manifest_encode_json(manifest, &encoded, &encoded_len)) {
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&record_ref, 0, sizeof(record_ref));
|
||||||
|
store_err = amduat_asl_record_store_put(
|
||||||
|
store,
|
||||||
|
amduat_octets(AMDUATD_SPACE_MANIFEST_1,
|
||||||
|
strlen(AMDUATD_SPACE_MANIFEST_1)),
|
||||||
|
amduat_octets((const uint8_t *)encoded, encoded_len),
|
||||||
|
&record_ref);
|
||||||
|
free(encoded);
|
||||||
|
encoded = NULL;
|
||||||
|
if (store_err != AMDUAT_ASL_STORE_OK) {
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_STORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!amduatd_space_scope_name(effective_space,
|
||||||
|
"manifest/head",
|
||||||
|
&pointer_name)) {
|
||||||
|
amduat_reference_free(&record_ref);
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
perr = amduat_asl_pointer_cas(pointer_store,
|
||||||
|
(const char *)pointer_name.data,
|
||||||
|
expected_ref != NULL,
|
||||||
|
expected_ref,
|
||||||
|
&record_ref,
|
||||||
|
&swapped);
|
||||||
|
amduat_octets_free(&pointer_name);
|
||||||
|
if (perr != AMDUAT_ASL_POINTER_OK) {
|
||||||
|
amduat_reference_free(&record_ref);
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_STORE;
|
||||||
|
}
|
||||||
|
if (!swapped) {
|
||||||
|
amduat_reference_free(&record_ref);
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_CONFLICT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_new_ref != NULL) {
|
||||||
|
if (!amduat_reference_clone(record_ref, out_new_ref)) {
|
||||||
|
amduat_reference_free(&record_ref);
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
return AMDUATD_SPACE_MANIFEST_ERR_STORE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amduat_reference_free(&record_ref);
|
||||||
|
if (out_manifest == NULL) {
|
||||||
|
amduatd_space_manifest_free(manifest);
|
||||||
|
}
|
||||||
|
return AMDUATD_SPACE_MANIFEST_OK;
|
||||||
|
}
|
||||||
|
|
||||||
amduatd_space_manifest_status_t amduatd_space_manifest_get(
|
amduatd_space_manifest_status_t amduatd_space_manifest_get(
|
||||||
amduat_asl_store_t *store,
|
amduat_asl_store_t *store,
|
||||||
amduat_asl_pointer_store_t *pointer_store,
|
amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ typedef enum {
|
||||||
AMDUATD_SPACE_MANIFEST_ERR_INVALID = 1,
|
AMDUATD_SPACE_MANIFEST_ERR_INVALID = 1,
|
||||||
AMDUATD_SPACE_MANIFEST_ERR_NOT_FOUND = 2,
|
AMDUATD_SPACE_MANIFEST_ERR_NOT_FOUND = 2,
|
||||||
AMDUATD_SPACE_MANIFEST_ERR_STORE = 3,
|
AMDUATD_SPACE_MANIFEST_ERR_STORE = 3,
|
||||||
AMDUATD_SPACE_MANIFEST_ERR_CODEC = 4
|
AMDUATD_SPACE_MANIFEST_ERR_CODEC = 4,
|
||||||
|
AMDUATD_SPACE_MANIFEST_ERR_CONFLICT = 5
|
||||||
} amduatd_space_manifest_status_t;
|
} amduatd_space_manifest_status_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
@ -51,6 +52,19 @@ amduatd_space_manifest_status_t amduatd_space_manifest_get(
|
||||||
amduat_reference_t *out_ref,
|
amduat_reference_t *out_ref,
|
||||||
amduatd_space_manifest_t *out_manifest);
|
amduatd_space_manifest_t *out_manifest);
|
||||||
|
|
||||||
|
amduatd_space_manifest_status_t amduatd_space_manifest_put(
|
||||||
|
amduat_asl_store_t *store,
|
||||||
|
amduat_asl_pointer_store_t *pointer_store,
|
||||||
|
const amduatd_space_t *effective_space,
|
||||||
|
amduat_octets_t payload,
|
||||||
|
const amduat_reference_t *expected_ref,
|
||||||
|
amduat_reference_t *out_new_ref,
|
||||||
|
amduatd_space_manifest_t *out_manifest);
|
||||||
|
|
||||||
|
bool amduatd_space_manifest_encode_json(const amduatd_space_manifest_t *manifest,
|
||||||
|
char **out_json,
|
||||||
|
size_t *out_len);
|
||||||
|
|
||||||
void amduatd_space_manifest_free(amduatd_space_manifest_t *manifest);
|
void amduatd_space_manifest_free(amduatd_space_manifest_t *manifest);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,212 @@ static int amduatd_test_manifest_decode(void) {
|
||||||
return failures == 0 ? 0 : 1;
|
return failures == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int amduatd_test_manifest_put(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_reference_t pinned_ref;
|
||||||
|
char *pinned_hex = NULL;
|
||||||
|
char payload[512];
|
||||||
|
amduatd_space_manifest_t manifest;
|
||||||
|
amduat_reference_t first_ref;
|
||||||
|
amduat_reference_t second_ref;
|
||||||
|
amduat_reference_t wrong_ref;
|
||||||
|
amduatd_space_manifest_status_t status;
|
||||||
|
|
||||||
|
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 (!amduatd_make_test_ref(0x22, &pinned_ref)) {
|
||||||
|
fprintf(stderr, "failed to make pinned ref\n");
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!amduat_asl_ref_encode_hex(pinned_ref, &pinned_hex)) {
|
||||||
|
fprintf(stderr, "failed to encode pinned ref\n");
|
||||||
|
amduat_reference_free(&pinned_ref);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
int n = snprintf(
|
||||||
|
payload,
|
||||||
|
sizeof(payload),
|
||||||
|
"{"
|
||||||
|
"\"version\":1,"
|
||||||
|
"\"mounts\":["
|
||||||
|
"{\"name\":\"beta\",\"peer_key\":\"peer-2\",\"space_id\":\"zeta\","
|
||||||
|
"\"mode\":\"track\"},"
|
||||||
|
"{\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"space_id\":\"zeta\","
|
||||||
|
"\"mode\":\"pinned\",\"pinned_root_ref\":\"%s\"},"
|
||||||
|
"{\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"space_id\":\"beta\","
|
||||||
|
"\"mode\":\"track\"}"
|
||||||
|
"]"
|
||||||
|
"}",
|
||||||
|
pinned_hex);
|
||||||
|
if (n <= 0 || (size_t)n >= sizeof(payload)) {
|
||||||
|
fprintf(stderr, "failed to build manifest payload\n");
|
||||||
|
free(pinned_hex);
|
||||||
|
amduat_reference_free(&pinned_ref);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(pinned_hex);
|
||||||
|
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
memset(&first_ref, 0, sizeof(first_ref));
|
||||||
|
status = amduatd_space_manifest_put(&store,
|
||||||
|
&pointer_store,
|
||||||
|
&space,
|
||||||
|
amduat_octets((const uint8_t *)payload,
|
||||||
|
strlen(payload)),
|
||||||
|
NULL,
|
||||||
|
&first_ref,
|
||||||
|
&manifest);
|
||||||
|
expect(status == AMDUATD_SPACE_MANIFEST_OK, "manifest put ok");
|
||||||
|
if (status == AMDUATD_SPACE_MANIFEST_OK) {
|
||||||
|
expect(manifest.mounts_len == 3u, "manifest put mounts count");
|
||||||
|
if (manifest.mounts_len == 3u) {
|
||||||
|
expect(strcmp(manifest.mounts[0].name, "alpha") == 0,
|
||||||
|
"put mount 0 name");
|
||||||
|
expect(strcmp(manifest.mounts[0].peer_key, "peer-1") == 0,
|
||||||
|
"put mount 0 peer");
|
||||||
|
expect(strcmp(manifest.mounts[0].space_id, "beta") == 0,
|
||||||
|
"put mount 0 space");
|
||||||
|
expect(manifest.mounts[0].mode == AMDUATD_SPACE_MANIFEST_MOUNT_TRACK,
|
||||||
|
"put mount 0 mode");
|
||||||
|
expect(strcmp(manifest.mounts[1].name, "alpha") == 0,
|
||||||
|
"put mount 1 name");
|
||||||
|
expect(strcmp(manifest.mounts[1].peer_key, "peer-1") == 0,
|
||||||
|
"put mount 1 peer");
|
||||||
|
expect(strcmp(manifest.mounts[1].space_id, "zeta") == 0,
|
||||||
|
"put mount 1 space");
|
||||||
|
expect(manifest.mounts[1].mode == AMDUATD_SPACE_MANIFEST_MOUNT_PINNED,
|
||||||
|
"put mount 1 mode");
|
||||||
|
expect(manifest.mounts[1].has_pinned_root_ref,
|
||||||
|
"put mount 1 pinned ref");
|
||||||
|
expect(strcmp(manifest.mounts[2].name, "beta") == 0,
|
||||||
|
"put mount 2 name");
|
||||||
|
expect(strcmp(manifest.mounts[2].peer_key, "peer-2") == 0,
|
||||||
|
"put mount 2 peer");
|
||||||
|
expect(strcmp(manifest.mounts[2].space_id, "zeta") == 0,
|
||||||
|
"put mount 2 space");
|
||||||
|
expect(manifest.mounts[2].mode == AMDUATD_SPACE_MANIFEST_MOUNT_TRACK,
|
||||||
|
"put mount 2 mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
memset(&second_ref, 0, sizeof(second_ref));
|
||||||
|
status = amduatd_space_manifest_get(&store,
|
||||||
|
&pointer_store,
|
||||||
|
&space,
|
||||||
|
&second_ref,
|
||||||
|
&manifest);
|
||||||
|
expect(status == AMDUATD_SPACE_MANIFEST_OK, "manifest get after put ok");
|
||||||
|
expect(amduat_reference_eq(second_ref, first_ref),
|
||||||
|
"manifest get ref matches put");
|
||||||
|
if (manifest.mounts_len == 3u) {
|
||||||
|
expect(strcmp(manifest.mounts[0].name, "alpha") == 0,
|
||||||
|
"get mount 0 name");
|
||||||
|
expect(strcmp(manifest.mounts[1].name, "alpha") == 0,
|
||||||
|
"get mount 1 name");
|
||||||
|
expect(strcmp(manifest.mounts[2].name, "beta") == 0,
|
||||||
|
"get mount 2 name");
|
||||||
|
}
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&second_ref);
|
||||||
|
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
memset(&second_ref, 0, sizeof(second_ref));
|
||||||
|
status = amduatd_space_manifest_put(&store,
|
||||||
|
&pointer_store,
|
||||||
|
&space,
|
||||||
|
amduat_octets((const uint8_t *)payload,
|
||||||
|
strlen(payload)),
|
||||||
|
NULL,
|
||||||
|
&second_ref,
|
||||||
|
&manifest);
|
||||||
|
expect(status == AMDUATD_SPACE_MANIFEST_ERR_CONFLICT,
|
||||||
|
"manifest put conflict without If-Match");
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&second_ref);
|
||||||
|
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
memset(&second_ref, 0, sizeof(second_ref));
|
||||||
|
status = amduatd_space_manifest_put(&store,
|
||||||
|
&pointer_store,
|
||||||
|
&space,
|
||||||
|
amduat_octets((const uint8_t *)payload,
|
||||||
|
strlen(payload)),
|
||||||
|
&first_ref,
|
||||||
|
&second_ref,
|
||||||
|
&manifest);
|
||||||
|
expect(status == AMDUATD_SPACE_MANIFEST_OK,
|
||||||
|
"manifest put ok with If-Match");
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&second_ref);
|
||||||
|
|
||||||
|
if (!amduatd_make_test_ref(0x99, &wrong_ref)) {
|
||||||
|
fprintf(stderr, "failed to make wrong ref\n");
|
||||||
|
amduat_reference_free(&first_ref);
|
||||||
|
amduat_reference_free(&pinned_ref);
|
||||||
|
free(root);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
memset(&manifest, 0, sizeof(manifest));
|
||||||
|
status = amduatd_space_manifest_put(&store,
|
||||||
|
&pointer_store,
|
||||||
|
&space,
|
||||||
|
amduat_octets((const uint8_t *)payload,
|
||||||
|
strlen(payload)),
|
||||||
|
&wrong_ref,
|
||||||
|
&second_ref,
|
||||||
|
&manifest);
|
||||||
|
expect(status == AMDUATD_SPACE_MANIFEST_ERR_CONFLICT,
|
||||||
|
"manifest put conflict with wrong If-Match");
|
||||||
|
amduatd_space_manifest_free(&manifest);
|
||||||
|
amduat_reference_free(&wrong_ref);
|
||||||
|
|
||||||
|
amduat_reference_free(&first_ref);
|
||||||
|
amduat_reference_free(&pinned_ref);
|
||||||
|
free(root);
|
||||||
|
return failures == 0 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
if (amduatd_test_manifest_missing() != 0) {
|
if (amduatd_test_manifest_missing() != 0) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -299,5 +505,8 @@ int main(void) {
|
||||||
if (amduatd_test_manifest_decode() != 0) {
|
if (amduatd_test_manifest_decode() != 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (amduatd_test_manifest_put() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
return failures == 0 ? 0 : 1;
|
return failures == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue