kit

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

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 }