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 }