#include "amduat/asl/artifact_io.h" #include "amduat/asl/asl_store_fs.h" #include "amduat/asl/asl_store_fs_meta.h" #include "amduat/asl/io.h" #include "amduat/asl/parse.h" #include "amduat/asl/ref_io.h" #include "amduat/asl/ref_text.h" #include "amduat/asl/asl_store_index_fs.h" #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core_codec.h" #include "amduat/enc/asl_core_index.h" #include "amduat/enc/asl_log.h" #include "amduat/format/parse.h" #include "amduat/format/ref.h" #include "amduat/hash/asl1.h" #include #include #include #include #include #include #include #include #include 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; uint16_t shard_count; bool has_shard_count; bool force; bool quiet; } amduat_asl_cli_init_opts_t; typedef struct { const char *root; const char *input_path; const char *output_path; amduat_asl_io_format_t input_format; amduat_format_ref_format_t ref_format; bool has_type_tag; amduat_type_tag_t type_tag; bool quiet; } amduat_asl_cli_put_opts_t; typedef struct { const char *root; const char *ref; const char *output_path; amduat_asl_io_format_t output_format; amduat_format_ref_format_t ref_format; bool has_expect_type_tag; amduat_type_tag_t expect_type_tag; bool print_type_tag; bool quiet; } amduat_asl_cli_get_opts_t; 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); 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" " [--hash HASH_ID|name] [--force] [--quiet]\n" " amduat-asl put [--root PATH] [--type-tag TAG]\n" " [--input PATH|-] [--input-format raw|artifact]\n" " [--ref-format hex|bytes] [--output PATH|-]\n" " [--quiet]\n" " 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" " [--quiet]\n" " amduat-asl log inspect [--root PATH]\n" " amduat-asl index init [--root PATH] [--store-id ID]\n" " [--profile PROFILE_ID|name]\n" " [--hash HASH_ID|name] [--shards N]\n" " [--force] [--quiet]\n" " amduat-asl index state [--root PATH]\n" " amduat-asl segment verify [--root PATH] [--segment ID]\n" "\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"; case AMDUAT_ASL_STORE_ERR_IO: return "io"; 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; case AMDUAT_ASL_STORE_ERR_IO: return AMDUAT_ASL_CLI_EXIT_IO; case AMDUAT_ASL_STORE_OK: default: return AMDUAT_ASL_CLI_EXIT_STORE; } } 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; } 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; } 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; } 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; } 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; } 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; } 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; } static bool amduat_asl_cli_open_store(const char *root_path, amduat_asl_cli_store_ctx_t *out_ctx) { bool use_index; uint16_t shard_count; 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) { if (!amduat_asl_cli_read_index_shards(root_path, &shard_count)) { return false; } if (!amduat_asl_store_index_fs_init(&out_ctx->index_fs, out_ctx->cfg.config, root_path)) { return false; } amduat_asl_store_index_fs_set_shard_count(&out_ctx->index_fs, shard_count); 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; } 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; } 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; } 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; } 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; } 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_in, 0, sizeof(cfg_in)); if (opts.store_id != NULL) { if (!amduat_asl_store_fs_meta_copy_store_id(cfg_in.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_in.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_in.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_in, &cfg_out); if (!ok && opts.force) { ok = amduat_asl_store_fs_init_root(opts.root, NULL, &cfg_out); 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; } if (!opts.quiet) { fprintf(stderr, "root=%s\n", opts.root); amduat_asl_cli_print_store_meta(&cfg_out); } return AMDUAT_ASL_CLI_EXIT_OK; } static int amduat_asl_cli_cmd_put(int argc, char **argv) { amduat_asl_cli_put_opts_t opts; amduat_asl_cli_store_ctx_t store_ctx; 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 = "-"; opts.input_format = AMDUAT_ASL_IO_RAW; opts.ref_format = AMDUAT_FORMAT_REF_HEX; 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; } if (!amduat_asl_parse_type_tag(argv[++i], &opts.type_tag)) { 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; } if (!amduat_asl_io_format_parse(argv[++i], &opts.input_format)) { 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; } if (!amduat_format_ref_parse(argv[++i], &opts.ref_format)) { 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], "--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; } } if (opts.input_format == AMDUAT_ASL_IO_ARTIFACT && opts.has_type_tag) { fprintf(stderr, "error: --type-tag is not valid with --input-format artifact\n"); return AMDUAT_ASL_CLI_EXIT_USAGE; } if (!amduat_asl_cli_open_store(opts.root, &store_ctx)) { fprintf(stderr, "error: failed to load store config: %s\n", opts.root); return AMDUAT_ASL_CLI_EXIT_CONFIG; } input_bytes = NULL; input_len = 0u; if (!amduat_asl_read_path(opts.input_path, &input_bytes, &input_len)) { fprintf(stderr, "error: failed to read input: %s\n", opts.input_path); return AMDUAT_ASL_CLI_EXIT_IO; } 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) { free(input_bytes); } if (!ok) { fprintf(stderr, "error: invalid artifact encoding\n"); return AMDUAT_ASL_CLI_EXIT_CODEC; } memset(&ref, 0, sizeof(ref)); exit_code = AMDUAT_ASL_CLI_EXIT_OK; { amduat_asl_store_error_t err = amduat_asl_store_put(&store_ctx.store, artifact, &ref); 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 { if (opts.ref_format == AMDUAT_FORMAT_REF_HEX) { char *hex_ref = NULL; if (!amduat_asl_ref_encode_hex(ref, &hex_ref)) { fprintf(stderr, "error: failed to encode reference\n"); exit_code = AMDUAT_ASL_CLI_EXIT_CODEC; } else { if (!amduat_asl_write_text_line(opts.output_path, hex_ref)) { 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 { if (!amduat_asl_write_path(opts.output_path, 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); } } if (exit_code == AMDUAT_ASL_CLI_EXIT_OK && !opts.quiet) { char *hex_ref = NULL; fprintf(stderr, "root=%s\n", opts.root); amduat_asl_cli_print_store_meta(&store_ctx.cfg); 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); } if (amduat_asl_ref_encode_hex(ref, &hex_ref)) { fprintf(stderr, "ref=%s\n", hex_ref); free(hex_ref); } } } } amduat_asl_artifact_free(&artifact); 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; amduat_asl_cli_store_ctx_t store_ctx; 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 = "-"; opts.output_format = AMDUAT_ASL_IO_RAW; opts.ref_format = AMDUAT_FORMAT_REF_HEX; 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; } if (!amduat_format_ref_parse(argv[++i], &opts.ref_format)) { 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; } if (!amduat_asl_io_format_parse(argv[++i], &opts.output_format)) { 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; } if (!amduat_asl_parse_type_tag(argv[++i], &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; } 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; } } if (opts.ref == NULL) { fprintf(stderr, "error: --ref is required\n"); return AMDUAT_ASL_CLI_EXIT_USAGE; } if (!amduat_asl_cli_open_store(opts.root, &store_ctx)) { fprintf(stderr, "error: failed to load store config: %s\n", opts.root); return AMDUAT_ASL_CLI_EXIT_CONFIG; } memset(&ref, 0, sizeof(ref)); 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) { fprintf(stderr, "error: invalid hex reference\n"); return AMDUAT_ASL_CLI_EXIT_CODEC; } } else { ref_bytes = NULL; ref_len = 0u; if (!amduat_asl_read_path(opts.ref, &ref_bytes, &ref_len)) { fprintf(stderr, "error: failed to read reference: %s\n", opts.ref); return AMDUAT_ASL_CLI_EXIT_IO; } ok = amduat_asl_ref_decode_format( opts.ref_format, amduat_octets(ref_bytes, ref_len), &ref); 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; { amduat_asl_store_error_t err = amduat_asl_store_get(&store_ctx.store, ref, &artifact); 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) { if (!amduat_asl_artifact_expect_type_tag(&artifact, opts.expect_type_tag)) { fprintf(stderr, "error: type-tag mismatch\n"); exit_code = AMDUAT_ASL_CLI_EXIT_UNSUPPORTED; } } if (exit_code == AMDUAT_ASL_CLI_EXIT_OK) { if (opts.output_format == AMDUAT_ASL_IO_RAW) { if (!amduat_asl_write_path(opts.output_path, 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 { if (!amduat_asl_write_path(opts.output_path, 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); } } } if (exit_code == AMDUAT_ASL_CLI_EXIT_OK && !opts.quiet) { fprintf(stderr, "root=%s\n", opts.root); amduat_asl_cli_print_store_meta(&store_ctx.cfg); 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); amduat_asl_artifact_free(&artifact); return exit_code; } 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; amduat_asl_cli_init_opts_t opts; amduat_asl_store_index_fs_t index_fs; amduat_asl_store_t store; amduat_asl_index_state_t state; size_t i; bool ok; if (argc < 1) { fprintf(stderr, "error: index command requires a subcommand\n"); return AMDUAT_ASL_CLI_EXIT_USAGE; } if (strcmp(argv[0], "init") != 0 && strcmp(argv[0], "state") != 0) { fprintf(stderr, "error: unknown index command: %s\n", argv[0]); return AMDUAT_ASL_CLI_EXIT_USAGE; } if (strcmp(argv[0], "init") == 0) { memset(&opts, 0, sizeof(opts)); opts.root = AMDUAT_ASL_CLI_DEFAULT_ROOT; opts.shard_count = 1u; 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]; } 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; } 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; } } 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; } 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; } 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; } { 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); } 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; } 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); } 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); } 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; }