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 }