Add CAS-native append-only log

This commit is contained in:
Carl Niklas Rydberg 2026-01-23 19:04:49 +01:00
parent c4571c3bfb
commit e2d26e53cd
6 changed files with 2221 additions and 0 deletions

View file

@ -75,6 +75,10 @@ set(AMDUAT_ASL_SRCS
src/near_core/asl/registry.c
)
set(AMDUAT_ASL_LOG_STORE_SRCS
src/core/asl_log_store.c
)
set(AMDUAT_HASH_ASL1_SRCS
src/near_core/hash/asl1.c
src/near_core/hash/sha256.c
@ -150,6 +154,10 @@ set(AMDUAT_ASL_MATERIALIZATION_CACHE_FS_SRCS
src/adapters/asl_materialization_cache_fs/asl_materialization_cache_fs.c
)
set(AMDUAT_ASL_POINTER_FS_SRCS
src/adapters/asl_pointer_fs/asl_pointer_fs.c
)
set(AMDUAT_TGK_STORE_MEM_SRCS
src/adapters/tgk_store_mem/tgk_store_mem.c
)
@ -201,6 +209,12 @@ amduat_add_lib(asl_materialization_cache_fs SRCS ${AMDUAT_ASL_MATERIALIZATION_CA
amduat_link(asl_materialization_cache_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
target_compile_definitions(amduat_asl_materialization_cache_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
amduat_add_lib(asl_pointer_fs SRCS ${AMDUAT_ASL_POINTER_FS_SRCS})
amduat_link(asl_pointer_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util)
target_compile_definitions(amduat_asl_pointer_fs_obj PRIVATE _POSIX_C_SOURCE=200809L)
amduat_add_lib(asl_log_store SRCS ${AMDUAT_ASL_LOG_STORE_SRCS})
amduat_link(asl_log_store amduat_asl_pointer_fs amduat_asl amduat_enc amduat_util)
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)
@ -254,6 +268,7 @@ target_include_directories(amduat_pel_cli
)
target_link_libraries(amduat_pel_cli
PRIVATE amduat_format amduat_pel amduat_asl_store_fs
amduat_asl_log_store amduat_asl_pointer_fs
amduat_asl_derivation_index_fs amduat_asl amduat_enc
amduat_hash_asl1 amduat_util
)

View file

@ -0,0 +1,58 @@
#ifndef AMDUAT_ASL_POINTER_FS_H
#define AMDUAT_ASL_POINTER_FS_H
#include "amduat/asl/core.h"
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
enum {
AMDUAT_ASL_POINTER_FS_ROOT_MAX = 1024,
AMDUAT_ASL_POINTER_NAME_MAX = 512
};
typedef enum {
AMDUAT_ASL_POINTER_OK = 0,
AMDUAT_ASL_POINTER_ERR_NOT_FOUND = 1,
AMDUAT_ASL_POINTER_ERR_IO = 2,
AMDUAT_ASL_POINTER_ERR_INVALID_NAME = 3,
AMDUAT_ASL_POINTER_ERR_INTEGRITY = 4
} amduat_asl_pointer_error_t;
typedef struct {
char root_path[AMDUAT_ASL_POINTER_FS_ROOT_MAX];
} amduat_asl_pointer_store_t;
/* Pointers are generic control-plane names for artifacts (e.g. dataset heads).
* Example names:
* - space/123/dataset/calendar_types/head
* - space/123/collection/events/head
*/
bool amduat_asl_pointer_store_init(amduat_asl_pointer_store_t *ps,
const char *root_path);
amduat_asl_pointer_error_t amduat_asl_pointer_get(
const amduat_asl_pointer_store_t *ps,
const char *name,
bool *out_exists,
amduat_reference_t *out_ref);
amduat_asl_pointer_error_t amduat_asl_pointer_cas(
const amduat_asl_pointer_store_t *ps,
const char *name,
bool expected_exists,
const amduat_reference_t *expected_ref,
const amduat_reference_t *new_ref,
bool *out_swapped);
bool amduat_asl_pointer_name_is_valid(const char *name);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_POINTER_FS_H */

View file

@ -0,0 +1,62 @@
#ifndef AMDUAT_ASL_LOG_STORE_H
#define AMDUAT_ASL_LOG_STORE_H
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/core.h"
#include "amduat/asl/store.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
enum { TYPE_TAG_ASL_LOG_CHUNK_1 = 0x00000401u };
enum { AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1 = TYPE_TAG_ASL_LOG_CHUNK_1 };
typedef struct {
uint16_t kind;
bool has_timestamp;
uint64_t timestamp;
amduat_reference_t payload_ref;
bool has_actor;
amduat_octets_t actor;
} amduat_asl_log_entry_t;
typedef struct {
amduat_asl_store_t *store;
amduat_asl_pointer_store_t pointer_store;
} amduat_asl_log_store_t;
bool amduat_asl_log_store_init(amduat_asl_log_store_t *log_store,
const char *root_path,
amduat_asl_store_t *store,
const amduat_asl_pointer_store_t *pointer_store);
amduat_asl_store_error_t amduat_asl_log_append(
amduat_asl_log_store_t *log_store,
const char *log_name,
const amduat_asl_log_entry_t *entries,
size_t entries_len,
uint64_t *out_first_offset);
amduat_asl_store_error_t amduat_asl_log_read(
amduat_asl_log_store_t *log_store,
const char *log_name,
uint64_t from_offset,
size_t max_entries,
amduat_asl_log_entry_t **out_entries,
size_t *out_len,
uint64_t *out_next_offset,
bool *out_end);
void amduat_asl_log_entries_free(amduat_asl_log_entry_t *entries,
size_t entries_len);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUAT_ASL_LOG_STORE_H */

View file

@ -0,0 +1,726 @@
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/util/log.h"
#include <errno.h>
#include <fcntl.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>
enum {
AMDUAT_ASL_POINTER_MAGIC_LEN = 8,
AMDUAT_ASL_POINTER_VERSION = 1
};
static const uint8_t k_amduat_asl_pointer_magic[AMDUAT_ASL_POINTER_MAGIC_LEN] = {
'A', 'S', 'L', 'P', 'T', 'R', '1', '\0'
};
enum { AMDUAT_ASL_POINTER_FLAG_HAS_EXPECTED = 1u << 0,
AMDUAT_ASL_POINTER_FLAG_HAS_PREV = 1u << 1 };
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} amduat_asl_pointer_cursor_t;
static void amduat_asl_pointer_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 bool amduat_asl_pointer_read_u32_le(amduat_asl_pointer_cursor_t *cur,
uint32_t *out) {
const uint8_t *data;
if (cur->len - cur->offset < 4u) {
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 += 4u;
return true;
}
static bool amduat_asl_pointer_read_u8(amduat_asl_pointer_cursor_t *cur,
uint8_t *out) {
if (cur->len - cur->offset < 1u) {
return false;
}
*out = cur->data[cur->offset++];
return true;
}
static bool amduat_asl_pointer_join_path(const char *base,
const char *segment,
char **out_path) {
size_t base_len;
size_t segment_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);
segment_len = strlen(segment);
needs_sep = base[base_len - 1u] != '/';
total_len = base_len + (needs_sep ? 1u : 0u) + segment_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, segment_len);
offset += segment_len;
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
static bool amduat_asl_pointer_ensure_directory(const char *path) {
struct stat st;
if (path == NULL || path[0] == '\0') {
return false;
}
if (stat(path, &st) == 0) {
return S_ISDIR(st.st_mode);
}
if (errno != ENOENT) {
return false;
}
if (mkdir(path, 0755) != 0) {
if (errno == EEXIST) {
return stat(path, &st) == 0 && S_ISDIR(st.st_mode);
}
return false;
}
return true;
}
static char *amduat_asl_pointer_parent_dir(const char *path) {
const char *slash;
size_t len;
char *dir;
if (path == NULL) {
return NULL;
}
slash = strrchr(path, '/');
if (slash == NULL || slash == path) {
return NULL;
}
len = (size_t)(slash - path);
dir = (char *)malloc(len + 1u);
if (dir == NULL) {
return NULL;
}
memcpy(dir, path, len);
dir[len] = '\0';
return dir;
}
static bool amduat_asl_pointer_fsync_directory(const char *path) {
int fd;
int flags = O_RDONLY;
if (path == NULL) {
return false;
}
#ifdef O_DIRECTORY
flags |= O_DIRECTORY;
#endif
fd = open(path, flags);
if (fd < 0) {
return false;
}
if (fsync(fd) != 0) {
close(fd);
return false;
}
return close(fd) == 0;
}
bool amduat_asl_pointer_name_is_valid(const char *name) {
size_t len;
size_t seg_start = 0u;
if (name == NULL) {
return false;
}
len = strlen(name);
if (len == 0u || len > AMDUAT_ASL_POINTER_NAME_MAX) {
return false;
}
if (name[0] == '/' || name[len - 1u] == '/') {
return false;
}
for (size_t i = 0u; i < len; ++i) {
char c = name[i];
bool ok = (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '.' || c == '_' || c == '-' || c == '/';
if (!ok) {
return false;
}
if (c == '/') {
size_t seg_len = i - seg_start;
if (seg_len == 0u) {
return false;
}
if (seg_len == 2u && name[seg_start] == '.' &&
name[seg_start + 1u] == '.') {
return false;
}
seg_start = i + 1u;
}
}
{
size_t seg_len = len - seg_start;
if (seg_len == 0u) {
return false;
}
if (seg_len == 2u && name[seg_start] == '.' &&
name[seg_start + 1u] == '.') {
return false;
}
}
return true;
}
static bool amduat_asl_pointer_build_head_path(const char *root_path,
const char *name,
bool ensure_dirs,
char **out_path) {
char *pointers_path = NULL;
char *cursor_path = NULL;
char *head_path = NULL;
const char *segment_start;
const char *segment_end;
bool ok = false;
if (root_path == NULL || name == NULL || out_path == NULL) {
return false;
}
*out_path = NULL;
if (!amduat_asl_pointer_join_path(root_path, "pointers", &pointers_path)) {
return false;
}
if (ensure_dirs &&
!amduat_asl_pointer_ensure_directory(pointers_path)) {
free(pointers_path);
return false;
}
cursor_path = pointers_path;
pointers_path = NULL;
segment_start = name;
while (*segment_start != '\0') {
segment_end = strchr(segment_start, '/');
if (segment_end == NULL) {
segment_end = segment_start + strlen(segment_start);
}
{
size_t seg_len = (size_t)(segment_end - segment_start);
char *segment = (char *)malloc(seg_len + 1u);
char *next_path = NULL;
if (segment == NULL) {
goto cleanup;
}
memcpy(segment, segment_start, seg_len);
segment[seg_len] = '\0';
if (!amduat_asl_pointer_join_path(cursor_path, segment, &next_path)) {
free(segment);
goto cleanup;
}
free(segment);
if (ensure_dirs &&
!amduat_asl_pointer_ensure_directory(next_path)) {
free(next_path);
goto cleanup;
}
free(cursor_path);
cursor_path = next_path;
}
if (*segment_end == '\0') {
break;
}
segment_start = segment_end + 1u;
}
if (!amduat_asl_pointer_join_path(cursor_path, "head", &head_path)) {
goto cleanup;
}
*out_path = head_path;
head_path = NULL;
ok = true;
cleanup:
free(pointers_path);
free(cursor_path);
free(head_path);
return ok;
}
bool amduat_asl_pointer_store_init(amduat_asl_pointer_store_t *ps,
const char *root_path) {
size_t len;
if (ps == NULL || root_path == NULL) {
return false;
}
len = strlen(root_path);
if (len == 0u || len >= AMDUAT_ASL_POINTER_FS_ROOT_MAX) {
return false;
}
memcpy(ps->root_path, root_path, len);
ps->root_path[len] = '\0';
return true;
}
static amduat_asl_pointer_error_t amduat_asl_pointer_read_head(
const char *path,
const char *name,
bool *out_exists,
amduat_reference_t *out_ref,
amduat_reference_t *out_prev_ref,
bool *out_has_prev) {
FILE *fp;
uint8_t *buffer;
long file_size;
amduat_asl_pointer_cursor_t cur;
uint32_t version;
uint32_t name_len;
uint32_t ref_len;
uint32_t prev_len;
uint8_t flags;
amduat_octets_t ref_bytes;
amduat_octets_t prev_bytes;
if (out_exists == NULL || out_ref == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
*out_exists = false;
memset(out_ref, 0, sizeof(*out_ref));
if (out_prev_ref != NULL) {
memset(out_prev_ref, 0, sizeof(*out_prev_ref));
}
if (out_has_prev != NULL) {
*out_has_prev = false;
}
fp = fopen(path, "rb");
if (fp == NULL) {
return errno == ENOENT ? AMDUAT_ASL_POINTER_ERR_NOT_FOUND
: AMDUAT_ASL_POINTER_ERR_IO;
}
if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_IO;
}
file_size = ftell(fp);
if (file_size < 0) {
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_IO;
}
if (file_size == 0) {
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_NOT_FOUND;
}
if (fseek(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_IO;
}
buffer = (uint8_t *)malloc((size_t)file_size);
if (buffer == NULL) {
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_IO;
}
if (fread(buffer, 1u, (size_t)file_size, fp) != (size_t)file_size) {
free(buffer);
fclose(fp);
return AMDUAT_ASL_POINTER_ERR_IO;
}
fclose(fp);
cur.data = buffer;
cur.len = (size_t)file_size;
cur.offset = 0u;
if (cur.len < AMDUAT_ASL_POINTER_MAGIC_LEN + 4u + 1u) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (memcmp(cur.data, k_amduat_asl_pointer_magic,
AMDUAT_ASL_POINTER_MAGIC_LEN) != 0) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
cur.offset += AMDUAT_ASL_POINTER_MAGIC_LEN;
if (!amduat_asl_pointer_read_u32_le(&cur, &version) ||
version != AMDUAT_ASL_POINTER_VERSION) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_read_u8(&cur, &flags)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_read_u32_le(&cur, &name_len)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (cur.len - cur.offset < name_len) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (strlen(name) != name_len ||
memcmp(cur.data + cur.offset, name, name_len) != 0) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
cur.offset += name_len;
if (!amduat_asl_pointer_read_u32_le(&cur, &ref_len)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (cur.len - cur.offset < ref_len || ref_len < 2u) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
ref_bytes = amduat_octets(cur.data + cur.offset, ref_len);
if (!amduat_enc_asl1_core_decode_reference_v1(ref_bytes, out_ref)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
cur.offset += ref_len;
if (!amduat_asl_pointer_read_u32_le(&cur, &prev_len)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (prev_len != 0u) {
if (!(flags & AMDUAT_ASL_POINTER_FLAG_HAS_PREV)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (cur.len - cur.offset < prev_len || prev_len < 2u) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
prev_bytes = amduat_octets(cur.data + cur.offset, prev_len);
if (out_prev_ref != NULL &&
!amduat_enc_asl1_core_decode_reference_v1(prev_bytes, out_prev_ref)) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
cur.offset += prev_len;
if (out_has_prev != NULL) {
*out_has_prev = true;
}
}
if (cur.offset != cur.len) {
free(buffer);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
*out_exists = true;
free(buffer);
return AMDUAT_ASL_POINTER_OK;
}
static amduat_asl_pointer_error_t amduat_asl_pointer_write_head(
const char *path,
const char *name,
const amduat_reference_t *ref,
const amduat_reference_t *prev_ref,
bool has_prev) {
char *tmp_path;
size_t tmp_len;
FILE *fp;
uint8_t header[AMDUAT_ASL_POINTER_MAGIC_LEN + 4u + 1u];
amduat_octets_t ref_bytes = amduat_octets(NULL, 0u);
amduat_octets_t prev_bytes = amduat_octets(NULL, 0u);
uint32_t name_len;
uint32_t ref_len;
uint32_t prev_len;
uint8_t flags = 0u;
amduat_asl_pointer_error_t err = AMDUAT_ASL_POINTER_OK;
if (ref == NULL || name == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
name_len = (uint32_t)strlen(name);
if (!amduat_enc_asl1_core_encode_reference_v1(*ref, &ref_bytes)) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
ref_len = (uint32_t)ref_bytes.len;
if (has_prev) {
if (prev_ref == NULL ||
!amduat_enc_asl1_core_encode_reference_v1(*prev_ref, &prev_bytes)) {
free((void *)ref_bytes.data);
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
prev_len = (uint32_t)prev_bytes.len;
flags |= AMDUAT_ASL_POINTER_FLAG_HAS_PREV;
} else {
prev_len = 0u;
}
tmp_len = strlen(path) + 5u;
tmp_path = (char *)malloc(tmp_len);
if (tmp_path == NULL) {
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
snprintf(tmp_path, tmp_len, "%s.tmp", path);
fp = fopen(tmp_path, "wb");
if (fp == NULL) {
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return AMDUAT_ASL_POINTER_ERR_IO;
}
memcpy(header, k_amduat_asl_pointer_magic, AMDUAT_ASL_POINTER_MAGIC_LEN);
amduat_asl_pointer_store_u32_le(
header + AMDUAT_ASL_POINTER_MAGIC_LEN, AMDUAT_ASL_POINTER_VERSION);
header[AMDUAT_ASL_POINTER_MAGIC_LEN + 4u] = flags;
if (fwrite(header, 1u, sizeof(header), fp) != sizeof(header)) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK) {
uint8_t len_buf[4u];
amduat_asl_pointer_store_u32_le(len_buf, name_len);
if (fwrite(len_buf, 1u, 4u, fp) != 4u ||
fwrite(name, 1u, name_len, fp) != name_len) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
}
if (err == AMDUAT_ASL_POINTER_OK) {
uint8_t len_buf[4u];
amduat_asl_pointer_store_u32_le(len_buf, ref_len);
if (fwrite(len_buf, 1u, 4u, fp) != 4u ||
fwrite(ref_bytes.data, 1u, ref_len, fp) != ref_len) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
}
if (err == AMDUAT_ASL_POINTER_OK) {
uint8_t len_buf[4u];
amduat_asl_pointer_store_u32_le(len_buf, prev_len);
if (fwrite(len_buf, 1u, 4u, fp) != 4u) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK && prev_len != 0u &&
fwrite(prev_bytes.data, 1u, prev_len, fp) != prev_len) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
}
if (err == AMDUAT_ASL_POINTER_OK && fflush(fp) != 0) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK && fsync(fileno(fp)) != 0) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (fclose(fp) != 0) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK && rename(tmp_path, path) != 0) {
err = AMDUAT_ASL_POINTER_ERR_IO;
}
if (err == AMDUAT_ASL_POINTER_OK) {
char *parent_dir = amduat_asl_pointer_parent_dir(path);
if (parent_dir != NULL) {
if (!amduat_asl_pointer_fsync_directory(parent_dir)) {
amduat_log(AMDUAT_LOG_WARN,
"pointer fsync dir failed for %s", parent_dir);
err = AMDUAT_ASL_POINTER_ERR_IO;
}
free(parent_dir);
}
}
if (err != AMDUAT_ASL_POINTER_OK) {
(void)remove(tmp_path);
}
free(tmp_path);
free((void *)ref_bytes.data);
free((void *)prev_bytes.data);
return err;
}
amduat_asl_pointer_error_t amduat_asl_pointer_get(
const amduat_asl_pointer_store_t *ps,
const char *name,
bool *out_exists,
amduat_reference_t *out_ref) {
char *head_path = NULL;
amduat_asl_pointer_error_t err;
if (ps == NULL || name == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_name_is_valid(name)) {
return AMDUAT_ASL_POINTER_ERR_INVALID_NAME;
}
if (!amduat_asl_pointer_build_head_path(ps->root_path, name, false,
&head_path)) {
return AMDUAT_ASL_POINTER_ERR_IO;
}
err = amduat_asl_pointer_read_head(head_path, name, out_exists, out_ref,
NULL, NULL);
if (err == AMDUAT_ASL_POINTER_ERR_NOT_FOUND) {
*out_exists = false;
err = AMDUAT_ASL_POINTER_OK;
}
free(head_path);
return err;
}
amduat_asl_pointer_error_t amduat_asl_pointer_cas(
const amduat_asl_pointer_store_t *ps,
const char *name,
bool expected_exists,
const amduat_reference_t *expected_ref,
const amduat_reference_t *new_ref,
bool *out_swapped) {
char *head_path = NULL;
int fd = -1;
struct flock lock;
bool exists = false;
amduat_reference_t current_ref;
amduat_reference_t prev_ref;
bool has_prev = false;
bool created = false;
amduat_asl_pointer_error_t err;
if (out_swapped == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
*out_swapped = false;
if (ps == NULL || name == NULL || new_ref == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_name_is_valid(name)) {
return AMDUAT_ASL_POINTER_ERR_INVALID_NAME;
}
if (expected_exists && expected_ref == NULL) {
return AMDUAT_ASL_POINTER_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_build_head_path(ps->root_path, name, true,
&head_path)) {
return AMDUAT_ASL_POINTER_ERR_IO;
}
fd = open(head_path, O_RDWR | O_CREAT, 0644);
if (fd < 0) {
free(head_path);
return AMDUAT_ASL_POINTER_ERR_IO;
}
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0;
if (fcntl(fd, F_SETLKW, &lock) != 0) {
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_ERR_IO;
}
{
struct stat st;
if (fstat(fd, &st) == 0 && st.st_size == 0) {
created = true;
}
}
err = amduat_asl_pointer_read_head(head_path, name, &exists, &current_ref,
&prev_ref, &has_prev);
if (err == AMDUAT_ASL_POINTER_ERR_NOT_FOUND) {
exists = false;
err = AMDUAT_ASL_POINTER_OK;
}
if (err != AMDUAT_ASL_POINTER_OK) {
close(fd);
free(head_path);
return err;
}
if (expected_exists != exists) {
if (created) {
(void)unlink(head_path);
}
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_OK;
}
if (expected_exists && !amduat_reference_eq(*expected_ref, current_ref)) {
if (created) {
(void)unlink(head_path);
}
close(fd);
free(head_path);
return AMDUAT_ASL_POINTER_OK;
}
err = amduat_asl_pointer_write_head(head_path, name, new_ref,
expected_exists ? &current_ref : NULL,
expected_exists);
if (err == AMDUAT_ASL_POINTER_OK) {
*out_swapped = true;
}
if (exists) {
amduat_reference_free(&current_ref);
}
if (has_prev) {
amduat_reference_free(&prev_ref);
}
close(fd);
free(head_path);
return err;
}

905
src/core/asl_log_store.c Normal file
View file

@ -0,0 +1,905 @@
#include "amduat/asl/log_store.h"
#include "amduat/enc/asl1_core_codec.h"
#include "amduat/util/log.h"
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
enum {
AMDUAT_ASL_LOG_MAGIC_LEN = 8,
AMDUAT_ASL_LOG_VERSION = 1,
AMDUAT_ASL_LOG_CHUNK_MAX_ENTRIES = 1024u,
AMDUAT_ASL_LOG_MAX_RETRIES = 8u
};
static const uint8_t k_amduat_asl_log_magic[AMDUAT_ASL_LOG_MAGIC_LEN] = {
'A', 'S', 'L', 'L', 'O', 'G', '1', '\0'
};
enum {
AMDUAT_ASL_LOG_FLAG_HAS_PREV = 1u << 0,
AMDUAT_ASL_LOG_FLAG_HAS_TIMESTAMP = 1u << 1,
AMDUAT_ASL_LOG_FLAG_HAS_ACTOR = 1u << 2
};
typedef struct {
bool has_prev;
amduat_reference_t prev_ref;
uint64_t base_offset;
uint32_t entry_count;
bool has_timestamp;
bool has_actor;
amduat_asl_log_entry_t *entries;
} amduat_asl_log_chunk_t;
typedef struct {
const uint8_t *data;
size_t len;
size_t offset;
} amduat_asl_log_cursor_t;
static void amduat_asl_log_store_u16_le(uint8_t *out, uint16_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
}
static void amduat_asl_log_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 amduat_asl_log_store_u64_le(uint8_t *out, uint64_t value) {
out[0] = (uint8_t)(value & 0xffu);
out[1] = (uint8_t)((value >> 8) & 0xffu);
out[2] = (uint8_t)((value >> 16) & 0xffu);
out[3] = (uint8_t)((value >> 24) & 0xffu);
out[4] = (uint8_t)((value >> 32) & 0xffu);
out[5] = (uint8_t)((value >> 40) & 0xffu);
out[6] = (uint8_t)((value >> 48) & 0xffu);
out[7] = (uint8_t)((value >> 56) & 0xffu);
}
static bool amduat_asl_log_read_u16_le(amduat_asl_log_cursor_t *cur,
uint16_t *out) {
const uint8_t *data;
if (cur->len - cur->offset < 2u) {
return false;
}
data = cur->data + cur->offset;
*out = (uint16_t)data[0] | ((uint16_t)data[1] << 8);
cur->offset += 2u;
return true;
}
static bool amduat_asl_log_read_u32_le(amduat_asl_log_cursor_t *cur,
uint32_t *out) {
const uint8_t *data;
if (cur->len - cur->offset < 4u) {
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 += 4u;
return true;
}
static bool amduat_asl_log_read_u64_le(amduat_asl_log_cursor_t *cur,
uint64_t *out) {
const uint8_t *data;
if (cur->len - cur->offset < 8u) {
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 += 8u;
return true;
}
static bool amduat_asl_log_add_size(size_t *acc, size_t add) {
if (*acc > SIZE_MAX - add) {
return false;
}
*acc += add;
return true;
}
static void amduat_asl_log_chunk_free(amduat_asl_log_chunk_t *chunk) {
if (chunk == NULL) {
return;
}
if (chunk->has_prev) {
amduat_reference_free(&chunk->prev_ref);
}
if (chunk->entries != NULL) {
for (uint32_t i = 0u; i < chunk->entry_count; ++i) {
amduat_reference_free(&chunk->entries[i].payload_ref);
amduat_octets_free(&chunk->entries[i].actor);
}
free(chunk->entries);
}
memset(chunk, 0, sizeof(*chunk));
}
static bool amduat_asl_log_build_pointer_name(const char *log_name,
char **out_name) {
size_t name_len;
size_t total_len;
char *buffer;
size_t offset;
if (log_name == NULL || out_name == NULL) {
return false;
}
if (!amduat_asl_pointer_name_is_valid(log_name)) {
return false;
}
name_len = strlen(log_name);
total_len = 4u + name_len + 5u + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
offset = 0u;
memcpy(buffer + offset, "log/", 4u);
offset += 4u;
memcpy(buffer + offset, log_name, name_len);
offset += name_len;
memcpy(buffer + offset, "/head", 5u);
offset += 5u;
buffer[offset] = '\0';
*out_name = buffer;
return true;
}
static bool amduat_asl_log_entries_consistent(
const amduat_asl_log_entry_t *entries,
size_t entries_len,
bool *out_has_timestamp,
bool *out_has_actor) {
bool has_timestamp = false;
bool has_actor = false;
if (out_has_timestamp == NULL || out_has_actor == NULL) {
return false;
}
*out_has_timestamp = false;
*out_has_actor = false;
if (entries_len == 0u) {
return false;
}
has_timestamp = entries[0].has_timestamp;
has_actor = entries[0].has_actor;
for (size_t i = 0u; i < entries_len; ++i) {
if (entries[i].has_timestamp != has_timestamp ||
entries[i].has_actor != has_actor) {
return false;
}
}
*out_has_timestamp = has_timestamp;
*out_has_actor = has_actor;
return true;
}
static amduat_asl_store_error_t amduat_asl_log_encode_chunk(
const amduat_asl_log_chunk_t *chunk,
amduat_octets_t *out_bytes) {
uint8_t *buffer;
size_t total_len = 0u;
size_t offset = 0u;
uint8_t flags = 0u;
amduat_octets_t prev_bytes = amduat_octets(NULL, 0u);
if (chunk == NULL || out_bytes == NULL) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
out_bytes->data = NULL;
out_bytes->len = 0u;
if (!amduat_asl_log_add_size(&total_len, AMDUAT_ASL_LOG_MAGIC_LEN + 4u + 1u) ||
!amduat_asl_log_add_size(&total_len, 4u) ||
!amduat_asl_log_add_size(&total_len, 8u) ||
!amduat_asl_log_add_size(&total_len, 4u)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (chunk->has_prev) {
if (!amduat_enc_asl1_core_encode_reference_v1(chunk->prev_ref,
&prev_bytes)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_add_size(&total_len, 4u + prev_bytes.len)) {
free((void *)prev_bytes.data);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
flags |= AMDUAT_ASL_LOG_FLAG_HAS_PREV;
} else {
if (!amduat_asl_log_add_size(&total_len, 4u)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
}
if (chunk->has_timestamp) {
flags |= AMDUAT_ASL_LOG_FLAG_HAS_TIMESTAMP;
}
if (chunk->has_actor) {
flags |= AMDUAT_ASL_LOG_FLAG_HAS_ACTOR;
}
for (uint32_t i = 0u; i < chunk->entry_count; ++i) {
amduat_octets_t ref_bytes = amduat_octets(NULL, 0u);
if (!amduat_asl_log_add_size(&total_len, 2u)) {
goto cleanup;
}
if (chunk->has_timestamp) {
if (!amduat_asl_log_add_size(&total_len, 8u)) {
goto cleanup;
}
}
if (!amduat_enc_asl1_core_encode_reference_v1(
chunk->entries[i].payload_ref, &ref_bytes)) {
goto cleanup;
}
if (!amduat_asl_log_add_size(&total_len, 4u + ref_bytes.len)) {
free((void *)ref_bytes.data);
goto cleanup;
}
if (chunk->has_actor) {
if (!amduat_asl_log_add_size(&total_len,
4u + chunk->entries[i].actor.len)) {
free((void *)ref_bytes.data);
goto cleanup;
}
}
free((void *)ref_bytes.data);
}
buffer = (uint8_t *)malloc(total_len);
if (buffer == NULL) {
free((void *)prev_bytes.data);
return AMDUAT_ASL_STORE_ERR_IO;
}
memcpy(buffer + offset, k_amduat_asl_log_magic, AMDUAT_ASL_LOG_MAGIC_LEN);
offset += AMDUAT_ASL_LOG_MAGIC_LEN;
amduat_asl_log_store_u32_le(buffer + offset, AMDUAT_ASL_LOG_VERSION);
offset += 4u;
buffer[offset++] = flags;
if (chunk->has_prev) {
amduat_asl_log_store_u32_le(buffer + offset,
(uint32_t)prev_bytes.len);
offset += 4u;
memcpy(buffer + offset, prev_bytes.data, prev_bytes.len);
offset += prev_bytes.len;
} else {
amduat_asl_log_store_u32_le(buffer + offset, 0u);
offset += 4u;
}
amduat_asl_log_store_u64_le(buffer + offset, chunk->base_offset);
offset += 8u;
amduat_asl_log_store_u32_le(buffer + offset, chunk->entry_count);
offset += 4u;
for (uint32_t i = 0u; i < chunk->entry_count; ++i) {
amduat_octets_t ref_bytes = amduat_octets(NULL, 0u);
if (!amduat_enc_asl1_core_encode_reference_v1(
chunk->entries[i].payload_ref, &ref_bytes)) {
free(buffer);
free((void *)prev_bytes.data);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
amduat_asl_log_store_u16_le(buffer + offset, chunk->entries[i].kind);
offset += 2u;
if (chunk->has_timestamp) {
amduat_asl_log_store_u64_le(buffer + offset,
chunk->entries[i].timestamp);
offset += 8u;
}
amduat_asl_log_store_u32_le(buffer + offset, (uint32_t)ref_bytes.len);
offset += 4u;
memcpy(buffer + offset, ref_bytes.data, ref_bytes.len);
offset += ref_bytes.len;
if (chunk->has_actor) {
amduat_asl_log_store_u32_le(buffer + offset,
(uint32_t)chunk->entries[i].actor.len);
offset += 4u;
if (chunk->entries[i].actor.len != 0u) {
memcpy(buffer + offset, chunk->entries[i].actor.data,
chunk->entries[i].actor.len);
offset += chunk->entries[i].actor.len;
}
}
free((void *)ref_bytes.data);
}
if (offset != total_len) {
free(buffer);
free((void *)prev_bytes.data);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
free((void *)prev_bytes.data);
out_bytes->data = buffer;
out_bytes->len = total_len;
return AMDUAT_ASL_STORE_OK;
cleanup:
free((void *)prev_bytes.data);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
static amduat_asl_store_error_t amduat_asl_log_decode_chunk(
amduat_octets_t bytes,
amduat_asl_log_chunk_t *out_chunk) {
amduat_asl_log_cursor_t cur;
uint32_t version;
uint32_t prev_len;
uint8_t flags;
uint32_t entry_count;
if (out_chunk == NULL) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
memset(out_chunk, 0, sizeof(*out_chunk));
if (bytes.len < AMDUAT_ASL_LOG_MAGIC_LEN + 4u + 1u) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
cur.data = bytes.data;
cur.len = bytes.len;
cur.offset = 0u;
if (memcmp(cur.data, k_amduat_asl_log_magic,
AMDUAT_ASL_LOG_MAGIC_LEN) != 0) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
cur.offset += AMDUAT_ASL_LOG_MAGIC_LEN;
if (!amduat_asl_log_read_u32_le(&cur, &version) ||
version != AMDUAT_ASL_LOG_VERSION) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (cur.len - cur.offset < 1u) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
flags = cur.data[cur.offset++];
out_chunk->has_prev = (flags & AMDUAT_ASL_LOG_FLAG_HAS_PREV) != 0u;
out_chunk->has_timestamp =
(flags & AMDUAT_ASL_LOG_FLAG_HAS_TIMESTAMP) != 0u;
out_chunk->has_actor = (flags & AMDUAT_ASL_LOG_FLAG_HAS_ACTOR) != 0u;
if (!amduat_asl_log_read_u32_le(&cur, &prev_len)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (prev_len != 0u) {
amduat_octets_t prev_bytes;
if (!out_chunk->has_prev ||
cur.len - cur.offset < prev_len) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
prev_bytes = amduat_octets(cur.data + cur.offset, prev_len);
if (!amduat_enc_asl1_core_decode_reference_v1(prev_bytes,
&out_chunk->prev_ref)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
cur.offset += prev_len;
} else if (out_chunk->has_prev) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_read_u64_le(&cur, &out_chunk->base_offset)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_read_u32_le(&cur, &entry_count)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
out_chunk->entry_count = entry_count;
if (entry_count != 0u) {
if (entry_count > SIZE_MAX / sizeof(amduat_asl_log_entry_t)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
out_chunk->entries = (amduat_asl_log_entry_t *)calloc(
entry_count, sizeof(*out_chunk->entries));
if (out_chunk->entries == NULL) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_IO;
}
}
for (uint32_t i = 0u; i < entry_count; ++i) {
amduat_asl_log_entry_t *entry = &out_chunk->entries[i];
uint16_t kind;
uint32_t ref_len;
amduat_octets_t ref_bytes;
uint32_t actor_len = 0u;
if (!amduat_asl_log_read_u16_le(&cur, &kind)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
entry->kind = kind;
if (out_chunk->has_timestamp) {
if (!amduat_asl_log_read_u64_le(&cur, &entry->timestamp)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
entry->has_timestamp = true;
}
if (!amduat_asl_log_read_u32_le(&cur, &ref_len)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (cur.len - cur.offset < ref_len || ref_len < 2u) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
ref_bytes = amduat_octets(cur.data + cur.offset, ref_len);
if (!amduat_enc_asl1_core_decode_reference_v1(ref_bytes,
&entry->payload_ref)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
cur.offset += ref_len;
if (out_chunk->has_actor) {
if (!amduat_asl_log_read_u32_le(&cur, &actor_len)) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (cur.len - cur.offset < actor_len) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (actor_len != 0u) {
uint8_t *actor = (uint8_t *)malloc(actor_len);
if (actor == NULL) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_IO;
}
memcpy(actor, cur.data + cur.offset, actor_len);
entry->actor = amduat_octets(actor, actor_len);
} else {
entry->actor = amduat_octets(NULL, 0u);
}
entry->has_actor = true;
cur.offset += actor_len;
}
}
if (cur.offset != cur.len) {
amduat_asl_log_chunk_free(out_chunk);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
return AMDUAT_ASL_STORE_OK;
}
bool amduat_asl_log_store_init(amduat_asl_log_store_t *log_store,
const char *root_path,
amduat_asl_store_t *store,
const amduat_asl_pointer_store_t *pointer_store) {
if (log_store == NULL || root_path == NULL || store == NULL) {
return false;
}
log_store->store = store;
if (pointer_store != NULL) {
log_store->pointer_store = *pointer_store;
return true;
}
return amduat_asl_pointer_store_init(&log_store->pointer_store, root_path);
}
amduat_asl_store_error_t amduat_asl_log_append(
amduat_asl_log_store_t *log_store,
const char *log_name,
const amduat_asl_log_entry_t *entries,
size_t entries_len,
uint64_t *out_first_offset) {
char *pointer_name = NULL;
amduat_asl_store_error_t store_err;
bool has_timestamp = false;
bool has_actor = false;
uint32_t entry_count;
if (out_first_offset != NULL) {
*out_first_offset = 0u;
}
if (log_store == NULL || log_store->store == NULL || log_name == NULL ||
entries == NULL) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_name_is_valid(log_name)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (entries_len == 0u || entries_len > AMDUAT_ASL_LOG_CHUNK_MAX_ENTRIES) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_entries_consistent(entries, entries_len,
&has_timestamp, &has_actor)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_build_pointer_name(log_name, &pointer_name)) {
return AMDUAT_ASL_STORE_ERR_IO;
}
entry_count = (uint32_t)entries_len;
for (uint32_t attempt = 0u; attempt < AMDUAT_ASL_LOG_MAX_RETRIES; ++attempt) {
bool head_exists = false;
amduat_reference_t head_ref;
amduat_artifact_t head_artifact;
amduat_asl_log_chunk_t head_chunk;
uint64_t base_offset = 0u;
amduat_asl_pointer_error_t ptr_err;
memset(&head_chunk, 0, sizeof(head_chunk));
ptr_err = amduat_asl_pointer_get(&log_store->pointer_store,
pointer_name, &head_exists, &head_ref);
if (ptr_err != AMDUAT_ASL_POINTER_OK) {
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
if (head_exists) {
store_err = amduat_asl_store_get(log_store->store, head_ref,
&head_artifact);
if (store_err != AMDUAT_ASL_STORE_OK) {
amduat_reference_free(&head_ref);
free(pointer_name);
return store_err;
}
if (!head_artifact.has_type_tag ||
head_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1) {
amduat_reference_free(&head_ref);
amduat_artifact_free(&head_artifact);
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
store_err = amduat_asl_log_decode_chunk(head_artifact.bytes, &head_chunk);
amduat_artifact_free(&head_artifact);
if (store_err != AMDUAT_ASL_STORE_OK) {
amduat_reference_free(&head_ref);
free(pointer_name);
return store_err;
}
if (head_chunk.base_offset >
UINT64_MAX - (uint64_t)head_chunk.entry_count) {
amduat_asl_log_chunk_free(&head_chunk);
amduat_reference_free(&head_ref);
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
base_offset = head_chunk.base_offset + head_chunk.entry_count;
amduat_asl_log_chunk_free(&head_chunk);
}
{
amduat_asl_log_chunk_t new_chunk;
amduat_octets_t encoded = amduat_octets(NULL, 0u);
amduat_reference_t new_ref;
amduat_artifact_t artifact;
memset(&new_chunk, 0, sizeof(new_chunk));
new_chunk.base_offset = base_offset;
new_chunk.entry_count = entry_count;
new_chunk.has_timestamp = has_timestamp;
new_chunk.has_actor = has_actor;
new_chunk.entries = (amduat_asl_log_entry_t *)entries;
if (head_exists) {
new_chunk.has_prev = true;
new_chunk.prev_ref = head_ref;
}
store_err = amduat_asl_log_encode_chunk(&new_chunk, &encoded);
if (store_err != AMDUAT_ASL_STORE_OK) {
if (head_exists) {
amduat_reference_free(&head_ref);
}
free(pointer_name);
return store_err;
}
artifact = amduat_artifact_with_type(
encoded, amduat_type_tag(AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1));
store_err = amduat_asl_store_put(log_store->store, artifact, &new_ref);
free((void *)encoded.data);
if (store_err != AMDUAT_ASL_STORE_OK) {
if (head_exists) {
amduat_reference_free(&head_ref);
}
free(pointer_name);
return store_err;
}
{
bool swapped = false;
amduat_asl_pointer_error_t cas_err = amduat_asl_pointer_cas(
&log_store->pointer_store,
pointer_name,
head_exists,
head_exists ? &head_ref : NULL,
&new_ref,
&swapped);
if (head_exists) {
amduat_reference_free(&head_ref);
}
if (cas_err != AMDUAT_ASL_POINTER_OK) {
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
if (swapped) {
/* Chunks are rooted by the head pointer; future GC can follow them. */
if (out_first_offset != NULL) {
*out_first_offset = base_offset;
}
free(pointer_name);
return AMDUAT_ASL_STORE_OK;
}
amduat_log(AMDUAT_LOG_DEBUG, "log append CAS retry");
}
}
}
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
amduat_asl_store_error_t amduat_asl_log_read(
amduat_asl_log_store_t *log_store,
const char *log_name,
uint64_t from_offset,
size_t max_entries,
amduat_asl_log_entry_t **out_entries,
size_t *out_len,
uint64_t *out_next_offset,
bool *out_end) {
char *pointer_name = NULL;
bool head_exists = false;
amduat_reference_t head_ref;
amduat_asl_pointer_error_t ptr_err;
amduat_asl_log_chunk_t *chunks = NULL;
size_t chunks_len = 0u;
uint64_t head_end_offset = 0u;
if (out_entries == NULL || out_len == NULL ||
out_next_offset == NULL || out_end == NULL) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
*out_entries = NULL;
*out_len = 0u;
*out_next_offset = from_offset;
*out_end = false;
if (log_store == NULL || log_store->store == NULL || log_name == NULL) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_pointer_name_is_valid(log_name)) {
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
if (!amduat_asl_log_build_pointer_name(log_name, &pointer_name)) {
return AMDUAT_ASL_STORE_ERR_IO;
}
ptr_err = amduat_asl_pointer_get(&log_store->pointer_store, pointer_name,
&head_exists, &head_ref);
if (ptr_err != AMDUAT_ASL_POINTER_OK) {
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
if (!head_exists) {
free(pointer_name);
*out_end = true;
return AMDUAT_ASL_STORE_OK;
}
{
amduat_reference_t current_ref = head_ref;
bool current_ref_owned = false;
bool has_prev = true;
while (has_prev) {
amduat_artifact_t artifact;
amduat_asl_log_chunk_t chunk;
amduat_asl_store_error_t err =
amduat_asl_store_get(log_store->store, current_ref, &artifact);
if (err != AMDUAT_ASL_STORE_OK) {
free(pointer_name);
if (chunks != NULL) {
for (size_t i = 0u; i < chunks_len; ++i) {
amduat_asl_log_chunk_free(&chunks[i]);
}
free(chunks);
}
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
amduat_reference_free(&head_ref);
return err;
}
if (!artifact.has_type_tag ||
artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_LOG_CHUNK_1) {
amduat_artifact_free(&artifact);
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
free(pointer_name);
amduat_reference_free(&head_ref);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
err = amduat_asl_log_decode_chunk(artifact.bytes, &chunk);
amduat_artifact_free(&artifact);
if (err != AMDUAT_ASL_STORE_OK) {
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
free(pointer_name);
amduat_reference_free(&head_ref);
return err;
}
if (chunks_len == 0u) {
if (chunk.base_offset >
UINT64_MAX - (uint64_t)chunk.entry_count) {
amduat_asl_log_chunk_free(&chunk);
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
free(pointer_name);
amduat_reference_free(&head_ref);
return AMDUAT_ASL_STORE_ERR_INTEGRITY;
}
head_end_offset = chunk.base_offset + chunk.entry_count;
}
{
amduat_asl_log_chunk_t *next =
(amduat_asl_log_chunk_t *)realloc(
chunks, (chunks_len + 1u) * sizeof(*chunks));
if (next == NULL) {
amduat_asl_log_chunk_free(&chunk);
amduat_reference_free(&current_ref);
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
chunks = next;
chunks[chunks_len++] = chunk;
}
if (chunk.base_offset <= from_offset || !chunk.has_prev) {
has_prev = false;
} else {
amduat_reference_t prev_ref = chunk.prev_ref;
amduat_reference_t next_ref;
if (!amduat_octets_clone(prev_ref.digest, &next_ref.digest)) {
amduat_asl_log_chunk_free(&chunk);
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
free(pointer_name);
return AMDUAT_ASL_STORE_ERR_IO;
}
next_ref.hash_id = prev_ref.hash_id;
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
current_ref = next_ref;
current_ref_owned = true;
}
}
if (current_ref_owned) {
amduat_reference_free(&current_ref);
}
}
if (from_offset >= head_end_offset) {
*out_end = true;
amduat_reference_free(&head_ref);
free(pointer_name);
for (size_t i = 0u; i < chunks_len; ++i) {
amduat_asl_log_chunk_free(&chunks[i]);
}
free(chunks);
return AMDUAT_ASL_STORE_OK;
}
{
size_t total_out = 0u;
amduat_asl_log_entry_t *out = NULL;
for (size_t i = 0u; i < chunks_len / 2u; ++i) {
amduat_asl_log_chunk_t tmp = chunks[i];
chunks[i] = chunks[chunks_len - 1u - i];
chunks[chunks_len - 1u - i] = tmp;
}
for (size_t i = 0u; i < chunks_len; ++i) {
amduat_asl_log_chunk_t *chunk = &chunks[i];
for (uint32_t j = 0u; j < chunk->entry_count; ++j) {
uint64_t offset = chunk->base_offset + j;
if (offset < from_offset) {
continue;
}
if (total_out == max_entries) {
*out_entries = out;
*out_len = total_out;
*out_next_offset = offset;
*out_end = false;
goto done;
}
{
amduat_asl_log_entry_t *next =
(amduat_asl_log_entry_t *)realloc(
out, (total_out + 1u) * sizeof(*out));
if (next == NULL) {
amduat_asl_log_entries_free(out, total_out);
return AMDUAT_ASL_STORE_ERR_IO;
}
out = next;
memset(&out[total_out], 0, sizeof(out[total_out]));
out[total_out].kind = chunk->entries[j].kind;
out[total_out].has_timestamp = chunk->entries[j].has_timestamp;
out[total_out].timestamp = chunk->entries[j].timestamp;
out[total_out].has_actor = chunk->entries[j].has_actor;
out[total_out].payload_ref.hash_id =
chunk->entries[j].payload_ref.hash_id;
if (!amduat_octets_clone(chunk->entries[j].payload_ref.digest,
&out[total_out].payload_ref.digest)) {
amduat_asl_log_entries_free(out, total_out + 1u);
return AMDUAT_ASL_STORE_ERR_IO;
}
if (out[total_out].has_actor) {
if (!amduat_octets_clone(chunk->entries[j].actor,
&out[total_out].actor)) {
amduat_asl_log_entries_free(out, total_out + 1u);
return AMDUAT_ASL_STORE_ERR_IO;
}
} else {
out[total_out].actor = amduat_octets(NULL, 0u);
}
total_out++;
*out_next_offset = offset + 1u;
}
}
}
*out_entries = out;
*out_len = total_out;
*out_end = (*out_next_offset >= head_end_offset);
}
done:
amduat_reference_free(&head_ref);
free(pointer_name);
for (size_t i = 0u; i < chunks_len; ++i) {
amduat_asl_log_chunk_free(&chunks[i]);
}
free(chunks);
return AMDUAT_ASL_STORE_OK;
}
void amduat_asl_log_entries_free(amduat_asl_log_entry_t *entries,
size_t entries_len) {
if (entries == NULL) {
return;
}
for (size_t i = 0u; i < entries_len; ++i) {
amduat_reference_free(&entries[i].payload_ref);
amduat_octets_free(&entries[i].actor);
}
free(entries);
}

View file

@ -1,6 +1,8 @@
#include "amduat/asl/artifact_io.h"
#include "amduat/asl/asl_derivation_index_fs.h"
#include "amduat/asl/asl_materialization_cache_fs.h"
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/log_store.h"
#include "amduat/asl/asl_store_fs.h"
#include "amduat/asl/asl_store_fs_meta.h"
#include "amduat/asl/io.h"
@ -69,7 +71,9 @@ static void amduat_pel_cli_print_usage(FILE *stream) {
" trace Trace DAG tools (decode, from-result).\n"
" result Surface result tools (decode).\n"
" op Kernel op registry tools.\n"
" log Append-only log tools (append, read).\n"
" matcache Materialization cache tools (get, sid).\n"
" ptr Named pointer tools (get, cas).\n"
" scheme Scheme refs, type tags, profile IDs.\n"
" help Show help for a command.\n"
"\n"
@ -135,6 +139,10 @@ static void amduat_pel_cli_print_usage(FILE *stream) {
" amduat-pel op params-decode --op NAME[=VERSION]\n"
" --input PATH|- [--format text|json]\n"
"\n"
"log:\n"
" amduat-pel log append --log NAME --kind N --ref REF [--actor TEXT]\n"
" amduat-pel log read --log NAME --from OFFSET --limit N\n"
"\n"
"scheme:\n"
" amduat-pel scheme show [--format text|json] [--ref-format hex|bytes]\n"
" amduat-pel scheme dag-ref [--format hex|bytes]\n"
@ -144,6 +152,10 @@ static void amduat_pel_cli_print_usage(FILE *stream) {
" amduat-pel matcache sid --program-ref REF --input-ref REF...\n"
" [--params-ref REF]\n"
"\n"
"ptr:\n"
" amduat-pel ptr get --name NAME\n"
" amduat-pel ptr cas --name NAME --expected REF|none --new REF\n"
"\n"
"defaults:\n"
" --root .amduat-asl\n"
" --ref-format hex\n"
@ -3010,6 +3022,443 @@ static int amduat_pel_cli_cmd_matcache(
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
static int amduat_pel_cli_cmd_ptr_get(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
const char *name = NULL;
amduat_asl_pointer_store_t store;
bool exists = false;
amduat_reference_t ref;
int i;
if (global == NULL) {
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--name") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --name requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
name = argv[++i];
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
amduat_pel_cli_print_usage(stdout);
return AMDUAT_PEL_CLI_EXIT_OK;
} else {
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (name == NULL) {
fprintf(stderr, "error: --name is required\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (!amduat_asl_pointer_store_init(&store, global->root)) {
fprintf(stderr, "error: failed to init pointer store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
{
amduat_asl_pointer_error_t err =
amduat_asl_pointer_get(&store, name, &exists, &ref);
if (err == AMDUAT_ASL_POINTER_ERR_INVALID_NAME) {
fprintf(stderr, "error: invalid pointer name\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (err != AMDUAT_ASL_POINTER_OK) {
fprintf(stderr, "error: pointer get failed\n");
return AMDUAT_PEL_CLI_EXIT_IO;
}
}
printf("exists=%s\n", exists ? "true" : "false");
if (exists) {
fputs("ref=", stdout);
if (!amduat_format_ref_write_text(stdout, ref, global->ref_format)) {
fprintf(stderr, "error: failed to format ref\n");
amduat_pel_cli_free_reference(&ref);
return AMDUAT_PEL_CLI_EXIT_CODEC;
}
fputc('\n', stdout);
amduat_pel_cli_free_reference(&ref);
}
return AMDUAT_PEL_CLI_EXIT_OK;
}
static int amduat_pel_cli_cmd_ptr_cas(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
const char *name = NULL;
const char *expected_text = NULL;
const char *new_text = NULL;
amduat_asl_pointer_store_t store;
amduat_reference_t expected_ref;
amduat_reference_t new_ref;
bool expected_exists = false;
bool swapped = false;
bool stdin_used = false;
int i;
if (global == NULL) {
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--name") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --name requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
name = argv[++i];
} else if (strcmp(argv[i], "--expected") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --expected requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
expected_text = argv[++i];
} else if (strcmp(argv[i], "--new") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --new requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
new_text = argv[++i];
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
amduat_pel_cli_print_usage(stdout);
return AMDUAT_PEL_CLI_EXIT_OK;
} else {
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (name == NULL || expected_text == NULL || new_text == NULL) {
fprintf(stderr, "error: --name, --expected, and --new are required\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (!amduat_asl_pointer_store_init(&store, global->root)) {
fprintf(stderr, "error: failed to init pointer store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (strcmp(expected_text, "none") == 0) {
expected_exists = false;
} else {
expected_exists = true;
if (!amduat_pel_cli_ref_from_text(expected_text, global->ref_format,
&stdin_used, &expected_ref)) {
fprintf(stderr, "error: invalid expected ref\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (!amduat_pel_cli_ref_from_text(new_text, global->ref_format,
&stdin_used, &new_ref)) {
fprintf(stderr, "error: invalid new ref\n");
if (expected_exists) {
amduat_pel_cli_free_reference(&expected_ref);
}
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
{
amduat_asl_pointer_error_t err = amduat_asl_pointer_cas(
&store, name, expected_exists,
expected_exists ? &expected_ref : NULL,
&new_ref, &swapped);
if (expected_exists) {
amduat_pel_cli_free_reference(&expected_ref);
}
amduat_pel_cli_free_reference(&new_ref);
if (err == AMDUAT_ASL_POINTER_ERR_INVALID_NAME) {
fprintf(stderr, "error: invalid pointer name\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (err != AMDUAT_ASL_POINTER_OK) {
fprintf(stderr, "error: pointer cas failed\n");
return AMDUAT_PEL_CLI_EXIT_IO;
}
}
printf("swapped=%s\n", swapped ? "true" : "false");
if (!swapped) {
return 2;
}
return AMDUAT_PEL_CLI_EXIT_OK;
}
static int amduat_pel_cli_cmd_ptr(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
if (argc < 1) {
fprintf(stderr, "error: ptr requires a subcommand\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (strcmp(argv[0], "get") == 0) {
return amduat_pel_cli_cmd_ptr_get(argc - 1, argv + 1, global);
}
if (strcmp(argv[0], "cas") == 0) {
return amduat_pel_cli_cmd_ptr_cas(argc - 1, argv + 1, global);
}
fprintf(stderr, "error: unknown ptr subcommand: %s\n", argv[0]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
static bool amduat_pel_cli_init_store(const char *root,
amduat_asl_store_t *out_store,
amduat_asl_store_fs_t *out_fs) {
amduat_asl_store_fs_config_t cfg;
if (root == NULL || out_store == NULL || out_fs == NULL) {
return false;
}
if (!amduat_asl_store_fs_load_config(root, &cfg)) {
return false;
}
if (!amduat_asl_store_fs_init(out_fs, cfg.config, root)) {
return false;
}
amduat_asl_store_init(out_store, cfg.config, amduat_asl_store_fs_ops(),
out_fs);
return true;
}
static int amduat_pel_cli_cmd_log_append(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
const char *log_name = NULL;
const char *ref_text = NULL;
const char *actor_text = NULL;
amduat_asl_store_t store;
amduat_asl_store_fs_t fs;
amduat_asl_log_store_t log_store;
amduat_asl_pointer_store_t pointer_store;
amduat_asl_log_entry_t entry;
amduat_reference_t ref;
uint64_t offset = 0u;
unsigned long kind = 0u;
bool stdin_used = false;
int i;
if (global == NULL) {
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--log") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --log requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
log_name = argv[++i];
} else if (strcmp(argv[i], "--kind") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --kind requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
kind = strtoul(argv[++i], NULL, 10);
} else if (strcmp(argv[i], "--ref") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --ref requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
ref_text = argv[++i];
} else if (strcmp(argv[i], "--actor") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --actor requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
actor_text = argv[++i];
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
amduat_pel_cli_print_usage(stdout);
return AMDUAT_PEL_CLI_EXIT_OK;
} else {
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (log_name == NULL || ref_text == NULL) {
fprintf(stderr, "error: --log and --ref are required\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (kind > UINT16_MAX) {
fprintf(stderr, "error: --kind out of range\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (!amduat_pel_cli_ref_from_text(ref_text, global->ref_format,
&stdin_used, &ref)) {
fprintf(stderr, "error: invalid --ref\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (!amduat_pel_cli_init_store(global->root, &store, &fs)) {
amduat_pel_cli_free_reference(&ref);
fprintf(stderr, "error: failed to initialize store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (!amduat_asl_pointer_store_init(&pointer_store, global->root)) {
amduat_pel_cli_free_reference(&ref);
fprintf(stderr, "error: failed to init pointer store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (!amduat_asl_log_store_init(&log_store, global->root, &store,
&pointer_store)) {
amduat_pel_cli_free_reference(&ref);
fprintf(stderr, "error: failed to init log store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
memset(&entry, 0, sizeof(entry));
entry.kind = (uint16_t)kind;
entry.payload_ref = ref;
entry.has_timestamp = false;
entry.has_actor = actor_text != NULL;
if (entry.has_actor) {
entry.actor = amduat_octets((const uint8_t *)actor_text,
strlen(actor_text));
}
{
amduat_asl_store_error_t err = amduat_asl_log_append(
&log_store, log_name, &entry, 1u, &offset);
amduat_pel_cli_free_reference(&ref);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "error: log append failed: %s\n",
amduat_pel_cli_store_error_str(err));
return amduat_pel_cli_map_store_error(err);
}
}
printf("offset=%" PRIu64 "\n", offset);
return AMDUAT_PEL_CLI_EXIT_OK;
}
static int amduat_pel_cli_cmd_log_read(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
const char *log_name = NULL;
uint64_t from_offset = 0u;
size_t limit = 0u;
amduat_asl_store_t store;
amduat_asl_store_fs_t fs;
amduat_asl_log_store_t log_store;
amduat_asl_pointer_store_t pointer_store;
amduat_asl_log_entry_t *entries = NULL;
size_t entries_len = 0u;
uint64_t next_offset = 0u;
bool end = false;
int i;
if (global == NULL) {
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
for (i = 0; i < argc; ++i) {
if (strcmp(argv[i], "--log") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --log requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
log_name = argv[++i];
} else if (strcmp(argv[i], "--from") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --from requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
from_offset = (uint64_t)strtoull(argv[++i], NULL, 10);
} else if (strcmp(argv[i], "--limit") == 0) {
if (i + 1 >= argc) {
fprintf(stderr, "error: --limit requires a value\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
limit = (size_t)strtoull(argv[++i], NULL, 10);
} else if (strcmp(argv[i], "--help") == 0 ||
strcmp(argv[i], "-h") == 0) {
amduat_pel_cli_print_usage(stdout);
return AMDUAT_PEL_CLI_EXIT_OK;
} else {
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
}
if (log_name == NULL || limit == 0u) {
fprintf(stderr, "error: --log and --limit are required\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (!amduat_pel_cli_init_store(global->root, &store, &fs)) {
fprintf(stderr, "error: failed to initialize store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (!amduat_asl_pointer_store_init(&pointer_store, global->root)) {
fprintf(stderr, "error: failed to init pointer store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
if (!amduat_asl_log_store_init(&log_store, global->root, &store,
&pointer_store)) {
fprintf(stderr, "error: failed to init log store\n");
return AMDUAT_PEL_CLI_EXIT_CONFIG;
}
{
amduat_asl_store_error_t err = amduat_asl_log_read(
&log_store, log_name, from_offset, limit,
&entries, &entries_len, &next_offset, &end);
if (err != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "error: log read failed: %s\n",
amduat_pel_cli_store_error_str(err));
return amduat_pel_cli_map_store_error(err);
}
}
for (size_t idx = 0u; idx < entries_len; ++idx) {
uint64_t offset = from_offset + idx;
printf("offset=%" PRIu64 " kind=%u ref=", offset, entries[idx].kind);
if (!amduat_format_ref_write_text(stdout, entries[idx].payload_ref,
global->ref_format)) {
fprintf(stderr, "error: failed to format ref\n");
amduat_asl_log_entries_free(entries, entries_len);
return AMDUAT_PEL_CLI_EXIT_CODEC;
}
if (entries[idx].has_actor) {
printf(" actor=%.*s", (int)entries[idx].actor.len,
(const char *)entries[idx].actor.data);
}
fputc('\n', stdout);
}
printf("next=%" PRIu64 " end=%s\n", next_offset, end ? "true" : "false");
amduat_asl_log_entries_free(entries, entries_len);
return AMDUAT_PEL_CLI_EXIT_OK;
}
static int amduat_pel_cli_cmd_log(
int argc,
char **argv,
const amduat_pel_cli_global_opts_t *global) {
if (argc < 1) {
fprintf(stderr, "error: log requires a subcommand\n");
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
if (strcmp(argv[0], "append") == 0) {
return amduat_pel_cli_cmd_log_append(argc - 1, argv + 1, global);
}
if (strcmp(argv[0], "read") == 0) {
return amduat_pel_cli_cmd_log_read(argc - 1, argv + 1, global);
}
fprintf(stderr, "error: unknown log subcommand: %s\n", argv[0]);
return AMDUAT_PEL_CLI_EXIT_USAGE;
}
static int amduat_pel_cli_cmd_help(int argc, char **argv) {
(void)argc;
(void)argv;
@ -3097,9 +3546,15 @@ int main(int argc, char **argv) {
if (strcmp(command, "op") == 0) {
return amduat_pel_cli_cmd_op(argc - i, argv + i, &global);
}
if (strcmp(command, "log") == 0) {
return amduat_pel_cli_cmd_log(argc - i, argv + i, &global);
}
if (strcmp(command, "matcache") == 0) {
return amduat_pel_cli_cmd_matcache(argc - i, argv + i, &global);
}
if (strcmp(command, "ptr") == 0) {
return amduat_pel_cli_cmd_ptr(argc - i, argv + i, &global);
}
if (strcmp(command, "scheme") == 0) {
return amduat_pel_cli_cmd_scheme(argc - i, argv + i, &global);
}