kit

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

cas.c (9263B)


      1 #include "cas.h"
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 
      6 #include "blob.h"
      7 
      8 static int dist_cas_object_relpath(char* out, size_t cap, const char* kind,
      9                                    const uint8_t id[DIST_BLAKE2B_LEN]) {
     10   char hex[2 * DIST_BLAKE2B_LEN + 1];
     11   int n;
     12   if (!out || !cap || !kind || !id) return DIST_ERR;
     13   dist_hex_encode(hex, id, DIST_BLAKE2B_LEN);
     14   n = snprintf(out, cap, "%s/%c%c/%s", kind, hex[0], hex[1], hex);
     15   return n > 0 && (size_t)n < cap ? DIST_OK : DIST_ERR;
     16 }
     17 
     18 static int dist_cas_join_path(char* out, size_t cap, const char* root,
     19                               const char* rel) {
     20   size_t rl, pl;
     21   int slash;
     22   if (!out || !cap || !root || !rel) return DIST_ERR;
     23   rl = strlen(root);
     24   pl = strlen(rel);
     25   slash = rl > 0 && root[rl - 1u] != '/';
     26   if (rl + (slash ? 1u : 0u) + pl + 1u > cap) return DIST_ERR;
     27   memcpy(out, root, rl);
     28   if (slash) out[rl++] = '/';
     29   memcpy(out + rl, rel, pl);
     30   out[rl + pl] = '\0';
     31   return DIST_OK;
     32 }
     33 
     34 static int dist_cas_object_path(char* out, size_t cap, const char* root,
     35                                 const char* kind,
     36                                 const uint8_t id[DIST_BLAKE2B_LEN]) {
     37   char rel[DIST_CAS_PATH_MAX];
     38   if (dist_cas_object_relpath(rel, sizeof rel, kind, id) != DIST_OK)
     39     return DIST_ERR;
     40   return dist_cas_join_path(out, cap, root, rel);
     41 }
     42 
     43 int dist_cas_blob_relpath(char* out, size_t cap,
     44                           const uint8_t blob[DIST_BLAKE2B_LEN]) {
     45   return dist_cas_object_relpath(out, cap, "blob", blob);
     46 }
     47 
     48 int dist_cas_tree_relpath(char* out, size_t cap,
     49                           const uint8_t tree[DIST_BLAKE2B_LEN]) {
     50   return dist_cas_object_relpath(out, cap, "tree", tree);
     51 }
     52 
     53 int dist_cas_index_relpath(char* out, size_t cap,
     54                            const uint8_t index[DIST_BLAKE2B_LEN]) {
     55   return dist_cas_object_relpath(out, cap, "index", index);
     56 }
     57 
     58 int dist_cas_chunk_relpath(char* out, size_t cap,
     59                            const uint8_t blob[DIST_BLAKE2B_LEN],
     60                            uint64_t chunk_index) {
     61   char hex[2 * DIST_BLAKE2B_LEN + 1];
     62   int n;
     63   if (!out || !cap || !blob) return DIST_ERR;
     64   dist_hex_encode(hex, blob, DIST_BLAKE2B_LEN);
     65   n = snprintf(out, cap, "chunk/%c%c/%s/%llu", hex[0], hex[1], hex,
     66                (unsigned long long)chunk_index);
     67   return n > 0 && (size_t)n < cap ? DIST_OK : DIST_ERR;
     68 }
     69 
     70 int dist_cas_blob_path(char* out, size_t cap, const char* root,
     71                        const uint8_t blob[DIST_BLAKE2B_LEN]) {
     72   return dist_cas_object_path(out, cap, root, "blob", blob);
     73 }
     74 
     75 int dist_cas_tree_path(char* out, size_t cap, const char* root,
     76                        const uint8_t tree[DIST_BLAKE2B_LEN]) {
     77   return dist_cas_object_path(out, cap, root, "tree", tree);
     78 }
     79 
     80 int dist_cas_index_path(char* out, size_t cap, const char* root,
     81                         const uint8_t index[DIST_BLAKE2B_LEN]) {
     82   return dist_cas_object_path(out, cap, root, "index", index);
     83 }
     84 
     85 int dist_cas_chunk_path(char* out, size_t cap, const char* root,
     86                         const uint8_t blob[DIST_BLAKE2B_LEN],
     87                         uint64_t chunk_index) {
     88   char rel[DIST_CAS_PATH_MAX];
     89   if (dist_cas_chunk_relpath(rel, sizeof rel, blob, chunk_index) != DIST_OK)
     90     return DIST_ERR;
     91   return dist_cas_join_path(out, cap, root, rel);
     92 }
     93 
     94 static int parent_dir(char* out, size_t cap, const char* path) {
     95   const char* slash = NULL;
     96   const char* p;
     97   size_t n;
     98   for (p = path; *p; ++p)
     99     if (*p == '/') slash = p;
    100   if (!slash) return DIST_ERR;
    101   n = (size_t)(slash - path);
    102   if (n >= cap) return DIST_ERR;
    103   memcpy(out, path, n);
    104   out[n] = '\0';
    105   return DIST_OK;
    106 }
    107 
    108 static int put_bytes(DistCas* cas, const char* path, const uint8_t* data,
    109                      size_t len) {
    110   KitWriter* w = NULL;
    111   char parent[DIST_CAS_PATH_MAX];
    112   if (!cas || !cas->root || !cas->host.file_io ||
    113       !cas->host.file_io->open_writer)
    114     return DIST_ERR;
    115   if (parent_dir(parent, sizeof parent, path) != DIST_OK) return DIST_ERR;
    116   if (cas->host.mkdir_p && cas->host.mkdir_p(cas->host.user, parent) != 0)
    117     return DIST_ERR;
    118   if (cas->host.file_io->open_writer(cas->host.file_io->user, path, &w) !=
    119       KIT_OK)
    120     return DIST_ERR;
    121   if (len && kit_writer_write(w, data, len) != KIT_OK) {
    122     kit_writer_close(w);
    123     return DIST_ERR;
    124   }
    125   if (kit_writer_status(w) != KIT_OK) {
    126     kit_writer_close(w);
    127     return DIST_ERR;
    128   }
    129   kit_writer_close(w);
    130   return DIST_OK;
    131 }
    132 
    133 int dist_cas_put_blob(DistCas* cas, const uint8_t blob[DIST_BLAKE2B_LEN],
    134                       const uint8_t* data, size_t len) {
    135   char path[DIST_CAS_PATH_MAX];
    136   uint8_t got[DIST_BLAKE2B_LEN];
    137   if (!cas || !blob || (len && !data)) return DIST_ERR;
    138   dist_blob_id(got, data, len);
    139   if (memcmp(got, blob, DIST_BLAKE2B_LEN) != 0) return DIST_ERR;
    140   if (dist_cas_blob_path(path, sizeof path, cas->root, blob) != DIST_OK)
    141     return DIST_ERR;
    142   return put_bytes(cas, path, data, len);
    143 }
    144 
    145 int dist_cas_get_blob(DistCas* cas, const uint8_t blob[DIST_BLAKE2B_LEN],
    146                       KitFileData* out) {
    147   char path[DIST_CAS_PATH_MAX];
    148   uint8_t got[DIST_BLAKE2B_LEN];
    149   if (!cas || !cas->root || !blob || !out || !cas->host.file_io ||
    150       !cas->host.file_io->read_all)
    151     return DIST_ERR;
    152   if (dist_cas_blob_path(path, sizeof path, cas->root, blob) != DIST_OK)
    153     return DIST_ERR;
    154   if (cas->host.file_io->read_all(cas->host.file_io->user, path, out) != KIT_OK)
    155     return DIST_ERR;
    156   dist_blob_id(got, out->data, out->size);
    157   if (memcmp(got, blob, DIST_BLAKE2B_LEN) == 0) return DIST_OK;
    158   if (cas->host.file_io->release)
    159     cas->host.file_io->release(cas->host.file_io->user, out);
    160   return DIST_ERR;
    161 }
    162 
    163 int dist_cas_put_tree(DistCas* cas, const uint8_t tree[DIST_BLAKE2B_LEN],
    164                       const uint8_t* data, size_t len) {
    165   char path[DIST_CAS_PATH_MAX];
    166   uint8_t got[DIST_BLAKE2B_LEN];
    167   if (!cas || !tree || (len && !data)) return DIST_ERR;
    168   dist_tree_id(got, data, len);
    169   if (memcmp(got, tree, DIST_BLAKE2B_LEN) != 0) return DIST_ERR;
    170   if (dist_cas_tree_path(path, sizeof path, cas->root, tree) != DIST_OK)
    171     return DIST_ERR;
    172   return put_bytes(cas, path, data, len);
    173 }
    174 
    175 int dist_cas_get_tree(DistCas* cas, const uint8_t tree[DIST_BLAKE2B_LEN],
    176                       KitFileData* out) {
    177   char path[DIST_CAS_PATH_MAX];
    178   uint8_t got[DIST_BLAKE2B_LEN];
    179   if (!cas || !cas->root || !tree || !out || !cas->host.file_io ||
    180       !cas->host.file_io->read_all)
    181     return DIST_ERR;
    182   if (dist_cas_tree_path(path, sizeof path, cas->root, tree) != DIST_OK)
    183     return DIST_ERR;
    184   if (cas->host.file_io->read_all(cas->host.file_io->user, path, out) != KIT_OK)
    185     return DIST_ERR;
    186   dist_tree_id(got, out->data, out->size);
    187   if (memcmp(got, tree, DIST_BLAKE2B_LEN) == 0) return DIST_OK;
    188   if (cas->host.file_io->release)
    189     cas->host.file_io->release(cas->host.file_io->user, out);
    190   return DIST_ERR;
    191 }
    192 
    193 static int join_tree_path(char* out, size_t cap, const char* dst,
    194                           const char* rel) {
    195   size_t dl, rl;
    196   int needs_slash;
    197   if (!out || !cap || !dst || !rel) return DIST_ERR;
    198   dl = strlen(dst);
    199   rl = strlen(rel);
    200   needs_slash = dl > 0 && dst[dl - 1u] != '/';
    201   if (dl + (needs_slash ? 1u : 0u) + rl + 1u > cap) return DIST_ERR;
    202   memcpy(out, dst, dl);
    203   if (needs_slash) out[dl++] = '/';
    204   memcpy(out + dl, rel, rl);
    205   out[dl + rl] = '\0';
    206   return DIST_OK;
    207 }
    208 
    209 int dist_cas_materialize_tree(DistCas* cas, const DistTree* tree,
    210                               const char* dst) {
    211   size_t i;
    212   if (!cas || !tree || !dst || !cas->host.file_io ||
    213       !cas->host.file_io->open_writer)
    214     return DIST_ERR;
    215   for (i = 0; i < tree->n_entries; ++i) {
    216     const DistTreeEntry* e = &tree->entries[i];
    217     KitFileData fd;
    218     DistBlobInfo bi;
    219     KitWriter* w = NULL;
    220     char outpath[DIST_CAS_PATH_MAX];
    221     char parent[DIST_CAS_PATH_MAX];
    222     int rc = DIST_ERR;
    223 
    224     fd.data = NULL;
    225     fd.size = 0;
    226     fd.token = NULL;
    227     if (!dist_tree_path_valid(e->path)) return DIST_ERR;
    228     if (!dist_tree_mode_name(e->mode)) return DIST_ERR;
    229     if (dist_cas_get_blob(cas, e->blob, &fd) != DIST_OK) return DIST_ERR;
    230     if (dist_blob_info(&bi, fd.data, fd.size, DIST_BLOB_CHUNK_SIZE_DEFAULT) !=
    231         DIST_OK)
    232       goto entry_out;
    233     if (bi.size != e->size || memcmp(bi.root, e->root, DIST_BLAKE2B_LEN) != 0)
    234       goto entry_out;
    235     if (join_tree_path(outpath, sizeof outpath, dst, e->path) != DIST_OK)
    236       goto entry_out;
    237     if (parent_dir(parent, sizeof parent, outpath) != DIST_OK) goto entry_out;
    238     if (cas->host.mkdir_p && cas->host.mkdir_p(cas->host.user, parent) != 0)
    239       goto entry_out;
    240     if (cas->host.file_io->open_writer(cas->host.file_io->user, outpath, &w) !=
    241         KIT_OK)
    242       goto entry_out;
    243     if (fd.size && kit_writer_write(w, fd.data, fd.size) != KIT_OK)
    244       goto entry_out;
    245     if (kit_writer_status(w) != KIT_OK) goto entry_out;
    246     kit_writer_close(w);
    247     w = NULL;
    248     if (e->mode == DIST_TREE_MODE_EXEC && cas->host.mark_executable &&
    249         cas->host.mark_executable(cas->host.user, outpath) != 0)
    250       goto entry_out;
    251     rc = DIST_OK;
    252 
    253   entry_out:
    254     if (w) kit_writer_close(w);
    255     if (cas->host.file_io->release)
    256       cas->host.file_io->release(cas->host.file_io->user, &fd);
    257     if (rc != DIST_OK) return DIST_ERR;
    258   }
    259   return DIST_OK;
    260 }