kit

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

bundle.c (58097B)


      1 #include "bundle.h"
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 #define BUILD_BUNDLE_MANIFEST_PATH "manifest"
      7 #define BUILD_BUNDLE_TRACE_DIR "trace"
      8 #define BUILD_BUNDLE_BLOB_DIR "blob"
      9 #define BUILD_BUNDLE_TEST_DEEP 2u
     10 #define BUILD_BUNDLE_TEST_SHALLOW 3u
     11 
     12 #if defined(__GNUC__) || defined(__clang__)
     13 #define BUILD_MAYBE_UNUSED __attribute__((unused))
     14 #else
     15 #define BUILD_MAYBE_UNUSED
     16 #endif
     17 
     18 typedef struct BuildIdVec {
     19   uint8_t (*ids)[BUILD_HASH_LEN];
     20   size_t n;
     21   size_t cap;
     22 } BuildIdVec;
     23 
     24 typedef struct ParsedTrace {
     25   char target[BUILD_TARGET_MAX];
     26   uint8_t kind;
     27   uint8_t output[BUILD_HASH_LEN];
     28 } ParsedTrace;
     29 
     30 typedef struct RenderedArgv {
     31   KitSlice* argv;
     32   size_t argc;
     33   char* storage;
     34   size_t storage_size;
     35 } RenderedArgv;
     36 
     37 typedef struct TraceRenderTokens {
     38   const char* target;
     39   const char* out;
     40 } TraceRenderTokens;
     41 
     42 static int write_cstr(KitWriter* out, const char* s) {
     43   return out && kit_writer_write(out, s, strlen(s)) == KIT_OK ? BUILD_OK
     44                                                               : BUILD_ERR;
     45 }
     46 
     47 static int hex_val(char c, unsigned* out) {
     48   if (c >= '0' && c <= '9') {
     49     *out = (unsigned)(c - '0');
     50     return BUILD_OK;
     51   }
     52   if (c >= 'a' && c <= 'f') {
     53     *out = (unsigned)(c - 'a') + 10u;
     54     return BUILD_OK;
     55   }
     56   return BUILD_ERR;
     57 }
     58 
     59 static void hex_encode(char out[BUILD_HEX_LEN],
     60                        const uint8_t in[BUILD_HASH_LEN]) {
     61   size_t i;
     62   static const char hexdigits[] = "0123456789abcdef";
     63   for (i = 0; i < BUILD_HASH_LEN; ++i) {
     64     out[2u * i] = hexdigits[in[i] >> 4];
     65     out[2u * i + 1u] = hexdigits[in[i] & 0x0fu];
     66   }
     67   out[2u * BUILD_HASH_LEN] = '\0';
     68 }
     69 
     70 static int hex_decode(const char* s, uint8_t out[BUILD_HASH_LEN]) {
     71   size_t i;
     72   if (!s || strlen(s) != 2u * BUILD_HASH_LEN) return BUILD_ERR;
     73   for (i = 0; i < BUILD_HASH_LEN; ++i) {
     74     unsigned hi, lo;
     75     if (hex_val(s[2u * i], &hi) != BUILD_OK ||
     76         hex_val(s[2u * i + 1u], &lo) != BUILD_OK)
     77       return BUILD_ERR;
     78     out[i] = (uint8_t)((hi << 4) | lo);
     79   }
     80   return BUILD_OK;
     81 }
     82 
     83 static int valid_token(const char* s, size_t cap) {
     84   size_t i;
     85   if (!s || !s[0]) return 0;
     86   for (i = 0; s[i]; ++i) {
     87     unsigned char c = (unsigned char)s[i];
     88     if (i + 1u >= cap || c <= 0x20u || c >= 0x7fu) return 0;
     89   }
     90   return 1;
     91 }
     92 
     93 static KitSlice str_slice(const char* s) { return kit_slice_cstr(s); }
     94 
     95 static int BUILD_MAYBE_UNUSED path_set(char* out, size_t cap, KitSlice s) {
     96   if (!out || cap == 0u || !s.s || s.len + 1u > cap) return BUILD_ERR;
     97   memcpy(out, s.s, s.len);
     98   out[s.len] = '\0';
     99   return BUILD_OK;
    100 }
    101 
    102 static int path_join2(char* out, size_t cap, const char* a, const char* b) {
    103   size_t na, nb;
    104   int need_sep;
    105   if (!out || cap == 0u || !a || !b) return BUILD_ERR;
    106   na = strlen(a);
    107   nb = strlen(b);
    108   need_sep = na > 0u && a[na - 1u] != '/';
    109   if (na + (need_sep ? 1u : 0u) + nb + 1u > cap) return BUILD_ERR;
    110   memcpy(out, a, na);
    111   if (need_sep) out[na++] = '/';
    112   memcpy(out + na, b, nb);
    113   out[na + nb] = '\0';
    114   return BUILD_OK;
    115 }
    116 
    117 static int mkdir_p_host(const KitBuildCoordinator* c, const char* path) {
    118   if (!c || !c->host.cas_host || !c->host.cas_host->mkdir_p || !path)
    119     return BUILD_ERR;
    120   return c->host.cas_host->mkdir_p(c->host.cas_host->user, path) == 0
    121              ? BUILD_OK
    122              : BUILD_ERR;
    123 }
    124 
    125 static int BUILD_MAYBE_UNUSED remove_path(const KitBuildCoordinator* c,
    126                                           const char* path, int recursive) {
    127   if (!c || !c->host.store_io || !c->host.store_io->remove || !path)
    128     return BUILD_ERR;
    129   return c->host.store_io->remove(c->host.store_io->user, str_slice(path),
    130                                   recursive) == 0
    131              ? BUILD_OK
    132              : BUILD_ERR;
    133 }
    134 
    135 static int BUILD_MAYBE_UNUSED make_tmp_dir(const KitBuildCoordinator* c,
    136                                            char* out, size_t cap) {
    137   char parent[BUILD_PATH_MAX];
    138   if (!c || !c->host.store_io || !c->host.store_io->make_temp_dir)
    139     return BUILD_ERR;
    140   if (path_join2(parent, sizeof parent, c->store.root, "tmp") != BUILD_OK)
    141     return BUILD_ERR;
    142   if (mkdir_p_host(c, parent) != BUILD_OK) return BUILD_ERR;
    143   return c->host.store_io->make_temp_dir(c->host.store_io->user,
    144                                          str_slice(parent), out, cap) == 0
    145              ? BUILD_OK
    146              : BUILD_ERR;
    147 }
    148 
    149 static int write_file(const KitBuildCoordinator* c, const char* path,
    150                       const uint8_t* data, size_t len) {
    151   KitWriter* w = NULL;
    152   KitStatus st;
    153   if (!c || !c->host.cas_host || !c->host.cas_host->file_io ||
    154       !c->host.cas_host->file_io->open_writer || !path || (!data && len))
    155     return BUILD_ERR;
    156   if (c->host.cas_host->file_io->open_writer(
    157           c->host.cas_host->file_io->user, path, &w) != KIT_OK ||
    158       !w)
    159     return BUILD_ERR;
    160   st = len ? kit_writer_write(w, data, len) : KIT_OK;
    161   if (st == KIT_OK) st = kit_writer_status(w);
    162   kit_writer_close(w);
    163   return st == KIT_OK ? BUILD_OK : BUILD_ERR;
    164 }
    165 
    166 static int read_file(const KitBuildCoordinator* c, const char* path,
    167                      KitFileData* out) {
    168   if (!c || !c->host.cas_host || !c->host.cas_host->file_io ||
    169       !c->host.cas_host->file_io->read_all || !path || !out)
    170     return BUILD_ERR;
    171   out->data = NULL;
    172   out->size = 0u;
    173   out->token = NULL;
    174   return c->host.cas_host->file_io->read_all(c->host.cas_host->file_io->user,
    175                                              path, out) == KIT_OK
    176              ? BUILD_OK
    177              : BUILD_ERR;
    178 }
    179 
    180 static void release_file(const KitBuildCoordinator* c, KitFileData* fd) {
    181   if (!c || !fd || !c->host.cas_host || !c->host.cas_host->file_io ||
    182       !c->host.cas_host->file_io->release)
    183     return;
    184   if (fd->data) c->host.cas_host->file_io->release(c->host.cas_host->file_io->user, fd);
    185   fd->data = NULL;
    186   fd->size = 0u;
    187   fd->token = NULL;
    188 }
    189 
    190 static int ensure_parent_dir(const KitBuildCoordinator* c, const char* path) {
    191   char parent[BUILD_PATH_MAX];
    192   size_t n, i;
    193   if (!path) return BUILD_ERR;
    194   n = strlen(path);
    195   for (i = n; i > 0u; --i) {
    196     if (path[i - 1u] == '/') {
    197       size_t len = i - 1u;
    198       if (len == 0u) len = 1u;
    199       if (len + 1u > sizeof parent) return BUILD_ERR;
    200       memcpy(parent, path, len);
    201       parent[len] = '\0';
    202       return mkdir_p_host(c, parent);
    203     }
    204   }
    205   return BUILD_OK;
    206 }
    207 
    208 static int payload_id_path(char* out, size_t cap, const char* root,
    209                            const char* kind,
    210                            const uint8_t id[BUILD_HASH_LEN]) {
    211   char hex[BUILD_HEX_LEN];
    212   char rel[BUILD_PATH_MAX];
    213   kit_hex_encode(hex, id, BUILD_HASH_LEN);
    214   if (!root || !kind || !id) return BUILD_ERR;
    215   if (snprintf(rel, sizeof rel, "%s/%c%c/%s", kind, hex[0], hex[1], hex) >=
    216       (int)sizeof rel)
    217     return BUILD_ERR;
    218   return path_join2(out, cap, root, rel);
    219 }
    220 
    221 static size_t count_lines(const uint8_t* data, size_t len) {
    222   size_t i, n = 0u;
    223   for (i = 0u; i < len; ++i)
    224     if (data[i] == '\n') ++n;
    225   return n;
    226 }
    227 
    228 static int id_vec_contains(const BuildIdVec* v,
    229                            const uint8_t id[BUILD_HASH_LEN]) {
    230   size_t i;
    231   if (!v || !id) return 0;
    232   for (i = 0u; i < v->n; ++i)
    233     if (build_id_eq(v->ids[i], id)) return 1;
    234   return 0;
    235 }
    236 
    237 static int id_vec_add(const KitContext* ctx, BuildIdVec* v,
    238                       const uint8_t id[BUILD_HASH_LEN]) {
    239   uint8_t (*next)[BUILD_HASH_LEN];
    240   size_t next_cap;
    241   if (!ctx || !ctx->heap || !v || !id) return BUILD_ERR;
    242   if (id_vec_contains(v, id)) return BUILD_OK;
    243   if (v->n == v->cap) {
    244     next_cap = v->cap ? 2u * v->cap : 16u;
    245     next = (uint8_t(*)[BUILD_HASH_LEN])ctx->heap->alloc(
    246         ctx->heap, next_cap * sizeof *next, _Alignof(uint8_t));
    247     if (!next) return BUILD_ERR;
    248     if (v->ids) {
    249       memcpy(next, v->ids, v->n * sizeof *next);
    250       ctx->heap->free(ctx->heap, v->ids, v->cap * sizeof *v->ids);
    251     }
    252     v->ids = next;
    253     v->cap = next_cap;
    254   }
    255   memcpy(v->ids[v->n++], id, BUILD_HASH_LEN);
    256   return BUILD_OK;
    257 }
    258 
    259 static void BUILD_MAYBE_UNUSED id_vec_free(const KitContext* ctx,
    260                                            BuildIdVec* v) {
    261   if (!ctx || !ctx->heap || !v) return;
    262   if (v->ids) ctx->heap->free(ctx->heap, v->ids, v->cap * sizeof *v->ids);
    263   v->ids = NULL;
    264   v->n = 0u;
    265   v->cap = 0u;
    266 }
    267 
    268 static int kind_valid(uint8_t kind) {
    269   return kind == (uint8_t)BUILD_TRACE_DEEP ||
    270          kind == (uint8_t)BUILD_TRACE_SHALLOW ||
    271          kind == (uint8_t)BUILD_BUNDLE_TEST_DEEP ||
    272          kind == (uint8_t)BUILD_BUNDLE_TEST_SHALLOW;
    273 }
    274 
    275 static const char* kind_name(uint8_t kind) {
    276   if (kind == (uint8_t)BUILD_TRACE_DEEP) return "deep";
    277   if (kind == (uint8_t)BUILD_TRACE_SHALLOW) return "shallow";
    278   if (kind == (uint8_t)BUILD_BUNDLE_TEST_DEEP) return "test-deep";
    279   if (kind == (uint8_t)BUILD_BUNDLE_TEST_SHALLOW) return "test-shallow";
    280   return NULL;
    281 }
    282 
    283 static int kind_parse(const char* s, uint8_t* out) {
    284   if (strcmp(s, "deep") == 0) {
    285     *out = (uint8_t)BUILD_TRACE_DEEP;
    286     return BUILD_OK;
    287   }
    288   if (strcmp(s, "shallow") == 0) {
    289     *out = (uint8_t)BUILD_TRACE_SHALLOW;
    290     return BUILD_OK;
    291   }
    292   if (strcmp(s, "test-deep") == 0) {
    293     *out = (uint8_t)BUILD_BUNDLE_TEST_DEEP;
    294     return BUILD_OK;
    295   }
    296   if (strcmp(s, "test-shallow") == 0) {
    297     *out = (uint8_t)BUILD_BUNDLE_TEST_SHALLOW;
    298     return BUILD_OK;
    299   }
    300   return BUILD_ERR;
    301 }
    302 
    303 static void* heap_array(const KitContext* ctx, size_t n, size_t elem,
    304                         size_t align) {
    305   if (!ctx || !ctx->heap || elem == 0u) return NULL;
    306   if (n == 0u) n = 1u;
    307   if (n > (size_t)-1 / elem) return NULL;
    308   return ctx->heap->alloc(ctx->heap, n * elem, align);
    309 }
    310 
    311 static void heap_free_array(const KitContext* ctx, void* p, size_t n,
    312                             size_t elem) {
    313   if (!ctx || !ctx->heap || !p || elem == 0u) return;
    314   if (n == 0u) n = 1u;
    315   ctx->heap->free(ctx->heap, p, n * elem);
    316 }
    317 
    318 static int BUILD_MAYBE_UNUSED parse_trace_header(const KitContext* ctx,
    319                                                  const uint8_t* data,
    320                                                  size_t len,
    321                                                  ParsedTrace* out) {
    322   char err[160];
    323   if (!ctx || !data || !out) return BUILD_ERR;
    324   memset(out, 0, sizeof *out);
    325   if (len >= sizeof BUILD_DEEP_MAGIC &&
    326       memcmp(data, BUILD_DEEP_MAGIC "\n", sizeof BUILD_DEEP_MAGIC) == 0) {
    327     BuildDeepTrace t;
    328     if (build_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    329       return BUILD_ERR;
    330     snprintf(out->target, sizeof out->target, "%s", t.target);
    331     out->kind = (uint8_t)BUILD_TRACE_DEEP;
    332     memcpy(out->output, t.output, BUILD_HASH_LEN);
    333     return BUILD_OK;
    334   }
    335   if (len >= sizeof BUILD_TEST_DEEP_MAGIC &&
    336       memcmp(data, BUILD_TEST_DEEP_MAGIC "\n",
    337              sizeof BUILD_TEST_DEEP_MAGIC) == 0) {
    338     BuildDeepTrace t;
    339     if (build_test_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    340       return BUILD_ERR;
    341     snprintf(out->target, sizeof out->target, "%s", t.target);
    342     out->kind = (uint8_t)BUILD_BUNDLE_TEST_DEEP;
    343     memcpy(out->output, t.output, BUILD_HASH_LEN);
    344     return BUILD_OK;
    345   }
    346   if (len >= sizeof BUILD_SHALLOW_MAGIC &&
    347       memcmp(data, BUILD_SHALLOW_MAGIC "\n", sizeof BUILD_SHALLOW_MAGIC) == 0) {
    348     BuildShallowTrace t;
    349     size_t rows = count_lines(data, len);
    350     memset(&t, 0, sizeof t);
    351     t.configs = (BuildConfigLeaf*)heap_array(
    352         ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    353     t.sources = (BuildSourceLeaf*)heap_array(ctx, rows, sizeof *t.sources,
    354                                              _Alignof(BuildSourceLeaf));
    355     t.globs = (BuildGlobLeaf*)heap_array(ctx, rows, sizeof *t.globs,
    356                                          _Alignof(BuildGlobLeaf));
    357     t.blobs = (BuildBlobLeaf*)heap_array(ctx, rows, sizeof *t.blobs,
    358                                             _Alignof(BuildBlobLeaf));
    359     t.deps = (BuildDepEdge*)heap_array(ctx, rows, sizeof *t.deps,
    360                                        _Alignof(BuildDepEdge));
    361     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps) {
    362       heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    363       heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    364       heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    365       heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    366       heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    367       return BUILD_ERR;
    368     }
    369     t.cap_configs = rows;
    370     t.cap_sources = rows;
    371     t.cap_globs = rows;
    372     t.cap_blobs = rows;
    373     t.cap_deps = rows;
    374     if (build_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK) {
    375       heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    376       heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    377       heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    378       heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    379       heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    380       return BUILD_ERR;
    381     }
    382     snprintf(out->target, sizeof out->target, "%s", t.target);
    383     out->kind = (uint8_t)BUILD_TRACE_SHALLOW;
    384     memcpy(out->output, t.output, BUILD_HASH_LEN);
    385     heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    386     heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    387     heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    388     heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    389     heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    390     return BUILD_OK;
    391   }
    392   if (len >= sizeof BUILD_TEST_SHALLOW_MAGIC &&
    393       memcmp(data, BUILD_TEST_SHALLOW_MAGIC "\n",
    394              sizeof BUILD_TEST_SHALLOW_MAGIC) == 0) {
    395     BuildShallowTrace t;
    396     size_t rows = count_lines(data, len);
    397     memset(&t, 0, sizeof t);
    398     t.configs = (BuildConfigLeaf*)heap_array(
    399         ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    400     t.sources = (BuildSourceLeaf*)heap_array(ctx, rows, sizeof *t.sources,
    401                                              _Alignof(BuildSourceLeaf));
    402     t.globs = (BuildGlobLeaf*)heap_array(ctx, rows, sizeof *t.globs,
    403                                          _Alignof(BuildGlobLeaf));
    404     t.blobs = (BuildBlobLeaf*)heap_array(ctx, rows, sizeof *t.blobs,
    405                                             _Alignof(BuildBlobLeaf));
    406     t.deps = (BuildDepEdge*)heap_array(ctx, rows, sizeof *t.deps,
    407                                        _Alignof(BuildDepEdge));
    408     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps) {
    409       heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    410       heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    411       heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    412       heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    413       heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    414       return BUILD_ERR;
    415     }
    416     t.cap_configs = rows;
    417     t.cap_sources = rows;
    418     t.cap_globs = rows;
    419     t.cap_blobs = rows;
    420     t.cap_deps = rows;
    421     if (build_test_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK) {
    422       heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    423       heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    424       heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    425       heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    426       heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    427       return BUILD_ERR;
    428     }
    429     snprintf(out->target, sizeof out->target, "%s", t.target);
    430     out->kind = (uint8_t)BUILD_BUNDLE_TEST_SHALLOW;
    431     memcpy(out->output, t.output, BUILD_HASH_LEN);
    432     heap_free_array(ctx, t.configs, rows, sizeof *t.configs);
    433     heap_free_array(ctx, t.sources, rows, sizeof *t.sources);
    434     heap_free_array(ctx, t.globs, rows, sizeof *t.globs);
    435     heap_free_array(ctx, t.blobs, rows, sizeof *t.blobs);
    436     heap_free_array(ctx, t.deps, rows, sizeof *t.deps);
    437     return BUILD_OK;
    438   }
    439   return BUILD_ERR;
    440 }
    441 
    442 static int claim_cmp(const BuildTraceClaim* a, const BuildTraceClaim* b) {
    443   int c = strcmp(a->target, b->target);
    444   if (c != 0) return c;
    445   if (a->kind != b->kind) return a->kind < b->kind ? -1 : 1;
    446   c = memcmp(a->trace_id, b->trace_id, BUILD_HASH_LEN);
    447   if (c != 0) return c;
    448   return memcmp(a->output_tree, b->output_tree, BUILD_HASH_LEN);
    449 }
    450 
    451 int build_bundle_manifest_emit(const BuildTraceClaim* claims, size_t n,
    452                                KitWriter* out) {
    453   const BuildTraceClaim* prev = NULL;
    454   size_t emitted = 0;
    455   if (!out || (n && !claims)) return BUILD_ERR;
    456   if (write_cstr(out, BUILD_TRACES_MAGIC "\n") != BUILD_OK) return BUILD_ERR;
    457   while (emitted < n) {
    458     const BuildTraceClaim* best = NULL;
    459     size_t i;
    460     for (i = 0; i < n; ++i) {
    461       const BuildTraceClaim* cur = &claims[i];
    462       if (!valid_token(cur->target, BUILD_TARGET_MAX) || !kind_valid(cur->kind))
    463         return BUILD_ERR;
    464       if ((!prev || claim_cmp(prev, cur) < 0) &&
    465           (!best || claim_cmp(cur, best) < 0))
    466         best = cur;
    467     }
    468     if (!best) return BUILD_ERR;
    469     {
    470       char tid[BUILD_HEX_LEN], tree[BUILD_HEX_LEN], line[BUILD_TARGET_MAX + 160u];
    471       hex_encode(tid, best->trace_id);
    472       hex_encode(tree, best->output_tree);
    473       snprintf(line, sizeof line, "%s %s %s %s\n", best->target,
    474                kind_name(best->kind), tid, tree);
    475       if (write_cstr(out, line) != BUILD_OK) return BUILD_ERR;
    476     }
    477     prev = best;
    478     ++emitted;
    479   }
    480   return kit_writer_status(out) == KIT_OK ? BUILD_OK : BUILD_ERR;
    481 }
    482 
    483 static int set_err(char* err, size_t errcap, const char* msg) {
    484   if (err && errcap) snprintf(err, errcap, "%s", msg);
    485   return BUILD_ERR;
    486 }
    487 
    488 static int next_line(const uint8_t* data, size_t len, size_t* pos,
    489                      char* line, size_t cap, char* err, size_t errcap) {
    490   size_t end = *pos;
    491   size_t n, i;
    492   if (*pos >= len) return 0;
    493   while (end < len && data[end] != '\n') ++end;
    494   if (end == len) return set_err(err, errcap, "missing final newline");
    495   n = end - *pos;
    496   if (n >= cap) return set_err(err, errcap, "manifest line too long");
    497   for (i = *pos; i < end; ++i)
    498     if (data[i] == 0 || data[i] == '\r')
    499       return set_err(err, errcap, "bad manifest byte");
    500   memcpy(line, data + *pos, n);
    501   line[n] = '\0';
    502   *pos = end + 1u;
    503   return 1;
    504 }
    505 
    506 static int split4(char* line, char** f) {
    507   size_t n = 0;
    508   char* p = line;
    509   while (*p) {
    510     if (*p == ' ' || n >= 4u) return BUILD_ERR;
    511     f[n++] = p;
    512     while (*p && *p != ' ') {
    513       unsigned char c = (unsigned char)*p;
    514       if (c <= 0x20u || c >= 0x7fu) return BUILD_ERR;
    515       ++p;
    516     }
    517     if (*p == ' ') {
    518       *p++ = '\0';
    519       if (!*p) return BUILD_ERR;
    520     }
    521   }
    522   return n == 4u ? BUILD_OK : BUILD_ERR;
    523 }
    524 
    525 int build_bundle_manifest_parse(const uint8_t* data, size_t len,
    526                                 BuildTraceClaim* out, size_t cap, size_t* n,
    527                                 char* err, size_t errcap) {
    528   size_t pos = 0, count = 0;
    529   char line[BUILD_TARGET_MAX + 160u];
    530   BuildTraceClaim prev;
    531   int have_prev = 0;
    532   int r;
    533   if (!data || !n || (cap && !out))
    534     return set_err(err, errcap, "missing manifest storage");
    535   *n = 0;
    536   r = next_line(data, len, &pos, line, sizeof line, err, errcap);
    537   if (r != 1 || strcmp(line, BUILD_TRACES_MAGIC) != 0)
    538     return set_err(err, errcap, "bad trace manifest magic/version");
    539   while (pos < len) {
    540     char* f[4];
    541     BuildTraceClaim row;
    542     r = next_line(data, len, &pos, line, sizeof line, err, errcap);
    543     if (r != 1) return BUILD_ERR;
    544     if (split4(line, f) != BUILD_OK)
    545       return set_err(err, errcap, "bad trace manifest row");
    546     if (!valid_token(f[0], BUILD_TARGET_MAX) ||
    547         kind_parse(f[1], &row.kind) != BUILD_OK ||
    548         hex_decode(f[2], row.trace_id) != BUILD_OK ||
    549         hex_decode(f[3], row.output_tree) != BUILD_OK)
    550       return set_err(err, errcap, "bad trace manifest field");
    551     snprintf(row.target, sizeof row.target, "%s", f[0]);
    552     if (have_prev && claim_cmp(&prev, &row) >= 0)
    553       return set_err(err, errcap, "non-canonical trace manifest ordering");
    554     if (count >= cap) return set_err(err, errcap, "too many trace claims");
    555     out[count++] = row;
    556     prev = row;
    557     have_prev = 1;
    558   }
    559   *n = count;
    560   return BUILD_OK;
    561 }
    562 
    563 static int payload_write_blob_from_cas(KitBuildCoordinator* c,
    564                                        const uint8_t id[BUILD_HASH_LEN],
    565                                        const char* root, BuildIdVec* seen) {
    566   KitFileData fd;
    567   char path[BUILD_PATH_MAX];
    568   int ok = BUILD_ERR;
    569   if (!c || !id || !root || !seen) return BUILD_ERR;
    570   if (id_vec_contains(seen, id)) return BUILD_OK;
    571   if (kit_cas_get_blob(c->cas, id, &fd) != KIT_OK) return BUILD_ERR;
    572   if (payload_id_path(path, sizeof path, root, BUILD_BUNDLE_BLOB_DIR, id) !=
    573           BUILD_OK ||
    574       ensure_parent_dir(c, path) != BUILD_OK ||
    575       write_file(c, path, fd.data, fd.size) != BUILD_OK)
    576     goto out;
    577   ok = id_vec_add(c->ctx, seen, id);
    578 out:
    579   kit_cas_release(c->cas, &fd);
    580   return ok;
    581 }
    582 
    583 static int payload_install_blob(KitBuildCoordinator* c,
    584                                 const uint8_t id[BUILD_HASH_LEN],
    585                                 const char* root, BuildIdVec* seen,
    586                                 const uint8_t** data_out, size_t* len_out,
    587                                 KitFileData* fd_out) {
    588   KitBlobInfo info;
    589   char path[BUILD_PATH_MAX];
    590   if (!c || !id || !root || !seen || !fd_out) return BUILD_ERR;
    591   if (data_out) *data_out = NULL;
    592   if (len_out) *len_out = 0u;
    593   fd_out->data = NULL;
    594   fd_out->size = 0u;
    595   fd_out->token = NULL;
    596   if (payload_id_path(path, sizeof path, root, BUILD_BUNDLE_BLOB_DIR, id) !=
    597           BUILD_OK ||
    598       read_file(c, path, fd_out) != BUILD_OK)
    599     return BUILD_ERR;
    600   if (kit_cas_add_blob(c->cas, fd_out->data, fd_out->size, &info) != KIT_OK ||
    601       !build_id_eq(info.id, id)) {
    602     release_file(c, fd_out);
    603     return BUILD_ERR;
    604   }
    605   if (id_vec_add(c->ctx, seen, id) != BUILD_OK) {
    606     release_file(c, fd_out);
    607     return BUILD_ERR;
    608   }
    609   if (data_out) *data_out = fd_out->data;
    610   if (len_out) *len_out = fd_out->size;
    611   return BUILD_OK;
    612 }
    613 
    614 static int export_deepset_closure(KitBuildCoordinator* c,
    615                                   const uint8_t id[BUILD_HASH_LEN],
    616                                   const char* root, BuildIdVec* seen) {
    617   KitFileData fd;
    618   BuildDeepSet ds;
    619   size_t rows;
    620   size_t i;
    621   char err[160];
    622   int ok = BUILD_ERR;
    623   if (id_vec_contains(seen, id)) return BUILD_OK;
    624   if (payload_write_blob_from_cas(c, id, root, seen) != BUILD_OK)
    625     return BUILD_ERR;
    626   if (kit_cas_get_blob(c->cas, id, &fd) != KIT_OK) return BUILD_ERR;
    627   rows = count_lines(fd.data, fd.size);
    628   memset(&ds, 0, sizeof ds);
    629   ds.configs = (BuildConfigLeaf*)heap_array(c->ctx, rows, sizeof *ds.configs,
    630                                             _Alignof(BuildConfigLeaf));
    631   ds.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *ds.sources,
    632                                             _Alignof(BuildSourceLeaf));
    633   ds.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *ds.globs,
    634                                         _Alignof(BuildGlobLeaf));
    635   ds.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *ds.blobs,
    636                                            _Alignof(BuildBlobLeaf));
    637   ds.children = (uint8_t(*)[BUILD_HASH_LEN])heap_array(
    638       c->ctx, rows, sizeof *ds.children, _Alignof(uint8_t));
    639   if (!ds.configs || !ds.sources || !ds.globs || !ds.blobs || !ds.children)
    640     goto out;
    641   ds.cap_configs = rows;
    642   ds.cap_sources = rows;
    643   ds.cap_globs = rows;
    644   ds.cap_blobs = rows;
    645   ds.cap_children = rows;
    646   if (build_deepset_parse(fd.data, fd.size, &ds, err, sizeof err) != BUILD_OK)
    647     goto out;
    648   for (i = 0u; i < ds.n_blobs; ++i)
    649     if (payload_write_blob_from_cas(c, ds.blobs[i].blob, root, seen) !=
    650         BUILD_OK)
    651       goto out;
    652   for (i = 0u; i < ds.n_children; ++i)
    653     if (export_deepset_closure(c, ds.children[i], root, seen) != BUILD_OK)
    654       goto out;
    655   ok = BUILD_OK;
    656 out:
    657   heap_free_array(c->ctx, ds.configs, rows, sizeof *ds.configs);
    658   heap_free_array(c->ctx, ds.sources, rows, sizeof *ds.sources);
    659   heap_free_array(c->ctx, ds.globs, rows, sizeof *ds.globs);
    660   heap_free_array(c->ctx, ds.blobs, rows, sizeof *ds.blobs);
    661   heap_free_array(c->ctx, ds.children, rows, sizeof *ds.children);
    662   kit_cas_release(c->cas, &fd);
    663   return ok;
    664 }
    665 
    666 static int import_deepset_closure(KitBuildCoordinator* c,
    667                                   const uint8_t id[BUILD_HASH_LEN],
    668                                   const char* root, BuildIdVec* seen) {
    669   KitFileData fd;
    670   const uint8_t* data = NULL;
    671   size_t len = 0u;
    672   BuildDeepSet ds;
    673   size_t rows;
    674   size_t i;
    675   char err[160];
    676   int ok = BUILD_ERR;
    677   if (id_vec_contains(seen, id)) return BUILD_OK;
    678   if (payload_install_blob(c, id, root, seen, &data, &len, &fd) != BUILD_OK)
    679     return BUILD_ERR;
    680   rows = count_lines(data, len);
    681   memset(&ds, 0, sizeof ds);
    682   ds.configs = (BuildConfigLeaf*)heap_array(c->ctx, rows, sizeof *ds.configs,
    683                                             _Alignof(BuildConfigLeaf));
    684   ds.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *ds.sources,
    685                                             _Alignof(BuildSourceLeaf));
    686   ds.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *ds.globs,
    687                                         _Alignof(BuildGlobLeaf));
    688   ds.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *ds.blobs,
    689                                            _Alignof(BuildBlobLeaf));
    690   ds.children = (uint8_t(*)[BUILD_HASH_LEN])heap_array(
    691       c->ctx, rows, sizeof *ds.children, _Alignof(uint8_t));
    692   if (!ds.configs || !ds.sources || !ds.globs || !ds.blobs || !ds.children)
    693     goto out;
    694   ds.cap_configs = rows;
    695   ds.cap_sources = rows;
    696   ds.cap_globs = rows;
    697   ds.cap_blobs = rows;
    698   ds.cap_children = rows;
    699   if (build_deepset_parse(data, len, &ds, err, sizeof err) != BUILD_OK)
    700     goto out;
    701   for (i = 0u; i < ds.n_blobs; ++i) {
    702     KitFileData dep_fd;
    703     memset(&dep_fd, 0, sizeof dep_fd);
    704     if (payload_install_blob(c, ds.blobs[i].blob, root, seen, NULL, NULL,
    705                              &dep_fd) != BUILD_OK)
    706       goto out;
    707     release_file(c, &dep_fd);
    708   }
    709   for (i = 0u; i < ds.n_children; ++i)
    710     if (import_deepset_closure(c, ds.children[i], root, seen) != BUILD_OK)
    711       goto out;
    712   ok = BUILD_OK;
    713 out:
    714   heap_free_array(c->ctx, ds.configs, rows, sizeof *ds.configs);
    715   heap_free_array(c->ctx, ds.sources, rows, sizeof *ds.sources);
    716   heap_free_array(c->ctx, ds.globs, rows, sizeof *ds.globs);
    717   heap_free_array(c->ctx, ds.blobs, rows, sizeof *ds.blobs);
    718   heap_free_array(c->ctx, ds.children, rows, sizeof *ds.children);
    719   release_file(c, &fd);
    720   return ok;
    721 }
    722 
    723 static int BUILD_MAYBE_UNUSED export_trace_refs(KitBuildCoordinator* c,
    724                                                 const uint8_t* data,
    725                                                 size_t len, const char* root,
    726                                                 BuildIdVec* seen) {
    727   char err[160];
    728   if (len >= sizeof BUILD_DEEP_MAGIC &&
    729       memcmp(data, BUILD_DEEP_MAGIC "\n", sizeof BUILD_DEEP_MAGIC) == 0) {
    730     BuildDeepTrace t;
    731     if (build_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    732       return BUILD_ERR;
    733     if (payload_write_blob_from_cas(c, t.argv, root, seen) != BUILD_OK ||
    734         export_deepset_closure(c, t.deepset, root, seen) != BUILD_OK)
    735       return BUILD_ERR;
    736     return BUILD_OK;
    737   }
    738   if (len >= sizeof BUILD_TEST_DEEP_MAGIC &&
    739       memcmp(data, BUILD_TEST_DEEP_MAGIC "\n",
    740              sizeof BUILD_TEST_DEEP_MAGIC) == 0) {
    741     BuildDeepTrace t;
    742     if (build_test_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    743       return BUILD_ERR;
    744     if (payload_write_blob_from_cas(c, t.argv, root, seen) != BUILD_OK ||
    745         export_deepset_closure(c, t.deepset, root, seen) != BUILD_OK)
    746       return BUILD_ERR;
    747     return BUILD_OK;
    748   }
    749   if (len >= sizeof BUILD_SHALLOW_MAGIC &&
    750       memcmp(data, BUILD_SHALLOW_MAGIC "\n", sizeof BUILD_SHALLOW_MAGIC) == 0) {
    751     BuildShallowTrace t;
    752     size_t rows = count_lines(data, len);
    753     size_t i;
    754     int ok = BUILD_ERR;
    755     memset(&t, 0, sizeof t);
    756     t.configs = (BuildConfigLeaf*)heap_array(
    757         c->ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    758     t.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *t.sources,
    759                                              _Alignof(BuildSourceLeaf));
    760     t.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *t.globs,
    761                                          _Alignof(BuildGlobLeaf));
    762     t.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *t.blobs,
    763                                             _Alignof(BuildBlobLeaf));
    764     t.deps = (BuildDepEdge*)heap_array(c->ctx, rows, sizeof *t.deps,
    765                                        _Alignof(BuildDepEdge));
    766     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps)
    767       goto shallow_out;
    768     t.cap_configs = rows;
    769     t.cap_sources = rows;
    770     t.cap_globs = rows;
    771     t.cap_blobs = rows;
    772     t.cap_deps = rows;
    773     if (build_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    774       goto shallow_out;
    775     if (payload_write_blob_from_cas(c, t.argv, root, seen) != BUILD_OK)
    776       goto shallow_out;
    777     for (i = 0u; i < t.n_deps; ++i) {
    778       if (payload_write_blob_from_cas(c, t.deps[i].overlay_id, root, seen) !=
    779               BUILD_OK ||
    780           payload_write_blob_from_cas(c, t.deps[i].argv_id, root, seen) !=
    781               BUILD_OK)
    782         goto shallow_out;
    783     }
    784     for (i = 0u; i < t.n_blobs; ++i)
    785       if (payload_write_blob_from_cas(c, t.blobs[i].blob, root, seen) !=
    786           BUILD_OK)
    787         goto shallow_out;
    788     ok = BUILD_OK;
    789 shallow_out:
    790     heap_free_array(c->ctx, t.configs, rows, sizeof *t.configs);
    791     heap_free_array(c->ctx, t.sources, rows, sizeof *t.sources);
    792     heap_free_array(c->ctx, t.globs, rows, sizeof *t.globs);
    793     heap_free_array(c->ctx, t.blobs, rows, sizeof *t.blobs);
    794     heap_free_array(c->ctx, t.deps, rows, sizeof *t.deps);
    795     return ok;
    796   }
    797   if (len >= sizeof BUILD_TEST_SHALLOW_MAGIC &&
    798       memcmp(data, BUILD_TEST_SHALLOW_MAGIC "\n",
    799              sizeof BUILD_TEST_SHALLOW_MAGIC) == 0) {
    800     BuildShallowTrace t;
    801     size_t rows = count_lines(data, len);
    802     size_t i;
    803     int ok = BUILD_ERR;
    804     memset(&t, 0, sizeof t);
    805     t.configs = (BuildConfigLeaf*)heap_array(
    806         c->ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    807     t.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *t.sources,
    808                                              _Alignof(BuildSourceLeaf));
    809     t.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *t.globs,
    810                                          _Alignof(BuildGlobLeaf));
    811     t.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *t.blobs,
    812                                             _Alignof(BuildBlobLeaf));
    813     t.deps = (BuildDepEdge*)heap_array(c->ctx, rows, sizeof *t.deps,
    814                                        _Alignof(BuildDepEdge));
    815     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps)
    816       goto test_out;
    817     t.cap_configs = rows;
    818     t.cap_sources = rows;
    819     t.cap_globs = rows;
    820     t.cap_blobs = rows;
    821     t.cap_deps = rows;
    822     if (build_test_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    823       goto test_out;
    824     if (payload_write_blob_from_cas(c, t.argv, root, seen) != BUILD_OK)
    825       goto test_out;
    826     for (i = 0u; i < t.n_deps; ++i) {
    827       if (payload_write_blob_from_cas(c, t.deps[i].overlay_id, root, seen) !=
    828               BUILD_OK ||
    829           payload_write_blob_from_cas(c, t.deps[i].argv_id, root, seen) !=
    830               BUILD_OK)
    831         goto test_out;
    832     }
    833     for (i = 0u; i < t.n_blobs; ++i)
    834       if (payload_write_blob_from_cas(c, t.blobs[i].blob, root, seen) !=
    835           BUILD_OK)
    836         goto test_out;
    837     ok = BUILD_OK;
    838 test_out:
    839     heap_free_array(c->ctx, t.configs, rows, sizeof *t.configs);
    840     heap_free_array(c->ctx, t.sources, rows, sizeof *t.sources);
    841     heap_free_array(c->ctx, t.globs, rows, sizeof *t.globs);
    842     heap_free_array(c->ctx, t.blobs, rows, sizeof *t.blobs);
    843     heap_free_array(c->ctx, t.deps, rows, sizeof *t.deps);
    844     return ok;
    845   }
    846   return BUILD_ERR;
    847 }
    848 
    849 static int BUILD_MAYBE_UNUSED import_trace_refs(KitBuildCoordinator* c,
    850                                                 const uint8_t* data,
    851                                                 size_t len, const char* root,
    852                                                 BuildIdVec* seen) {
    853   char err[160];
    854   if (len >= sizeof BUILD_DEEP_MAGIC &&
    855       memcmp(data, BUILD_DEEP_MAGIC "\n", sizeof BUILD_DEEP_MAGIC) == 0) {
    856     BuildDeepTrace t;
    857     KitFileData fd;
    858     if (build_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    859       return BUILD_ERR;
    860     if (payload_install_blob(c, t.argv, root, seen, NULL, NULL, &fd) !=
    861         BUILD_OK)
    862       return BUILD_ERR;
    863     release_file(c, &fd);
    864     return import_deepset_closure(c, t.deepset, root, seen);
    865   }
    866   if (len >= sizeof BUILD_TEST_DEEP_MAGIC &&
    867       memcmp(data, BUILD_TEST_DEEP_MAGIC "\n",
    868              sizeof BUILD_TEST_DEEP_MAGIC) == 0) {
    869     BuildDeepTrace t;
    870     KitFileData fd;
    871     if (build_test_deep_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    872       return BUILD_ERR;
    873     if (payload_install_blob(c, t.argv, root, seen, NULL, NULL, &fd) !=
    874         BUILD_OK)
    875       return BUILD_ERR;
    876     release_file(c, &fd);
    877     return import_deepset_closure(c, t.deepset, root, seen);
    878   }
    879   if (len >= sizeof BUILD_SHALLOW_MAGIC &&
    880       memcmp(data, BUILD_SHALLOW_MAGIC "\n", sizeof BUILD_SHALLOW_MAGIC) == 0) {
    881     BuildShallowTrace t;
    882     size_t rows = count_lines(data, len);
    883     size_t i;
    884     int ok = BUILD_ERR;
    885     KitFileData fd;
    886     memset(&t, 0, sizeof t);
    887     memset(&fd, 0, sizeof fd);
    888     t.configs = (BuildConfigLeaf*)heap_array(
    889         c->ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    890     t.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *t.sources,
    891                                              _Alignof(BuildSourceLeaf));
    892     t.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *t.globs,
    893                                          _Alignof(BuildGlobLeaf));
    894     t.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *t.blobs,
    895                                             _Alignof(BuildBlobLeaf));
    896     t.deps = (BuildDepEdge*)heap_array(c->ctx, rows, sizeof *t.deps,
    897                                        _Alignof(BuildDepEdge));
    898     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps)
    899       goto shallow_out;
    900     t.cap_configs = rows;
    901     t.cap_sources = rows;
    902     t.cap_globs = rows;
    903     t.cap_blobs = rows;
    904     t.cap_deps = rows;
    905     if (build_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    906       goto shallow_out;
    907     if (payload_install_blob(c, t.argv, root, seen, NULL, NULL, &fd) !=
    908         BUILD_OK)
    909       goto shallow_out;
    910     release_file(c, &fd);
    911     for (i = 0u; i < t.n_deps; ++i) {
    912       if (payload_install_blob(c, t.deps[i].overlay_id, root, seen, NULL, NULL,
    913                                &fd) != BUILD_OK)
    914         goto shallow_out;
    915       release_file(c, &fd);
    916       if (payload_install_blob(c, t.deps[i].argv_id, root, seen, NULL, NULL,
    917                                &fd) != BUILD_OK)
    918         goto shallow_out;
    919       release_file(c, &fd);
    920     }
    921     for (i = 0u; i < t.n_blobs; ++i) {
    922       if (payload_install_blob(c, t.blobs[i].blob, root, seen, NULL, NULL,
    923                                &fd) != BUILD_OK)
    924         goto shallow_out;
    925       release_file(c, &fd);
    926     }
    927     ok = BUILD_OK;
    928 shallow_out:
    929     release_file(c, &fd);
    930     heap_free_array(c->ctx, t.configs, rows, sizeof *t.configs);
    931     heap_free_array(c->ctx, t.sources, rows, sizeof *t.sources);
    932     heap_free_array(c->ctx, t.globs, rows, sizeof *t.globs);
    933     heap_free_array(c->ctx, t.blobs, rows, sizeof *t.blobs);
    934     heap_free_array(c->ctx, t.deps, rows, sizeof *t.deps);
    935     return ok;
    936   }
    937   if (len >= sizeof BUILD_TEST_SHALLOW_MAGIC &&
    938       memcmp(data, BUILD_TEST_SHALLOW_MAGIC "\n",
    939              sizeof BUILD_TEST_SHALLOW_MAGIC) == 0) {
    940     BuildShallowTrace t;
    941     size_t rows = count_lines(data, len);
    942     size_t i;
    943     int ok = BUILD_ERR;
    944     KitFileData fd;
    945     memset(&t, 0, sizeof t);
    946     memset(&fd, 0, sizeof fd);
    947     t.configs = (BuildConfigLeaf*)heap_array(
    948         c->ctx, rows, sizeof *t.configs, _Alignof(BuildConfigLeaf));
    949     t.sources = (BuildSourceLeaf*)heap_array(c->ctx, rows, sizeof *t.sources,
    950                                              _Alignof(BuildSourceLeaf));
    951     t.globs = (BuildGlobLeaf*)heap_array(c->ctx, rows, sizeof *t.globs,
    952                                          _Alignof(BuildGlobLeaf));
    953     t.blobs = (BuildBlobLeaf*)heap_array(c->ctx, rows, sizeof *t.blobs,
    954                                             _Alignof(BuildBlobLeaf));
    955     t.deps = (BuildDepEdge*)heap_array(c->ctx, rows, sizeof *t.deps,
    956                                        _Alignof(BuildDepEdge));
    957     if (!t.configs || !t.sources || !t.globs || !t.blobs || !t.deps)
    958       goto test_out;
    959     t.cap_configs = rows;
    960     t.cap_sources = rows;
    961     t.cap_globs = rows;
    962     t.cap_blobs = rows;
    963     t.cap_deps = rows;
    964     if (build_test_shallow_parse(data, len, &t, err, sizeof err) != BUILD_OK)
    965       goto test_out;
    966     if (payload_install_blob(c, t.argv, root, seen, NULL, NULL, &fd) !=
    967         BUILD_OK)
    968       goto test_out;
    969     release_file(c, &fd);
    970     for (i = 0u; i < t.n_deps; ++i) {
    971       if (payload_install_blob(c, t.deps[i].overlay_id, root, seen, NULL, NULL,
    972                                &fd) != BUILD_OK)
    973         goto test_out;
    974       release_file(c, &fd);
    975       if (payload_install_blob(c, t.deps[i].argv_id, root, seen, NULL, NULL,
    976                                &fd) != BUILD_OK)
    977         goto test_out;
    978       release_file(c, &fd);
    979     }
    980     for (i = 0u; i < t.n_blobs; ++i) {
    981       if (payload_install_blob(c, t.blobs[i].blob, root, seen, NULL, NULL,
    982                                &fd) != BUILD_OK)
    983         goto test_out;
    984       release_file(c, &fd);
    985     }
    986     ok = BUILD_OK;
    987 test_out:
    988     release_file(c, &fd);
    989     heap_free_array(c->ctx, t.configs, rows, sizeof *t.configs);
    990     heap_free_array(c->ctx, t.sources, rows, sizeof *t.sources);
    991     heap_free_array(c->ctx, t.globs, rows, sizeof *t.globs);
    992     heap_free_array(c->ctx, t.blobs, rows, sizeof *t.blobs);
    993     heap_free_array(c->ctx, t.deps, rows, sizeof *t.deps);
    994     return ok;
    995   }
    996   return BUILD_ERR;
    997 }
    998 
    999 static int gather_export_claims(KitBuildCoordinator* c,
   1000                                 const KitBuildExportOptions* opts,
   1001                                 BuildTraceClaim* claims, size_t cap,
   1002                                 size_t* nclaims, const char* root,
   1003                                 BuildIdVec* seen_blobs) {
   1004   size_t ti;
   1005   if (!c || !opts || !claims || !nclaims || !root || !seen_blobs)
   1006     return BUILD_ERR;
   1007   *nclaims = 0u;
   1008   for (ti = 0u; ti < opts->ntargets; ++ti) {
   1009     uint8_t key[BUILD_HASH_LEN];
   1010     BuildRecordRow rows[2u * KIT_BUILD_RECORD_CAP];
   1011     BuildTargetRecord rec;
   1012     size_t ri;
   1013     if (build_target_key(opts->targets[ti], key) != BUILD_OK)
   1014       return BUILD_ERR;
   1015     memset(&rec, 0, sizeof rec);
   1016     rec.rows = rows;
   1017     rec.cap_rows = sizeof rows / sizeof rows[0];
   1018     if (build_store_record_load(&c->store, key, opts->targets[ti], &rec) !=
   1019         BUILD_OK)
   1020       return BUILD_ERR;
   1021     for (ri = 0u; ri < rec.n_rows; ++ri) {
   1022       KitFileData fd;
   1023       ParsedTrace pt;
   1024       BuildTraceClaim* cl;
   1025       char path[BUILD_PATH_MAX];
   1026       if (*nclaims >= cap) return BUILD_ERR;
   1027       if (build_store_get_trace(&c->store, rec.rows[ri].trace_id, &fd) !=
   1028           BUILD_OK)
   1029         continue;
   1030       if (parse_trace_header(c->ctx, fd.data, fd.size, &pt) != BUILD_OK ||
   1031           pt.kind != rec.rows[ri].kind ||
   1032           !kit_slice_eq_cstr(opts->targets[ti], pt.target)) {
   1033         build_store_release(&c->store, &fd);
   1034         continue;
   1035       }
   1036       if (payload_id_path(path, sizeof path, root, BUILD_BUNDLE_TRACE_DIR,
   1037                           rec.rows[ri].trace_id) != BUILD_OK ||
   1038           ensure_parent_dir(c, path) != BUILD_OK ||
   1039           write_file(c, path, fd.data, fd.size) != BUILD_OK ||
   1040           export_trace_refs(c, fd.data, fd.size, root, seen_blobs) !=
   1041               BUILD_OK) {
   1042         build_store_release(&c->store, &fd);
   1043         return BUILD_ERR;
   1044       }
   1045       cl = &claims[(*nclaims)++];
   1046       snprintf(cl->target, sizeof cl->target, "%s", pt.target);
   1047       cl->kind = pt.kind;
   1048       memcpy(cl->trace_id, rec.rows[ri].trace_id, BUILD_HASH_LEN);
   1049       memcpy(cl->output_tree, pt.output, BUILD_HASH_LEN);
   1050       build_store_release(&c->store, &fd);
   1051     }
   1052     if (build_test_target_key(opts->targets[ti], key) != BUILD_OK)
   1053       return BUILD_ERR;
   1054     memset(&rec, 0, sizeof rec);
   1055     rec.rows = rows;
   1056     rec.cap_rows = sizeof rows / sizeof rows[0];
   1057     if (build_store_record_load(&c->store, key, opts->targets[ti], &rec) !=
   1058         BUILD_OK)
   1059       return BUILD_ERR;
   1060     for (ri = 0u; ri < rec.n_rows; ++ri) {
   1061       KitFileData fd;
   1062       ParsedTrace pt;
   1063       BuildTraceClaim* cl;
   1064       char path[BUILD_PATH_MAX];
   1065       uint8_t want_kind =
   1066           rec.rows[ri].kind == (uint8_t)BUILD_TRACE_DEEP
   1067               ? (uint8_t)BUILD_BUNDLE_TEST_DEEP
   1068               : (uint8_t)BUILD_BUNDLE_TEST_SHALLOW;
   1069       if (*nclaims >= cap) return BUILD_ERR;
   1070       if (build_store_get_trace(&c->store, rec.rows[ri].trace_id, &fd) !=
   1071           BUILD_OK)
   1072         continue;
   1073       if (parse_trace_header(c->ctx, fd.data, fd.size, &pt) != BUILD_OK ||
   1074           pt.kind != want_kind ||
   1075           !kit_slice_eq_cstr(opts->targets[ti], pt.target)) {
   1076         build_store_release(&c->store, &fd);
   1077         continue;
   1078       }
   1079       if (payload_id_path(path, sizeof path, root, BUILD_BUNDLE_TRACE_DIR,
   1080                           rec.rows[ri].trace_id) != BUILD_OK ||
   1081           ensure_parent_dir(c, path) != BUILD_OK ||
   1082           write_file(c, path, fd.data, fd.size) != BUILD_OK ||
   1083           export_trace_refs(c, fd.data, fd.size, root, seen_blobs) !=
   1084               BUILD_OK) {
   1085         build_store_release(&c->store, &fd);
   1086         return BUILD_ERR;
   1087       }
   1088       cl = &claims[(*nclaims)++];
   1089       snprintf(cl->target, sizeof cl->target, "%s", pt.target);
   1090       cl->kind = pt.kind;
   1091       memcpy(cl->trace_id, rec.rows[ri].trace_id, BUILD_HASH_LEN);
   1092       memcpy(cl->output_tree, pt.output, BUILD_HASH_LEN);
   1093       build_store_release(&c->store, &fd);
   1094     }
   1095   }
   1096   return BUILD_OK;
   1097 }
   1098 
   1099 int build_bundle_export(KitBuildCoordinator* c,
   1100                         const KitBuildExportOptions* opts) {
   1101   BuildTraceClaim* claims = NULL;
   1102   size_t cap, nclaims = 0u;
   1103   KitWriter* manw = NULL;
   1104   const uint8_t* man;
   1105   size_t man_len;
   1106   char root[BUILD_PATH_MAX];
   1107   char manifest_path[BUILD_PATH_MAX];
   1108   char out_path[BUILD_PATH_MAX];
   1109   KitPkgCreateOptions popts;
   1110   KitPkgCreateResult pres;
   1111   BuildIdVec seen_blobs;
   1112   int ok = BUILD_ERR;
   1113 
   1114   memset(&seen_blobs, 0, sizeof seen_blobs);
   1115   root[0] = '\0';
   1116   if (!c || !opts || !opts->targets || opts->ntargets == 0u || !opts->sk ||
   1117       !opts->keyid || !opts->out_path.s)
   1118     return BUILD_ERR;
   1119   if (opts->ntargets > (size_t)-1 / (4u * KIT_BUILD_RECORD_CAP))
   1120     return BUILD_ERR;
   1121   if (opts->format == KIT_PKG_FORMAT_AUTO) {
   1122     build_diagf(c->ctx, "trace bundle export: explicit package format required");
   1123     return BUILD_ERR;
   1124   }
   1125   if (opts->include_outputs) {
   1126     build_diagf(c->ctx,
   1127                 "trace bundle export: include_outputs needs public CAS tree "
   1128                 "entry enumeration");
   1129     return BUILD_ERR;
   1130   }
   1131   if (!c->host.cas_host || !c->host.cas_host->walk_regular_files) {
   1132     build_diagf(c->ctx,
   1133                 "trace bundle export: package root walk is unavailable");
   1134     return BUILD_ERR;
   1135   }
   1136   if (path_set(out_path, sizeof out_path, opts->out_path) != BUILD_OK)
   1137     return BUILD_ERR;
   1138   cap = opts->ntargets * 4u * KIT_BUILD_RECORD_CAP;
   1139   claims = (BuildTraceClaim*)heap_array(c->ctx, cap, sizeof *claims,
   1140                                         _Alignof(BuildTraceClaim));
   1141   if (!claims) return BUILD_ERR;
   1142   if (make_tmp_dir(c, root, sizeof root) != BUILD_OK) goto out;
   1143   if (gather_export_claims(c, opts, claims, cap, &nclaims, root,
   1144                            &seen_blobs) != BUILD_OK)
   1145     goto out;
   1146   if (kit_writer_mem(c->ctx->heap, &manw) != KIT_OK || !manw) goto out;
   1147   if (build_bundle_manifest_emit(claims, nclaims, manw) != BUILD_OK ||
   1148       kit_writer_status(manw) != KIT_OK)
   1149     goto out;
   1150   man = kit_writer_mem_bytes(manw, &man_len);
   1151   if (path_join2(manifest_path, sizeof manifest_path, root,
   1152                  BUILD_BUNDLE_MANIFEST_PATH) != BUILD_OK ||
   1153       write_file(c, manifest_path, man, man_len) != BUILD_OK)
   1154     goto out;
   1155 
   1156   memset(&popts, 0, sizeof popts);
   1157   memset(&pres, 0, sizeof pres);
   1158   popts.name = "kit-build-traces";
   1159   popts.version = "1";
   1160   popts.description = "kit build trace bundle";
   1161   popts.format = opts->format;
   1162   popts.native_shape = KIT_PKG_SHAPE_FAT;
   1163   popts.compression = KIT_PKG_COMPRESSION_NONE;
   1164   popts.root_dir = root;
   1165   popts.sk = opts->sk;
   1166   popts.keyid = opts->keyid;
   1167   popts.out_path = out_path;
   1168   if (kit_pkg_create(c->ctx, c->host.cas_host, &popts, &pres) != KIT_OK)
   1169     goto out;
   1170   ok = BUILD_OK;
   1171 out:
   1172   if (manw) kit_writer_close(manw);
   1173   if (root[0]) (void)remove_path(c, root, 1);
   1174   id_vec_free(c->ctx, &seen_blobs);
   1175   heap_free_array(c->ctx, claims, cap, sizeof *claims);
   1176   if (ok != BUILD_OK)
   1177     build_diagf(c->ctx, "trace bundle export: failed to create bundle");
   1178   return ok;
   1179 }
   1180 
   1181 static int read_payload_manifest(KitBuildCoordinator* c, const char* root,
   1182                                  KitFileData* out) {
   1183   char path[BUILD_PATH_MAX];
   1184   if (path_join2(path, sizeof path, root, BUILD_BUNDLE_MANIFEST_PATH) !=
   1185       BUILD_OK)
   1186     return BUILD_ERR;
   1187   return read_file(c, path, out);
   1188 }
   1189 
   1190 static int validate_import_claim(KitBuildCoordinator* c, const char* root,
   1191                                  const BuildTraceClaim* cl,
   1192                                  BuildIdVec* seen_blobs) {
   1193   KitFileData fd;
   1194   ParsedTrace pt;
   1195   uint8_t got[BUILD_HASH_LEN];
   1196   char path[BUILD_PATH_MAX];
   1197   int ok = BUILD_ERR;
   1198   if (!c || !root || !cl || !seen_blobs) return BUILD_ERR;
   1199   if (payload_id_path(path, sizeof path, root, BUILD_BUNDLE_TRACE_DIR,
   1200                       cl->trace_id) != BUILD_OK ||
   1201       read_file(c, path, &fd) != BUILD_OK)
   1202     return BUILD_ERR;
   1203   build_trace_id(fd.data, fd.size, got);
   1204   if (!build_id_eq(got, cl->trace_id)) goto out;
   1205   if (parse_trace_header(c->ctx, fd.data, fd.size, &pt) != BUILD_OK)
   1206     goto out;
   1207   if (strcmp(pt.target, cl->target) != 0 || pt.kind != cl->kind ||
   1208       !build_id_eq(pt.output, cl->output_tree))
   1209     goto out;
   1210   if (import_trace_refs(c, fd.data, fd.size, root, seen_blobs) != BUILD_OK)
   1211     goto out;
   1212   if (build_store_put_trace(&c->store, fd.data, fd.size, got) != BUILD_OK ||
   1213       !build_id_eq(got, cl->trace_id))
   1214     goto out;
   1215   ok = BUILD_OK;
   1216 out:
   1217   release_file(c, &fd);
   1218   return ok;
   1219 }
   1220 
   1221 int build_bundle_import(KitBuildCoordinator* c, const KitBuildImportOptions* opts,
   1222                         KitBuildImportResult* result) {
   1223   KitPkgVerifyOptions vopts;
   1224   KitPkgVerifyResult vres;
   1225   KitFileData manfd;
   1226   BuildTraceClaim* claims = NULL;
   1227   size_t nclaims = 0u, cap = 0u;
   1228   char unpack[BUILD_PATH_MAX];
   1229   char err[160];
   1230   BuildIdVec seen_blobs;
   1231   size_t i;
   1232   int ok = BUILD_ERR;
   1233 
   1234   memset(&seen_blobs, 0, sizeof seen_blobs);
   1235   memset(&manfd, 0, sizeof manfd);
   1236   unpack[0] = '\0';
   1237   if (result) memset(result, 0, sizeof *result);
   1238   if (!c || !opts || !opts->pkg_data || opts->pkg_len == 0u ||
   1239       opts->format == KIT_PKG_FORMAT_AUTO)
   1240     return BUILD_ERR;
   1241   if (make_tmp_dir(c, unpack, sizeof unpack) != BUILD_OK) goto out;
   1242   memset(&vopts, 0, sizeof vopts);
   1243   memset(&vres, 0, sizeof vres);
   1244   vopts.pkg_data = opts->pkg_data;
   1245   vopts.pkg_len = opts->pkg_len;
   1246   vopts.format = opts->format;
   1247   vopts.unpack_dir = unpack;
   1248   vopts.pubkey_bytes = opts->pubkey_bytes;
   1249   vopts.pubkey_len = opts->pubkey_len;
   1250   vopts.tofu = opts->tofu;
   1251   vopts.trusted_keys = opts->trusted_keys;
   1252   vopts.trusted_keys_len = opts->trusted_keys_len;
   1253   if (kit_pkg_verify(c->ctx, c->host.cas_host, &vopts, &vres) != KIT_OK)
   1254     goto out;
   1255   if (strcmp(vres.name, "kit-build-traces") != 0 ||
   1256       strcmp(vres.version, "1") != 0) {
   1257     build_diagf(c->ctx, "trace bundle import: package is not a trace bundle");
   1258     goto out;
   1259   }
   1260   if (read_payload_manifest(c, unpack, &manfd) != BUILD_OK) goto out;
   1261   cap = count_lines(manfd.data, manfd.size);
   1262   claims = (BuildTraceClaim*)heap_array(c->ctx, cap, sizeof *claims,
   1263                                         _Alignof(BuildTraceClaim));
   1264   if (!claims) goto out;
   1265   if (build_bundle_manifest_parse(manfd.data, manfd.size, claims, cap,
   1266                                   &nclaims, err, sizeof err) != BUILD_OK) {
   1267     build_diagf(c->ctx, "trace bundle import: %s", err);
   1268     goto out;
   1269   }
   1270   for (i = 0u; i < nclaims; ++i)
   1271     if (validate_import_claim(c, unpack, &claims[i], &seen_blobs) != BUILD_OK)
   1272       goto out;
   1273   if (nclaims > (uint32_t)-1) goto out;
   1274   for (i = 0u; i < nclaims; ++i) {
   1275     uint8_t key[BUILD_HASH_LEN];
   1276     BuildTraceKind record_kind;
   1277     if (claims[i].kind == (uint8_t)BUILD_BUNDLE_TEST_DEEP ||
   1278         claims[i].kind == (uint8_t)BUILD_BUNDLE_TEST_SHALLOW) {
   1279       record_kind = claims[i].kind == (uint8_t)BUILD_BUNDLE_TEST_DEEP
   1280                         ? BUILD_TRACE_DEEP
   1281                         : BUILD_TRACE_SHALLOW;
   1282       if (build_test_target_key(str_slice(claims[i].target), key) != BUILD_OK)
   1283         goto out;
   1284     } else {
   1285       record_kind = (BuildTraceKind)claims[i].kind;
   1286       if (build_target_key(str_slice(claims[i].target), key) != BUILD_OK)
   1287         goto out;
   1288     }
   1289     if (build_store_record_update(&c->store, key, str_slice(claims[i].target),
   1290                                   record_kind, claims[i].trace_id) != BUILD_OK)
   1291       goto out;
   1292   }
   1293   if (result) {
   1294     result->n_traces = (uint32_t)nclaims;
   1295     memcpy(result->keyid, vres.keyid, KIT_PKG_KEYID_LEN);
   1296     result->tofu_pin = vres.tofu_pin;
   1297     memcpy(result->tofu_pk, vres.tofu_pk, KIT_PKG_PK_LEN);
   1298   }
   1299   ok = BUILD_OK;
   1300 out:
   1301   release_file(c, &manfd);
   1302   if (unpack[0]) (void)remove_path(c, unpack, 1);
   1303   id_vec_free(c->ctx, &seen_blobs);
   1304   heap_free_array(c->ctx, claims, cap, sizeof *claims);
   1305   if (ok != BUILD_OK)
   1306     build_diagf(c->ctx, "trace bundle import: failed to verify/install bundle");
   1307   return ok;
   1308 }
   1309 
   1310 static int ascii_space(char c) {
   1311   return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' ||
   1312          c == '\v';
   1313 }
   1314 
   1315 static int add_size(size_t* v, size_t n) {
   1316   if (!v || *v > (size_t)-1 - n) return BUILD_ERR;
   1317   *v += n;
   1318   return BUILD_OK;
   1319 }
   1320 
   1321 static int trace_token_match(const char* p, const char* end, const char* lit,
   1322                              const char* repl, const char** repl_out,
   1323                              size_t* lit_len_out, size_t* repl_len_out) {
   1324   size_t n;
   1325   if (!lit || !repl) return 0;
   1326   n = strlen(lit);
   1327   if ((size_t)(end - p) < n || memcmp(p, lit, n) != 0) return 0;
   1328   *repl_out = repl;
   1329   *lit_len_out = n;
   1330   *repl_len_out = strlen(repl);
   1331   return 1;
   1332 }
   1333 
   1334 static int trace_find_token(const char* p, const char* end,
   1335                             const TraceRenderTokens* toks,
   1336                             const char** repl_out, size_t* lit_len_out,
   1337                             size_t* repl_len_out) {
   1338   return trace_token_match(p, end, "{target}", toks->target, repl_out,
   1339                            lit_len_out, repl_len_out) ||
   1340          trace_token_match(p, end, "{out}", toks->out, repl_out, lit_len_out,
   1341                            repl_len_out);
   1342 }
   1343 
   1344 static int render_emit_bytes(char* storage, size_t* off, size_t cap,
   1345                              const char* data, size_t len) {
   1346   if (!off || (!data && len)) return BUILD_ERR;
   1347   if (storage) {
   1348     if (*off > cap || len > cap - *off) return BUILD_ERR;
   1349     if (len) memcpy(storage + *off, data, len);
   1350   }
   1351   return add_size(off, len);
   1352 }
   1353 
   1354 static int trace_render_pass(KitSlice tmpl, const TraceRenderTokens* toks,
   1355                              KitSlice* argv, size_t* argc_io, char* storage,
   1356                              size_t storage_cap, size_t* storage_len_out) {
   1357   const char* p;
   1358   const char* end;
   1359   size_t argc = 0u;
   1360   size_t off = 0u;
   1361   size_t word_start = 0u;
   1362   size_t word_len = 0u;
   1363   int in_word = 0;
   1364   char quote = 0;
   1365   if (!tmpl.s || !toks || !argc_io || !storage_len_out) return BUILD_ERR;
   1366   p = tmpl.s;
   1367   end = tmpl.s + tmpl.len;
   1368   while (p < end) {
   1369     char ch = *p;
   1370     if (quote == 0 && ascii_space(ch)) {
   1371       if (in_word) {
   1372         if (argv) {
   1373           if (argc >= *argc_io || off >= storage_cap) return BUILD_ERR;
   1374           storage[off] = '\0';
   1375           argv[argc].s = storage + word_start;
   1376           argv[argc].len = word_len;
   1377         }
   1378         if (add_size(&off, 1u) != BUILD_OK) return BUILD_ERR;
   1379         ++argc;
   1380         in_word = 0;
   1381         word_len = 0u;
   1382       }
   1383       ++p;
   1384       continue;
   1385     }
   1386     if (!in_word) {
   1387       in_word = 1;
   1388       word_start = off;
   1389       word_len = 0u;
   1390     }
   1391     if (quote == 0 && (ch == '\'' || ch == '"')) {
   1392       quote = ch;
   1393       ++p;
   1394       continue;
   1395     }
   1396     if (quote != 0 && ch == quote) {
   1397       quote = 0;
   1398       ++p;
   1399       continue;
   1400     }
   1401     if ((quote == 0 || quote == '"') && ch == '\\') {
   1402       if (p + 1 >= end) return BUILD_ERR;
   1403       if (render_emit_bytes(storage, &off, storage_cap, p + 1, 1u) !=
   1404           BUILD_OK)
   1405         return BUILD_ERR;
   1406       ++word_len;
   1407       p += 2;
   1408       continue;
   1409     }
   1410     {
   1411       const char* repl = NULL;
   1412       size_t lit_len = 0u;
   1413       size_t repl_len = 0u;
   1414       if (trace_find_token(p, end, toks, &repl, &lit_len, &repl_len)) {
   1415         if (render_emit_bytes(storage, &off, storage_cap, repl, repl_len) !=
   1416             BUILD_OK)
   1417           return BUILD_ERR;
   1418         if (add_size(&word_len, repl_len) != BUILD_OK) return BUILD_ERR;
   1419         p += lit_len;
   1420       } else {
   1421         if (render_emit_bytes(storage, &off, storage_cap, p, 1u) != BUILD_OK)
   1422           return BUILD_ERR;
   1423         ++word_len;
   1424         ++p;
   1425       }
   1426     }
   1427   }
   1428   if (quote != 0) return BUILD_ERR;
   1429   if (in_word) {
   1430     if (argv) {
   1431       if (argc >= *argc_io || off >= storage_cap) return BUILD_ERR;
   1432       storage[off] = '\0';
   1433       argv[argc].s = storage + word_start;
   1434       argv[argc].len = word_len;
   1435     }
   1436     if (add_size(&off, 1u) != BUILD_OK) return BUILD_ERR;
   1437     ++argc;
   1438   }
   1439   if (argv && argc != *argc_io) return BUILD_ERR;
   1440   *argc_io = argc;
   1441   *storage_len_out = off;
   1442   return BUILD_OK;
   1443 }
   1444 
   1445 static void rendered_argv_free(const KitContext* ctx, RenderedArgv* r) {
   1446   if (!ctx || !ctx->heap || !r) return;
   1447   if (r->storage) ctx->heap->free(ctx->heap, r->storage, r->storage_size);
   1448   if (r->argv) ctx->heap->free(ctx->heap, r->argv, r->argc * sizeof *r->argv);
   1449   r->argv = NULL;
   1450   r->argc = 0u;
   1451   r->storage = NULL;
   1452   r->storage_size = 0u;
   1453 }
   1454 
   1455 static int trace_render_argv(const KitContext* ctx, KitSlice tmpl,
   1456                              const TraceRenderTokens* toks,
   1457                              RenderedArgv* out) {
   1458   size_t argc = 0u;
   1459   size_t storage_len = 0u;
   1460   if (!ctx || !ctx->heap || !out) return BUILD_ERR;
   1461   memset(out, 0, sizeof *out);
   1462   if (trace_render_pass(tmpl, toks, NULL, &argc, NULL, 0u, &storage_len) !=
   1463           BUILD_OK ||
   1464       argc == 0u)
   1465     return BUILD_ERR;
   1466   out->argv = (KitSlice*)ctx->heap->alloc(ctx->heap, argc * sizeof *out->argv,
   1467                                           _Alignof(KitSlice));
   1468   out->storage =
   1469       (char*)ctx->heap->alloc(ctx->heap, storage_len, _Alignof(char));
   1470   if (!out->argv || !out->storage) {
   1471     rendered_argv_free(ctx, out);
   1472     return BUILD_ERR;
   1473   }
   1474   out->argc = argc;
   1475   out->storage_size = storage_len;
   1476   if (trace_render_pass(tmpl, toks, out->argv, &argc, out->storage,
   1477                         storage_len, &storage_len) != BUILD_OK) {
   1478     rendered_argv_free(ctx, out);
   1479     return BUILD_ERR;
   1480   }
   1481   return BUILD_OK;
   1482 }
   1483 
   1484 static int run_fetch(const KitBuildExec* exec, const KitSlice* argv,
   1485                      size_t argc, KitSlice cwd) {
   1486   KitBuildProc* proc = NULL;
   1487   KitExecOpts eo;
   1488   int exit_code = 1;
   1489   if (!exec || !exec->spawn || !exec->wait || !argv || argc == 0u)
   1490     return BUILD_ERR;
   1491   memset(&eo, 0, sizeof eo);
   1492   eo.argv = argv;
   1493   eo.argc = argc;
   1494   eo.cwd = cwd;
   1495   if (exec->spawn(exec->user, &eo, &proc) != 0 || !proc) return BUILD_ERR;
   1496   if (exec->wait(exec->user, proc, &exit_code, NULL, NULL) != 0)
   1497     return BUILD_ERR;
   1498   return exit_code == 0 ? BUILD_OK : BUILD_ERR;
   1499 }
   1500 
   1501 int build_trace_remote_pull(KitBuildCoordinator* c, KitSlice target) {
   1502   size_t i;
   1503   char tmp[BUILD_PATH_MAX];
   1504   char out_path[BUILD_PATH_MAX];
   1505   int ok = BUILD_ERR;
   1506   if (!c || !target.s || target.len == 0u || !c->opts.trace_remotes ||
   1507       c->opts.n_trace_remotes == 0u || !c->host.exec) {
   1508     build_diagf(c ? c->ctx : NULL,
   1509                 "trace remote pull: no trace remote/exec configured");
   1510     return BUILD_ERR;
   1511   }
   1512   if (make_tmp_dir(c, tmp, sizeof tmp) != BUILD_OK) return BUILD_ERR;
   1513   for (i = 0u; i < c->opts.n_trace_remotes; ++i) {
   1514     const KitBuildTraceRemote* r = &c->opts.trace_remotes[i];
   1515     RenderedArgv argv;
   1516     TraceRenderTokens toks;
   1517     KitFileData fd;
   1518     KitBuildImportOptions iopts;
   1519     KitBuildImportResult ires;
   1520     char target_buf[BUILD_TARGET_MAX];
   1521     char leaf[64];
   1522     memset(&argv, 0, sizeof argv);
   1523     memset(&fd, 0, sizeof fd);
   1524     if (path_set(target_buf, sizeof target_buf, target) != BUILD_OK) break;
   1525     snprintf(leaf, sizeof leaf, "trace-%llu.kpkg", (unsigned long long)i);
   1526     if (path_join2(out_path, sizeof out_path, tmp, leaf) != BUILD_OK) break;
   1527     toks.target = target_buf;
   1528     toks.out = out_path;
   1529     if (trace_render_argv(c->ctx, r->fetch_argv_template, &toks, &argv) !=
   1530         BUILD_OK) {
   1531       build_diagf(c->ctx, "trace remote pull: bad argv template");
   1532       continue;
   1533     }
   1534     if (run_fetch(c->host.exec, argv.argv, argv.argc, str_slice(tmp)) !=
   1535         BUILD_OK) {
   1536       rendered_argv_free(c->ctx, &argv);
   1537       continue;
   1538     }
   1539     rendered_argv_free(c->ctx, &argv);
   1540     if (read_file(c, out_path, &fd) != BUILD_OK) continue;
   1541     memset(&iopts, 0, sizeof iopts);
   1542     memset(&ires, 0, sizeof ires);
   1543     iopts.pkg_data = fd.data;
   1544     iopts.pkg_len = fd.size;
   1545     iopts.format = KIT_PKG_FORMAT_KPKG;
   1546     iopts.trusted_keys = r->trusted_keys.data;
   1547     iopts.trusted_keys_len = r->trusted_keys.len;
   1548     iopts.tofu = r->tofu;
   1549     if (build_bundle_import(c, &iopts, &ires) == BUILD_OK && ires.n_traces) {
   1550       release_file(c, &fd);
   1551       ok = BUILD_OK;
   1552       break;
   1553     }
   1554     release_file(c, &fd);
   1555   }
   1556   (void)remove_path(c, tmp, 1);
   1557   return ok;
   1558 }