Add filesystem-backed ASL index store
This commit is contained in:
parent
68371b1554
commit
347d2e4dda
|
|
@ -63,6 +63,7 @@ set(AMDUAT_ASL_SRCS
|
|||
src/kernel/asl/core.c
|
||||
src/near_core/asl/artifact_io.c
|
||||
src/near_core/asl/io.c
|
||||
src/near_core/asl/index_replay.c
|
||||
src/near_core/asl/parse.c
|
||||
src/near_core/asl/ref_io.c
|
||||
src/near_core/asl/store.c
|
||||
|
|
@ -123,6 +124,11 @@ set(AMDUAT_ASL_STORE_FS_SRCS
|
|||
src/adapters/asl_store_fs/asl_store_fs_meta.c
|
||||
)
|
||||
|
||||
set(AMDUAT_ASL_STORE_INDEX_FS_SRCS
|
||||
src/adapters/asl_store_index_fs/asl_store_index_fs.c
|
||||
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
|
||||
)
|
||||
|
||||
set(AMDUAT_TGK_STORE_MEM_SRCS
|
||||
src/adapters/tgk_store_mem/tgk_store_mem.c
|
||||
)
|
||||
|
|
@ -155,6 +161,10 @@ amduat_add_lib(asl_store_fs SRCS ${AMDUAT_ASL_STORE_FS_SRCS})
|
|||
amduat_link(asl_store_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||
target_compile_definitions(amduat_asl_store_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||
|
||||
amduat_add_lib(asl_store_index_fs SRCS ${AMDUAT_ASL_STORE_INDEX_FS_SRCS})
|
||||
amduat_link(asl_store_index_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||
target_compile_definitions(amduat_asl_store_index_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
|
||||
|
||||
amduat_add_lib(tgk_store_mem SRCS ${AMDUAT_TGK_STORE_MEM_SRCS})
|
||||
amduat_link(tgk_store_mem amduat_tgk amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
|
||||
|
||||
|
|
@ -343,6 +353,30 @@ target_link_libraries(amduat_test_asl_store_indexed_ops
|
|||
)
|
||||
add_test(NAME asl_store_indexed_ops COMMAND amduat_test_asl_store_indexed_ops)
|
||||
|
||||
add_executable(amduat_test_asl_replay tests/asl/test_asl_replay.c)
|
||||
target_include_directories(amduat_test_asl_replay
|
||||
PRIVATE ${AMDUAT_INTERNAL_DIR}
|
||||
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||
)
|
||||
target_link_libraries(amduat_test_asl_replay
|
||||
PRIVATE amduat_asl
|
||||
)
|
||||
add_test(NAME asl_replay COMMAND amduat_test_asl_replay)
|
||||
|
||||
add_executable(amduat_test_asl_store_index_fs
|
||||
tests/asl/test_asl_store_index_fs.c)
|
||||
target_include_directories(amduat_test_asl_store_index_fs
|
||||
PRIVATE ${AMDUAT_INTERNAL_DIR}
|
||||
PRIVATE ${AMDUAT_INCLUDE_DIR}
|
||||
)
|
||||
target_compile_definitions(amduat_test_asl_store_index_fs
|
||||
PRIVATE _POSIX_C_SOURCE=200809L
|
||||
)
|
||||
target_link_libraries(amduat_test_asl_store_index_fs
|
||||
PRIVATE amduat_asl_store_index_fs
|
||||
)
|
||||
add_test(NAME asl_store_index_fs COMMAND amduat_test_asl_store_index_fs)
|
||||
|
||||
add_executable(amduat_test_pel_program_dag_exec
|
||||
tests/pel/test_pel_program_dag_exec.c)
|
||||
target_include_directories(amduat_test_pel_program_dag_exec
|
||||
|
|
|
|||
30
include/amduat/asl/asl_store_index_fs.h
Normal file
30
include/amduat/asl/asl_store_index_fs.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef AMDUAT_ASL_STORE_INDEX_FS_H
|
||||
#define AMDUAT_ASL_STORE_INDEX_FS_H
|
||||
|
||||
#include "amduat/asl/store.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { AMDUAT_ASL_STORE_INDEX_FS_ROOT_MAX = 1024 };
|
||||
|
||||
typedef struct {
|
||||
amduat_asl_store_config_t config;
|
||||
char root_path[AMDUAT_ASL_STORE_INDEX_FS_ROOT_MAX];
|
||||
} amduat_asl_store_index_fs_t;
|
||||
|
||||
bool amduat_asl_store_index_fs_init(amduat_asl_store_index_fs_t *fs,
|
||||
amduat_asl_store_config_t config,
|
||||
const char *root_path);
|
||||
|
||||
amduat_asl_store_ops_t amduat_asl_store_index_fs_ops(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AMDUAT_ASL_STORE_INDEX_FS_H */
|
||||
47
include/amduat/asl/index_replay.h
Normal file
47
include/amduat/asl/index_replay.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef AMDUAT_ASL_INDEX_REPLAY_H
|
||||
#define AMDUAT_ASL_INDEX_REPLAY_H
|
||||
|
||||
#include "amduat/asl/core.h"
|
||||
#include "amduat/asl/store.h"
|
||||
#include "amduat/enc/asl_log.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint64_t segment_id;
|
||||
uint8_t segment_hash[32];
|
||||
} amduat_asl_segment_seal_t;
|
||||
|
||||
typedef struct {
|
||||
amduat_reference_t ref;
|
||||
uint64_t tombstone_logseq;
|
||||
} amduat_asl_tombstone_entry_t;
|
||||
|
||||
typedef struct {
|
||||
amduat_asl_segment_seal_t *segments;
|
||||
size_t segments_len;
|
||||
amduat_asl_tombstone_entry_t *tombstones;
|
||||
size_t tombstones_len;
|
||||
amduat_asl_index_state_t state;
|
||||
} amduat_asl_replay_state_t;
|
||||
|
||||
bool amduat_asl_replay_init(amduat_asl_replay_state_t *out);
|
||||
void amduat_asl_replay_free(amduat_asl_replay_state_t *state);
|
||||
|
||||
bool amduat_asl_replay_apply_log(
|
||||
const amduat_asl_log_record_t *records,
|
||||
size_t record_count,
|
||||
amduat_asl_log_position_t log_position,
|
||||
amduat_asl_replay_state_t *state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AMDUAT_ASL_INDEX_REPLAY_H */
|
||||
1446
src/adapters/asl_store_index_fs/asl_store_index_fs.c
Normal file
1446
src/adapters/asl_store_index_fs/asl_store_index_fs.c
Normal file
File diff suppressed because it is too large
Load diff
191
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
Normal file
191
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
#include "asl_store_index_fs_layout.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool amduat_asl_store_index_fs_layout_join(const char *base,
|
||||
const char *suffix,
|
||||
char **out_path) {
|
||||
size_t base_len;
|
||||
size_t suffix_len;
|
||||
bool needs_sep;
|
||||
size_t total_len;
|
||||
char *buffer;
|
||||
size_t offset;
|
||||
|
||||
if (base == NULL || suffix == NULL || out_path == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (base[0] == '\0' || suffix[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
base_len = strlen(base);
|
||||
suffix_len = strlen(suffix);
|
||||
needs_sep = base[base_len - 1u] != '/';
|
||||
total_len = base_len + (needs_sep ? 1u : 0u) + suffix_len + 1u;
|
||||
|
||||
buffer = (char *)malloc(total_len);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = 0u;
|
||||
memcpy(buffer + offset, base, base_len);
|
||||
offset += base_len;
|
||||
if (needs_sep) {
|
||||
buffer[offset++] = '/';
|
||||
}
|
||||
memcpy(buffer + offset, suffix, suffix_len);
|
||||
offset += suffix_len;
|
||||
buffer[offset] = '\0';
|
||||
|
||||
*out_path = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_store_index_fs_layout_format_id(const char *prefix,
|
||||
uint64_t id,
|
||||
const char *suffix,
|
||||
char **out_name) {
|
||||
int needed;
|
||||
char *buffer;
|
||||
|
||||
if (prefix == NULL || suffix == NULL || out_name == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
needed = snprintf(NULL, 0, "%s%016" PRIx64 "%s", prefix, id, suffix);
|
||||
if (needed <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer = (char *)malloc((size_t)needed + 1u);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
snprintf(buffer, (size_t)needed + 1u, "%s%016" PRIx64 "%s", prefix, id,
|
||||
suffix);
|
||||
*out_name = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_index_path(const char *root_path,
|
||||
char **out_path) {
|
||||
return amduat_asl_store_index_fs_layout_join(root_path, "index", out_path);
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segments_path(
|
||||
const char *root_path,
|
||||
char **out_path) {
|
||||
char *index_path;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_index_path(root_path,
|
||||
&index_path)) {
|
||||
return false;
|
||||
}
|
||||
ok = amduat_asl_store_index_fs_layout_join(index_path, "segments", out_path);
|
||||
free(index_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_blocks_path(const char *root_path,
|
||||
char **out_path) {
|
||||
char *index_path;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_index_path(root_path,
|
||||
&index_path)) {
|
||||
return false;
|
||||
}
|
||||
ok = amduat_asl_store_index_fs_layout_join(index_path, "blocks", out_path);
|
||||
free(index_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_log_path(const char *root_path,
|
||||
char **out_path) {
|
||||
char *index_path;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_index_path(root_path,
|
||||
&index_path)) {
|
||||
return false;
|
||||
}
|
||||
ok = amduat_asl_store_index_fs_layout_join(index_path, "log.asl", out_path);
|
||||
free(index_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segment_meta_path(
|
||||
const char *root_path,
|
||||
char **out_path) {
|
||||
char *segments_path;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_segments_path(root_path,
|
||||
&segments_path)) {
|
||||
return false;
|
||||
}
|
||||
ok = amduat_asl_store_index_fs_layout_join(segments_path,
|
||||
"next_id",
|
||||
out_path);
|
||||
free(segments_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segment_path(
|
||||
const char *root_path,
|
||||
uint64_t segment_id,
|
||||
char **out_path) {
|
||||
char *segments_path;
|
||||
char *name;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_segments_path(root_path,
|
||||
&segments_path)) {
|
||||
return false;
|
||||
}
|
||||
if (!amduat_asl_store_index_fs_layout_format_id("segment-",
|
||||
segment_id,
|
||||
".asl",
|
||||
&name)) {
|
||||
free(segments_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = amduat_asl_store_index_fs_layout_join(segments_path, name, out_path);
|
||||
free(name);
|
||||
free(segments_path);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_block_path(
|
||||
const char *root_path,
|
||||
uint64_t block_id,
|
||||
char **out_path) {
|
||||
char *blocks_path;
|
||||
char *name;
|
||||
bool ok;
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_blocks_path(root_path,
|
||||
&blocks_path)) {
|
||||
return false;
|
||||
}
|
||||
if (!amduat_asl_store_index_fs_layout_format_id("block-",
|
||||
block_id,
|
||||
".asl",
|
||||
&name)) {
|
||||
free(blocks_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
ok = amduat_asl_store_index_fs_layout_join(blocks_path, name, out_path);
|
||||
free(name);
|
||||
free(blocks_path);
|
||||
return ok;
|
||||
}
|
||||
42
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.h
Normal file
42
src/adapters/asl_store_index_fs/asl_store_index_fs_layout.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef AMDUAT_ASL_STORE_INDEX_FS_LAYOUT_H
|
||||
#define AMDUAT_ASL_STORE_INDEX_FS_LAYOUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_index_path(const char *root_path,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segments_path(const char *root_path,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_blocks_path(const char *root_path,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_log_path(const char *root_path,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segment_meta_path(
|
||||
const char *root_path,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_segment_path(
|
||||
const char *root_path,
|
||||
uint64_t segment_id,
|
||||
char **out_path);
|
||||
|
||||
bool amduat_asl_store_index_fs_layout_build_block_path(
|
||||
const char *root_path,
|
||||
uint64_t block_id,
|
||||
char **out_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AMDUAT_ASL_STORE_INDEX_FS_LAYOUT_H */
|
||||
313
src/near_core/asl/index_replay.c
Normal file
313
src/near_core/asl/index_replay.c
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
#include "amduat/asl/index_replay.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
const uint8_t *data;
|
||||
size_t len;
|
||||
size_t offset;
|
||||
} amduat_asl_replay_cursor_t;
|
||||
|
||||
static bool amduat_asl_replay_read_u16_le(amduat_asl_replay_cursor_t *cur,
|
||||
uint16_t *out) {
|
||||
const uint8_t *data;
|
||||
|
||||
if (cur->len - cur->offset < 2) {
|
||||
return false;
|
||||
}
|
||||
data = cur->data + cur->offset;
|
||||
*out = (uint16_t)data[0] | ((uint16_t)data[1] << 8);
|
||||
cur->offset += 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_read_u32_le(amduat_asl_replay_cursor_t *cur,
|
||||
uint32_t *out) {
|
||||
const uint8_t *data;
|
||||
|
||||
if (cur->len - cur->offset < 4) {
|
||||
return false;
|
||||
}
|
||||
data = cur->data + cur->offset;
|
||||
*out = (uint32_t)data[0] | ((uint32_t)data[1] << 8) |
|
||||
((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
|
||||
cur->offset += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_read_u64_le(amduat_asl_replay_cursor_t *cur,
|
||||
uint64_t *out) {
|
||||
const uint8_t *data;
|
||||
|
||||
if (cur->len - cur->offset < 8) {
|
||||
return false;
|
||||
}
|
||||
data = cur->data + cur->offset;
|
||||
*out = (uint64_t)data[0] | ((uint64_t)data[1] << 8) |
|
||||
((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) |
|
||||
((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) |
|
||||
((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56);
|
||||
cur->offset += 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_parse_artifact_ref(
|
||||
amduat_asl_replay_cursor_t *cur,
|
||||
amduat_reference_t *out_ref) {
|
||||
uint32_t hash_id_raw;
|
||||
uint16_t digest_len;
|
||||
uint16_t reserved0;
|
||||
const uint8_t *digest;
|
||||
|
||||
if (!amduat_asl_replay_read_u32_le(cur, &hash_id_raw) ||
|
||||
!amduat_asl_replay_read_u16_le(cur, &digest_len) ||
|
||||
!amduat_asl_replay_read_u16_le(cur, &reserved0)) {
|
||||
return false;
|
||||
}
|
||||
if (hash_id_raw > UINT16_MAX || digest_len == 0 || reserved0 != 0) {
|
||||
return false;
|
||||
}
|
||||
if (cur->len - cur->offset < digest_len) {
|
||||
return false;
|
||||
}
|
||||
digest = cur->data + cur->offset;
|
||||
cur->offset += digest_len;
|
||||
*out_ref = amduat_reference((amduat_hash_id_t)hash_id_raw,
|
||||
amduat_octets(digest, digest_len));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_parse_segment_seal(
|
||||
amduat_octets_t payload,
|
||||
amduat_asl_segment_seal_t *out) {
|
||||
amduat_asl_replay_cursor_t cur;
|
||||
uint64_t segment_id;
|
||||
|
||||
if (payload.len < 8 + 32 || payload.data == NULL || out == NULL) {
|
||||
return false;
|
||||
}
|
||||
cur.data = payload.data;
|
||||
cur.len = payload.len;
|
||||
cur.offset = 0;
|
||||
if (!amduat_asl_replay_read_u64_le(&cur, &segment_id)) {
|
||||
return false;
|
||||
}
|
||||
if (cur.len - cur.offset < 32) {
|
||||
return false;
|
||||
}
|
||||
out->segment_id = segment_id;
|
||||
memcpy(out->segment_hash, cur.data + cur.offset, 32);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_parse_tombstone(
|
||||
amduat_octets_t payload,
|
||||
amduat_reference_t *out_ref) {
|
||||
amduat_asl_replay_cursor_t cur;
|
||||
uint32_t scope;
|
||||
uint32_t reason;
|
||||
|
||||
if (payload.len == 0 || payload.data == NULL || out_ref == NULL) {
|
||||
return false;
|
||||
}
|
||||
cur.data = payload.data;
|
||||
cur.len = payload.len;
|
||||
cur.offset = 0;
|
||||
if (!amduat_asl_replay_parse_artifact_ref(&cur, out_ref)) {
|
||||
return false;
|
||||
}
|
||||
if (!amduat_asl_replay_read_u32_le(&cur, &scope) ||
|
||||
!amduat_asl_replay_read_u32_le(&cur, &reason)) {
|
||||
return false;
|
||||
}
|
||||
(void)scope;
|
||||
(void)reason;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_parse_tombstone_lift(
|
||||
amduat_octets_t payload,
|
||||
amduat_reference_t *out_ref,
|
||||
uint64_t *out_logseq) {
|
||||
amduat_asl_replay_cursor_t cur;
|
||||
uint64_t tombstone_logseq;
|
||||
|
||||
if (payload.len == 0 || payload.data == NULL || out_ref == NULL ||
|
||||
out_logseq == NULL) {
|
||||
return false;
|
||||
}
|
||||
cur.data = payload.data;
|
||||
cur.len = payload.len;
|
||||
cur.offset = 0;
|
||||
if (!amduat_asl_replay_parse_artifact_ref(&cur, out_ref) ||
|
||||
!amduat_asl_replay_read_u64_le(&cur, &tombstone_logseq)) {
|
||||
return false;
|
||||
}
|
||||
*out_logseq = tombstone_logseq;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_update_segment(
|
||||
amduat_asl_replay_state_t *state,
|
||||
const amduat_asl_segment_seal_t *seal) {
|
||||
size_t i;
|
||||
amduat_asl_segment_seal_t *next;
|
||||
|
||||
for (i = 0; i < state->segments_len; ++i) {
|
||||
if (state->segments[i].segment_id == seal->segment_id) {
|
||||
memcpy(state->segments[i].segment_hash, seal->segment_hash, 32);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
next = (amduat_asl_segment_seal_t *)realloc(
|
||||
state->segments,
|
||||
(state->segments_len + 1u) * sizeof(*state->segments));
|
||||
if (next == NULL) {
|
||||
return false;
|
||||
}
|
||||
state->segments = next;
|
||||
state->segments[state->segments_len] = *seal;
|
||||
state->segments_len += 1u;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduat_asl_replay_add_tombstone(
|
||||
amduat_asl_replay_state_t *state,
|
||||
amduat_reference_t ref,
|
||||
uint64_t logseq) {
|
||||
amduat_asl_tombstone_entry_t *next;
|
||||
amduat_reference_t stored;
|
||||
|
||||
if (!amduat_reference_clone(ref, &stored)) {
|
||||
return false;
|
||||
}
|
||||
next = (amduat_asl_tombstone_entry_t *)realloc(
|
||||
state->tombstones,
|
||||
(state->tombstones_len + 1u) * sizeof(*state->tombstones));
|
||||
if (next == NULL) {
|
||||
amduat_reference_free(&stored);
|
||||
return false;
|
||||
}
|
||||
state->tombstones = next;
|
||||
state->tombstones[state->tombstones_len].ref = stored;
|
||||
state->tombstones[state->tombstones_len].tombstone_logseq = logseq;
|
||||
state->tombstones_len += 1u;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void amduat_asl_replay_remove_tombstone(
|
||||
amduat_asl_replay_state_t *state,
|
||||
amduat_reference_t ref,
|
||||
uint64_t tombstone_logseq) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < state->tombstones_len; ++i) {
|
||||
if (state->tombstones[i].tombstone_logseq != tombstone_logseq) {
|
||||
continue;
|
||||
}
|
||||
if (!amduat_reference_eq(state->tombstones[i].ref, ref)) {
|
||||
continue;
|
||||
}
|
||||
amduat_reference_free(&state->tombstones[i].ref);
|
||||
if (i + 1u < state->tombstones_len) {
|
||||
state->tombstones[i] = state->tombstones[state->tombstones_len - 1u];
|
||||
}
|
||||
state->tombstones_len -= 1u;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool amduat_asl_replay_init(amduat_asl_replay_state_t *out) {
|
||||
if (out == NULL) {
|
||||
return false;
|
||||
}
|
||||
out->segments = NULL;
|
||||
out->segments_len = 0;
|
||||
out->tombstones = NULL;
|
||||
out->tombstones_len = 0;
|
||||
out->state.snapshot_id = 0;
|
||||
out->state.log_position = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void amduat_asl_replay_free(amduat_asl_replay_state_t *state) {
|
||||
size_t i;
|
||||
|
||||
if (state == NULL) {
|
||||
return;
|
||||
}
|
||||
free(state->segments);
|
||||
state->segments = NULL;
|
||||
state->segments_len = 0;
|
||||
if (state->tombstones != NULL) {
|
||||
for (i = 0; i < state->tombstones_len; ++i) {
|
||||
amduat_reference_free(&state->tombstones[i].ref);
|
||||
}
|
||||
}
|
||||
free(state->tombstones);
|
||||
state->tombstones = NULL;
|
||||
state->tombstones_len = 0;
|
||||
state->state.snapshot_id = 0;
|
||||
state->state.log_position = 0;
|
||||
}
|
||||
|
||||
bool amduat_asl_replay_apply_log(
|
||||
const amduat_asl_log_record_t *records,
|
||||
size_t record_count,
|
||||
amduat_asl_log_position_t log_position,
|
||||
amduat_asl_replay_state_t *state) {
|
||||
size_t i;
|
||||
|
||||
if (state == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (record_count != 0 && records == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < record_count; ++i) {
|
||||
const amduat_asl_log_record_t *record = &records[i];
|
||||
amduat_asl_segment_seal_t seal;
|
||||
amduat_reference_t ref;
|
||||
uint64_t tombstone_logseq;
|
||||
|
||||
if (record->logseq > log_position) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (record->record_type) {
|
||||
case AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL:
|
||||
if (!amduat_asl_replay_parse_segment_seal(record->payload, &seal)) {
|
||||
return false;
|
||||
}
|
||||
if (!amduat_asl_replay_update_segment(state, &seal)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AMDUAT_ASL_LOG_RECORD_TOMBSTONE:
|
||||
if (!amduat_asl_replay_parse_tombstone(record->payload, &ref)) {
|
||||
return false;
|
||||
}
|
||||
if (!amduat_asl_replay_add_tombstone(state, ref, record->logseq)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case AMDUAT_ASL_LOG_RECORD_TOMBSTONE_LIFT:
|
||||
if (!amduat_asl_replay_parse_tombstone_lift(record->payload,
|
||||
&ref,
|
||||
&tombstone_logseq)) {
|
||||
return false;
|
||||
}
|
||||
amduat_asl_replay_remove_tombstone(state, ref, tombstone_logseq);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->state.log_position = log_position;
|
||||
return true;
|
||||
}
|
||||
291
tests/asl/test_asl_replay.c
Normal file
291
tests/asl/test_asl_replay.c
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#include "amduat/asl/index_replay.h"
|
||||
#include "amduat/hash/asl1.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static void store_u16_le(uint8_t *out, uint16_t value) {
|
||||
out[0] = (uint8_t)(value & 0xffu);
|
||||
out[1] = (uint8_t)((value >> 8) & 0xffu);
|
||||
}
|
||||
|
||||
static void store_u32_le(uint8_t *out, uint32_t value) {
|
||||
out[0] = (uint8_t)(value & 0xffu);
|
||||
out[1] = (uint8_t)((value >> 8) & 0xffu);
|
||||
out[2] = (uint8_t)((value >> 16) & 0xffu);
|
||||
out[3] = (uint8_t)((value >> 24) & 0xffu);
|
||||
}
|
||||
|
||||
static void store_u64_le(uint8_t *out, uint64_t value) {
|
||||
out[0] = (uint8_t)(value & 0xffu);
|
||||
out[1] = (uint8_t)((value >> 8) & 0xffu);
|
||||
out[2] = (uint8_t)((value >> 16) & 0xffu);
|
||||
out[3] = (uint8_t)((value >> 24) & 0xffu);
|
||||
out[4] = (uint8_t)((value >> 32) & 0xffu);
|
||||
out[5] = (uint8_t)((value >> 40) & 0xffu);
|
||||
out[6] = (uint8_t)((value >> 48) & 0xffu);
|
||||
out[7] = (uint8_t)((value >> 56) & 0xffu);
|
||||
}
|
||||
|
||||
static size_t build_artifact_ref(uint8_t *out,
|
||||
size_t out_cap,
|
||||
amduat_hash_id_t hash_id,
|
||||
const uint8_t *digest,
|
||||
size_t digest_len) {
|
||||
size_t total = 4 + 2 + 2 + digest_len;
|
||||
if (out_cap < total) {
|
||||
return 0;
|
||||
}
|
||||
store_u32_le(out, (uint32_t)hash_id);
|
||||
store_u16_le(out + 4, (uint16_t)digest_len);
|
||||
store_u16_le(out + 6, 0);
|
||||
memcpy(out + 8, digest, digest_len);
|
||||
return total;
|
||||
}
|
||||
|
||||
static size_t build_tombstone_payload(uint8_t *out,
|
||||
size_t out_cap,
|
||||
amduat_hash_id_t hash_id,
|
||||
const uint8_t *digest,
|
||||
size_t digest_len) {
|
||||
size_t offset = 0;
|
||||
size_t ref_len = build_artifact_ref(out,
|
||||
out_cap,
|
||||
hash_id,
|
||||
digest,
|
||||
digest_len);
|
||||
if (ref_len == 0 || out_cap < ref_len + 8) {
|
||||
return 0;
|
||||
}
|
||||
offset += ref_len;
|
||||
store_u32_le(out + offset, 0);
|
||||
offset += 4;
|
||||
store_u32_le(out + offset, 0);
|
||||
offset += 4;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static size_t build_tombstone_lift_payload(uint8_t *out,
|
||||
size_t out_cap,
|
||||
amduat_hash_id_t hash_id,
|
||||
const uint8_t *digest,
|
||||
size_t digest_len,
|
||||
uint64_t tombstone_logseq) {
|
||||
size_t offset = 0;
|
||||
size_t ref_len = build_artifact_ref(out,
|
||||
out_cap,
|
||||
hash_id,
|
||||
digest,
|
||||
digest_len);
|
||||
if (ref_len == 0 || out_cap < ref_len + 8) {
|
||||
return 0;
|
||||
}
|
||||
offset += ref_len;
|
||||
store_u64_le(out + offset, tombstone_logseq);
|
||||
offset += 8;
|
||||
return offset;
|
||||
}
|
||||
|
||||
static size_t build_segment_seal_payload(uint8_t *out,
|
||||
size_t out_cap,
|
||||
uint64_t segment_id,
|
||||
const uint8_t hash[32]) {
|
||||
if (out_cap < 8 + 32) {
|
||||
return 0;
|
||||
}
|
||||
store_u64_le(out, segment_id);
|
||||
memcpy(out + 8, hash, 32);
|
||||
return 8 + 32;
|
||||
}
|
||||
|
||||
static int test_tombstone_lift_cutoff(void) {
|
||||
amduat_asl_log_record_t records[2];
|
||||
amduat_asl_replay_state_t state;
|
||||
uint8_t digest[32];
|
||||
uint8_t tombstone_payload[4 + 2 + 2 + 32 + 4 + 4];
|
||||
uint8_t lift_payload[4 + 2 + 2 + 32 + 8];
|
||||
size_t tombstone_len;
|
||||
size_t lift_len;
|
||||
amduat_reference_t ref;
|
||||
|
||||
memset(digest, 0x5a, sizeof(digest));
|
||||
tombstone_len = build_tombstone_payload(tombstone_payload,
|
||||
sizeof(tombstone_payload),
|
||||
AMDUAT_HASH_ASL1_ID_SHA256,
|
||||
digest,
|
||||
sizeof(digest));
|
||||
lift_len = build_tombstone_lift_payload(lift_payload,
|
||||
sizeof(lift_payload),
|
||||
AMDUAT_HASH_ASL1_ID_SHA256,
|
||||
digest,
|
||||
sizeof(digest),
|
||||
20);
|
||||
if (tombstone_len == 0 || lift_len == 0) {
|
||||
fprintf(stderr, "payload build failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(records, 0, sizeof(records));
|
||||
records[0].logseq = 20;
|
||||
records[0].record_type = AMDUAT_ASL_LOG_RECORD_TOMBSTONE;
|
||||
records[0].payload = amduat_octets(tombstone_payload, tombstone_len);
|
||||
records[1].logseq = 40;
|
||||
records[1].record_type = AMDUAT_ASL_LOG_RECORD_TOMBSTONE_LIFT;
|
||||
records[1].payload = amduat_octets(lift_payload, lift_len);
|
||||
|
||||
if (!amduat_asl_replay_init(&state)) {
|
||||
fprintf(stderr, "replay init failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!amduat_asl_replay_apply_log(records, 2, 30, &state)) {
|
||||
fprintf(stderr, "replay apply failed\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
if (state.tombstones_len != 1) {
|
||||
fprintf(stderr, "tombstone not applied\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
ref = amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256,
|
||||
amduat_octets(digest, sizeof(digest)));
|
||||
if (state.tombstones[0].tombstone_logseq != 20 ||
|
||||
!amduat_reference_eq(state.tombstones[0].ref, ref)) {
|
||||
fprintf(stderr, "tombstone mismatch\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
amduat_asl_replay_free(&state);
|
||||
|
||||
if (!amduat_asl_replay_init(&state)) {
|
||||
fprintf(stderr, "replay init failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!amduat_asl_replay_apply_log(records, 2, 40, &state)) {
|
||||
fprintf(stderr, "replay apply failed\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
if (state.tombstones_len != 0) {
|
||||
fprintf(stderr, "tombstone lift failed\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
amduat_asl_replay_free(&state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_unknown_record_skip(void) {
|
||||
amduat_asl_log_record_t records[2];
|
||||
amduat_asl_replay_state_t state;
|
||||
uint8_t unknown_payload[1] = {0x01};
|
||||
uint8_t seal_payload[8 + 32];
|
||||
uint8_t seal_hash[32];
|
||||
size_t seal_len;
|
||||
|
||||
memset(seal_hash, 0xab, sizeof(seal_hash));
|
||||
seal_len = build_segment_seal_payload(seal_payload,
|
||||
sizeof(seal_payload),
|
||||
9,
|
||||
seal_hash);
|
||||
if (seal_len == 0) {
|
||||
fprintf(stderr, "seal payload build failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(records, 0, sizeof(records));
|
||||
records[0].logseq = 10;
|
||||
records[0].record_type = 0x99;
|
||||
records[0].payload = amduat_octets(unknown_payload, sizeof(unknown_payload));
|
||||
records[1].logseq = 11;
|
||||
records[1].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL;
|
||||
records[1].payload = amduat_octets(seal_payload, seal_len);
|
||||
|
||||
if (!amduat_asl_replay_init(&state)) {
|
||||
fprintf(stderr, "replay init failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!amduat_asl_replay_apply_log(records, 2, 11, &state)) {
|
||||
fprintf(stderr, "replay apply failed\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
if (state.segments_len != 1) {
|
||||
fprintf(stderr, "segment seal missing\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
amduat_asl_replay_free(&state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_multiple_seals_latest(void) {
|
||||
amduat_asl_log_record_t records[2];
|
||||
amduat_asl_replay_state_t state;
|
||||
uint8_t seal_payload_a[8 + 32];
|
||||
uint8_t seal_payload_b[8 + 32];
|
||||
uint8_t hash_a[32];
|
||||
uint8_t hash_b[32];
|
||||
size_t seal_len_a;
|
||||
size_t seal_len_b;
|
||||
|
||||
memset(hash_a, 0x11, sizeof(hash_a));
|
||||
memset(hash_b, 0x22, sizeof(hash_b));
|
||||
seal_len_a = build_segment_seal_payload(seal_payload_a,
|
||||
sizeof(seal_payload_a),
|
||||
5,
|
||||
hash_a);
|
||||
seal_len_b = build_segment_seal_payload(seal_payload_b,
|
||||
sizeof(seal_payload_b),
|
||||
5,
|
||||
hash_b);
|
||||
if (seal_len_a == 0 || seal_len_b == 0) {
|
||||
fprintf(stderr, "seal payload build failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(records, 0, sizeof(records));
|
||||
records[0].logseq = 10;
|
||||
records[0].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL;
|
||||
records[0].payload = amduat_octets(seal_payload_a, seal_len_a);
|
||||
records[1].logseq = 20;
|
||||
records[1].record_type = AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL;
|
||||
records[1].payload = amduat_octets(seal_payload_b, seal_len_b);
|
||||
|
||||
if (!amduat_asl_replay_init(&state)) {
|
||||
fprintf(stderr, "replay init failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (!amduat_asl_replay_apply_log(records, 2, 20, &state)) {
|
||||
fprintf(stderr, "replay apply failed\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
if (state.segments_len != 1) {
|
||||
fprintf(stderr, "segment seal count mismatch\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
if (memcmp(state.segments[0].segment_hash, hash_b, sizeof(hash_b)) != 0) {
|
||||
fprintf(stderr, "segment seal not updated\n");
|
||||
amduat_asl_replay_free(&state);
|
||||
return 1;
|
||||
}
|
||||
amduat_asl_replay_free(&state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
if (test_tombstone_lift_cutoff() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_unknown_record_skip() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_multiple_seals_latest() != 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
203
tests/asl/test_asl_store_index_fs.c
Normal file
203
tests/asl/test_asl_store_index_fs.c
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#include "amduat/asl/asl_store_index_fs.h"
|
||||
#include "amduat/asl/store.h"
|
||||
#include "amduat/enc/asl1_core.h"
|
||||
#include "amduat/hash/asl1.h"
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static bool 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;
|
||||
|
||||
if (base == NULL || segment == NULL || out_path == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (base[0] == '\0' || segment[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
base_len = strlen(base);
|
||||
seg_len = strlen(segment);
|
||||
needs_sep = base[base_len - 1u] != '/';
|
||||
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
|
||||
|
||||
buffer = (char *)malloc(total_len);
|
||||
if (buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset = 0u;
|
||||
memcpy(buffer + offset, base, base_len);
|
||||
offset += base_len;
|
||||
if (needs_sep) {
|
||||
buffer[offset++] = '/';
|
||||
}
|
||||
memcpy(buffer + offset, segment, seg_len);
|
||||
offset += seg_len;
|
||||
buffer[offset] = '\0';
|
||||
|
||||
*out_path = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove_tree(const char *path) {
|
||||
struct stat st;
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if (path == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (lstat(path, &st) != 0) {
|
||||
return errno == ENOENT;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
return unlink(path) == 0;
|
||||
}
|
||||
|
||||
dir = opendir(path);
|
||||
if (dir == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char *child = NULL;
|
||||
if (strcmp(entry->d_name, ".") == 0 ||
|
||||
strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!join_path(path, entry->d_name, &child)) {
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
if (!remove_tree(child)) {
|
||||
free(child);
|
||||
closedir(dir);
|
||||
return false;
|
||||
}
|
||||
free(child);
|
||||
}
|
||||
|
||||
if (closedir(dir) != 0) {
|
||||
return false;
|
||||
}
|
||||
return rmdir(path) == 0;
|
||||
}
|
||||
|
||||
static char *make_temp_root(void) {
|
||||
char *templ;
|
||||
const char template_prefix[] = "/tmp/amduat_test_asl_store_index_fs_XXXXXX";
|
||||
|
||||
templ = (char *)malloc(sizeof(template_prefix));
|
||||
if (templ == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(templ, template_prefix, sizeof(template_prefix));
|
||||
if (mkdtemp(templ) == NULL) {
|
||||
free(templ);
|
||||
return NULL;
|
||||
}
|
||||
return templ;
|
||||
}
|
||||
|
||||
static int test_round_trip(void) {
|
||||
amduat_asl_store_config_t config;
|
||||
amduat_asl_store_index_fs_t fs;
|
||||
amduat_asl_store_t store;
|
||||
amduat_asl_store_error_t err;
|
||||
amduat_asl_index_state_t state;
|
||||
amduat_asl_index_state_t current_state;
|
||||
amduat_artifact_t artifact;
|
||||
amduat_artifact_t loaded;
|
||||
amduat_reference_t ref;
|
||||
uint8_t payload[6];
|
||||
char *root;
|
||||
int exit_code = 1;
|
||||
|
||||
root = make_temp_root();
|
||||
if (root == NULL) {
|
||||
fprintf(stderr, "temp root failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
|
||||
config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
|
||||
if (!amduat_asl_store_index_fs_init(&fs, config, root)) {
|
||||
fprintf(stderr, "index fs init failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs);
|
||||
|
||||
memset(payload, 0x5a, sizeof(payload));
|
||||
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
|
||||
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||
|
||||
err = amduat_asl_store_put_indexed(&store, artifact, &ref, &state);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "put_indexed failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!amduat_asl_index_current_state(&store, ¤t_state)) {
|
||||
fprintf(stderr, "current_state failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if (current_state.log_position != state.log_position) {
|
||||
fprintf(stderr, "log position mismatch\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
loaded = amduat_artifact(amduat_octets(NULL, 0u));
|
||||
err = amduat_asl_store_get_indexed(&store, ref, state, &loaded);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "get_indexed failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_artifact_eq(artifact, loaded)) {
|
||||
fprintf(stderr, "artifact mismatch\n");
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
if (state.log_position > 0u) {
|
||||
amduat_asl_index_state_t cutoff = state;
|
||||
cutoff.log_position = state.log_position - 1u;
|
||||
loaded = amduat_artifact(amduat_octets(NULL, 0u));
|
||||
err = amduat_asl_store_get_indexed(&store, ref, cutoff, &loaded);
|
||||
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
|
||||
fprintf(stderr, "cutoff lookup expected not found: %d\n", err);
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
cleanup:
|
||||
amduat_reference_free(&ref);
|
||||
if (!remove_tree(root)) {
|
||||
fprintf(stderr, "cleanup failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
free(root);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
return test_round_trip();
|
||||
}
|
||||
Loading…
Reference in a new issue