remote.c (10416B)
1 #include "remote.h" 2 3 #include <stdio.h> 4 #include <string.h> 5 6 typedef struct RenderedArgv { 7 KitSlice* argv; 8 size_t argc; 9 char* storage; 10 size_t storage_size; 11 } RenderedArgv; 12 13 typedef struct RenderTokenSet { 14 const char* kind; 15 const char* pp; 16 const char* id; 17 const char* out; 18 } RenderTokenSet; 19 20 static int ascii_space(char c) { 21 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || 22 c == '\v'; 23 } 24 25 static int add_size(size_t* v, size_t n) { 26 if (!v || *v > (size_t)-1 - n) return BUILD_ERR; 27 *v += n; 28 return BUILD_OK; 29 } 30 31 static int path_join(char* out, size_t cap, KitSlice dir, const char* leaf) { 32 size_t nd, nl; 33 int slash; 34 if (!out || cap == 0u || !dir.s || dir.len == 0u || !leaf) return BUILD_ERR; 35 nd = dir.len; 36 nl = strlen(leaf); 37 slash = dir.s[nd - 1u] != '/'; 38 if (nd + (slash ? 1u : 0u) + nl + 1u > cap) return BUILD_ERR; 39 memcpy(out, dir.s, nd); 40 if (slash) out[nd++] = '/'; 41 memcpy(out + nd, leaf, nl); 42 out[nd + nl] = '\0'; 43 return BUILD_OK; 44 } 45 46 static const char* remote_kind_name(BuildRemoteKind kind) { 47 if (kind == BUILD_REMOTE_BLOB) return "blob"; 48 if (kind == BUILD_REMOTE_TREE) return "tree"; 49 return NULL; 50 } 51 52 static int token_match(const char* p, const char* end, const char* lit, 53 const char* repl, const char** repl_out, 54 size_t* lit_len_out, size_t* repl_len_out) { 55 size_t n; 56 if (!lit || !repl) return 0; 57 n = strlen(lit); 58 if ((size_t)(end - p) < n || memcmp(p, lit, n) != 0) return 0; 59 *repl_out = repl; 60 *lit_len_out = n; 61 *repl_len_out = strlen(repl); 62 return 1; 63 } 64 65 static int find_token(const char* p, const char* end, 66 const RenderTokenSet* toks, const char** repl_out, 67 size_t* lit_len_out, size_t* repl_len_out) { 68 return token_match(p, end, "{kind}", toks->kind, repl_out, lit_len_out, 69 repl_len_out) || 70 token_match(p, end, "{pp}", toks->pp, repl_out, lit_len_out, 71 repl_len_out) || 72 token_match(p, end, "{id}", toks->id, repl_out, lit_len_out, 73 repl_len_out) || 74 token_match(p, end, "{out}", toks->out, repl_out, lit_len_out, 75 repl_len_out); 76 } 77 78 static int emit_bytes(char* storage, size_t* off, size_t cap, const char* data, 79 size_t len) { 80 if (!off || (!data && len)) return BUILD_ERR; 81 if (storage) { 82 if (*off > cap || len > cap - *off) return BUILD_ERR; 83 if (len) memcpy(storage + *off, data, len); 84 } 85 return add_size(off, len); 86 } 87 88 static int render_pass(KitSlice tmpl, const RenderTokenSet* toks, 89 KitSlice* argv, size_t* argc_io, char* storage, 90 size_t storage_cap, size_t* storage_len_out) { 91 const char* p; 92 const char* end; 93 size_t argc = 0u; 94 size_t off = 0u; 95 size_t word_start = 0u; 96 size_t word_len = 0u; 97 int in_word = 0; 98 char quote = 0; 99 100 if (!tmpl.s || !toks || !argc_io || !storage_len_out) return BUILD_ERR; 101 p = tmpl.s; 102 end = tmpl.s + tmpl.len; 103 while (p < end) { 104 char c = *p; 105 if (quote == 0 && ascii_space(c)) { 106 if (in_word) { 107 if (argv) { 108 if (argc >= *argc_io || off >= storage_cap) return BUILD_ERR; 109 storage[off] = '\0'; 110 argv[argc].s = storage + word_start; 111 argv[argc].len = word_len; 112 } 113 if (add_size(&off, 1u) != BUILD_OK) return BUILD_ERR; 114 ++argc; 115 in_word = 0; 116 word_len = 0u; 117 } 118 ++p; 119 continue; 120 } 121 122 if (!in_word) { 123 in_word = 1; 124 word_start = off; 125 word_len = 0u; 126 } 127 128 if (quote == 0 && (c == '\'' || c == '"')) { 129 quote = c; 130 ++p; 131 continue; 132 } 133 if (quote != 0 && c == quote) { 134 quote = 0; 135 ++p; 136 continue; 137 } 138 if ((quote == 0 || quote == '"') && c == '\\') { 139 if (p + 1 >= end) return BUILD_ERR; 140 if (emit_bytes(storage, &off, storage_cap, p + 1, 1u) != BUILD_OK) 141 return BUILD_ERR; 142 ++word_len; 143 p += 2; 144 continue; 145 } 146 147 { 148 const char* repl = NULL; 149 size_t lit_len = 0u; 150 size_t repl_len = 0u; 151 if (find_token(p, end, toks, &repl, &lit_len, &repl_len)) { 152 if (emit_bytes(storage, &off, storage_cap, repl, repl_len) != BUILD_OK) 153 return BUILD_ERR; 154 if (add_size(&word_len, repl_len) != BUILD_OK) return BUILD_ERR; 155 p += lit_len; 156 } else { 157 if (emit_bytes(storage, &off, storage_cap, p, 1u) != BUILD_OK) 158 return BUILD_ERR; 159 ++word_len; 160 ++p; 161 } 162 } 163 } 164 165 if (quote != 0) return BUILD_ERR; 166 if (in_word) { 167 if (argv) { 168 if (argc >= *argc_io || off >= storage_cap) return BUILD_ERR; 169 storage[off] = '\0'; 170 argv[argc].s = storage + word_start; 171 argv[argc].len = word_len; 172 } 173 if (add_size(&off, 1u) != BUILD_OK) return BUILD_ERR; 174 ++argc; 175 } 176 if (argv && argc != *argc_io) return BUILD_ERR; 177 *argc_io = argc; 178 *storage_len_out = off; 179 return BUILD_OK; 180 } 181 182 static void rendered_argv_free(const KitContext* ctx, RenderedArgv* r) { 183 if (!ctx || !ctx->heap || !r) return; 184 if (r->storage) ctx->heap->free(ctx->heap, r->storage, r->storage_size); 185 if (r->argv) ctx->heap->free(ctx->heap, r->argv, r->argc * sizeof *r->argv); 186 r->argv = NULL; 187 r->argc = 0u; 188 r->storage = NULL; 189 r->storage_size = 0u; 190 } 191 192 static int render_argv(const KitContext* ctx, KitSlice tmpl, 193 const RenderTokenSet* toks, RenderedArgv* out) { 194 size_t argc = 0u; 195 size_t storage_len = 0u; 196 if (!ctx || !ctx->heap || !out) return BUILD_ERR; 197 memset(out, 0, sizeof *out); 198 if (render_pass(tmpl, toks, NULL, &argc, NULL, 0u, &storage_len) != 199 BUILD_OK || 200 argc == 0u) 201 return BUILD_ERR; 202 out->argv = (KitSlice*)ctx->heap->alloc(ctx->heap, argc * sizeof *out->argv, 203 _Alignof(KitSlice)); 204 if (!out->argv) return BUILD_ERR; 205 out->storage = 206 (char*)ctx->heap->alloc(ctx->heap, storage_len, _Alignof(char)); 207 if (!out->storage) { 208 rendered_argv_free(ctx, out); 209 return BUILD_ERR; 210 } 211 out->argc = argc; 212 out->storage_size = storage_len; 213 if (render_pass(tmpl, toks, out->argv, &argc, out->storage, storage_len, 214 &storage_len) != BUILD_OK) { 215 rendered_argv_free(ctx, out); 216 return BUILD_ERR; 217 } 218 return BUILD_OK; 219 } 220 221 static int read_fetched_file(const KitContext* ctx, const char* path, 222 KitFileData* out) { 223 if (!ctx || !ctx->file_io || !ctx->file_io->read_all || !path || !out) 224 return BUILD_ERR; 225 out->data = NULL; 226 out->size = 0u; 227 out->token = NULL; 228 return ctx->file_io->read_all(ctx->file_io->user, path, out) == KIT_OK 229 ? BUILD_OK 230 : BUILD_ERR; 231 } 232 233 static void release_fetched_file(const KitContext* ctx, KitFileData* fd) { 234 if (!ctx || !ctx->file_io || !fd) return; 235 if (fd->data && ctx->file_io->release) 236 ctx->file_io->release(ctx->file_io->user, fd); 237 fd->data = NULL; 238 fd->size = 0u; 239 fd->token = NULL; 240 } 241 242 static int verify_bytes(const uint8_t* data, size_t len, 243 const uint8_t id[BUILD_HASH_LEN], KitBlobInfo* out) { 244 KitBlobInfo bi; 245 if (!id || (!data && len)) return BUILD_ERR; 246 kit_blob_info(&bi, data, len); 247 if (!build_id_eq(bi.id, id)) return BUILD_ERR; 248 if (out) *out = bi; 249 return BUILD_OK; 250 } 251 252 static int run_fetch(const KitBuildExec* exec, const KitSlice* argv, 253 size_t argc, KitSlice cwd) { 254 KitBuildProc* proc = NULL; 255 KitExecOpts eo; 256 int exit_code = 1; 257 if (!exec || !exec->spawn || !exec->wait || !argv || argc == 0u) 258 return BUILD_ERR; 259 memset(&eo, 0, sizeof eo); 260 eo.argv = argv; 261 eo.argc = argc; 262 eo.cwd = cwd; 263 if (exec->spawn(exec->user, &eo, &proc) != 0 || !proc) return BUILD_ERR; 264 if (exec->wait(exec->user, proc, &exit_code, NULL, NULL) != 0) 265 return BUILD_ERR; 266 return exit_code == 0 ? BUILD_OK : BUILD_ERR; 267 } 268 269 int build_remote_fetch(const KitContext* ctx, const KitBuildExec* exec, 270 const KitBuildObjectRemote* remotes, size_t nremotes, 271 KitCas* cas, KitSlice tmp_dir, BuildRemoteKind kind, 272 const uint8_t id[BUILD_HASH_LEN]) { 273 char hex[BUILD_HEX_LEN]; 274 char pp[BUILD_PP_LEN + 1u]; 275 char leaf[16u + BUILD_HEX_LEN]; 276 char out_path[BUILD_PATH_MAX]; 277 const char* kind_s; 278 size_t i; 279 280 if (!ctx || !ctx->heap || !exec || !remotes || nremotes == 0u || !cas || !id) 281 return BUILD_ERR; 282 kind_s = remote_kind_name(kind); 283 if (!kind_s) return BUILD_ERR; 284 kit_hex_encode(hex, id, BUILD_HASH_LEN); 285 pp[0] = hex[0]; 286 pp[1] = hex[1]; 287 pp[2] = '\0'; 288 if (snprintf(leaf, sizeof leaf, "fetch-%s-%s", kind_s, hex) >= 289 (int)sizeof leaf) 290 return BUILD_ERR; 291 if (path_join(out_path, sizeof out_path, tmp_dir, leaf) != BUILD_OK) 292 return BUILD_ERR; 293 294 for (i = 0u; i < nremotes; ++i) { 295 RenderTokenSet toks; 296 RenderedArgv argv; 297 KitFileData fd; 298 KitBlobInfo bi; 299 int verified; 300 301 toks.kind = kind_s; 302 toks.pp = pp; 303 toks.id = hex; 304 toks.out = out_path; 305 memset(&argv, 0, sizeof argv); 306 if (render_argv(ctx, remotes[i].fetch_argv_template, &toks, &argv) != 307 BUILD_OK) { 308 build_diagf(ctx, "remote fetch: bad argv template"); 309 continue; 310 } 311 if (run_fetch(exec, argv.argv, argv.argc, tmp_dir) != BUILD_OK) { 312 rendered_argv_free(ctx, &argv); 313 continue; 314 } 315 rendered_argv_free(ctx, &argv); 316 317 if (read_fetched_file(ctx, out_path, &fd) != BUILD_OK) continue; 318 verified = verify_bytes(fd.data, fd.size, id, &bi); 319 if (verified != BUILD_OK) { 320 release_fetched_file(ctx, &fd); 321 build_diagf(ctx, "remote fetch: %s %s failed content verification", 322 kind_s, hex); 323 continue; 324 } 325 326 if (kind == BUILD_REMOTE_BLOB) { 327 KitBlobInfo stored; 328 if (kit_cas_add_blob(cas, fd.data, fd.size, &stored) == KIT_OK && 329 build_id_eq(stored.id, id)) { 330 release_fetched_file(ctx, &fd); 331 return BUILD_OK; 332 } 333 release_fetched_file(ctx, &fd); 334 continue; 335 } 336 337 { 338 uint8_t stored_tree[BUILD_HASH_LEN]; 339 if (kit_cas_add_tree_manifest(cas, fd.data, fd.size, stored_tree) == 340 KIT_OK && 341 build_id_eq(stored_tree, id)) { 342 release_fetched_file(ctx, &fd); 343 return BUILD_OK; 344 } 345 } 346 release_fetched_file(ctx, &fd); 347 } 348 return BUILD_ERR; 349 }