cas.c (8778B)
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 int dist_cas_materialize_tree(DistCas* cas, const DistTree* tree, 194 const char* dst) { 195 size_t i; 196 if (!cas || !tree || !dst || !cas->host.file_io || 197 !cas->host.file_io->open_writer) 198 return DIST_ERR; 199 for (i = 0; i < tree->n_entries; ++i) { 200 const DistTreeEntry* e = &tree->entries[i]; 201 KitFileData fd; 202 DistBlobInfo bi; 203 KitWriter* w = NULL; 204 char outpath[DIST_CAS_PATH_MAX]; 205 char parent[DIST_CAS_PATH_MAX]; 206 int rc = DIST_ERR; 207 208 fd.data = NULL; 209 fd.size = 0; 210 fd.token = NULL; 211 if (!dist_tree_path_valid(e->path)) return DIST_ERR; 212 if (!dist_tree_mode_name(e->mode)) return DIST_ERR; 213 if (dist_cas_get_blob(cas, e->blob, &fd) != DIST_OK) return DIST_ERR; 214 if (dist_blob_info(&bi, fd.data, fd.size, DIST_BLOB_CHUNK_SIZE_DEFAULT) != 215 DIST_OK) 216 goto entry_out; 217 if (bi.size != e->size || memcmp(bi.root, e->root, DIST_BLAKE2B_LEN) != 0) 218 goto entry_out; 219 if (dist_cas_join_path(outpath, sizeof outpath, dst, e->path) != DIST_OK) 220 goto entry_out; 221 if (parent_dir(parent, sizeof parent, outpath) != DIST_OK) goto entry_out; 222 if (cas->host.mkdir_p && cas->host.mkdir_p(cas->host.user, parent) != 0) 223 goto entry_out; 224 if (cas->host.file_io->open_writer(cas->host.file_io->user, outpath, &w) != 225 KIT_OK) 226 goto entry_out; 227 if (fd.size && kit_writer_write(w, fd.data, fd.size) != KIT_OK) 228 goto entry_out; 229 if (kit_writer_status(w) != KIT_OK) goto entry_out; 230 kit_writer_close(w); 231 w = NULL; 232 if (e->mode == DIST_TREE_MODE_EXEC && cas->host.mark_executable && 233 cas->host.mark_executable(cas->host.user, outpath) != 0) 234 goto entry_out; 235 rc = DIST_OK; 236 237 entry_out: 238 if (w) kit_writer_close(w); 239 if (cas->host.file_io->release) 240 cas->host.file_io->release(cas->host.file_io->user, &fd); 241 if (rc != DIST_OK) return DIST_ERR; 242 } 243 return DIST_OK; 244 }