kit

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

manifest.c (19244B)


      1 #include "manifest.h"
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 #include "dist_parse.h"
      7 #include "tree.h"
      8 
      9 static int emit(KitWriter* out, const char* s) {
     10   return kit_writer_write(out, s, strlen(s)) == KIT_OK ? DIST_OK : DIST_ERR;
     11 }
     12 
     13 static int emit_hex(KitWriter* out, const char* key, const uint8_t* h,
     14                     size_t n) {
     15   char hex[2 * DIST_BLAKE2B_LEN + 1];
     16   dist_hex_encode(hex, h, n);
     17   return dist_emit_kv(out, key, hex);
     18 }
     19 
     20 static int emit_u64(KitWriter* out, const char* key, uint64_t v) {
     21   char num[24];
     22   snprintf(num, sizeof num, "%llu", (unsigned long long)v);
     23   return dist_emit_kv(out, key, num);
     24 }
     25 
     26 static int kind_valid(const char* k) {
     27   return strcmp(k, "exe") == 0 || strcmp(k, "dso") == 0 ||
     28          strcmp(k, "obj") == 0 || strcmp(k, "wasm") == 0 ||
     29          strcmp(k, "lib") == 0 || strcmp(k, "data") == 0 ||
     30          strcmp(k, "source") == 0;
     31 }
     32 
     33 int dist_manifest_path_valid(const char* p) {
     34   /* Shared with dist_tree_path_valid (the strictest variant, which also
     35    * rejects embedded '\n'/'\r'). Manifest path values are already line-scanned
     36    * and field_text_valid-checked before reaching here, so delegating only adds
     37    * defence in depth: it does not change any reachable accept/reject result. */
     38   return dist_tree_path_valid(p);
     39 }
     40 
     41 #define P3_F_NAME 0x00000001u
     42 #define P3_F_VERSION 0x00000002u
     43 #define P3_F_DESCRIPTION 0x00000004u
     44 #define P3_F_HASH 0x00000008u
     45 #define P3_F_TREE_FORMAT 0x00000010u
     46 #define P3_F_BLOB_FORMAT 0x00000020u
     47 #define P3_F_ID 0x00000040u
     48 #define P3_F_OUTPUT_NAME 0x00000080u
     49 #define P3_F_TREE_ID 0x00000100u
     50 #define P3_F_TARGET 0x00000200u
     51 #define P3_F_DEFAULT 0x00000400u
     52 #define P3_F_OUTPUT 0x00000800u
     53 #define P3_F_PATH 0x00001000u
     54 #define P3_F_KIND 0x00002000u
     55 #define P3_F_ENTRY 0x00004000u
     56 #define P3_F_PACKAGE 0x00008000u
     57 #define P3_F_KEY 0x00010000u
     58 
     59 typedef enum {
     60   P3_SEC_TOP,
     61   P3_SEC_OUTPUT,
     62   P3_SEC_ARTIFACT,
     63   P3_SEC_DEPENDENCY
     64 } PackageSection;
     65 
     66 static int field_text_valid(const char* s, int required) {
     67   if (required && !s[0]) return 0;
     68   for (; *s; ++s) {
     69     if (*s == '\n' || *s == '\r') return 0;
     70   }
     71   return 1;
     72 }
     73 
     74 static int parse_bool3(const char* s, int* out) {
     75   if (strcmp(s, "true") == 0) {
     76     *out = 1;
     77     return DIST_OK;
     78   }
     79   if (strcmp(s, "false") == 0) {
     80     *out = 0;
     81     return DIST_OK;
     82   }
     83   return DIST_ERR;
     84 }
     85 
     86 static int decode_hash3(uint8_t out[DIST_BLAKE2B_LEN], const char* val,
     87                         const char* err_msg, char* err, size_t errcap) {
     88   if (strlen(val) != 2u * DIST_BLAKE2B_LEN ||
     89       dist_hex_decode(out, val, DIST_BLAKE2B_LEN) != DIST_OK)
     90     return dist_set_err(err, errcap, err_msg);
     91   return DIST_OK;
     92 }
     93 
     94 static int decode_keyid3(uint8_t out[DIST_KEYID_LEN], const char* val,
     95                          char* err, size_t errcap) {
     96   if (strlen(val) != 2u * DIST_KEYID_LEN ||
     97       dist_hex_decode(out, val, DIST_KEYID_LEN) != DIST_OK)
     98     return dist_set_err(err, errcap, "bad dependency key id");
     99   return DIST_OK;
    100 }
    101 
    102 static int seen_once3(uint32_t* seen, uint32_t bit, const char* msg, char* err,
    103                       size_t errcap) {
    104   if (*seen & bit) return dist_set_err(err, errcap, msg);
    105   *seen |= bit;
    106   return DIST_OK;
    107 }
    108 
    109 static int find_output3(const DistPackageManifest* m, uint64_t id) {
    110   size_t i;
    111   for (i = 0; i < m->n_outputs; ++i) {
    112     if (m->outputs[i].id == id) return (int)i;
    113   }
    114   return -1;
    115 }
    116 
    117 static int finalize_package_section3(PackageSection sec, uint32_t seen,
    118                                      char* err, size_t errcap) {
    119   if (sec == P3_SEC_TOP) {
    120     if ((seen & (P3_F_NAME | P3_F_VERSION | P3_F_HASH | P3_F_TREE_FORMAT |
    121                  P3_F_BLOB_FORMAT)) != (P3_F_NAME | P3_F_VERSION | P3_F_HASH |
    122                                         P3_F_TREE_FORMAT | P3_F_BLOB_FORMAT))
    123       return dist_set_err(err, errcap, "missing required top-level field");
    124   } else if (sec == P3_SEC_OUTPUT) {
    125     if ((seen & (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID)) !=
    126         (P3_F_ID | P3_F_OUTPUT_NAME | P3_F_TREE_ID))
    127       return dist_set_err(err, errcap, "missing required [output] field");
    128   } else if (sec == P3_SEC_ARTIFACT) {
    129     if ((seen & (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND)) !=
    130         (P3_F_OUTPUT | P3_F_PATH | P3_F_KIND))
    131       return dist_set_err(err, errcap, "missing required [artifact] field");
    132   } else {
    133     if ((seen & (P3_F_NAME | P3_F_VERSION)) != (P3_F_NAME | P3_F_VERSION))
    134       return dist_set_err(err, errcap, "missing required [dependency] field");
    135   }
    136   return DIST_OK;
    137 }
    138 
    139 int dist_package_manifest_validate(const DistPackageManifest* m, char* err,
    140                                    size_t errcap) {
    141   size_t i, j;
    142   size_t default_outputs = 0;
    143 
    144   if (!field_text_valid(m->name, 1) || !field_text_valid(m->version, 1) ||
    145       !field_text_valid(m->description, 0))
    146     return dist_set_err(err, errcap, "bad package string field");
    147   if (m->n_outputs == 0) return dist_set_err(err, errcap, "missing [output]");
    148   if (m->n_outputs > DIST_MAX_OUTPUTS)
    149     return dist_set_err(err, errcap, "too many outputs");
    150   if (m->n_artifacts > DIST_MAX_ARTIFACTS)
    151     return dist_set_err(err, errcap, "too many artifacts");
    152   if (m->n_deps > DIST_MAX_DEPS)
    153     return dist_set_err(err, errcap, "too many dependencies");
    154 
    155   for (i = 0; i < m->n_outputs; ++i) {
    156     const DistPackageOutput* out = &m->outputs[i];
    157     if (!field_text_valid(out->name, 1) || !field_text_valid(out->target, 0))
    158       return dist_set_err(err, errcap, "bad output string field");
    159     if (out->is_default) ++default_outputs;
    160     for (j = i + 1u; j < m->n_outputs; ++j) {
    161       if (out->id == m->outputs[j].id)
    162         return dist_set_err(err, errcap, "duplicate output id");
    163     }
    164   }
    165   if (default_outputs > 1u)
    166     return dist_set_err(err, errcap, "duplicate default output");
    167 
    168   for (i = 0; i < m->n_artifacts; ++i) {
    169     const DistPackageArtifact* art = &m->artifacts[i];
    170     if (find_output3(m, art->output_id) < 0)
    171       return dist_set_err(err, errcap, "artifact references unknown output");
    172     if (!field_text_valid(art->path, 1) || !dist_manifest_path_valid(art->path))
    173       return dist_set_err(err, errcap, "unsafe artifact path");
    174     if (!kind_valid(art->kind))
    175       return dist_set_err(err, errcap, "unknown artifact kind");
    176     for (j = i + 1u; j < m->n_artifacts; ++j) {
    177       const DistPackageArtifact* other = &m->artifacts[j];
    178       if (art->output_id == other->output_id &&
    179           strcmp(art->path, other->path) == 0)
    180         return dist_set_err(err, errcap, "duplicate artifact path");
    181     }
    182   }
    183 
    184   for (i = 0; i < m->n_deps; ++i) {
    185     const DistPackageDependency* dep = &m->deps[i];
    186     if (!field_text_valid(dep->name, 1) || !field_text_valid(dep->version, 1))
    187       return dist_set_err(err, errcap, "bad dependency string field");
    188   }
    189 
    190   return DIST_OK;
    191 }
    192 
    193 int dist_package_manifest_emit(const DistPackageManifest* m, KitWriter* out) {
    194   size_t i;
    195   char err[128];
    196 
    197   if (dist_package_manifest_validate(m, err, sizeof err) != DIST_OK)
    198     return DIST_ERR;
    199 
    200   if (emit(out, DIST_PACKAGE3_MAGIC "\n") != DIST_OK) return DIST_ERR;
    201   if (dist_emit_kv(out, "name", m->name) != DIST_OK) return DIST_ERR;
    202   if (dist_emit_kv(out, "version", m->version) != DIST_OK) return DIST_ERR;
    203   if (m->description[0] &&
    204       dist_emit_kv(out, "description", m->description) != DIST_OK)
    205     return DIST_ERR;
    206   if (dist_emit_kv(out, "hash", DIST_PACKAGE3_HASH) != DIST_OK) return DIST_ERR;
    207   if (dist_emit_kv(out, "tree", DIST_PACKAGE3_TREE_FORMAT) != DIST_OK)
    208     return DIST_ERR;
    209   if (dist_emit_kv(out, "blob", DIST_PACKAGE3_BLOB_FORMAT) != DIST_OK)
    210     return DIST_ERR;
    211 
    212   for (i = 0; i < m->n_outputs; ++i) {
    213     const DistPackageOutput* pkg_out = &m->outputs[i];
    214     if (emit(out, "\n[output]\n") != DIST_OK) return DIST_ERR;
    215     if (emit_u64(out, "id", pkg_out->id) != DIST_OK) return DIST_ERR;
    216     if (dist_emit_kv(out, "name", pkg_out->name) != DIST_OK) return DIST_ERR;
    217     if (emit_hex(out, "tree", pkg_out->tree, DIST_BLAKE2B_LEN) != DIST_OK)
    218       return DIST_ERR;
    219     if (pkg_out->target[0] &&
    220         dist_emit_kv(out, "target", pkg_out->target) != DIST_OK)
    221       return DIST_ERR;
    222     if (pkg_out->is_default && dist_emit_kv(out, "default", "true") != DIST_OK)
    223       return DIST_ERR;
    224   }
    225 
    226   for (i = 0; i < m->n_artifacts; ++i) {
    227     const DistPackageArtifact* art = &m->artifacts[i];
    228     if (emit(out, "\n[artifact]\n") != DIST_OK) return DIST_ERR;
    229     if (emit_u64(out, "output", art->output_id) != DIST_OK) return DIST_ERR;
    230     if (dist_emit_kv(out, "path", art->path) != DIST_OK) return DIST_ERR;
    231     if (dist_emit_kv(out, "kind", art->kind) != DIST_OK) return DIST_ERR;
    232     if (art->entry && dist_emit_kv(out, "entry", "true") != DIST_OK)
    233       return DIST_ERR;
    234   }
    235 
    236   for (i = 0; i < m->n_deps; ++i) {
    237     const DistPackageDependency* dep = &m->deps[i];
    238     if (emit(out, "\n[dependency]\n") != DIST_OK) return DIST_ERR;
    239     if (dist_emit_kv(out, "name", dep->name) != DIST_OK) return DIST_ERR;
    240     if (dist_emit_kv(out, "version", dep->version) != DIST_OK) return DIST_ERR;
    241     if (dep->has_package &&
    242         emit_hex(out, "package", dep->package, DIST_BLAKE2B_LEN) != DIST_OK)
    243       return DIST_ERR;
    244     if (dep->has_keyid &&
    245         emit_hex(out, "key", dep->keyid, DIST_KEYID_LEN) != DIST_OK)
    246       return DIST_ERR;
    247   }
    248 
    249   return DIST_OK;
    250 }
    251 
    252 int dist_package_manifest_parse(const uint8_t* data, size_t len,
    253                                 DistPackageManifest* m, char* err,
    254                                 size_t errcap) {
    255   size_t pos = 0;
    256   int first = 1;
    257   PackageSection sec = P3_SEC_TOP;
    258   uint32_t seen = 0;
    259   DistPackageOutput* pkg_out = NULL;
    260   DistPackageArtifact* art = NULL;
    261   DistPackageDependency* dep = NULL;
    262 
    263   memset(m, 0, sizeof *m);
    264 
    265   while (pos < len) {
    266     char buf[DIST_KV_LINE_MAX];
    267     size_t end = pos;
    268     size_t n, i;
    269     char *t, *key, *val, *eq;
    270 
    271     while (end < len && data[end] != '\n') ++end;
    272     n = end - pos;
    273     if (n >= sizeof buf) return dist_set_err(err, errcap, "line too long");
    274     for (i = pos; i < end; ++i)
    275       if (data[i] == 0)
    276         return dist_set_err(err, errcap, "NUL byte in package manifest");
    277     memcpy(buf, data + pos, n);
    278     buf[n] = '\0';
    279     pos = (end < len) ? end + 1 : end;
    280     dist_trim_trail(buf);
    281 
    282     if (first) {
    283       first = 0;
    284       if (strcmp(buf, DIST_PACKAGE3_MAGIC) != 0)
    285         return dist_set_err(err, errcap, "bad package manifest magic/version");
    286       continue;
    287     }
    288 
    289     t = dist_trim_lead(buf);
    290     if (*t == '\0' || *t == '#') continue;
    291 
    292     if (*t == '[') {
    293       if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK)
    294         return DIST_ERR;
    295       seen = 0;
    296       if (strcmp(t, "[output]") == 0) {
    297         if (m->n_outputs >= DIST_MAX_OUTPUTS)
    298           return dist_set_err(err, errcap, "too many outputs");
    299         sec = P3_SEC_OUTPUT;
    300         pkg_out = &m->outputs[m->n_outputs++];
    301       } else if (strcmp(t, "[artifact]") == 0) {
    302         if (m->n_artifacts >= DIST_MAX_ARTIFACTS)
    303           return dist_set_err(err, errcap, "too many artifacts");
    304         sec = P3_SEC_ARTIFACT;
    305         art = &m->artifacts[m->n_artifacts++];
    306       } else if (strcmp(t, "[dependency]") == 0) {
    307         if (m->n_deps >= DIST_MAX_DEPS)
    308           return dist_set_err(err, errcap, "too many dependencies");
    309         sec = P3_SEC_DEPENDENCY;
    310         dep = &m->deps[m->n_deps++];
    311       } else {
    312         return dist_set_err(err, errcap, "unknown section");
    313       }
    314       continue;
    315     }
    316 
    317     eq = strchr(t, '=');
    318     if (!eq) return dist_set_err(err, errcap, "expected key = value");
    319     *eq = '\0';
    320     key = t;
    321     dist_trim_trail(key);
    322     val = dist_trim_lead(eq + 1);
    323 
    324     if (sec == P3_SEC_TOP) {
    325       if (strcmp(key, "name") == 0) {
    326         if (seen_once3(&seen, P3_F_NAME, "duplicate top-level key", err,
    327                        errcap) != DIST_OK)
    328           return DIST_ERR;
    329         if (!field_text_valid(val, 1))
    330           return dist_set_err(err, errcap, "bad package name");
    331         if (dist_copy_field(m->name, sizeof m->name, val, err, errcap))
    332           return DIST_ERR;
    333       } else if (strcmp(key, "version") == 0) {
    334         if (seen_once3(&seen, P3_F_VERSION, "duplicate top-level key", err,
    335                        errcap) != DIST_OK)
    336           return DIST_ERR;
    337         if (!field_text_valid(val, 1))
    338           return dist_set_err(err, errcap, "bad package version");
    339         if (dist_copy_field(m->version, sizeof m->version, val, err, errcap))
    340           return DIST_ERR;
    341       } else if (strcmp(key, "description") == 0) {
    342         if (seen_once3(&seen, P3_F_DESCRIPTION, "duplicate top-level key", err,
    343                        errcap) != DIST_OK)
    344           return DIST_ERR;
    345         if (!field_text_valid(val, 0))
    346           return dist_set_err(err, errcap, "bad package description");
    347         if (dist_copy_field(m->description, sizeof m->description, val, err,
    348                             errcap))
    349           return DIST_ERR;
    350       } else if (strcmp(key, "hash") == 0) {
    351         if (seen_once3(&seen, P3_F_HASH, "duplicate top-level key", err,
    352                        errcap) != DIST_OK)
    353           return DIST_ERR;
    354         if (strcmp(val, DIST_PACKAGE3_HASH) != 0)
    355           return dist_set_err(err, errcap, "unsupported hash algorithm");
    356       } else if (strcmp(key, "tree") == 0) {
    357         if (seen_once3(&seen, P3_F_TREE_FORMAT, "duplicate top-level key", err,
    358                        errcap) != DIST_OK)
    359           return DIST_ERR;
    360         if (strcmp(val, DIST_PACKAGE3_TREE_FORMAT) != 0)
    361           return dist_set_err(err, errcap, "unsupported tree format");
    362       } else if (strcmp(key, "blob") == 0) {
    363         if (seen_once3(&seen, P3_F_BLOB_FORMAT, "duplicate top-level key", err,
    364                        errcap) != DIST_OK)
    365           return DIST_ERR;
    366         if (strcmp(val, DIST_PACKAGE3_BLOB_FORMAT) != 0)
    367           return dist_set_err(err, errcap, "unsupported blob format");
    368       } else {
    369         return dist_set_err(err, errcap, "unknown top-level key");
    370       }
    371     } else if (sec == P3_SEC_OUTPUT) {
    372       if (strcmp(key, "id") == 0) {
    373         if (seen_once3(&seen, P3_F_ID, "duplicate [output] key", err, errcap) !=
    374             DIST_OK)
    375           return DIST_ERR;
    376         if (dist_parse_u64(val, &pkg_out->id) != DIST_OK)
    377           return dist_set_err(err, errcap, "bad output id");
    378       } else if (strcmp(key, "name") == 0) {
    379         if (seen_once3(&seen, P3_F_OUTPUT_NAME, "duplicate [output] key", err,
    380                        errcap) != DIST_OK)
    381           return DIST_ERR;
    382         if (!field_text_valid(val, 1))
    383           return dist_set_err(err, errcap, "bad output name");
    384         if (dist_copy_field(pkg_out->name, sizeof pkg_out->name, val, err,
    385                             errcap))
    386           return DIST_ERR;
    387       } else if (strcmp(key, "tree") == 0) {
    388         if (seen_once3(&seen, P3_F_TREE_ID, "duplicate [output] key", err,
    389                        errcap) != DIST_OK)
    390           return DIST_ERR;
    391         if (decode_hash3(pkg_out->tree, val, "bad output tree id", err,
    392                          errcap) != DIST_OK)
    393           return DIST_ERR;
    394       } else if (strcmp(key, "target") == 0) {
    395         if (seen_once3(&seen, P3_F_TARGET, "duplicate [output] key", err,
    396                        errcap) != DIST_OK)
    397           return DIST_ERR;
    398         if (!field_text_valid(val, 0))
    399           return dist_set_err(err, errcap, "bad output target");
    400         if (dist_copy_field(pkg_out->target, sizeof pkg_out->target, val, err,
    401                             errcap))
    402           return DIST_ERR;
    403       } else if (strcmp(key, "default") == 0) {
    404         if (seen_once3(&seen, P3_F_DEFAULT, "duplicate [output] key", err,
    405                        errcap) != DIST_OK)
    406           return DIST_ERR;
    407         if (parse_bool3(val, &pkg_out->is_default) != DIST_OK)
    408           return dist_set_err(err, errcap, "bad default value");
    409       } else {
    410         return dist_set_err(err, errcap, "unknown [output] key");
    411       }
    412     } else if (sec == P3_SEC_ARTIFACT) {
    413       if (strcmp(key, "output") == 0) {
    414         if (seen_once3(&seen, P3_F_OUTPUT, "duplicate [artifact] key", err,
    415                        errcap) != DIST_OK)
    416           return DIST_ERR;
    417         if (dist_parse_u64(val, &art->output_id) != DIST_OK)
    418           return dist_set_err(err, errcap, "bad artifact output id");
    419       } else if (strcmp(key, "path") == 0) {
    420         if (seen_once3(&seen, P3_F_PATH, "duplicate [artifact] key", err,
    421                        errcap) != DIST_OK)
    422           return DIST_ERR;
    423         if (!dist_manifest_path_valid(val))
    424           return dist_set_err(err, errcap, "unsafe artifact path");
    425         if (dist_copy_field(art->path, sizeof art->path, val, err, errcap))
    426           return DIST_ERR;
    427       } else if (strcmp(key, "kind") == 0) {
    428         if (seen_once3(&seen, P3_F_KIND, "duplicate [artifact] key", err,
    429                        errcap) != DIST_OK)
    430           return DIST_ERR;
    431         if (!kind_valid(val))
    432           return dist_set_err(err, errcap, "unknown artifact kind");
    433         if (dist_copy_field(art->kind, sizeof art->kind, val, err, errcap))
    434           return DIST_ERR;
    435       } else if (strcmp(key, "entry") == 0) {
    436         if (seen_once3(&seen, P3_F_ENTRY, "duplicate [artifact] key", err,
    437                        errcap) != DIST_OK)
    438           return DIST_ERR;
    439         if (parse_bool3(val, &art->entry) != DIST_OK)
    440           return dist_set_err(err, errcap, "bad entry value");
    441       } else {
    442         return dist_set_err(err, errcap, "unknown [artifact] key");
    443       }
    444     } else {
    445       if (strcmp(key, "name") == 0) {
    446         if (seen_once3(&seen, P3_F_NAME, "duplicate [dependency] key", err,
    447                        errcap) != DIST_OK)
    448           return DIST_ERR;
    449         if (!field_text_valid(val, 1))
    450           return dist_set_err(err, errcap, "bad dependency name");
    451         if (dist_copy_field(dep->name, sizeof dep->name, val, err, errcap))
    452           return DIST_ERR;
    453       } else if (strcmp(key, "version") == 0) {
    454         if (seen_once3(&seen, P3_F_VERSION, "duplicate [dependency] key", err,
    455                        errcap) != DIST_OK)
    456           return DIST_ERR;
    457         if (!field_text_valid(val, 1))
    458           return dist_set_err(err, errcap, "bad dependency version");
    459         if (dist_copy_field(dep->version, sizeof dep->version, val, err,
    460                             errcap))
    461           return DIST_ERR;
    462       } else if (strcmp(key, "package") == 0) {
    463         if (seen_once3(&seen, P3_F_PACKAGE, "duplicate [dependency] key", err,
    464                        errcap) != DIST_OK)
    465           return DIST_ERR;
    466         if (decode_hash3(dep->package, val, "bad dependency package id", err,
    467                          errcap) != DIST_OK)
    468           return DIST_ERR;
    469         dep->has_package = 1;
    470       } else if (strcmp(key, "key") == 0) {
    471         if (seen_once3(&seen, P3_F_KEY, "duplicate [dependency] key", err,
    472                        errcap) != DIST_OK)
    473           return DIST_ERR;
    474         if (decode_keyid3(dep->keyid, val, err, errcap) != DIST_OK)
    475           return DIST_ERR;
    476         dep->has_keyid = 1;
    477       } else {
    478         return dist_set_err(err, errcap, "unknown [dependency] key");
    479       }
    480     }
    481   }
    482 
    483   if (first)
    484     return dist_set_err(err, errcap, "bad package manifest magic/version");
    485   if (finalize_package_section3(sec, seen, err, errcap) != DIST_OK)
    486     return DIST_ERR;
    487   return dist_package_manifest_validate(m, err, errcap);
    488 }