asl/index-fs: add snapshot-anchor fast path for get and tests
This commit is contained in:
parent
bc55ed994b
commit
807816e0f2
|
|
@ -4089,6 +4089,29 @@ static amduat_asl_store_error_t amduat_asl_store_index_fs_get_indexed_impl(
|
|||
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
|
||||
}
|
||||
|
||||
if (state.snapshot_id != 0u) {
|
||||
/*
|
||||
* Fast path: when the requested state is exactly a snapshot anchor,
|
||||
* snapshot metadata is already a complete replay state.
|
||||
*/
|
||||
if (amduat_asl_store_index_fs_load_snapshot_replay(fs->root_path,
|
||||
state.snapshot_id,
|
||||
NULL,
|
||||
&replay_state,
|
||||
&anchor_logseq)) {
|
||||
if (replay_state.state.log_position == anchor_logseq &&
|
||||
state.log_position == anchor_logseq) {
|
||||
err = amduat_asl_store_index_fs_scan_segments(fs,
|
||||
&replay_state,
|
||||
ref,
|
||||
out_artifact);
|
||||
amduat_asl_replay_free(&replay_state);
|
||||
return err;
|
||||
}
|
||||
amduat_asl_replay_free(&replay_state);
|
||||
}
|
||||
}
|
||||
|
||||
if (!amduat_asl_store_index_fs_layout_build_log_path(fs->root_path,
|
||||
&log_path)) {
|
||||
return AMDUAT_ASL_STORE_ERR_IO;
|
||||
|
|
|
|||
|
|
@ -156,6 +156,24 @@ static bool read_file(const char *path, uint8_t **out_bytes, size_t *out_len) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool write_file(const char *path, const uint8_t *bytes, size_t len) {
|
||||
FILE *fp;
|
||||
size_t written;
|
||||
|
||||
if (path == NULL || (len != 0u && bytes == NULL)) {
|
||||
return false;
|
||||
}
|
||||
fp = fopen(path, "wb");
|
||||
if (fp == NULL) {
|
||||
return false;
|
||||
}
|
||||
written = fwrite(bytes, 1u, len, fp);
|
||||
if (fclose(fp) != 0) {
|
||||
return false;
|
||||
}
|
||||
return written == len;
|
||||
}
|
||||
|
||||
static bool build_log_path(const char *root, char **out_path) {
|
||||
char *index_path = NULL;
|
||||
bool ok;
|
||||
|
|
@ -725,6 +743,349 @@ cleanup:
|
|||
return exit_code;
|
||||
}
|
||||
|
||||
static int test_snapshot_fast_path_with_corrupt_log(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_artifact_t artifact;
|
||||
amduat_artifact_t loaded;
|
||||
amduat_reference_t ref;
|
||||
uint8_t payload[10];
|
||||
char *root;
|
||||
char *log_path = NULL;
|
||||
static const uint8_t corrupt_log[] = {'B', 'A', 'D'};
|
||||
int exit_code = 1;
|
||||
|
||||
root = make_temp_root();
|
||||
if (root == NULL) {
|
||||
fprintf(stderr, "temp root failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
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, 0x4c, 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;
|
||||
}
|
||||
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "snapshot create failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_asl_index_current_state(&store, &state)) {
|
||||
fprintf(stderr, "current_state failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if (state.snapshot_id != 1u) {
|
||||
fprintf(stderr, "expected snapshot_id=1\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!build_log_path(root, &log_path) ||
|
||||
!write_file(log_path, corrupt_log, sizeof(corrupt_log))) {
|
||||
fprintf(stderr, "corrupt log write failed\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 with snapshot fast path failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_artifact_eq(artifact, loaded)) {
|
||||
fprintf(stderr, "artifact mismatch from snapshot fast path\n");
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
cleanup:
|
||||
free(log_path);
|
||||
amduat_reference_free(&ref);
|
||||
if (!remove_tree(root)) {
|
||||
fprintf(stderr, "cleanup failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
free(root);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static int test_snapshot_fallback_replays_delta(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 snapshot_state;
|
||||
amduat_asl_index_state_t fallback_state;
|
||||
amduat_artifact_t artifact_a;
|
||||
amduat_artifact_t loaded;
|
||||
amduat_reference_t ref_a;
|
||||
uint8_t payload_a[7];
|
||||
char *root;
|
||||
int exit_code = 1;
|
||||
|
||||
root = make_temp_root();
|
||||
if (root == NULL) {
|
||||
fprintf(stderr, "temp root failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
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_a, 0x11, sizeof(payload_a));
|
||||
artifact_a = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a)));
|
||||
ref_a = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||
|
||||
err = amduat_asl_store_put_indexed(&store, artifact_a, &ref_a, &snapshot_state);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "put_indexed a failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "snapshot create failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_asl_index_current_state(&store, &snapshot_state)) {
|
||||
fprintf(stderr, "snapshot current_state failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
if (snapshot_state.log_position == 0u) {
|
||||
fprintf(stderr, "snapshot log position was zero\n");
|
||||
goto cleanup;
|
||||
}
|
||||
fallback_state = snapshot_state;
|
||||
fallback_state.log_position -= 1u;
|
||||
|
||||
loaded = amduat_artifact(amduat_octets(NULL, 0u));
|
||||
err = amduat_asl_store_get_indexed(&store, ref_a, fallback_state, &loaded);
|
||||
if (err != AMDUAT_ASL_STORE_ERR_INTEGRITY) {
|
||||
fprintf(stderr, "fallback expected integrity: %d\n", err);
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
cleanup:
|
||||
amduat_reference_free(&ref_a);
|
||||
if (!remove_tree(root)) {
|
||||
fprintf(stderr, "cleanup failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
free(root);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static int test_snapshot_fast_path_not_found_and_tombstoned(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 tombstone_state;
|
||||
amduat_reference_t ref;
|
||||
amduat_reference_t missing_ref;
|
||||
amduat_artifact_t artifact;
|
||||
amduat_artifact_t loaded;
|
||||
uint8_t payload[8];
|
||||
uint8_t *missing_digest = NULL;
|
||||
int exit_code = 1;
|
||||
char *root;
|
||||
|
||||
root = make_temp_root();
|
||||
if (root == NULL) {
|
||||
fprintf(stderr, "temp root failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
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, 0x66, sizeof(payload));
|
||||
artifact = amduat_artifact(amduat_octets(payload, sizeof(payload)));
|
||||
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||
missing_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;
|
||||
}
|
||||
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "snapshot create failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_asl_index_current_state(&store, &state)) {
|
||||
fprintf(stderr, "current_state failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
missing_digest = (uint8_t *)malloc(ref.digest.len);
|
||||
if (missing_digest == NULL || ref.digest.len == 0u) {
|
||||
fprintf(stderr, "missing ref build failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
memcpy(missing_digest, ref.digest.data, ref.digest.len);
|
||||
missing_digest[0] ^= 0xffu;
|
||||
missing_ref.hash_id = ref.hash_id;
|
||||
missing_ref.digest = amduat_octets(missing_digest, ref.digest.len);
|
||||
loaded = amduat_artifact(amduat_octets(NULL, 0u));
|
||||
err = amduat_asl_store_get_indexed(&store, missing_ref, state, &loaded);
|
||||
if (err != AMDUAT_ASL_STORE_ERR_NOT_FOUND) {
|
||||
fprintf(stderr, "snapshot not found expected: %d\n", err);
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
err = amduat_asl_store_tombstone(&store, ref, 1u, 2u, &tombstone_state);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "tombstone failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
err = amduat_asl_store_index_fs_snapshot_create(&fs, 2u, NULL, NULL);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "snapshot create after tombstone failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_asl_index_current_state(&store, &state)) {
|
||||
fprintf(stderr, "current_state after tombstone failed\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_ERR_NOT_FOUND) {
|
||||
fprintf(stderr, "snapshot tombstoned expected not found: %d\n", err);
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
cleanup:
|
||||
amduat_reference_free(&ref);
|
||||
amduat_reference_free(&missing_ref);
|
||||
if (!remove_tree(root)) {
|
||||
fprintf(stderr, "cleanup failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
free(root);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static int test_snapshot_fast_path_integrity_error_unchanged(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_artifact_t artifact;
|
||||
amduat_artifact_t loaded;
|
||||
amduat_reference_t ref;
|
||||
uint8_t payload[6];
|
||||
char *root;
|
||||
char *segment_path = NULL;
|
||||
static const uint8_t corrupt_segment[] = {'X', 'X', 'X'};
|
||||
int exit_code = 1;
|
||||
|
||||
root = make_temp_root();
|
||||
if (root == NULL) {
|
||||
fprintf(stderr, "temp root failed\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
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, 0x31, 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;
|
||||
}
|
||||
err = amduat_asl_store_index_fs_snapshot_create(&fs, 1u, NULL, NULL);
|
||||
if (err != AMDUAT_ASL_STORE_OK) {
|
||||
fprintf(stderr, "snapshot create failed: %d\n", err);
|
||||
goto cleanup;
|
||||
}
|
||||
if (!amduat_asl_index_current_state(&store, &state)) {
|
||||
fprintf(stderr, "current_state failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!build_segment_path(root, 1u, &segment_path) ||
|
||||
!write_file(segment_path, corrupt_segment, sizeof(corrupt_segment))) {
|
||||
fprintf(stderr, "segment corruption failed\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_ERR_INTEGRITY) {
|
||||
fprintf(stderr, "expected integrity error, got: %d\n", err);
|
||||
amduat_artifact_free(&loaded);
|
||||
goto cleanup;
|
||||
}
|
||||
amduat_artifact_free(&loaded);
|
||||
|
||||
exit_code = 0;
|
||||
|
||||
cleanup:
|
||||
free(segment_path);
|
||||
amduat_reference_free(&ref);
|
||||
if (!remove_tree(root)) {
|
||||
fprintf(stderr, "cleanup failed\n");
|
||||
exit_code = 1;
|
||||
}
|
||||
free(root);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
static int test_segment_batch_packing(void) {
|
||||
amduat_asl_store_config_t config;
|
||||
amduat_asl_store_index_fs_t fs;
|
||||
|
|
@ -1598,6 +1959,18 @@ int main(void) {
|
|||
if (test_round_trip() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_snapshot_fast_path_with_corrupt_log() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_snapshot_fallback_replays_delta() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_snapshot_fast_path_not_found_and_tombstoned() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_snapshot_fast_path_integrity_error_unchanged() != 0) {
|
||||
return 1;
|
||||
}
|
||||
if (test_snapshot_truncation() != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue