kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

cas.c (9507B)


      1 #include <kit/cas.h>
      2 #include <kit/core.h>
      3 #include <stddef.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "dist_host.h"
      8 #include "driver.h"
      9 #include "env.h"
     10 
     11 #define CAS_TOOL "cas"
     12 #define CAS_HEX_MAX (2u * KIT_CAS_HASH_LEN + 1u)
     13 
     14 void driver_help_cas(void) {
     15   driver_printf(
     16       "kit cas - shared kit blob/tree content-addressed store\n"
     17       "\n"
     18       "USAGE\n"
     19       "  kit cas add-blob     --cas DIR FILE\n"
     20       "  kit cas add-tree     --cas DIR --root DIR\n"
     21       "  kit cas add-tree     --cas DIR --map FILE\n"
     22       "  kit cas inspect-tree --cas DIR TREE_ID\n"
     23       "  kit cas verify-tree  --cas DIR TREE_ID\n"
     24       "  kit cas materialize  --cas DIR TREE_ID -C DIR\n");
     25 }
     26 
     27 static int cas_parse_id(const char* s, uint8_t out[KIT_CAS_HASH_LEN]) {
     28   if (driver_strlen(s) != 2u * KIT_CAS_HASH_LEN) return 1;
     29   return kit_hex_decode(out, s, KIT_CAS_HASH_LEN) == KIT_OK ? 0 : 1;
     30 }
     31 
     32 static int cas_open(DriverEnv* env, const char* dir, KitCas** out) {
     33   KitContext ctx = driver_env_to_context(env);
     34   KitCasHost host = driver_cas_host(env);
     35   if (kit_cas_open(&ctx, &host, dir, out) != KIT_OK) {
     36     driver_errf(CAS_TOOL, "failed to open store: %s", dir);
     37     return 1;
     38   }
     39   return 0;
     40 }
     41 
     42 static int cas_cmd_add_blob(DriverEnv* env, int argc, char** argv) {
     43   const char* cas_dir = NULL;
     44   const char* file = NULL;
     45   DriverLoad load;
     46   KitSlice in;
     47   KitCas* cas;
     48   KitBlobInfo bi;
     49   char hex[CAS_HEX_MAX];
     50   int i;
     51   int rc = 1;
     52   load.loaded = 0;
     53   for (i = 2; i < argc; ++i) {
     54     if (driver_streq(argv[i], "--cas") && i + 1 < argc) {
     55       cas_dir = argv[++i];
     56     } else if (!file) {
     57       file = argv[i];
     58     } else {
     59       driver_errf(CAS_TOOL, "unexpected argument: %s", argv[i]);
     60       return 2;
     61     }
     62   }
     63   if (!cas_dir || !file) {
     64     driver_errf(CAS_TOOL, "usage: kit cas add-blob --cas DIR FILE");
     65     return 2;
     66   }
     67   if (cas_open(env, cas_dir, &cas) != 0) return 1;
     68   if (driver_load_bytes(&env->file_io, CAS_TOOL, file, &load, &in) != 0) {
     69     kit_cas_close(cas);
     70     return 1;
     71   }
     72   if (kit_cas_add_blob(cas, in.data, in.len, &bi) == KIT_OK) {
     73     kit_hex_encode(hex, bi.id, KIT_CAS_HASH_LEN);
     74     driver_printf("%s\n", hex);
     75     rc = 0;
     76   }
     77   driver_release_bytes(&env->file_io, &load);
     78   kit_cas_close(cas);
     79   return rc;
     80 }
     81 
     82 /* Map-file parsing stays in the driver: it is the CLI's input format. Each
     83  * line is "tree/path mode source/path"; the source bytes are read here and
     84  * handed to the library tree builder. */
     85 static int cas_read_token(const uint8_t* line, size_t len, size_t* pos,
     86                           const uint8_t** start, size_t* tok_len) {
     87   size_t i = *pos;
     88   while (i < len && (line[i] == ' ' || line[i] == '\t')) ++i;
     89   if (i >= len) return 0;
     90   *start = line + i;
     91   while (i < len && line[i] != ' ' && line[i] != '\t') ++i;
     92   *tok_len = (size_t)(line + i - *start);
     93   *pos = i;
     94   return 1;
     95 }
     96 
     97 static int cas_map_add_line(DriverEnv* env, KitCasTreeBuilder* b,
     98                             const uint8_t* line, size_t len, unsigned line_no) {
     99   const uint8_t *path_b, *mode_b, *src_b;
    100   size_t path_l, mode_l, src_l;
    101   size_t pos = 0;
    102   char path[KIT_CAS_HASH_LEN * 4];
    103   char* src;
    104   KitFileData fd;
    105   KitTreeMode mode;
    106   int rc = 1;
    107   while (len && line[len - 1u] == '\r') --len;
    108   while (pos < len && (line[pos] == ' ' || line[pos] == '\t')) ++pos;
    109   if (pos == len || line[pos] == '#') return 0;
    110   if (!cas_read_token(line, len, &pos, &path_b, &path_l) ||
    111       !cas_read_token(line, len, &pos, &mode_b, &mode_l) ||
    112       !cas_read_token(line, len, &pos, &src_b, &src_l)) {
    113     driver_errf(CAS_TOOL, "bad map line %u", line_no);
    114     return 1;
    115   }
    116   while (pos < len && (line[pos] == ' ' || line[pos] == '\t')) ++pos;
    117   if (pos != len || path_l >= sizeof path || mode_l != 1u || src_l == 0) {
    118     driver_errf(CAS_TOOL, "bad map line %u", line_no);
    119     return 1;
    120   }
    121   memcpy(path, path_b, path_l);
    122   path[path_l] = '\0';
    123   if (mode_b[0] == '-') {
    124     mode = KIT_TREE_MODE_FILE;
    125   } else if (mode_b[0] == 'x') {
    126     mode = KIT_TREE_MODE_EXEC;
    127   } else {
    128     driver_errf(CAS_TOOL, "bad mode on map line %u", line_no);
    129     return 1;
    130   }
    131   src = (char*)driver_alloc(env, src_l + 1u);
    132   if (!src) {
    133     driver_errf(CAS_TOOL, "out of memory");
    134     return 1;
    135   }
    136   memcpy(src, src_b, src_l);
    137   src[src_l] = '\0';
    138   fd.data = NULL;
    139   fd.size = 0;
    140   fd.token = NULL;
    141   if (env->file_io.read_all(env->file_io.user, src, &fd) != KIT_OK) {
    142     driver_errf(CAS_TOOL, "failed to read: %s", src);
    143     driver_free(env, src, src_l + 1u);
    144     return 1;
    145   }
    146   /* v1 map files intentionally split on ASCII whitespace, so paths with
    147    * spaces are not representable yet. */
    148   if (kit_cas_tree_builder_add(b, path, mode, fd.data, fd.size) == KIT_OK)
    149     rc = 0;
    150   if (env->file_io.release) env->file_io.release(env->file_io.user, &fd);
    151   driver_free(env, src, src_l + 1u);
    152   return rc;
    153 }
    154 
    155 static int cas_add_tree_map(DriverEnv* env, KitCasTreeBuilder* b,
    156                             const uint8_t* data, size_t len) {
    157   size_t start = 0;
    158   unsigned line_no = 1;
    159   while (start <= len) {
    160     size_t end = start;
    161     while (end < len && data[end] != '\n') ++end;
    162     if (cas_map_add_line(env, b, data + start, end - start, line_no) != 0)
    163       return 1;
    164     if (end == len) break;
    165     start = end + 1u;
    166     ++line_no;
    167   }
    168   return 0;
    169 }
    170 
    171 static int cas_cmd_add_tree(DriverEnv* env, int argc, char** argv) {
    172   const char* cas_dir = NULL;
    173   const char* root = NULL;
    174   const char* map = NULL;
    175   KitCas* cas;
    176   uint8_t tree_id[KIT_CAS_HASH_LEN];
    177   char hex[CAS_HEX_MAX];
    178   int i;
    179   int rc = 1;
    180   for (i = 2; i < argc; ++i) {
    181     if (driver_streq(argv[i], "--cas") && i + 1 < argc) {
    182       cas_dir = argv[++i];
    183     } else if (driver_streq(argv[i], "--root") && i + 1 < argc) {
    184       root = argv[++i];
    185     } else if (driver_streq(argv[i], "--map") && i + 1 < argc) {
    186       map = argv[++i];
    187     } else {
    188       driver_errf(CAS_TOOL, "unexpected argument: %s", argv[i]);
    189       return 2;
    190     }
    191   }
    192   if (!cas_dir || ((root != NULL) == (map != NULL))) {
    193     driver_errf(CAS_TOOL,
    194                 "usage: kit cas add-tree --cas DIR (--root DIR | --map FILE)");
    195     return 2;
    196   }
    197   if (cas_open(env, cas_dir, &cas) != 0) return 1;
    198   if (root) {
    199     if (kit_cas_add_tree_from_dir(cas, root, tree_id) == KIT_OK) rc = 0;
    200   } else {
    201     KitCasTreeBuilder* b;
    202     KitFileData fd;
    203     fd.data = NULL;
    204     fd.size = 0;
    205     fd.token = NULL;
    206     if (kit_cas_tree_builder_new(cas, &b) != KIT_OK) {
    207       kit_cas_close(cas);
    208       driver_errf(CAS_TOOL, "out of memory");
    209       return 1;
    210     }
    211     if (env->file_io.read_all(env->file_io.user, map, &fd) != KIT_OK) {
    212       driver_errf(CAS_TOOL, "failed to read map: %s", map);
    213     } else {
    214       if (cas_add_tree_map(env, b, fd.data, fd.size) == 0 &&
    215           kit_cas_tree_builder_finish(b, tree_id) == KIT_OK)
    216         rc = 0;
    217       if (env->file_io.release) env->file_io.release(env->file_io.user, &fd);
    218     }
    219     kit_cas_tree_builder_free(b);
    220   }
    221   if (rc == 0) {
    222     kit_hex_encode(hex, tree_id, KIT_CAS_HASH_LEN);
    223     driver_printf("%s\n", hex);
    224   }
    225   kit_cas_close(cas);
    226   return rc;
    227 }
    228 
    229 static int cas_cmd_tree_common(DriverEnv* env, int argc, char** argv,
    230                                const char* cmd) {
    231   const char* cas_dir = NULL;
    232   const char* tree_s = NULL;
    233   const char* out_dir = NULL;
    234   uint8_t tree_id[KIT_CAS_HASH_LEN];
    235   KitCas* cas;
    236   int i;
    237   int rc = 1;
    238   for (i = 2; i < argc; ++i) {
    239     if (driver_streq(argv[i], "--cas") && i + 1 < argc) {
    240       cas_dir = argv[++i];
    241     } else if (driver_streq(argv[i], "-C") && i + 1 < argc) {
    242       out_dir = argv[++i];
    243     } else if (!tree_s) {
    244       tree_s = argv[i];
    245     } else {
    246       driver_errf(CAS_TOOL, "unexpected argument: %s", argv[i]);
    247       return 2;
    248     }
    249   }
    250   if (!cas_dir || !tree_s || (driver_streq(cmd, "materialize") && !out_dir) ||
    251       (!driver_streq(cmd, "materialize") && out_dir)) {
    252     if (driver_streq(cmd, "materialize"))
    253       driver_errf(CAS_TOOL,
    254                   "usage: kit cas materialize --cas DIR TREE_ID -C DIR");
    255     else
    256       driver_errf(CAS_TOOL, "usage: kit cas %s --cas DIR TREE_ID", cmd);
    257     return 2;
    258   }
    259   if (cas_parse_id(tree_s, tree_id) != 0) {
    260     driver_errf(CAS_TOOL, "bad tree id: %s", tree_s);
    261     return 2;
    262   }
    263   if (cas_open(env, cas_dir, &cas) != 0) return 1;
    264   if (driver_streq(cmd, "inspect-tree")) {
    265     KitWriter* w = driver_stdout_writer(env);
    266     if (w) {
    267       if (kit_cas_inspect_tree(cas, tree_id, w) == KIT_OK) rc = 0;
    268       kit_writer_close(w);
    269     } else {
    270       driver_errf(CAS_TOOL, "failed to open stdout");
    271     }
    272   } else if (driver_streq(cmd, "verify-tree")) {
    273     if (kit_cas_verify_tree(cas, tree_id) == KIT_OK) {
    274       driver_printf("ok\n");
    275       rc = 0;
    276     }
    277   } else {
    278     if (kit_cas_materialize_tree(cas, tree_id, out_dir) == KIT_OK) rc = 0;
    279   }
    280   kit_cas_close(cas);
    281   return rc;
    282 }
    283 
    284 int driver_cas(int argc, char** argv) {
    285   DriverEnv env;
    286   int rc;
    287   if (argc < 2 || driver_argv_wants_help(argc, argv, 1)) {
    288     driver_help_cas();
    289     return 0;
    290   }
    291   driver_env_init(&env);
    292   if (driver_streq(argv[1], "add-blob")) {
    293     rc = cas_cmd_add_blob(&env, argc, argv);
    294   } else if (driver_streq(argv[1], "add-tree")) {
    295     rc = cas_cmd_add_tree(&env, argc, argv);
    296   } else if (driver_streq(argv[1], "inspect-tree") ||
    297              driver_streq(argv[1], "verify-tree") ||
    298              driver_streq(argv[1], "materialize")) {
    299     rc = cas_cmd_tree_common(&env, argc, argv, argv[1]);
    300   } else {
    301     driver_errf(CAS_TOOL, "unknown command: %s", argv[1]);
    302     rc = 2;
    303   }
    304   driver_env_fini(&env);
    305   return rc;
    306 }