#include "amduat/asl/asl_store_fs.h" #include "amduat/asl/asl_store_fs_meta.h" #include "amduat/asl/ref_text.h" #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" #include "amduat/enc/asl1_core_codec.h" #include "amduat/hash/asl1.h" #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 }; enum { AMDUAT_ASL_CLI_STORE_ID_MAX = AMDUAT_ASL_STORE_FS_STORE_ID_MAX, AMDUAT_ASL_CLI_BUF_CHUNK = 4096 }; static const char *const AMDUAT_ASL_CLI_DEFAULT_ROOT = ".amduat-asl"; typedef enum { AMDUAT_ASL_CLI_REF_HEX = 0, AMDUAT_ASL_CLI_REF_BYTES = 1 } amduat_asl_cli_ref_format_t; typedef enum { AMDUAT_ASL_CLI_IO_RAW = 0, AMDUAT_ASL_CLI_IO_ARTIFACT = 1 } amduat_asl_cli_io_format_t; typedef struct { const char *root; const char *store_id; const char *profile; const char *hash; 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_cli_io_format_t input_format; amduat_asl_cli_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_cli_io_format_t output_format; amduat_asl_cli_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; 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" "\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 bool amduat_asl_cli_is_store_id_char(char c) { if (c >= 'a' && c <= 'z') { return true; } if (c >= 'A' && c <= 'Z') { return true; } if (c >= '0' && c <= '9') { return true; } return c == '.' || c == '_' || c == '-'; } static bool amduat_asl_cli_validate_store_id(const char *store_id) { size_t i; if (store_id == NULL || store_id[0] == '\0') { return false; } for (i = 0; store_id[i] != '\0'; ++i) { if (i >= AMDUAT_ASL_CLI_STORE_ID_MAX) { return false; } if (!amduat_asl_cli_is_store_id_char(store_id[i])) { return false; } } return true; } static bool amduat_asl_cli_copy_store_id( char out_id[AMDUAT_ASL_CLI_STORE_ID_MAX + 1], const char *store_id) { size_t len; if (out_id == NULL || store_id == NULL) { return false; } if (!amduat_asl_cli_validate_store_id(store_id)) { return false; } len = strlen(store_id); if (len > AMDUAT_ASL_CLI_STORE_ID_MAX) { return false; } memcpy(out_id, store_id, len); out_id[len] = '\0'; return true; } static bool amduat_asl_cli_parse_u16(const char *text, uint16_t *out) { char *end; unsigned long value; if (text == NULL || text[0] == '\0' || out == NULL) { return false; } errno = 0; value = strtoul(text, &end, 0); if (errno != 0 || end == text || *end != '\0' || value > UINT16_MAX) { return false; } *out = (uint16_t)value; return true; } static bool amduat_asl_cli_parse_u32(const char *text, uint32_t *out) { char *end; unsigned long value; if (text == NULL || text[0] == '\0' || out == NULL) { return false; } errno = 0; value = strtoul(text, &end, 0); if (errno != 0 || end == text || *end != '\0' || value > UINT32_MAX) { return false; } *out = (uint32_t)value; return true; } static bool amduat_asl_cli_parse_profile_id( const char *text, amduat_asl_encoding_profile_id_t *out_id) { uint16_t id; size_t i; size_t count; const amduat_enc_asl1_core_profile_desc_t *descs; if (text == NULL || out_id == NULL) { return false; } if (amduat_asl_cli_parse_u16(text, &id)) { if (amduat_enc_asl1_core_desc_lookup(id) == NULL) { return false; } *out_id = id; return true; } descs = amduat_enc_asl1_core_descs(&count); for (i = 0; i < count; ++i) { if (descs[i].name != NULL && strcmp(descs[i].name, text) == 0) { *out_id = descs[i].profile_id; return true; } } return false; } static bool amduat_asl_cli_parse_hash_id(const char *text, amduat_hash_id_t *out_id) { uint16_t id; size_t i; size_t count; const amduat_hash_asl1_desc_t *descs; if (text == NULL || out_id == NULL) { return false; } if (amduat_asl_cli_parse_u16(text, &id)) { if (amduat_hash_asl1_desc_lookup(id) == NULL) { return false; } *out_id = id; return true; } descs = amduat_hash_asl1_descs(&count); for (i = 0; i < count; ++i) { if (descs[i].name != NULL && strcmp(descs[i].name, text) == 0) { *out_id = descs[i].hash_id; return true; } } return false; } static bool amduat_asl_cli_parse_type_tag(const char *text, amduat_type_tag_t *out_tag) { uint32_t tag_id; if (text == NULL || out_tag == NULL) { return false; } if (!amduat_asl_cli_parse_u32(text, &tag_id)) { return false; } *out_tag = amduat_type_tag(tag_id); return true; } static bool amduat_asl_cli_parse_ref_format( const char *text, amduat_asl_cli_ref_format_t *out_fmt) { if (text == NULL || out_fmt == NULL) { return false; } if (strcmp(text, "hex") == 0) { *out_fmt = AMDUAT_ASL_CLI_REF_HEX; return true; } if (strcmp(text, "bytes") == 0) { *out_fmt = AMDUAT_ASL_CLI_REF_BYTES; return true; } return false; } static bool amduat_asl_cli_parse_io_format( const char *text, amduat_asl_cli_io_format_t *out_fmt) { if (text == NULL || out_fmt == NULL) { return false; } if (strcmp(text, "raw") == 0) { *out_fmt = AMDUAT_ASL_CLI_IO_RAW; return true; } if (strcmp(text, "artifact") == 0) { *out_fmt = AMDUAT_ASL_CLI_IO_ARTIFACT; return true; } return false; } static bool amduat_asl_cli_read_stream(FILE *stream, uint8_t **out, size_t *out_len) { uint8_t *buffer; size_t cap; size_t len; if (out == NULL || out_len == NULL) { return false; } *out = NULL; *out_len = 0; buffer = NULL; cap = 0; len = 0; while (true) { if (len == cap) { size_t new_cap = cap == 0 ? AMDUAT_ASL_CLI_BUF_CHUNK : cap * 2u; uint8_t *next; if (new_cap < cap) { free(buffer); return false; } next = (uint8_t *)realloc(buffer, new_cap); if (next == NULL) { free(buffer); return false; } buffer = next; cap = new_cap; } size_t nread = fread(buffer + len, 1u, cap - len, stream); len += nread; if (nread == 0u) { if (ferror(stream)) { free(buffer); return false; } break; } } if (len == 0u) { free(buffer); buffer = NULL; } *out = buffer; *out_len = len; return true; } static bool amduat_asl_cli_read_path(const char *path, uint8_t **out, size_t *out_len) { FILE *stream; bool ok; if (path == NULL || out == NULL || out_len == NULL) { return false; } if (strcmp(path, "-") == 0) { return amduat_asl_cli_read_stream(stdin, out, out_len); } stream = fopen(path, "rb"); if (stream == NULL) { return false; } ok = amduat_asl_cli_read_stream(stream, out, out_len); if (fclose(stream) != 0) { ok = false; } return ok; } static bool amduat_asl_cli_write_stream(FILE *stream, const uint8_t *bytes, size_t len) { size_t offset; if (len != 0u && bytes == NULL) { return false; } offset = 0u; while (offset < len) { size_t written = fwrite(bytes + offset, 1u, len - offset, stream); if (written == 0u) { return false; } offset += written; } return fflush(stream) == 0; } static bool amduat_asl_cli_write_path(const char *path, const uint8_t *bytes, size_t len) { FILE *stream; bool ok; if (path == NULL) { return false; } if (strcmp(path, "-") == 0) { return amduat_asl_cli_write_stream(stdout, bytes, len); } stream = fopen(path, "wb"); if (stream == NULL) { return false; } ok = amduat_asl_cli_write_stream(stream, bytes, len); if (fclose(stream) != 0) { ok = false; } return ok; } static bool amduat_asl_cli_write_text_line(const char *path, const char *text) { FILE *stream; bool ok; if (path == NULL || text == NULL) { return false; } if (strcmp(path, "-") == 0) { stream = stdout; } else { stream = fopen(path, "wb"); if (stream == NULL) { return false; } } ok = fputs(text, stream) >= 0 && fputc('\n', stream) != EOF && fflush(stream) == 0; if (stream != stdout && fclose(stream) != 0) { ok = false; } return ok; } 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_free_artifact(amduat_artifact_t *artifact) { if (artifact == NULL) { return; } free((void *)artifact->bytes.data); artifact->bytes.data = NULL; artifact->bytes.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_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_OK: default: return AMDUAT_ASL_CLI_EXIT_STORE; } } 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_cli_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_cli_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_cli_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_store_fs_config_t cfg; amduat_asl_store_fs_t fs; amduat_asl_store_t store; 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_CLI_IO_RAW; opts.ref_format = AMDUAT_ASL_CLI_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_cli_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_cli_parse_io_format(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_asl_cli_parse_ref_format(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_CLI_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_store_fs_load_config(opts.root, &cfg)) { fprintf(stderr, "error: failed to load store config: %s\n", opts.root); return AMDUAT_ASL_CLI_EXIT_CONFIG; } if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { fprintf(stderr, "error: failed to initialize store\n"); return AMDUAT_ASL_CLI_EXIT_CONFIG; } amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); input_bytes = NULL; input_len = 0u; if (!amduat_asl_cli_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; } if (opts.input_format == AMDUAT_ASL_CLI_IO_ARTIFACT) { ok = amduat_enc_asl1_core_decode_artifact_v1( amduat_octets(input_bytes, input_len), &artifact); free(input_bytes); if (!ok) { fprintf(stderr, "error: invalid artifact encoding\n"); return AMDUAT_ASL_CLI_EXIT_CODEC; } } else { artifact = opts.has_type_tag ? amduat_artifact_with_type( amduat_octets(input_bytes, input_len), opts.type_tag) : amduat_artifact(amduat_octets(input_bytes, input_len)); } memset(&ref, 0, sizeof(ref)); exit_code = AMDUAT_ASL_CLI_EXIT_OK; { amduat_asl_store_error_t err = amduat_asl_store_put(&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_ASL_CLI_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_cli_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_cli_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(&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); } } } } if (opts.input_format == AMDUAT_ASL_CLI_IO_ARTIFACT) { amduat_asl_cli_free_artifact(&artifact); } else { free((void *)artifact.bytes.data); } 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_store_fs_config_t cfg; amduat_asl_store_fs_t fs; amduat_asl_store_t store; 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_CLI_IO_RAW; opts.ref_format = AMDUAT_ASL_CLI_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_asl_cli_parse_ref_format(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_cli_parse_io_format(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_cli_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_store_fs_load_config(opts.root, &cfg)) { fprintf(stderr, "error: failed to load store config: %s\n", opts.root); return AMDUAT_ASL_CLI_EXIT_CONFIG; } if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { fprintf(stderr, "error: failed to initialize store\n"); return AMDUAT_ASL_CLI_EXIT_CONFIG; } amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); memset(&ref, 0, sizeof(ref)); if (opts.ref_format == AMDUAT_ASL_CLI_REF_HEX) { if (!amduat_asl_ref_decode_hex(opts.ref, &ref)) { fprintf(stderr, "error: invalid hex reference\n"); return AMDUAT_ASL_CLI_EXIT_CODEC; } } else { ref_bytes = NULL; ref_len = 0u; if (!amduat_asl_cli_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_bytes(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, 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 (!artifact.has_type_tag || artifact.type_tag.tag_id != opts.expect_type_tag.tag_id) { 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_CLI_IO_RAW) { if (!amduat_asl_cli_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_cli_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(&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_cli_free_artifact(&artifact); return exit_code; } 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], "--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; }