2025-12-20 21:03:31 +01:00
|
|
|
#include "amduat/asl/artifact_io.h"
|
2025-12-20 08:40:57 +01:00
|
|
|
#include "amduat/asl/asl_store_fs.h"
|
|
|
|
|
#include "amduat/asl/asl_store_fs_meta.h"
|
2025-12-20 11:19:08 +01:00
|
|
|
#include "amduat/asl/io.h"
|
2025-12-20 11:16:16 +01:00
|
|
|
#include "amduat/asl/parse.h"
|
2025-12-20 21:03:31 +01:00
|
|
|
#include "amduat/asl/ref_io.h"
|
2025-12-20 11:14:06 +01:00
|
|
|
#include "amduat/asl/ref_text.h"
|
2026-01-17 14:26:17 +01:00
|
|
|
#include "amduat/asl/asl_store_index_fs.h"
|
2025-12-20 08:40:57 +01:00
|
|
|
#include "amduat/asl/store.h"
|
2026-01-17 14:26:17 +01:00
|
|
|
#include "amduat/enc/asl1_core.h"
|
2025-12-20 08:40:57 +01:00
|
|
|
#include "amduat/enc/asl1_core_codec.h"
|
2026-01-17 14:26:17 +01:00
|
|
|
#include "amduat/enc/asl_core_index.h"
|
|
|
|
|
#include "amduat/enc/asl_log.h"
|
2025-12-20 21:03:31 +01:00
|
|
|
#include "amduat/format/parse.h"
|
|
|
|
|
#include "amduat/format/ref.h"
|
2026-01-17 14:26:17 +01:00
|
|
|
#include "amduat/hash/asl1.h"
|
|
|
|
|
#include <dirent.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <inttypes.h>
|
2025-12-20 08:40:57 +01:00
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2026-01-17 14:26:17 +01:00
|
|
|
#include <sys/stat.h>
|
2025-12-20 08:40:57 +01:00
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_OK = 0,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_USAGE = 2,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_IO = 3,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_NOT_FOUND = 4,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_STORE = 5,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_UNSUPPORTED = 6,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_CODEC = 7,
|
|
|
|
|
AMDUAT_ASL_CLI_EXIT_CONFIG = 8
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char *const AMDUAT_ASL_CLI_DEFAULT_ROOT = ".amduat-asl";
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *root;
|
|
|
|
|
const char *store_id;
|
|
|
|
|
const char *profile;
|
|
|
|
|
const char *hash;
|
2026-01-17 17:58:59 +01:00
|
|
|
uint16_t shard_count;
|
|
|
|
|
bool has_shard_count;
|
2025-12-20 08:40:57 +01:00
|
|
|
bool force;
|
2025-12-20 10:53:22 +01:00
|
|
|
bool quiet;
|
2025-12-20 08:40:57 +01:00
|
|
|
} amduat_asl_cli_init_opts_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *root;
|
|
|
|
|
const char *input_path;
|
|
|
|
|
const char *output_path;
|
2025-12-20 21:03:31 +01:00
|
|
|
amduat_asl_io_format_t input_format;
|
|
|
|
|
amduat_format_ref_format_t ref_format;
|
2025-12-20 08:40:57 +01:00
|
|
|
bool has_type_tag;
|
|
|
|
|
amduat_type_tag_t type_tag;
|
2025-12-20 10:53:22 +01:00
|
|
|
bool quiet;
|
2025-12-20 08:40:57 +01:00
|
|
|
} amduat_asl_cli_put_opts_t;
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
const char *root;
|
|
|
|
|
const char *ref;
|
|
|
|
|
const char *output_path;
|
2025-12-20 21:03:31 +01:00
|
|
|
amduat_asl_io_format_t output_format;
|
|
|
|
|
amduat_format_ref_format_t ref_format;
|
2025-12-20 08:40:57 +01:00
|
|
|
bool has_expect_type_tag;
|
|
|
|
|
amduat_type_tag_t expect_type_tag;
|
|
|
|
|
bool print_type_tag;
|
2025-12-20 10:53:22 +01:00
|
|
|
bool quiet;
|
2025-12-20 08:40:57 +01:00
|
|
|
} amduat_asl_cli_get_opts_t;
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
typedef struct {
|
|
|
|
|
amduat_asl_store_fs_config_t cfg;
|
|
|
|
|
amduat_asl_store_fs_t fs;
|
|
|
|
|
amduat_asl_store_index_fs_t index_fs;
|
|
|
|
|
amduat_asl_store_t store;
|
|
|
|
|
bool is_index;
|
|
|
|
|
} amduat_asl_cli_store_ctx_t;
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_is_index_store_root(const char *root_path);
|
|
|
|
|
|
2025-12-20 08:40:57 +01:00
|
|
|
static void amduat_asl_cli_print_usage(FILE *stream) {
|
|
|
|
|
fprintf(stream,
|
|
|
|
|
"usage:\n"
|
|
|
|
|
" amduat-asl init [--root PATH] [--store-id ID]\n"
|
|
|
|
|
" [--profile PROFILE_ID|name]\n"
|
2025-12-20 10:53:22 +01:00
|
|
|
" [--hash HASH_ID|name] [--force] [--quiet]\n"
|
2025-12-20 08:40:57 +01:00
|
|
|
" amduat-asl put [--root PATH] [--type-tag TAG]\n"
|
|
|
|
|
" [--input PATH|-] [--input-format raw|artifact]\n"
|
|
|
|
|
" [--ref-format hex|bytes] [--output PATH|-]\n"
|
2025-12-20 10:53:22 +01:00
|
|
|
" [--quiet]\n"
|
2025-12-20 08:40:57 +01:00
|
|
|
" amduat-asl get --ref REF [--root PATH]\n"
|
|
|
|
|
" [--ref-format hex|bytes] [--output PATH|-]\n"
|
|
|
|
|
" [--output-format raw|artifact]\n"
|
|
|
|
|
" [--expect-type-tag TAG] [--print-type-tag]\n"
|
2025-12-20 10:53:22 +01:00
|
|
|
" [--quiet]\n"
|
2026-01-17 14:26:17 +01:00
|
|
|
" amduat-asl log inspect [--root PATH]\n"
|
2026-01-17 16:43:47 +01:00
|
|
|
" amduat-asl index init [--root PATH] [--store-id ID]\n"
|
|
|
|
|
" [--profile PROFILE_ID|name]\n"
|
2026-01-17 17:58:59 +01:00
|
|
|
" [--hash HASH_ID|name] [--shards N]\n"
|
|
|
|
|
" [--force] [--quiet]\n"
|
2026-01-17 14:26:17 +01:00
|
|
|
" amduat-asl index state [--root PATH]\n"
|
|
|
|
|
" amduat-asl segment verify [--root PATH] [--segment ID]\n"
|
2025-12-20 08:40:57 +01:00
|
|
|
"\n"
|
|
|
|
|
"defaults:\n"
|
|
|
|
|
" --root %s\n"
|
|
|
|
|
" --input -\n"
|
|
|
|
|
" --output -\n"
|
|
|
|
|
" --input-format raw\n"
|
|
|
|
|
" --output-format raw\n"
|
|
|
|
|
" --ref-format hex\n"
|
|
|
|
|
"\n"
|
|
|
|
|
"notes:\n"
|
|
|
|
|
" --ref-format bytes expects --ref as a path or -\n",
|
|
|
|
|
AMDUAT_ASL_CLI_DEFAULT_ROOT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void amduat_asl_cli_free_reference(amduat_reference_t *ref) {
|
|
|
|
|
if (ref == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
free((void *)ref->digest.data);
|
|
|
|
|
ref->digest.data = NULL;
|
|
|
|
|
ref->digest.len = 0u;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void amduat_asl_cli_print_store_meta(
|
|
|
|
|
const amduat_asl_store_fs_config_t *cfg) {
|
|
|
|
|
if (cfg == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "store_id=%s\n", cfg->store_id);
|
|
|
|
|
fprintf(stderr, "profile=0x%04x\n",
|
|
|
|
|
(unsigned int)cfg->config.encoding_profile_id);
|
|
|
|
|
fprintf(stderr, "hash=0x%04x\n", (unsigned int)cfg->config.hash_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const char *amduat_asl_cli_store_error_str(
|
|
|
|
|
amduat_asl_store_error_t err) {
|
|
|
|
|
switch (err) {
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_NOT_FOUND:
|
|
|
|
|
return "not found";
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_UNSUPPORTED:
|
|
|
|
|
return "unsupported";
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_INTEGRITY:
|
|
|
|
|
return "integrity";
|
2025-12-21 22:59:31 +01:00
|
|
|
case AMDUAT_ASL_STORE_ERR_IO:
|
|
|
|
|
return "io";
|
2025-12-20 08:40:57 +01:00
|
|
|
case AMDUAT_ASL_STORE_OK:
|
|
|
|
|
return "ok";
|
|
|
|
|
default:
|
|
|
|
|
return "unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_map_store_error(amduat_asl_store_error_t err) {
|
|
|
|
|
switch (err) {
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_NOT_FOUND:
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_NOT_FOUND;
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_UNSUPPORTED:
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_UNSUPPORTED;
|
|
|
|
|
case AMDUAT_ASL_STORE_ERR_INTEGRITY:
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_STORE;
|
2025-12-21 22:59:31 +01:00
|
|
|
case AMDUAT_ASL_STORE_ERR_IO:
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
2025-12-20 08:40:57 +01:00
|
|
|
case AMDUAT_ASL_STORE_OK:
|
|
|
|
|
default:
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_STORE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static bool amduat_asl_cli_join_path(const char *base,
|
|
|
|
|
const char *suffix,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
size_t base_len;
|
|
|
|
|
size_t suffix_len;
|
|
|
|
|
bool needs_sep;
|
|
|
|
|
size_t total_len;
|
|
|
|
|
char *buffer;
|
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
|
|
if (base == NULL || suffix == NULL || out_path == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (base[0] == '\0' || suffix[0] == '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
base_len = strlen(base);
|
|
|
|
|
suffix_len = strlen(suffix);
|
|
|
|
|
needs_sep = base[base_len - 1u] != '/';
|
|
|
|
|
total_len = base_len + (needs_sep ? 1u : 0u) + suffix_len + 1u;
|
|
|
|
|
|
|
|
|
|
buffer = (char *)malloc(total_len);
|
|
|
|
|
if (buffer == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
offset = 0u;
|
|
|
|
|
memcpy(buffer + offset, base, base_len);
|
|
|
|
|
offset += base_len;
|
|
|
|
|
if (needs_sep) {
|
|
|
|
|
buffer[offset++] = '/';
|
|
|
|
|
}
|
|
|
|
|
memcpy(buffer + offset, suffix, suffix_len);
|
|
|
|
|
offset += suffix_len;
|
|
|
|
|
buffer[offset] = '\0';
|
|
|
|
|
|
|
|
|
|
*out_path = buffer;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
static bool amduat_asl_cli_ensure_dir(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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static bool amduat_asl_cli_build_index_path(const char *root_path,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
return amduat_asl_cli_join_path(root_path, "index", out_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_build_log_path(const char *root_path,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
char *index_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root_path, &index_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_cli_join_path(index_path, "log.asl", out_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_build_segments_path(const char *root_path,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
char *index_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root_path, &index_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_cli_join_path(index_path, "segments", out_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
static bool amduat_asl_cli_build_blocks_path(const char *root_path,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
char *index_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root_path, &index_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_cli_join_path(index_path, "blocks", out_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 17:58:59 +01:00
|
|
|
static bool amduat_asl_cli_build_index_config_path(const char *root_path,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
char *index_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root_path, &index_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_cli_join_path(index_path, "config", out_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static bool amduat_asl_cli_build_segment_path(const char *root_path,
|
|
|
|
|
uint64_t segment_id,
|
|
|
|
|
char **out_path) {
|
|
|
|
|
char name[32];
|
|
|
|
|
char *segments_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_segments_path(root_path, &segments_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
snprintf(name, sizeof(name), "segment-%016" PRIx64 ".asl", segment_id);
|
|
|
|
|
ok = amduat_asl_cli_join_path(segments_path, name, out_path);
|
|
|
|
|
free(segments_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_path_is_dir(const char *path) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
if (path == NULL || path[0] == '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (stat(path, &st) != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return S_ISDIR(st.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_path_is_file(const char *path) {
|
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
|
|
if (path == NULL || path[0] == '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (stat(path, &st) != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return S_ISREG(st.st_mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_is_index_store_root(const char *root_path) {
|
|
|
|
|
char *index_path;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root_path, &index_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_cli_path_is_dir(index_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 17:58:59 +01:00
|
|
|
static bool amduat_asl_cli_parse_u16(const char *value, uint16_t *out) {
|
|
|
|
|
char *endptr;
|
|
|
|
|
unsigned long parsed;
|
|
|
|
|
|
|
|
|
|
if (value == NULL || out == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
parsed = strtoul(value, &endptr, 10);
|
|
|
|
|
if (endptr == value || *endptr != '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (parsed == 0 || parsed > UINT16_MAX) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out = (uint16_t)parsed;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_read_index_shards(const char *root_path,
|
|
|
|
|
uint16_t *out_shards) {
|
|
|
|
|
char *config_path;
|
|
|
|
|
uint8_t *bytes;
|
|
|
|
|
size_t len;
|
|
|
|
|
char *text;
|
|
|
|
|
char *token;
|
|
|
|
|
bool ok;
|
|
|
|
|
uint16_t shards;
|
|
|
|
|
|
|
|
|
|
if (out_shards == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_shards = 1u;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_index_config_path(root_path, &config_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_path_is_file(config_path)) {
|
|
|
|
|
free(config_path);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bytes = NULL;
|
|
|
|
|
len = 0u;
|
|
|
|
|
if (!amduat_asl_read_path(config_path, &bytes, &len)) {
|
|
|
|
|
free(config_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
free(config_path);
|
|
|
|
|
|
|
|
|
|
text = (char *)malloc(len + 1u);
|
|
|
|
|
if (text == NULL) {
|
|
|
|
|
free(bytes);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
memcpy(text, bytes, len);
|
|
|
|
|
text[len] = '\0';
|
|
|
|
|
free(bytes);
|
|
|
|
|
|
|
|
|
|
token = strtok(text, " \t\r\n");
|
|
|
|
|
if (token == NULL || strcmp(token, "amduat-asl-index-v1") != 0) {
|
|
|
|
|
free(text);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
|
shards = 1u;
|
|
|
|
|
while ((token = strtok(NULL, " \t\r\n")) != NULL) {
|
|
|
|
|
if (strncmp(token, "shards=", 7u) == 0) {
|
|
|
|
|
if (!amduat_asl_cli_parse_u16(token + 7u, &shards)) {
|
|
|
|
|
free(text);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(text);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_shards = shards;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_write_index_config(const char *root_path,
|
|
|
|
|
uint16_t shard_count,
|
|
|
|
|
bool force) {
|
|
|
|
|
char *config_path;
|
|
|
|
|
char buffer[128];
|
|
|
|
|
int len;
|
|
|
|
|
uint16_t existing_shards;
|
|
|
|
|
|
|
|
|
|
if (root_path == NULL || shard_count == 0u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_build_index_config_path(root_path, &config_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!force && amduat_asl_cli_path_is_file(config_path)) {
|
|
|
|
|
if (!amduat_asl_cli_read_index_shards(root_path, &existing_shards)) {
|
|
|
|
|
free(config_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
free(config_path);
|
|
|
|
|
return existing_shards == shard_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = snprintf(buffer,
|
|
|
|
|
sizeof(buffer),
|
|
|
|
|
"amduat-asl-index-v1\nshards=%" PRIu16 "\n",
|
|
|
|
|
shard_count);
|
|
|
|
|
if (len <= 0 || (size_t)len >= sizeof(buffer)) {
|
|
|
|
|
free(config_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_write_path(config_path, (const uint8_t *)buffer,
|
|
|
|
|
(size_t)len)) {
|
|
|
|
|
free(config_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
free(config_path);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
static bool amduat_asl_cli_open_store(const char *root_path,
|
|
|
|
|
amduat_asl_cli_store_ctx_t *out_ctx) {
|
|
|
|
|
bool use_index;
|
2026-01-17 17:58:59 +01:00
|
|
|
uint16_t shard_count;
|
2026-01-17 16:43:47 +01:00
|
|
|
|
|
|
|
|
if (root_path == NULL || out_ctx == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
memset(out_ctx, 0, sizeof(*out_ctx));
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_store_fs_load_config(root_path, &out_ctx->cfg)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use_index = amduat_asl_cli_is_index_store_root(root_path);
|
|
|
|
|
out_ctx->is_index = use_index;
|
|
|
|
|
if (use_index) {
|
2026-01-17 17:58:59 +01:00
|
|
|
if (!amduat_asl_cli_read_index_shards(root_path, &shard_count)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-01-17 16:43:47 +01:00
|
|
|
if (!amduat_asl_store_index_fs_init(&out_ctx->index_fs,
|
|
|
|
|
out_ctx->cfg.config,
|
|
|
|
|
root_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-01-17 17:58:59 +01:00
|
|
|
amduat_asl_store_index_fs_set_shard_count(&out_ctx->index_fs, shard_count);
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_store_init(&out_ctx->store,
|
|
|
|
|
out_ctx->cfg.config,
|
|
|
|
|
amduat_asl_store_index_fs_ops(),
|
|
|
|
|
&out_ctx->index_fs);
|
|
|
|
|
} else {
|
|
|
|
|
if (!amduat_asl_store_fs_init(&out_ctx->fs,
|
|
|
|
|
out_ctx->cfg.config,
|
|
|
|
|
root_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
amduat_asl_store_init(&out_ctx->store,
|
|
|
|
|
out_ctx->cfg.config,
|
|
|
|
|
amduat_asl_store_fs_ops(),
|
|
|
|
|
&out_ctx->fs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static bool amduat_asl_cli_read_u64_le(const uint8_t *bytes,
|
|
|
|
|
size_t len,
|
|
|
|
|
uint64_t *out_value) {
|
|
|
|
|
uint64_t value;
|
|
|
|
|
|
|
|
|
|
if (bytes == NULL || out_value == NULL || len < 8u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
value = 0u;
|
|
|
|
|
value |= (uint64_t)bytes[0];
|
|
|
|
|
value |= (uint64_t)bytes[1] << 8;
|
|
|
|
|
value |= (uint64_t)bytes[2] << 16;
|
|
|
|
|
value |= (uint64_t)bytes[3] << 24;
|
|
|
|
|
value |= (uint64_t)bytes[4] << 32;
|
|
|
|
|
value |= (uint64_t)bytes[5] << 40;
|
|
|
|
|
value |= (uint64_t)bytes[6] << 48;
|
|
|
|
|
value |= (uint64_t)bytes[7] << 56;
|
|
|
|
|
*out_value = value;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_parse_segment_id(const char *value,
|
|
|
|
|
uint64_t *out_segment_id) {
|
|
|
|
|
char *end;
|
|
|
|
|
unsigned long long parsed;
|
|
|
|
|
|
|
|
|
|
if (value == NULL || out_segment_id == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
parsed = strtoull(value, &end, 0);
|
|
|
|
|
if (errno != 0 || end == value || *end != '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_segment_id = (uint64_t)parsed;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_parse_segment_name(const char *name,
|
|
|
|
|
uint64_t *out_segment_id) {
|
|
|
|
|
const char *prefix = "segment-";
|
|
|
|
|
const char *suffix = ".asl";
|
|
|
|
|
size_t name_len;
|
|
|
|
|
const char *hex_start;
|
|
|
|
|
size_t hex_len;
|
|
|
|
|
char hex_buf[17];
|
|
|
|
|
char *end;
|
|
|
|
|
unsigned long long parsed;
|
|
|
|
|
|
|
|
|
|
if (name == NULL || out_segment_id == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name_len = strlen(name);
|
|
|
|
|
if (name_len != 28u) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (strncmp(name, prefix, 8) != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(name + name_len - 4u, suffix) != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hex_start = name + 8;
|
|
|
|
|
hex_len = 16u;
|
|
|
|
|
memcpy(hex_buf, hex_start, hex_len);
|
|
|
|
|
hex_buf[hex_len] = '\0';
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
parsed = strtoull(hex_buf, &end, 16);
|
|
|
|
|
if (errno != 0 || end == hex_buf || *end != '\0') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_segment_id = (uint64_t)parsed;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_segment_id_cmp(const void *lhs, const void *rhs) {
|
|
|
|
|
const uint64_t *a = (const uint64_t *)lhs;
|
|
|
|
|
const uint64_t *b = (const uint64_t *)rhs;
|
|
|
|
|
|
|
|
|
|
if (*a < *b) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (*a > *b) {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
static bool amduat_asl_cli_write_empty_log(const char *root_path) {
|
|
|
|
|
char *log_path;
|
|
|
|
|
amduat_octets_t log_bytes;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_log_path(root_path, &log_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (amduat_asl_cli_path_is_file(log_path)) {
|
|
|
|
|
free(log_path);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_bytes.data = NULL;
|
|
|
|
|
log_bytes.len = 0u;
|
|
|
|
|
ok = amduat_enc_asl_log_encode_v1(NULL, 0u, &log_bytes);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
free(log_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ok = amduat_asl_write_path(log_path, log_bytes.data, log_bytes.len);
|
|
|
|
|
amduat_octets_free(&log_bytes);
|
|
|
|
|
free(log_path);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static bool amduat_asl_cli_collect_segment_ids(const char *root_path,
|
|
|
|
|
uint64_t **out_ids,
|
|
|
|
|
size_t *out_len) {
|
|
|
|
|
char *segments_path;
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *entry;
|
|
|
|
|
uint64_t *ids;
|
|
|
|
|
size_t ids_len;
|
|
|
|
|
size_t ids_cap;
|
|
|
|
|
|
|
|
|
|
if (out_ids == NULL || out_len == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_ids = NULL;
|
|
|
|
|
*out_len = 0u;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_segments_path(root_path, &segments_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_path_is_dir(segments_path)) {
|
|
|
|
|
free(segments_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir = opendir(segments_path);
|
|
|
|
|
free(segments_path);
|
|
|
|
|
if (dir == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ids = NULL;
|
|
|
|
|
ids_len = 0u;
|
|
|
|
|
ids_cap = 0u;
|
|
|
|
|
|
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
|
|
|
uint64_t segment_id;
|
|
|
|
|
uint64_t *next;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_parse_segment_name(entry->d_name, &segment_id)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (ids_len == ids_cap) {
|
|
|
|
|
size_t next_cap = ids_cap == 0u ? 8u : ids_cap * 2u;
|
|
|
|
|
next = (uint64_t *)realloc(ids, next_cap * sizeof(*next));
|
|
|
|
|
if (next == NULL) {
|
|
|
|
|
free(ids);
|
|
|
|
|
closedir(dir);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
ids = next;
|
|
|
|
|
ids_cap = next_cap;
|
|
|
|
|
}
|
|
|
|
|
ids[ids_len++] = segment_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
if (ids_len != 0u) {
|
|
|
|
|
qsort(ids, ids_len, sizeof(*ids), amduat_asl_cli_segment_id_cmp);
|
|
|
|
|
}
|
|
|
|
|
*out_ids = ids;
|
|
|
|
|
*out_len = ids_len;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool amduat_asl_cli_load_log_records(
|
|
|
|
|
const char *root_path,
|
|
|
|
|
amduat_asl_log_record_t **out_records,
|
|
|
|
|
size_t *out_count) {
|
|
|
|
|
char *log_path;
|
|
|
|
|
uint8_t *log_bytes;
|
|
|
|
|
size_t log_len;
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
|
|
if (out_records == NULL || out_count == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
*out_records = NULL;
|
|
|
|
|
*out_count = 0u;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_log_path(root_path, &log_path)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_path_is_file(log_path)) {
|
|
|
|
|
free(log_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_bytes = NULL;
|
|
|
|
|
log_len = 0u;
|
|
|
|
|
if (!amduat_asl_read_path(log_path, &log_bytes, &log_len)) {
|
|
|
|
|
free(log_path);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
free(log_path);
|
|
|
|
|
|
|
|
|
|
ok = amduat_enc_asl_log_decode_v1(amduat_octets(log_bytes, log_len),
|
|
|
|
|
out_records, out_count);
|
|
|
|
|
free(log_bytes);
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_log_find_segment_hash(
|
|
|
|
|
const amduat_asl_log_record_t *records,
|
|
|
|
|
size_t record_count,
|
|
|
|
|
uint64_t segment_id,
|
|
|
|
|
uint8_t out_hash[32]) {
|
|
|
|
|
size_t i;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
if (out_hash == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
memset(out_hash, 0, 32u);
|
|
|
|
|
|
|
|
|
|
if (records == NULL || record_count == 0u) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = 0;
|
|
|
|
|
for (i = 0; i < record_count; ++i) {
|
|
|
|
|
const amduat_asl_log_record_t *record = &records[i];
|
|
|
|
|
uint64_t payload_segment_id;
|
|
|
|
|
if (record->record_type != AMDUAT_ASL_LOG_RECORD_SEGMENT_SEAL) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (record->payload.len < 40u) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_read_u64_le(record->payload.data,
|
|
|
|
|
record->payload.len,
|
|
|
|
|
&payload_segment_id)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (payload_segment_id != segment_id) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
memcpy(out_hash, record->payload.data + 8, 32u);
|
|
|
|
|
status = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 08:40:57 +01:00
|
|
|
static int amduat_asl_cli_cmd_init(int argc, char **argv) {
|
|
|
|
|
amduat_asl_cli_init_opts_t opts;
|
|
|
|
|
amduat_asl_store_fs_config_t cfg_in;
|
|
|
|
|
amduat_asl_store_fs_config_t cfg_out;
|
|
|
|
|
bool ok;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--store-id") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --store-id requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.store_id = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--profile") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --profile requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.profile = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--hash") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --hash requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.hash = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--force") == 0) {
|
|
|
|
|
opts.force = true;
|
2025-12-20 10:53:22 +01:00
|
|
|
} else if (strcmp(argv[i], "--quiet") == 0) {
|
|
|
|
|
opts.quiet = true;
|
2025-12-20 08:40:57 +01:00
|
|
|
} else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&cfg_in, 0, sizeof(cfg_in));
|
|
|
|
|
if (opts.store_id != NULL) {
|
2025-12-20 11:21:07 +01:00
|
|
|
if (!amduat_asl_store_fs_meta_copy_store_id(cfg_in.store_id,
|
|
|
|
|
opts.store_id)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid store-id\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (opts.profile != NULL) {
|
2025-12-20 11:16:16 +01:00
|
|
|
if (!amduat_asl_parse_profile_id(
|
2025-12-20 08:40:57 +01:00
|
|
|
opts.profile,
|
|
|
|
|
&cfg_in.config.encoding_profile_id)) {
|
|
|
|
|
fprintf(stderr, "error: unknown profile: %s\n", opts.profile);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (opts.hash != NULL) {
|
2025-12-20 11:16:16 +01:00
|
|
|
if (!amduat_asl_parse_hash_id(opts.hash, &cfg_in.config.hash_id)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: unknown hash: %s\n", opts.hash);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok = amduat_asl_store_fs_init_root(opts.root, &cfg_in, &cfg_out);
|
|
|
|
|
if (!ok && opts.force) {
|
|
|
|
|
ok = amduat_asl_store_fs_init_root(opts.root, NULL, &cfg_out);
|
2025-12-20 10:53:22 +01:00
|
|
|
if (ok && (opts.store_id || opts.profile || opts.hash) && !opts.quiet) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
|
"warning: existing config kept; overrides ignored\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: failed to initialize store root: %s\n",
|
|
|
|
|
opts.root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 10:53:22 +01:00
|
|
|
if (!opts.quiet) {
|
|
|
|
|
fprintf(stderr, "root=%s\n", opts.root);
|
|
|
|
|
amduat_asl_cli_print_store_meta(&cfg_out);
|
|
|
|
|
}
|
2025-12-20 08:40:57 +01:00
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_cmd_put(int argc, char **argv) {
|
|
|
|
|
amduat_asl_cli_put_opts_t opts;
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_cli_store_ctx_t store_ctx;
|
2025-12-20 08:40:57 +01:00
|
|
|
amduat_artifact_t artifact;
|
|
|
|
|
amduat_reference_t ref;
|
|
|
|
|
uint8_t *input_bytes;
|
|
|
|
|
size_t input_len;
|
|
|
|
|
amduat_octets_t encoded_ref;
|
|
|
|
|
bool ok;
|
|
|
|
|
int i;
|
|
|
|
|
int exit_code;
|
|
|
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
opts.input_path = "-";
|
|
|
|
|
opts.output_path = "-";
|
2025-12-20 21:03:31 +01:00
|
|
|
opts.input_format = AMDUAT_ASL_IO_RAW;
|
|
|
|
|
opts.ref_format = AMDUAT_FORMAT_REF_HEX;
|
2025-12-20 08:40:57 +01:00
|
|
|
|
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--type-tag") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --type-tag requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 11:16:16 +01:00
|
|
|
if (!amduat_asl_parse_type_tag(argv[++i], &opts.type_tag)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid type-tag\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.has_type_tag = true;
|
|
|
|
|
} else if (strcmp(argv[i], "--input") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --input requires a path or -\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.input_path = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--input-format") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --input-format requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 21:03:31 +01:00
|
|
|
if (!amduat_asl_io_format_parse(argv[++i], &opts.input_format)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid input-format\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(argv[i], "--ref-format") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --ref-format requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 21:03:31 +01:00
|
|
|
if (!amduat_format_ref_parse(argv[++i], &opts.ref_format)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid ref-format\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(argv[i], "--output") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --output requires a path or -\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.output_path = argv[++i];
|
2025-12-20 10:53:22 +01:00
|
|
|
} else if (strcmp(argv[i], "--quiet") == 0) {
|
|
|
|
|
opts.quiet = true;
|
2025-12-20 08:40:57 +01:00
|
|
|
} else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 21:03:31 +01:00
|
|
|
if (opts.input_format == AMDUAT_ASL_IO_ARTIFACT && opts.has_type_tag) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr,
|
|
|
|
|
"error: --type-tag is not valid with --input-format artifact\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
if (!amduat_asl_cli_open_store(opts.root, &store_ctx)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to load store config: %s\n", opts.root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
input_bytes = NULL;
|
|
|
|
|
input_len = 0u;
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_read_path(opts.input_path, &input_bytes, &input_len)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to read input: %s\n", opts.input_path);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 21:03:31 +01:00
|
|
|
ok = amduat_asl_artifact_from_bytes(amduat_octets(input_bytes, input_len),
|
|
|
|
|
opts.input_format,
|
|
|
|
|
opts.has_type_tag,
|
|
|
|
|
opts.type_tag,
|
|
|
|
|
&artifact);
|
|
|
|
|
if (opts.input_format == AMDUAT_ASL_IO_ARTIFACT) {
|
2025-12-20 08:40:57 +01:00
|
|
|
free(input_bytes);
|
2025-12-20 21:03:31 +01:00
|
|
|
}
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: invalid artifact encoding\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CODEC;
|
2025-12-20 08:40:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&ref, 0, sizeof(ref));
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
{
|
|
|
|
|
amduat_asl_store_error_t err =
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_store_put(&store_ctx.store, artifact, &ref);
|
2025-12-20 08:40:57 +01:00
|
|
|
if (err != AMDUAT_ASL_STORE_OK) {
|
|
|
|
|
fprintf(stderr, "error: store put failed: %s\n",
|
|
|
|
|
amduat_asl_cli_store_error_str(err));
|
|
|
|
|
exit_code = amduat_asl_cli_map_store_error(err);
|
|
|
|
|
} else {
|
2025-12-20 21:03:31 +01:00
|
|
|
if (opts.ref_format == AMDUAT_FORMAT_REF_HEX) {
|
2025-12-20 08:40:57 +01:00
|
|
|
char *hex_ref = NULL;
|
2025-12-20 11:14:06 +01:00
|
|
|
if (!amduat_asl_ref_encode_hex(ref, &hex_ref)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to encode reference\n");
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
} else {
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_write_text_line(opts.output_path, hex_ref)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to write output: %s\n",
|
|
|
|
|
opts.output_path);
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
free(hex_ref);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!amduat_enc_asl1_core_encode_reference_v1(ref, &encoded_ref)) {
|
|
|
|
|
fprintf(stderr, "error: failed to encode reference\n");
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
} else {
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_write_path(opts.output_path,
|
2025-12-20 08:40:57 +01:00
|
|
|
encoded_ref.data,
|
|
|
|
|
encoded_ref.len)) {
|
|
|
|
|
fprintf(stderr, "error: failed to write output: %s\n",
|
|
|
|
|
opts.output_path);
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
free((void *)encoded_ref.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 10:53:22 +01:00
|
|
|
if (exit_code == AMDUAT_ASL_CLI_EXIT_OK && !opts.quiet) {
|
2025-12-20 08:40:57 +01:00
|
|
|
char *hex_ref = NULL;
|
|
|
|
|
fprintf(stderr, "root=%s\n", opts.root);
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_cli_print_store_meta(&store_ctx.cfg);
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "bytes=%zu\n", artifact.bytes.len);
|
|
|
|
|
if (artifact.has_type_tag) {
|
|
|
|
|
fprintf(stderr, "type_tag=0x%08x\n",
|
|
|
|
|
(unsigned int)artifact.type_tag.tag_id);
|
|
|
|
|
}
|
2025-12-20 11:14:06 +01:00
|
|
|
if (amduat_asl_ref_encode_hex(ref, &hex_ref)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "ref=%s\n", hex_ref);
|
|
|
|
|
free(hex_ref);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 21:03:31 +01:00
|
|
|
amduat_asl_artifact_free(&artifact);
|
2025-12-20 08:40:57 +01:00
|
|
|
amduat_asl_cli_free_reference(&ref);
|
|
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_cmd_get(int argc, char **argv) {
|
|
|
|
|
amduat_asl_cli_get_opts_t opts;
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_cli_store_ctx_t store_ctx;
|
2025-12-20 08:40:57 +01:00
|
|
|
amduat_artifact_t artifact;
|
|
|
|
|
amduat_reference_t ref;
|
|
|
|
|
uint8_t *ref_bytes;
|
|
|
|
|
size_t ref_len;
|
|
|
|
|
amduat_octets_t encoded_artifact;
|
|
|
|
|
bool ok;
|
|
|
|
|
int i;
|
|
|
|
|
int exit_code;
|
|
|
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
opts.output_path = "-";
|
2025-12-20 21:03:31 +01:00
|
|
|
opts.output_format = AMDUAT_ASL_IO_RAW;
|
|
|
|
|
opts.ref_format = AMDUAT_FORMAT_REF_HEX;
|
2025-12-20 08:40:57 +01:00
|
|
|
|
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--ref") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --ref requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.ref = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--ref-format") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --ref-format requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 21:03:31 +01:00
|
|
|
if (!amduat_format_ref_parse(argv[++i], &opts.ref_format)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid ref-format\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(argv[i], "--output") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --output requires a path or -\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.output_path = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--output-format") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --output-format requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 21:03:31 +01:00
|
|
|
if (!amduat_asl_io_format_parse(argv[++i], &opts.output_format)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid output-format\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(argv[i], "--expect-type-tag") == 0) {
|
|
|
|
|
if (i + 1 >= argc) {
|
|
|
|
|
fprintf(stderr, "error: --expect-type-tag requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2025-12-20 11:16:16 +01:00
|
|
|
if (!amduat_asl_parse_type_tag(argv[++i],
|
2025-12-20 08:40:57 +01:00
|
|
|
&opts.expect_type_tag)) {
|
|
|
|
|
fprintf(stderr, "error: invalid expect-type-tag\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.has_expect_type_tag = true;
|
|
|
|
|
} else if (strcmp(argv[i], "--print-type-tag") == 0) {
|
|
|
|
|
opts.print_type_tag = true;
|
2025-12-20 10:53:22 +01:00
|
|
|
} else if (strcmp(argv[i], "--quiet") == 0) {
|
|
|
|
|
opts.quiet = true;
|
2025-12-20 08:40:57 +01:00
|
|
|
} else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opts.ref == NULL) {
|
|
|
|
|
fprintf(stderr, "error: --ref is required\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
if (!amduat_asl_cli_open_store(opts.root, &store_ctx)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to load store config: %s\n", opts.root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&ref, 0, sizeof(ref));
|
2025-12-20 21:03:31 +01:00
|
|
|
if (opts.ref_format == AMDUAT_FORMAT_REF_HEX) {
|
|
|
|
|
ok = amduat_asl_ref_decode_format(
|
|
|
|
|
opts.ref_format, amduat_octets(opts.ref, strlen(opts.ref)), &ref);
|
|
|
|
|
if (!ok) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: invalid hex reference\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ref_bytes = NULL;
|
|
|
|
|
ref_len = 0u;
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_read_path(opts.ref, &ref_bytes, &ref_len)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: failed to read reference: %s\n", opts.ref);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
2025-12-20 21:03:31 +01:00
|
|
|
ok = amduat_asl_ref_decode_format(
|
|
|
|
|
opts.ref_format, amduat_octets(ref_bytes, ref_len), &ref);
|
2025-12-20 08:40:57 +01:00
|
|
|
free(ref_bytes);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: invalid reference bytes\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&artifact, 0, sizeof(artifact));
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
{
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_store_error_t err =
|
|
|
|
|
amduat_asl_store_get(&store_ctx.store, ref, &artifact);
|
2025-12-20 08:40:57 +01:00
|
|
|
if (err != AMDUAT_ASL_STORE_OK) {
|
|
|
|
|
fprintf(stderr, "error: store get failed: %s\n",
|
|
|
|
|
amduat_asl_cli_store_error_str(err));
|
|
|
|
|
exit_code = amduat_asl_cli_map_store_error(err);
|
|
|
|
|
} else {
|
|
|
|
|
if (opts.has_expect_type_tag) {
|
2025-12-20 21:03:31 +01:00
|
|
|
if (!amduat_asl_artifact_expect_type_tag(&artifact,
|
|
|
|
|
opts.expect_type_tag)) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "error: type-tag mismatch\n");
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (exit_code == AMDUAT_ASL_CLI_EXIT_OK) {
|
2025-12-20 21:03:31 +01:00
|
|
|
if (opts.output_format == AMDUAT_ASL_IO_RAW) {
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_write_path(opts.output_path,
|
2025-12-20 08:40:57 +01:00
|
|
|
artifact.bytes.data,
|
|
|
|
|
artifact.bytes.len)) {
|
|
|
|
|
fprintf(stderr, "error: failed to write output: %s\n",
|
|
|
|
|
opts.output_path);
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!amduat_enc_asl1_core_encode_artifact_v1(
|
|
|
|
|
artifact, &encoded_artifact)) {
|
|
|
|
|
fprintf(stderr, "error: failed to encode artifact\n");
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
} else {
|
2025-12-20 11:19:08 +01:00
|
|
|
if (!amduat_asl_write_path(opts.output_path,
|
2025-12-20 08:40:57 +01:00
|
|
|
encoded_artifact.data,
|
|
|
|
|
encoded_artifact.len)) {
|
|
|
|
|
fprintf(stderr, "error: failed to write output: %s\n",
|
|
|
|
|
opts.output_path);
|
|
|
|
|
exit_code = AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
free((void *)encoded_artifact.data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 10:53:22 +01:00
|
|
|
if (exit_code == AMDUAT_ASL_CLI_EXIT_OK && !opts.quiet) {
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "root=%s\n", opts.root);
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_cli_print_store_meta(&store_ctx.cfg);
|
2025-12-20 08:40:57 +01:00
|
|
|
fprintf(stderr, "bytes=%zu\n", artifact.bytes.len);
|
|
|
|
|
if (opts.print_type_tag) {
|
|
|
|
|
if (artifact.has_type_tag) {
|
|
|
|
|
fprintf(stderr, "type_tag=0x%08x\n",
|
|
|
|
|
(unsigned int)artifact.type_tag.tag_id);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "type_tag=none\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amduat_asl_cli_free_reference(&ref);
|
2025-12-20 21:03:31 +01:00
|
|
|
amduat_asl_artifact_free(&artifact);
|
2025-12-20 08:40:57 +01:00
|
|
|
return exit_code;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
static int amduat_asl_cli_cmd_log(int argc, char **argv) {
|
|
|
|
|
const char *root;
|
|
|
|
|
uint8_t *log_bytes;
|
|
|
|
|
size_t log_len;
|
|
|
|
|
amduat_asl_log_record_t *records;
|
|
|
|
|
size_t record_count;
|
|
|
|
|
char *log_path;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
|
fprintf(stderr, "error: log command requires a subcommand\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[0], "inspect") != 0) {
|
|
|
|
|
fprintf(stderr, "error: unknown log command: %s\n", argv[0]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
for (i = 1; i < (size_t)argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--help") == 0 ||
|
|
|
|
|
strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_is_index_store_root(root)) {
|
|
|
|
|
fprintf(stderr, "error: index/log store not found at: %s\n", root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_build_log_path(root, &log_path)) {
|
|
|
|
|
fprintf(stderr, "error: failed to build log path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_path_is_file(log_path)) {
|
|
|
|
|
fprintf(stderr, "error: log file not found: %s\n", log_path);
|
|
|
|
|
free(log_path);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_bytes = NULL;
|
|
|
|
|
log_len = 0u;
|
|
|
|
|
if (!amduat_asl_read_path(log_path, &log_bytes, &log_len)) {
|
|
|
|
|
fprintf(stderr, "error: failed to read log: %s\n", log_path);
|
|
|
|
|
free(log_path);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
free(log_path);
|
|
|
|
|
|
|
|
|
|
records = NULL;
|
|
|
|
|
record_count = 0u;
|
|
|
|
|
if (!amduat_enc_asl_log_decode_v1(amduat_octets(log_bytes, log_len),
|
|
|
|
|
&records, &record_count)) {
|
|
|
|
|
free(log_bytes);
|
|
|
|
|
fprintf(stderr, "error: invalid log encoding\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
}
|
|
|
|
|
free(log_bytes);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < record_count; ++i) {
|
|
|
|
|
const amduat_asl_log_record_t *record = &records[i];
|
|
|
|
|
printf("logseq=%" PRIu64 " type=0x%08x\n",
|
|
|
|
|
record->logseq,
|
|
|
|
|
(unsigned int)record->record_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amduat_enc_asl_log_free(records, record_count);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_cmd_index(int argc, char **argv) {
|
|
|
|
|
const char *root;
|
|
|
|
|
amduat_asl_store_fs_config_t cfg;
|
2026-01-17 16:43:47 +01:00
|
|
|
amduat_asl_cli_init_opts_t opts;
|
2026-01-17 14:26:17 +01:00
|
|
|
amduat_asl_store_index_fs_t index_fs;
|
|
|
|
|
amduat_asl_store_t store;
|
|
|
|
|
amduat_asl_index_state_t state;
|
|
|
|
|
size_t i;
|
2026-01-17 16:43:47 +01:00
|
|
|
bool ok;
|
2026-01-17 14:26:17 +01:00
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
|
fprintf(stderr, "error: index command requires a subcommand\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
2026-01-17 16:43:47 +01:00
|
|
|
if (strcmp(argv[0], "init") != 0 && strcmp(argv[0], "state") != 0) {
|
2026-01-17 14:26:17 +01:00
|
|
|
fprintf(stderr, "error: unknown index command: %s\n", argv[0]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
if (strcmp(argv[0], "init") == 0) {
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
2026-01-17 17:58:59 +01:00
|
|
|
opts.shard_count = 1u;
|
2026-01-17 16:43:47 +01:00
|
|
|
|
|
|
|
|
for (i = 1; i < (size_t)argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--store-id") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --store-id requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.store_id = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--profile") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --profile requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.profile = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--hash") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --hash requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.hash = argv[++i];
|
2026-01-17 17:58:59 +01:00
|
|
|
} else if (strcmp(argv[i], "--shards") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --shards requires a value\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_parse_u16(argv[++i], &opts.shard_count)) {
|
|
|
|
|
fprintf(stderr, "error: invalid shard count\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
opts.has_shard_count = true;
|
2026-01-17 16:43:47 +01:00
|
|
|
} else if (strcmp(argv[i], "--force") == 0) {
|
|
|
|
|
opts.force = true;
|
|
|
|
|
} else if (strcmp(argv[i], "--quiet") == 0) {
|
|
|
|
|
opts.quiet = true;
|
|
|
|
|
} else if (strcmp(argv[i], "--help") == 0 ||
|
|
|
|
|
strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&cfg, 0, sizeof(cfg));
|
|
|
|
|
if (opts.store_id != NULL) {
|
|
|
|
|
if (!amduat_asl_store_fs_meta_copy_store_id(cfg.store_id,
|
|
|
|
|
opts.store_id)) {
|
|
|
|
|
fprintf(stderr, "error: invalid store-id\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (opts.profile != NULL) {
|
|
|
|
|
if (!amduat_asl_parse_profile_id(
|
|
|
|
|
opts.profile,
|
|
|
|
|
&cfg.config.encoding_profile_id)) {
|
|
|
|
|
fprintf(stderr, "error: unknown profile: %s\n", opts.profile);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (opts.hash != NULL) {
|
|
|
|
|
if (!amduat_asl_parse_hash_id(opts.hash, &cfg.config.hash_id)) {
|
|
|
|
|
fprintf(stderr, "error: unknown hash: %s\n", opts.hash);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok = amduat_asl_store_fs_init_root(opts.root, &cfg, &cfg);
|
|
|
|
|
if (!ok && opts.force) {
|
|
|
|
|
ok = amduat_asl_store_fs_init_root(opts.root, NULL, &cfg);
|
|
|
|
|
if (ok && (opts.store_id || opts.profile || opts.hash) && !opts.quiet) {
|
|
|
|
|
fprintf(stderr,
|
|
|
|
|
"warning: existing config kept; overrides ignored\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: failed to initialize store root: %s\n",
|
|
|
|
|
opts.root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root = opts.root;
|
|
|
|
|
{
|
|
|
|
|
char *index_path;
|
|
|
|
|
char *segments_path;
|
|
|
|
|
char *blocks_path;
|
|
|
|
|
|
|
|
|
|
index_path = NULL;
|
|
|
|
|
segments_path = NULL;
|
|
|
|
|
blocks_path = NULL;
|
|
|
|
|
if (!amduat_asl_cli_build_index_path(root, &index_path) ||
|
|
|
|
|
!amduat_asl_cli_build_segments_path(root, &segments_path) ||
|
|
|
|
|
!amduat_asl_cli_build_blocks_path(root, &blocks_path)) {
|
|
|
|
|
free(index_path);
|
|
|
|
|
free(segments_path);
|
|
|
|
|
free(blocks_path);
|
|
|
|
|
fprintf(stderr, "error: failed to build index paths\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ok = amduat_asl_cli_ensure_dir(index_path) &&
|
|
|
|
|
amduat_asl_cli_ensure_dir(segments_path) &&
|
|
|
|
|
amduat_asl_cli_ensure_dir(blocks_path);
|
|
|
|
|
free(index_path);
|
|
|
|
|
free(segments_path);
|
|
|
|
|
free(blocks_path);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: failed to initialize index directories\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 17:58:59 +01:00
|
|
|
if (!amduat_asl_cli_write_index_config(root,
|
|
|
|
|
opts.shard_count,
|
|
|
|
|
opts.force)) {
|
|
|
|
|
fprintf(stderr, "error: failed to write index config\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 16:43:47 +01:00
|
|
|
if (!amduat_asl_cli_write_empty_log(root)) {
|
|
|
|
|
fprintf(stderr, "error: failed to initialize log file\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!opts.quiet) {
|
|
|
|
|
fprintf(stderr, "root=%s\n", root);
|
|
|
|
|
amduat_asl_cli_print_store_meta(&cfg);
|
|
|
|
|
}
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 14:26:17 +01:00
|
|
|
root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
for (i = 1; i < (size_t)argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--help") == 0 ||
|
|
|
|
|
strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_is_index_store_root(root)) {
|
|
|
|
|
fprintf(stderr, "error: index/log store not found at: %s\n", root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_store_fs_load_config(root, &cfg)) {
|
|
|
|
|
fprintf(stderr, "error: failed to load store config: %s\n", root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_store_index_fs_init(&index_fs, cfg.config, root)) {
|
|
|
|
|
fprintf(stderr, "error: failed to initialize index store\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
2026-01-17 17:58:59 +01:00
|
|
|
{
|
|
|
|
|
uint16_t shard_count = 1u;
|
|
|
|
|
if (!amduat_asl_cli_read_index_shards(root, &shard_count)) {
|
|
|
|
|
fprintf(stderr, "error: failed to load index config\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
amduat_asl_store_index_fs_set_shard_count(&index_fs, shard_count);
|
|
|
|
|
}
|
2026-01-17 14:26:17 +01:00
|
|
|
amduat_asl_store_init(&store, cfg.config,
|
|
|
|
|
amduat_asl_store_index_fs_ops(), &index_fs);
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_index_current_state(&store, &state)) {
|
|
|
|
|
fprintf(stderr, "error: failed to read index state\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_STORE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf("snapshot_id=%" PRIu64 "\n", state.snapshot_id);
|
|
|
|
|
printf("log_position=%" PRIu64 "\n", state.log_position);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int amduat_asl_cli_cmd_segment(int argc, char **argv) {
|
|
|
|
|
const char *root;
|
|
|
|
|
bool has_segment;
|
|
|
|
|
uint64_t segment_id;
|
|
|
|
|
uint64_t *segment_ids;
|
|
|
|
|
size_t segment_count;
|
|
|
|
|
size_t i;
|
|
|
|
|
amduat_asl_log_record_t *log_records;
|
|
|
|
|
size_t log_count;
|
|
|
|
|
bool have_log;
|
|
|
|
|
bool all_ok;
|
|
|
|
|
|
|
|
|
|
if (argc < 1) {
|
|
|
|
|
fprintf(stderr, "error: segment command requires a subcommand\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[0], "verify") != 0) {
|
|
|
|
|
fprintf(stderr, "error: unknown segment command: %s\n", argv[0]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
root = AMDUAT_ASL_CLI_DEFAULT_ROOT;
|
|
|
|
|
has_segment = false;
|
|
|
|
|
segment_id = 0u;
|
|
|
|
|
for (i = 1; i < (size_t)argc; ++i) {
|
|
|
|
|
if (strcmp(argv[i], "--root") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --root requires a path\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
root = argv[++i];
|
|
|
|
|
} else if (strcmp(argv[i], "--segment") == 0) {
|
|
|
|
|
if (i + 1 >= (size_t)argc) {
|
|
|
|
|
fprintf(stderr, "error: --segment requires an id\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_asl_cli_parse_segment_id(argv[++i], &segment_id)) {
|
|
|
|
|
fprintf(stderr, "error: invalid segment id\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
has_segment = true;
|
|
|
|
|
} else if (strcmp(argv[i], "--help") == 0 ||
|
|
|
|
|
strcmp(argv[i], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(stderr, "error: unknown option: %s\n", argv[i]);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_is_index_store_root(root)) {
|
|
|
|
|
fprintf(stderr, "error: index/log store not found at: %s\n", root);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CONFIG;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log_records = NULL;
|
|
|
|
|
log_count = 0u;
|
|
|
|
|
have_log = amduat_asl_cli_load_log_records(root, &log_records, &log_count);
|
|
|
|
|
if (!have_log) {
|
|
|
|
|
char *log_path;
|
|
|
|
|
bool log_exists;
|
|
|
|
|
|
|
|
|
|
log_path = NULL;
|
|
|
|
|
log_exists = false;
|
|
|
|
|
if (amduat_asl_cli_build_log_path(root, &log_path)) {
|
|
|
|
|
log_exists = amduat_asl_cli_path_is_file(log_path);
|
|
|
|
|
free(log_path);
|
|
|
|
|
}
|
|
|
|
|
if (log_exists) {
|
|
|
|
|
fprintf(stderr, "error: failed to load log records\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_CODEC;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
segment_ids = NULL;
|
|
|
|
|
segment_count = 0u;
|
|
|
|
|
if (has_segment) {
|
|
|
|
|
segment_ids = (uint64_t *)malloc(sizeof(*segment_ids));
|
|
|
|
|
if (segment_ids == NULL) {
|
|
|
|
|
amduat_enc_asl_log_free(log_records, log_count);
|
|
|
|
|
fprintf(stderr, "error: out of memory\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
segment_ids[0] = segment_id;
|
|
|
|
|
segment_count = 1u;
|
|
|
|
|
} else {
|
|
|
|
|
if (!amduat_asl_cli_collect_segment_ids(root, &segment_ids,
|
|
|
|
|
&segment_count)) {
|
|
|
|
|
amduat_enc_asl_log_free(log_records, log_count);
|
|
|
|
|
fprintf(stderr, "error: failed to read segments directory\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_IO;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (segment_count == 0u) {
|
|
|
|
|
amduat_enc_asl_log_free(log_records, log_count);
|
|
|
|
|
free(segment_ids);
|
|
|
|
|
fprintf(stderr, "error: no segments found\n");
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
all_ok = true;
|
|
|
|
|
for (i = 0; i < segment_count; ++i) {
|
|
|
|
|
char *segment_path;
|
|
|
|
|
uint8_t *segment_bytes;
|
|
|
|
|
size_t segment_len;
|
|
|
|
|
uint8_t segment_hash[32];
|
|
|
|
|
uint8_t log_hash[32];
|
|
|
|
|
amduat_asl_core_index_segment_t segment;
|
|
|
|
|
bool ok;
|
|
|
|
|
int hash_status;
|
|
|
|
|
bool hash_match;
|
|
|
|
|
|
|
|
|
|
if (!amduat_asl_cli_build_segment_path(root, segment_ids[i],
|
|
|
|
|
&segment_path)) {
|
|
|
|
|
fprintf(stderr, "error: failed to build segment path\n");
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
segment_bytes = NULL;
|
|
|
|
|
segment_len = 0u;
|
|
|
|
|
if (!amduat_asl_read_path(segment_path, &segment_bytes, &segment_len)) {
|
|
|
|
|
fprintf(stderr, "error: failed to read segment: %s\n", segment_path);
|
|
|
|
|
free(segment_path);
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
free(segment_path);
|
|
|
|
|
|
|
|
|
|
ok = amduat_hash_asl1_digest(AMDUAT_HASH_ASL1_ID_SHA256,
|
|
|
|
|
amduat_octets(segment_bytes, segment_len),
|
|
|
|
|
segment_hash,
|
|
|
|
|
sizeof(segment_hash));
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: failed to hash segment\n");
|
|
|
|
|
free(segment_bytes);
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset(&segment, 0, sizeof(segment));
|
|
|
|
|
ok = amduat_enc_asl_core_index_decode_v1(
|
|
|
|
|
amduat_octets(segment_bytes, segment_len), &segment);
|
|
|
|
|
free(segment_bytes);
|
|
|
|
|
if (!ok) {
|
|
|
|
|
fprintf(stderr, "error: invalid segment encoding\n");
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
amduat_enc_asl_core_index_free(&segment);
|
|
|
|
|
|
|
|
|
|
hash_match = true;
|
|
|
|
|
hash_status = 0;
|
|
|
|
|
if (have_log) {
|
|
|
|
|
hash_status = amduat_asl_cli_log_find_segment_hash(
|
|
|
|
|
log_records, log_count, segment_ids[i], log_hash);
|
|
|
|
|
if (hash_status < 0) {
|
|
|
|
|
fprintf(stderr, "error: invalid log record payload\n");
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (hash_status > 0) {
|
|
|
|
|
hash_match = memcmp(segment_hash, log_hash, sizeof(log_hash)) == 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hash_match) {
|
|
|
|
|
fprintf(stderr, "error: segment hash mismatch\n");
|
|
|
|
|
printf("segment=0x%016" PRIx64 " FAIL\n", segment_ids[i]);
|
|
|
|
|
all_ok = false;
|
|
|
|
|
} else {
|
|
|
|
|
printf("segment=0x%016" PRIx64 " OK\n", segment_ids[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amduat_enc_asl_log_free(log_records, log_count);
|
|
|
|
|
free(segment_ids);
|
|
|
|
|
|
|
|
|
|
return all_ok ? AMDUAT_ASL_CLI_EXIT_OK : AMDUAT_ASL_CLI_EXIT_STORE;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 08:40:57 +01:00
|
|
|
int main(int argc, char **argv) {
|
|
|
|
|
if (argc < 2) {
|
|
|
|
|
amduat_asl_cli_print_usage(stderr);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[1], "init") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_init(argc - 2, argv + 2);
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[1], "put") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_put(argc - 2, argv + 2);
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[1], "get") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_get(argc - 2, argv + 2);
|
|
|
|
|
}
|
2026-01-17 14:26:17 +01:00
|
|
|
if (strcmp(argv[1], "log") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_log(argc - 2, argv + 2);
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[1], "index") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_index(argc - 2, argv + 2);
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(argv[1], "segment") == 0) {
|
|
|
|
|
return amduat_asl_cli_cmd_segment(argc - 2, argv + 2);
|
|
|
|
|
}
|
2025-12-20 08:40:57 +01:00
|
|
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
|
|
|
|
amduat_asl_cli_print_usage(stdout);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "error: unknown command: %s\n", argv[1]);
|
|
|
|
|
amduat_asl_cli_print_usage(stderr);
|
|
|
|
|
return AMDUAT_ASL_CLI_EXIT_USAGE;
|
|
|
|
|
}
|