Add CAS-native append-only log
This commit is contained in:
parent
c4571c3bfb
commit
e2d26e53cd
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
58
include/amduat/asl/asl_pointer_fs.h
Normal file
58
include/amduat/asl/asl_pointer_fs.h
Normal 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 */
|
||||
62
include/amduat/asl/log_store.h
Normal file
62
include/amduat/asl/log_store.h
Normal 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 */
|
||||
726
src/adapters/asl_pointer_fs/asl_pointer_fs.c
Normal file
726
src/adapters/asl_pointer_fs/asl_pointer_fs.c
Normal 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, ¤t_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 ? ¤t_ref : NULL,
|
||||
expected_exists);
|
||||
if (err == AMDUAT_ASL_POINTER_OK) {
|
||||
*out_swapped = true;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
amduat_reference_free(¤t_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
905
src/core/asl_log_store.c
Normal 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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_ref);
|
||||
}
|
||||
current_ref = next_ref;
|
||||
current_ref_owned = true;
|
||||
}
|
||||
}
|
||||
if (current_ref_owned) {
|
||||
amduat_reference_free(¤t_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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue