#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/store.h" #include "amduat/enc/asl1_core_codec.h" #include "amduat/format/parse.h" #include "amduat/format/ref.h" #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; 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; 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 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_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_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_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_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_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_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, 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(&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_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_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_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_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, 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(&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; } 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; }