]>
Commit | Line | Data |
---|---|---|
c15648cb | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
c15648cb | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
c15648cb SP |
6 | */ |
7 | ||
d44cfd46 | 8 | #include "common.h" |
0c3bae62 | 9 | #include <zlib.h> |
44908fe7 | 10 | #include "git2/object.h" |
83cc70d9 | 11 | #include "git2/sys/odb_backend.h" |
3d3552e8 | 12 | #include "fileops.h" |
c960d6a3 | 13 | #include "hash.h" |
a7c60cfc | 14 | #include "odb.h" |
7e4f56a5 | 15 | #include "delta-apply.h" |
60b9d3fc | 16 | #include "filter.h" |
5df18424 | 17 | #include "repository.h" |
c15648cb | 18 | |
44908fe7 | 19 | #include "git2/odb_backend.h" |
c07d9c95 | 20 | #include "git2/oid.h" |
7b6e8067 | 21 | |
5a800efc VM |
22 | #define GIT_ALTERNATES_FILE "info/alternates" |
23 | ||
b0d7f329 CMN |
24 | /* |
25 | * We work under the assumption that most objects for long-running | |
26 | * operations will be packed | |
27 | */ | |
28 | #define GIT_LOOSE_PRIORITY 1 | |
29 | #define GIT_PACKED_PRIORITY 2 | |
d4b5a4e2 | 30 | |
85e7efa1 CMN |
31 | #define GIT_ALTERNATES_MAX_DEPTH 5 |
32 | ||
d4b5a4e2 VM |
33 | typedef struct |
34 | { | |
35 | git_odb_backend *backend; | |
36 | int priority; | |
a29c6b5f VM |
37 | bool is_alternate; |
38 | ino_t disk_inode; | |
d4b5a4e2 VM |
39 | } backend_internal; |
40 | ||
5df18424 VM |
41 | static git_cache *odb_cache(git_odb *odb) |
42 | { | |
43 | if (odb->rc.owner != NULL) { | |
44 | git_repository *owner = odb->rc.owner; | |
45 | return &owner->objects; | |
46 | } | |
47 | ||
48 | return &odb->own_cache; | |
49 | } | |
f5e28202 | 50 | |
85e7efa1 CMN |
51 | static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); |
52 | ||
77b339f7 | 53 | int git_odb__format_object_header(char *hdr, size_t n, git_off_t obj_len, git_otype obj_type) |
c960d6a3 | 54 | { |
c52736fa | 55 | const char *type_str = git_object_type2string(obj_type); |
9f3c18e2 | 56 | int len = p_snprintf(hdr, n, "%s %lld", type_str, obj_len); |
ae9e29fd | 57 | assert(len > 0 && len <= (int)n); |
c960d6a3 RJ |
58 | return len+1; |
59 | } | |
60 | ||
18e5b854 | 61 | int git_odb__hashobj(git_oid *id, git_rawobj *obj) |
c960d6a3 RJ |
62 | { |
63 | git_buf_vec vec[2]; | |
c85e08b1 VM |
64 | char header[64]; |
65 | int hdrlen; | |
c960d6a3 | 66 | |
c85e08b1 | 67 | assert(id && obj); |
c960d6a3 | 68 | |
d12299fe | 69 | if (!git_object_typeisloose(obj->type)) |
ae9e29fd | 70 | return -1; |
8842c75f | 71 | |
c960d6a3 | 72 | if (!obj->data && obj->len != 0) |
ae9e29fd | 73 | return -1; |
c960d6a3 | 74 | |
f56f8585 | 75 | hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type); |
c960d6a3 | 76 | |
c85e08b1 | 77 | vec[0].data = header; |
87d9869f | 78 | vec[0].len = hdrlen; |
c960d6a3 | 79 | vec[1].data = obj->data; |
87d9869f | 80 | vec[1].len = obj->len; |
c960d6a3 RJ |
81 | |
82 | git_hash_vec(id, vec, 2); | |
83 | ||
ae9e29fd | 84 | return 0; |
c960d6a3 RJ |
85 | } |
86 | ||
d12299fe | 87 | |
78606263 | 88 | static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source) |
e17a3f56 | 89 | { |
78606263 | 90 | git_odb_object *object = git__calloc(1, sizeof(git_odb_object)); |
e17a3f56 | 91 | |
78606263 RB |
92 | if (object != NULL) { |
93 | git_oid_cpy(&object->cached.oid, oid); | |
94 | object->cached.type = source->type; | |
95 | object->cached.size = source->len; | |
96 | object->buffer = source->data; | |
97 | } | |
e17a3f56 | 98 | |
72a3fe42 | 99 | return object; |
3d3552e8 RJ |
100 | } |
101 | ||
78606263 | 102 | void git_odb_object__free(void *object) |
3d3552e8 | 103 | { |
72a3fe42 | 104 | if (object != NULL) { |
78606263 | 105 | git__free(((git_odb_object *)object)->buffer); |
3286c408 | 106 | git__free(object); |
72a3fe42 VM |
107 | } |
108 | } | |
3d3552e8 | 109 | |
1881f078 VM |
110 | const git_oid *git_odb_object_id(git_odb_object *object) |
111 | { | |
112 | return &object->cached.oid; | |
113 | } | |
114 | ||
115 | const void *git_odb_object_data(git_odb_object *object) | |
116 | { | |
8842c75f | 117 | return object->buffer; |
1881f078 VM |
118 | } |
119 | ||
120 | size_t git_odb_object_size(git_odb_object *object) | |
121 | { | |
8842c75f | 122 | return object->cached.size; |
1881f078 VM |
123 | } |
124 | ||
125 | git_otype git_odb_object_type(git_odb_object *object) | |
126 | { | |
8842c75f | 127 | return object->cached.type; |
1881f078 VM |
128 | } |
129 | ||
98fec8a9 VM |
130 | int git_odb_object_dup(git_odb_object **dest, git_odb_object *source) |
131 | { | |
132 | git_cached_obj_incref(source); | |
133 | *dest = source; | |
134 | return 0; | |
135 | } | |
136 | ||
45e79e37 | 137 | void git_odb_object_free(git_odb_object *object) |
72a3fe42 | 138 | { |
edca6c8f MS |
139 | if (object == NULL) |
140 | return; | |
141 | ||
5df18424 | 142 | git_cached_obj_decref(object); |
72a3fe42 | 143 | } |
3d3552e8 | 144 | |
18e5b854 | 145 | int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type) |
c52736fa | 146 | { |
18e5b854 | 147 | int hdr_len; |
7dd22538 | 148 | char hdr[64], buffer[FILEIO_BUFSIZE]; |
603bee07 | 149 | git_hash_ctx ctx; |
addc9be4 | 150 | ssize_t read_len = 0; |
d6fb0924 | 151 | int error = 0; |
c52736fa | 152 | |
a13fb55a RB |
153 | if (!git_object_typeisloose(type)) { |
154 | giterr_set(GITERR_INVALID, "Invalid object type for hash"); | |
155 | return -1; | |
156 | } | |
157 | ||
603bee07 ET |
158 | if ((error = git_hash_ctx_init(&ctx)) < 0) |
159 | return -1; | |
c52736fa | 160 | |
f56f8585 | 161 | hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type); |
d6fb0924 | 162 | |
603bee07 | 163 | if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0) |
d6fb0924 | 164 | goto done; |
c52736fa | 165 | |
c859184b | 166 | while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { |
603bee07 | 167 | if ((error = git_hash_update(&ctx, buffer, read_len)) < 0) |
d6fb0924 ET |
168 | goto done; |
169 | ||
c52736fa VM |
170 | size -= read_len; |
171 | } | |
172 | ||
c859184b VM |
173 | /* If p_read returned an error code, the read obviously failed. |
174 | * If size is not zero, the file was truncated after we originally | |
175 | * stat'd it, so we consider this a read failure too */ | |
176 | if (read_len < 0 || size > 0) { | |
c859184b | 177 | giterr_set(GITERR_OS, "Error reading file for hashing"); |
d6fb0924 ET |
178 | error = -1; |
179 | ||
180 | goto done; | |
c859184b VM |
181 | } |
182 | ||
603bee07 | 183 | error = git_hash_final(out, &ctx); |
c52736fa | 184 | |
d6fb0924 | 185 | done: |
603bee07 | 186 | git_hash_ctx_cleanup(&ctx); |
d6fb0924 | 187 | return error; |
c52736fa VM |
188 | } |
189 | ||
60b9d3fc | 190 | int git_odb__hashfd_filtered( |
85d54812 | 191 | git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl) |
60b9d3fc RB |
192 | { |
193 | int error; | |
194 | git_buf raw = GIT_BUF_INIT; | |
60b9d3fc | 195 | |
85d54812 | 196 | if (!fl) |
60b9d3fc RB |
197 | return git_odb__hashfd(out, fd, size, type); |
198 | ||
199 | /* size of data is used in header, so we have to read the whole file | |
200 | * into memory to apply filters before beginning to calculate the hash | |
201 | */ | |
202 | ||
2a7d224f | 203 | if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) { |
a9f51e43 | 204 | git_buf post = GIT_BUF_INIT; |
60b9d3fc | 205 | |
a9f51e43 | 206 | error = git_filter_list_apply_to_data(&post, fl, &raw); |
60b9d3fc | 207 | |
a9f51e43 | 208 | git_buf_free(&raw); |
60b9d3fc | 209 | |
2a7d224f RB |
210 | if (!error) |
211 | error = git_odb_hash(out, post.ptr, post.size, type); | |
212 | ||
a9f51e43 | 213 | git_buf_free(&post); |
2a7d224f | 214 | } |
60b9d3fc RB |
215 | |
216 | return error; | |
217 | } | |
218 | ||
f19e3ca2 VM |
219 | int git_odb__hashlink(git_oid *out, const char *path) |
220 | { | |
221 | struct stat st; | |
f1453c59 | 222 | int size; |
ae9e29fd | 223 | int result; |
f19e3ca2 | 224 | |
deafee7b | 225 | if (git_path_lstat(path, &st) < 0) |
ae9e29fd | 226 | return -1; |
f19e3ca2 | 227 | |
f1453c59 | 228 | if (!git__is_int(st.st_size) || (int)st.st_size < 0) { |
15d54fdd | 229 | giterr_set(GITERR_FILESYSTEM, "File size overflow for 32-bit systems"); |
ae9e29fd RB |
230 | return -1; |
231 | } | |
f19e3ca2 | 232 | |
f1453c59 | 233 | size = (int)st.st_size; |
15d54fdd | 234 | |
f19e3ca2 VM |
235 | if (S_ISLNK(st.st_mode)) { |
236 | char *link_data; | |
f1453c59 ET |
237 | int read_len; |
238 | size_t alloc_size; | |
f19e3ca2 | 239 | |
f1453c59 ET |
240 | GITERR_CHECK_ALLOC_ADD(&alloc_size, size, 1); |
241 | link_data = git__malloc(alloc_size); | |
ae9e29fd | 242 | GITERR_CHECK_ALLOC(link_data); |
f19e3ca2 | 243 | |
15d54fdd | 244 | read_len = p_readlink(path, link_data, size); |
e8776d30 | 245 | link_data[size] = '\0'; |
f1453c59 | 246 | if (read_len != size) { |
ae9e29fd | 247 | giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path); |
c6451624 | 248 | git__free(link_data); |
ae9e29fd RB |
249 | return -1; |
250 | } | |
f19e3ca2 | 251 | |
15d54fdd | 252 | result = git_odb_hash(out, link_data, size, GIT_OBJ_BLOB); |
2bc8fa02 | 253 | git__free(link_data); |
60b9d3fc | 254 | } else { |
ae9e29fd RB |
255 | int fd = git_futils_open_ro(path); |
256 | if (fd < 0) | |
257 | return -1; | |
15d54fdd | 258 | result = git_odb__hashfd(out, fd, size, GIT_OBJ_BLOB); |
f19e3ca2 VM |
259 | p_close(fd); |
260 | } | |
261 | ||
ae9e29fd | 262 | return result; |
f19e3ca2 VM |
263 | } |
264 | ||
18e5b854 VM |
265 | int git_odb_hashfile(git_oid *out, const char *path, git_otype type) |
266 | { | |
18e5b854 | 267 | git_off_t size; |
ae9e29fd RB |
268 | int result, fd = git_futils_open_ro(path); |
269 | if (fd < 0) | |
e1de726c | 270 | return fd; |
18e5b854 VM |
271 | |
272 | if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) { | |
ae9e29fd | 273 | giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); |
18e5b854 | 274 | p_close(fd); |
ae9e29fd | 275 | return -1; |
18e5b854 VM |
276 | } |
277 | ||
ae9e29fd | 278 | result = git_odb__hashfd(out, fd, (size_t)size, type); |
18e5b854 | 279 | p_close(fd); |
ae9e29fd | 280 | return result; |
18e5b854 VM |
281 | } |
282 | ||
72a3fe42 VM |
283 | int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type) |
284 | { | |
72a3fe42 | 285 | git_rawobj raw; |
3d3552e8 | 286 | |
72a3fe42 | 287 | assert(id); |
3d3552e8 | 288 | |
72a3fe42 VM |
289 | raw.data = (void *)data; |
290 | raw.len = len; | |
291 | raw.type = type; | |
3d3552e8 | 292 | |
18e5b854 | 293 | return git_odb__hashobj(id, &raw); |
3d3552e8 RJ |
294 | } |
295 | ||
d69d0185 VM |
296 | /** |
297 | * FAKE WSTREAM | |
298 | */ | |
299 | ||
300 | typedef struct { | |
301 | git_odb_stream stream; | |
302 | char *buffer; | |
303 | size_t size, written; | |
304 | git_otype type; | |
305 | } fake_wstream; | |
306 | ||
fe0c6d4e | 307 | static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid) |
d69d0185 VM |
308 | { |
309 | fake_wstream *stream = (fake_wstream *)_stream; | |
fe0c6d4e | 310 | return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type); |
d69d0185 VM |
311 | } |
312 | ||
313 | static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len) | |
314 | { | |
315 | fake_wstream *stream = (fake_wstream *)_stream; | |
316 | ||
411823a3 | 317 | if (stream->written + len > stream->size) |
ae9e29fd | 318 | return -1; |
d69d0185 VM |
319 | |
320 | memcpy(stream->buffer + stream->written, data, len); | |
321 | stream->written += len; | |
ae9e29fd | 322 | return 0; |
d69d0185 VM |
323 | } |
324 | ||
325 | static void fake_wstream__free(git_odb_stream *_stream) | |
326 | { | |
327 | fake_wstream *stream = (fake_wstream *)_stream; | |
328 | ||
3286c408 VM |
329 | git__free(stream->buffer); |
330 | git__free(stream); | |
d69d0185 VM |
331 | } |
332 | ||
77b339f7 | 333 | static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_off_t size, git_otype type) |
d69d0185 VM |
334 | { |
335 | fake_wstream *stream; | |
336 | ||
77b339f7 CMN |
337 | if (!git__is_ssizet(size)) { |
338 | giterr_set(GITERR_ODB, "object size too large to keep in memory"); | |
339 | return -1; | |
340 | } | |
341 | ||
d69d0185 | 342 | stream = git__calloc(1, sizeof(fake_wstream)); |
ae9e29fd | 343 | GITERR_CHECK_ALLOC(stream); |
d69d0185 VM |
344 | |
345 | stream->size = size; | |
346 | stream->type = type; | |
347 | stream->buffer = git__malloc(size); | |
348 | if (stream->buffer == NULL) { | |
3286c408 | 349 | git__free(stream); |
ae9e29fd | 350 | return -1; |
d69d0185 VM |
351 | } |
352 | ||
353 | stream->stream.backend = backend; | |
354 | stream->stream.read = NULL; /* read only */ | |
355 | stream->stream.write = &fake_wstream__write; | |
356 | stream->stream.finalize_write = &fake_wstream__fwrite; | |
357 | stream->stream.free = &fake_wstream__free; | |
358 | stream->stream.mode = GIT_STREAM_WRONLY; | |
359 | ||
360 | *stream_p = (git_odb_stream *)stream; | |
ae9e29fd | 361 | return 0; |
d69d0185 | 362 | } |
3d3552e8 | 363 | |
7d7cd885 VM |
364 | /*********************************************************** |
365 | * | |
366 | * OBJECT DATABASE PUBLIC API | |
367 | * | |
368 | * Public calls for the ODB functionality | |
369 | * | |
370 | ***********************************************************/ | |
3d3552e8 | 371 | |
39e1032c | 372 | static int backend_sort_cmp(const void *a, const void *b) |
7d7cd885 | 373 | { |
de18f276 VM |
374 | const backend_internal *backend_a = (const backend_internal *)(a); |
375 | const backend_internal *backend_b = (const backend_internal *)(b); | |
d4b5a4e2 VM |
376 | |
377 | if (backend_a->is_alternate == backend_b->is_alternate) | |
378 | return (backend_b->priority - backend_a->priority); | |
3d3552e8 | 379 | |
d4b5a4e2 | 380 | return backend_a->is_alternate ? 1 : -1; |
3d3552e8 RJ |
381 | } |
382 | ||
7d7cd885 | 383 | int git_odb_new(git_odb **out) |
3d3552e8 | 384 | { |
7d7cd885 | 385 | git_odb *db = git__calloc(1, sizeof(*db)); |
ae9e29fd | 386 | GITERR_CHECK_ALLOC(db); |
3d3552e8 | 387 | |
5df18424 VM |
388 | if (git_cache_init(&db->own_cache) < 0 || |
389 | git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { | |
3286c408 | 390 | git__free(db); |
ae9e29fd | 391 | return -1; |
7d7cd885 | 392 | } |
3d3552e8 | 393 | |
7d7cd885 | 394 | *out = db; |
9462c471 | 395 | GIT_REFCOUNT_INC(db); |
ae9e29fd | 396 | return 0; |
3d3552e8 RJ |
397 | } |
398 | ||
a29c6b5f VM |
399 | static int add_backend_internal( |
400 | git_odb *odb, git_odb_backend *backend, | |
401 | int priority, bool is_alternate, ino_t disk_inode) | |
e17a3f56 | 402 | { |
d4b5a4e2 VM |
403 | backend_internal *internal; |
404 | ||
7d7cd885 | 405 | assert(odb && backend); |
e17a3f56 | 406 | |
c7231c45 | 407 | GITERR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend"); |
55f6f21b | 408 | |
998f7b3d RB |
409 | /* Check if the backend is already owned by another ODB */ |
410 | assert(!backend->odb || backend->odb == odb); | |
e17a3f56 | 411 | |
d4b5a4e2 | 412 | internal = git__malloc(sizeof(backend_internal)); |
ae9e29fd | 413 | GITERR_CHECK_ALLOC(internal); |
d4b5a4e2 VM |
414 | |
415 | internal->backend = backend; | |
416 | internal->priority = priority; | |
417 | internal->is_alternate = is_alternate; | |
a29c6b5f | 418 | internal->disk_inode = disk_inode; |
e17a3f56 | 419 | |
d4b5a4e2 | 420 | if (git_vector_insert(&odb->backends, internal) < 0) { |
3286c408 | 421 | git__free(internal); |
ae9e29fd | 422 | return -1; |
d4b5a4e2 | 423 | } |
e17a3f56 | 424 | |
7d7cd885 | 425 | git_vector_sort(&odb->backends); |
d4b5a4e2 | 426 | internal->backend->odb = odb; |
ae9e29fd | 427 | return 0; |
e17a3f56 RJ |
428 | } |
429 | ||
d4b5a4e2 VM |
430 | int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority) |
431 | { | |
a29c6b5f | 432 | return add_backend_internal(odb, backend, priority, false, 0); |
d4b5a4e2 VM |
433 | } |
434 | ||
435 | int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) | |
436 | { | |
a29c6b5f | 437 | return add_backend_internal(odb, backend, priority, true, 0); |
d4b5a4e2 VM |
438 | } |
439 | ||
83cc70d9 RB |
440 | size_t git_odb_num_backends(git_odb *odb) |
441 | { | |
442 | assert(odb); | |
443 | return odb->backends.length; | |
444 | } | |
445 | ||
f063f578 RB |
446 | static int git_odb__error_unsupported_in_backend(const char *action) |
447 | { | |
448 | giterr_set(GITERR_ODB, | |
449 | "Cannot %s - unsupported in the loaded odb backends", action); | |
450 | return -1; | |
451 | } | |
452 | ||
453 | ||
83cc70d9 RB |
454 | int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) |
455 | { | |
456 | backend_internal *internal; | |
457 | ||
31a14982 | 458 | assert(out && odb); |
83cc70d9 RB |
459 | internal = git_vector_get(&odb->backends, pos); |
460 | ||
461 | if (internal && internal->backend) { | |
462 | *out = internal->backend; | |
463 | return 0; | |
464 | } | |
465 | ||
8cf80525 | 466 | giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos); |
83cc70d9 RB |
467 | return GIT_ENOTFOUND; |
468 | } | |
469 | ||
a29c6b5f VM |
470 | static int add_default_backends( |
471 | git_odb *db, const char *objects_dir, | |
472 | bool as_alternates, int alternate_depth) | |
5a800efc | 473 | { |
a29c6b5f VM |
474 | size_t i; |
475 | struct stat st; | |
c8a4e8a5 | 476 | ino_t inode; |
5a800efc | 477 | git_odb_backend *loose, *packed; |
5a800efc | 478 | |
4ef2c79c VM |
479 | /* TODO: inodes are not really relevant on Win32, so we need to find |
480 | * a cross-platform workaround for this */ | |
c8a4e8a5 ET |
481 | #ifdef GIT_WIN32 |
482 | GIT_UNUSED(i); | |
483 | GIT_UNUSED(st); | |
484 | ||
485 | inode = 0; | |
486 | #else | |
a29c6b5f | 487 | if (p_stat(objects_dir, &st) < 0) { |
dfec726b VM |
488 | if (as_alternates) |
489 | return 0; | |
490 | ||
a29c6b5f VM |
491 | giterr_set(GITERR_ODB, "Failed to load object database in '%s'", objects_dir); |
492 | return -1; | |
493 | } | |
494 | ||
c8a4e8a5 ET |
495 | inode = st.st_ino; |
496 | ||
a29c6b5f VM |
497 | for (i = 0; i < db->backends.length; ++i) { |
498 | backend_internal *backend = git_vector_get(&db->backends, i); | |
c8a4e8a5 | 499 | if (backend->disk_inode == inode) |
a29c6b5f VM |
500 | return 0; |
501 | } | |
4ef2c79c | 502 | #endif |
f063f578 | 503 | |
5a800efc | 504 | /* add the loose object backend */ |
dd64c71c | 505 | if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 || |
c8a4e8a5 | 506 | add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0) |
ae9e29fd | 507 | return -1; |
5a800efc VM |
508 | |
509 | /* add the packed file backend */ | |
ae9e29fd | 510 | if (git_odb_backend_pack(&packed, objects_dir) < 0 || |
c8a4e8a5 | 511 | add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates, inode) < 0) |
ae9e29fd | 512 | return -1; |
5a800efc | 513 | |
85e7efa1 | 514 | return load_alternates(db, objects_dir, alternate_depth); |
5a800efc VM |
515 | } |
516 | ||
85e7efa1 | 517 | static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth) |
5a800efc | 518 | { |
97769280 | 519 | git_buf alternates_path = GIT_BUF_INIT; |
13224ea4 | 520 | git_buf alternates_buf = GIT_BUF_INIT; |
97769280 | 521 | char *buffer; |
97769280 | 522 | const char *alternate; |
ae9e29fd | 523 | int result = 0; |
5a800efc | 524 | |
85e7efa1 | 525 | /* Git reports an error, we just ignore anything deeper */ |
f063f578 | 526 | if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) |
85e7efa1 | 527 | return 0; |
85e7efa1 | 528 | |
ae9e29fd RB |
529 | if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) |
530 | return -1; | |
5a800efc | 531 | |
1a481123 | 532 | if (git_path_exists(alternates_path.ptr) == false) { |
97769280 | 533 | git_buf_free(&alternates_path); |
ae9e29fd | 534 | return 0; |
97769280 | 535 | } |
5a800efc | 536 | |
ae9e29fd | 537 | if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) { |
97769280 | 538 | git_buf_free(&alternates_path); |
ae9e29fd | 539 | return -1; |
97769280 | 540 | } |
5a800efc | 541 | |
13224ea4 | 542 | buffer = (char *)alternates_buf.ptr; |
5a800efc VM |
543 | |
544 | /* add each alternate as a new backend; one alternate per line */ | |
0291b5b7 | 545 | while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) { |
0291b5b7 VM |
546 | if (*alternate == '\0' || *alternate == '#') |
547 | continue; | |
548 | ||
85e7efa1 CMN |
549 | /* |
550 | * Relative path: build based on the current `objects` | |
551 | * folder. However, relative paths are only allowed in | |
552 | * the current repository. | |
553 | */ | |
554 | if (*alternate == '.' && !alternate_depth) { | |
ae9e29fd | 555 | if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0) |
97769280 RB |
556 | break; |
557 | alternate = git_buf_cstr(&alternates_path); | |
0291b5b7 VM |
558 | } |
559 | ||
a29c6b5f | 560 | if ((result = add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) |
0291b5b7 VM |
561 | break; |
562 | } | |
5a800efc | 563 | |
97769280 | 564 | git_buf_free(&alternates_path); |
13224ea4 VM |
565 | git_buf_free(&alternates_buf); |
566 | ||
ae9e29fd | 567 | return result; |
5a800efc | 568 | } |
e17a3f56 | 569 | |
9507a434 VM |
570 | int git_odb_add_disk_alternate(git_odb *odb, const char *path) |
571 | { | |
a29c6b5f | 572 | return add_default_backends(odb, path, true, 0); |
9507a434 VM |
573 | } |
574 | ||
7d7cd885 | 575 | int git_odb_open(git_odb **out, const char *objects_dir) |
e17a3f56 | 576 | { |
7d7cd885 | 577 | git_odb *db; |
e17a3f56 | 578 | |
5a800efc VM |
579 | assert(out && objects_dir); |
580 | ||
581 | *out = NULL; | |
582 | ||
ae9e29fd RB |
583 | if (git_odb_new(&db) < 0) |
584 | return -1; | |
e17a3f56 | 585 | |
9507a434 | 586 | if (add_default_backends(db, objects_dir, 0, 0) < 0) { |
ae9e29fd RB |
587 | git_odb_free(db); |
588 | return -1; | |
589 | } | |
e17a3f56 | 590 | |
7d7cd885 | 591 | *out = db; |
ae9e29fd | 592 | return 0; |
7d7cd885 | 593 | } |
e17a3f56 | 594 | |
9462c471 | 595 | static void odb_free(git_odb *db) |
a7c60cfc | 596 | { |
10c06114 | 597 | size_t i; |
a7c60cfc | 598 | |
7d7cd885 | 599 | for (i = 0; i < db->backends.length; ++i) { |
d4b5a4e2 VM |
600 | backend_internal *internal = git_vector_get(&db->backends, i); |
601 | git_odb_backend *backend = internal->backend; | |
a7c60cfc | 602 | |
d4b5a4e2 | 603 | if (backend->free) backend->free(backend); |
3286c408 | 604 | else git__free(backend); |
d4b5a4e2 | 605 | |
3286c408 | 606 | git__free(internal); |
a7c60cfc SP |
607 | } |
608 | ||
7d7cd885 | 609 | git_vector_free(&db->backends); |
5df18424 | 610 | git_cache_free(&db->own_cache); |
f658dc43 | 611 | |
6de9b2ee | 612 | git__memzero(db, sizeof(*db)); |
3286c408 | 613 | git__free(db); |
608d33fa RJ |
614 | } |
615 | ||
9462c471 VM |
616 | void git_odb_free(git_odb *db) |
617 | { | |
618 | if (db == NULL) | |
619 | return; | |
620 | ||
621 | GIT_REFCOUNT_DEC(db, odb_free); | |
622 | } | |
623 | ||
7d7cd885 | 624 | int git_odb_exists(git_odb *db, const git_oid *id) |
608d33fa | 625 | { |
72a3fe42 | 626 | git_odb_object *object; |
10c06114 | 627 | size_t i; |
ae9e29fd | 628 | bool found = false; |
608d33fa | 629 | |
7d7cd885 | 630 | assert(db && id); |
608d33fa | 631 | |
5df18424 | 632 | if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { |
45e79e37 | 633 | git_odb_object_free(object); |
ae9e29fd | 634 | return (int)true; |
72a3fe42 VM |
635 | } |
636 | ||
7d7cd885 | 637 | for (i = 0; i < db->backends.length && !found; ++i) { |
d4b5a4e2 VM |
638 | backend_internal *internal = git_vector_get(&db->backends, i); |
639 | git_odb_backend *b = internal->backend; | |
a7c60cfc | 640 | |
b1a6c316 | 641 | if (b->exists != NULL) |
66566516 | 642 | found = (bool)b->exists(b, id); |
608d33fa | 643 | } |
608d33fa | 644 | |
ae9e29fd | 645 | return (int)found; |
2cdc4544 RJ |
646 | } |
647 | ||
f5753999 RB |
648 | int git_odb_exists_prefix( |
649 | git_oid *out, git_odb *db, const git_oid *short_id, size_t len) | |
650 | { | |
651 | int error = GIT_ENOTFOUND, num_found = 0; | |
652 | size_t i; | |
89499078 | 653 | git_oid key = {{0}}, last_found = {{0}}, found; |
f5753999 RB |
654 | |
655 | assert(db && short_id); | |
656 | ||
657 | if (len < GIT_OID_MINPREFIXLEN) | |
658 | return git_odb__error_ambiguous("prefix length too short"); | |
659 | if (len > GIT_OID_HEXSZ) | |
660 | len = GIT_OID_HEXSZ; | |
661 | ||
662 | if (len == GIT_OID_HEXSZ) { | |
663 | if (git_odb_exists(db, short_id)) { | |
664 | if (out) | |
665 | git_oid_cpy(out, short_id); | |
666 | return 0; | |
667 | } else { | |
668 | return git_odb__error_notfound("no match for id prefix", short_id); | |
669 | } | |
670 | } | |
671 | ||
89499078 RB |
672 | /* just copy valid part of short_id */ |
673 | memcpy(&key.id, short_id->id, (len + 1) / 2); | |
674 | if (len & 1) | |
675 | key.id[len / 2] &= 0xF0; | |
676 | ||
f5753999 RB |
677 | for (i = 0; i < db->backends.length; ++i) { |
678 | backend_internal *internal = git_vector_get(&db->backends, i); | |
679 | git_odb_backend *b = internal->backend; | |
680 | ||
681 | if (!b->exists_prefix) | |
682 | continue; | |
683 | ||
89499078 | 684 | error = b->exists_prefix(&found, b, &key, len); |
f5753999 RB |
685 | if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) |
686 | continue; | |
687 | if (error) | |
688 | return error; | |
689 | ||
690 | /* make sure found item doesn't introduce ambiguity */ | |
691 | if (num_found) { | |
692 | if (git_oid__cmp(&last_found, &found)) | |
693 | return git_odb__error_ambiguous("multiple matches for prefix"); | |
694 | } else { | |
695 | git_oid_cpy(&last_found, &found); | |
696 | num_found++; | |
697 | } | |
698 | } | |
699 | ||
700 | if (!num_found) | |
89499078 | 701 | return git_odb__error_notfound("no match for id prefix", &key); |
f5753999 RB |
702 | if (out) |
703 | git_oid_cpy(out, &last_found); | |
704 | ||
89499078 | 705 | return 0; |
f5753999 RB |
706 | } |
707 | ||
72a3fe42 | 708 | int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) |
c6ac28fd RB |
709 | { |
710 | int error; | |
711 | git_odb_object *object; | |
712 | ||
713 | error = git_odb__read_header_or_object(&object, len_p, type_p, db, id); | |
714 | ||
715 | if (object) | |
716 | git_odb_object_free(object); | |
717 | ||
718 | return error; | |
719 | } | |
720 | ||
721 | int git_odb__read_header_or_object( | |
722 | git_odb_object **out, size_t *len_p, git_otype *type_p, | |
723 | git_odb *db, const git_oid *id) | |
a7c60cfc | 724 | { |
10c06114 | 725 | size_t i; |
904b67e6 | 726 | int error = GIT_ENOTFOUND; |
72a3fe42 | 727 | git_odb_object *object; |
a7c60cfc | 728 | |
c6ac28fd | 729 | assert(db && id && out && len_p && type_p); |
72a3fe42 | 730 | |
5df18424 | 731 | if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { |
8842c75f VM |
732 | *len_p = object->cached.size; |
733 | *type_p = object->cached.type; | |
c6ac28fd | 734 | *out = object; |
ae9e29fd | 735 | return 0; |
72a3fe42 | 736 | } |
608d33fa | 737 | |
c6ac28fd RB |
738 | *out = NULL; |
739 | ||
7d7cd885 | 740 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
d4b5a4e2 VM |
741 | backend_internal *internal = git_vector_get(&db->backends, i); |
742 | git_odb_backend *b = internal->backend; | |
608d33fa | 743 | |
7d7cd885 | 744 | if (b->read_header != NULL) |
72a3fe42 | 745 | error = b->read_header(len_p, type_p, b, id); |
608d33fa | 746 | } |
608d33fa | 747 | |
e172cf08 | 748 | if (!error || error == GIT_PASSTHROUGH) |
ae9e29fd | 749 | return 0; |
984ed6b6 | 750 | |
7d7cd885 VM |
751 | /* |
752 | * no backend could read only the header. | |
753 | * try reading the whole object and freeing the contents | |
754 | */ | |
e1de726c RB |
755 | if ((error = git_odb_read(&object, db, id)) < 0) |
756 | return error; /* error already set - pass along */ | |
608d33fa | 757 | |
8842c75f VM |
758 | *len_p = object->cached.size; |
759 | *type_p = object->cached.type; | |
c6ac28fd RB |
760 | *out = object; |
761 | ||
e1de726c | 762 | return 0; |
a7c60cfc SP |
763 | } |
764 | ||
e1ac0101 CMN |
765 | static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b, |
766 | 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }}; | |
767 | static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, | |
768 | 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }}; | |
769 | ||
770 | static int hardcoded_objects(git_rawobj *raw, const git_oid *id) | |
771 | { | |
772 | if (!git_oid_cmp(id, &empty_blob)) { | |
773 | raw->type = GIT_OBJ_BLOB; | |
774 | raw->len = 0; | |
e0156651 | 775 | raw->data = git__calloc(1, sizeof(uint8_t)); |
e1ac0101 CMN |
776 | return 0; |
777 | } else if (!git_oid_cmp(id, &empty_tree)) { | |
778 | raw->type = GIT_OBJ_TREE; | |
779 | raw->len = 0; | |
e0156651 | 780 | raw->data = git__calloc(1, sizeof(uint8_t)); |
e1ac0101 CMN |
781 | return 0; |
782 | } else { | |
783 | return GIT_ENOTFOUND; | |
784 | } | |
785 | } | |
786 | ||
72a3fe42 | 787 | int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) |
a7c60cfc | 788 | { |
f063f578 | 789 | size_t i, reads = 0; |
891a4681 | 790 | int error; |
72a3fe42 | 791 | git_rawobj raw; |
78606263 | 792 | git_odb_object *object; |
a7c60cfc | 793 | |
7d7cd885 | 794 | assert(out && db && id); |
608d33fa | 795 | |
5df18424 | 796 | *out = git_cache_get_raw(odb_cache(db), id); |
72a3fe42 | 797 | if (*out != NULL) |
e1de726c | 798 | return 0; |
72a3fe42 | 799 | |
e1ac0101 | 800 | error = hardcoded_objects(&raw, id); |
4a863c06 | 801 | |
7d7cd885 | 802 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
d4b5a4e2 VM |
803 | backend_internal *internal = git_vector_get(&db->backends, i); |
804 | git_odb_backend *b = internal->backend; | |
7d7cd885 | 805 | |
b1a6c316 | 806 | if (b->read != NULL) { |
f063f578 | 807 | ++reads; |
72a3fe42 | 808 | error = b->read(&raw.data, &raw.len, &raw.type, b, id); |
f063f578 | 809 | } |
72a3fe42 VM |
810 | } |
811 | ||
f063f578 RB |
812 | if (error && error != GIT_PASSTHROUGH) { |
813 | if (!reads) | |
814 | return git_odb__error_notfound("no match for id", id); | |
e1de726c | 815 | return error; |
f063f578 | 816 | } |
7d7cd885 | 817 | |
530594c0 | 818 | giterr_clear(); |
78606263 RB |
819 | if ((object = odb_object__alloc(id, &raw)) == NULL) |
820 | return -1; | |
821 | ||
822 | *out = git_cache_store_raw(odb_cache(db), object); | |
e1de726c | 823 | return 0; |
2cdc4544 RJ |
824 | } |
825 | ||
e1de726c | 826 | int git_odb_read_prefix( |
b8457baa | 827 | git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) |
dd453c4d | 828 | { |
10c06114 | 829 | size_t i; |
904b67e6 | 830 | int error = GIT_ENOTFOUND; |
89499078 | 831 | git_oid key = {{0}}, found_full_oid = {{0}}; |
dd453c4d | 832 | git_rawobj raw; |
c06e0003 | 833 | void *data = NULL; |
b1a6c316 | 834 | bool found = false; |
78606263 | 835 | git_odb_object *object; |
dd453c4d | 836 | |
d0323a5f | 837 | assert(out && db); |
dd453c4d | 838 | |
6c8ca697 | 839 | if (len < GIT_OID_MINPREFIXLEN) |
e1de726c | 840 | return git_odb__error_ambiguous("prefix length too short"); |
dd453c4d MP |
841 | if (len > GIT_OID_HEXSZ) |
842 | len = GIT_OID_HEXSZ; | |
843 | ||
844 | if (len == GIT_OID_HEXSZ) { | |
5df18424 | 845 | *out = git_cache_get_raw(odb_cache(db), short_id); |
d0323a5f | 846 | if (*out != NULL) |
e1de726c | 847 | return 0; |
dd453c4d MP |
848 | } |
849 | ||
89499078 RB |
850 | /* just copy valid part of short_id */ |
851 | memcpy(&key.id, short_id->id, (len + 1) / 2); | |
852 | if (len & 1) | |
853 | key.id[len / 2] &= 0xF0; | |
854 | ||
24634c6f | 855 | for (i = 0; i < db->backends.length; ++i) { |
dd453c4d MP |
856 | backend_internal *internal = git_vector_get(&db->backends, i); |
857 | git_odb_backend *b = internal->backend; | |
858 | ||
b1a6c316 | 859 | if (b->read_prefix != NULL) { |
24634c6f | 860 | git_oid full_oid; |
89499078 | 861 | error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len); |
904b67e6 | 862 | if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) |
24634c6f HWN |
863 | continue; |
864 | ||
865 | if (error) | |
e1de726c | 866 | return error; |
24634c6f | 867 | |
c06e0003 CMN |
868 | git__free(data); |
869 | data = raw.data; | |
4a863c06 | 870 | |
e54cfb9b CMN |
871 | if (found && git_oid__cmp(&full_oid, &found_full_oid)) { |
872 | git__free(raw.data); | |
24634c6f | 873 | return git_odb__error_ambiguous("multiple matches for prefix"); |
e54cfb9b | 874 | } |
4a863c06 | 875 | |
24634c6f HWN |
876 | found_full_oid = full_oid; |
877 | found = true; | |
dd453c4d MP |
878 | } |
879 | } | |
880 | ||
24634c6f | 881 | if (!found) |
89499078 | 882 | return git_odb__error_notfound("no match for prefix", &key); |
dd453c4d | 883 | |
78606263 RB |
884 | if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) |
885 | return -1; | |
886 | ||
887 | *out = git_cache_store_raw(odb_cache(db), object); | |
e1de726c | 888 | return 0; |
dd453c4d MP |
889 | } |
890 | ||
2e76b5fc | 891 | int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) |
521aedad CMN |
892 | { |
893 | unsigned int i; | |
894 | backend_internal *internal; | |
5dca2010 | 895 | |
521aedad CMN |
896 | git_vector_foreach(&db->backends, i, internal) { |
897 | git_odb_backend *b = internal->backend; | |
2e76b5fc | 898 | int error = b->foreach(b, cb, payload); |
5dca2010 RB |
899 | if (error < 0) |
900 | return error; | |
521aedad CMN |
901 | } |
902 | ||
903 | return 0; | |
904 | } | |
905 | ||
e1de726c RB |
906 | int git_odb_write( |
907 | git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type) | |
f6f72d7e | 908 | { |
10c06114 | 909 | size_t i; |
f6f72d7e | 910 | int error = GIT_ERROR; |
984ed6b6 | 911 | git_odb_stream *stream; |
f6f72d7e VM |
912 | |
913 | assert(oid && db); | |
914 | ||
4d185dd9 DMB |
915 | git_odb_hash(oid, data, len, type); |
916 | if (git_odb_exists(db, oid)) | |
917 | return 0; | |
918 | ||
f6f72d7e VM |
919 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
920 | backend_internal *internal = git_vector_get(&db->backends, i); | |
921 | git_odb_backend *b = internal->backend; | |
922 | ||
923 | /* we don't write in alternates! */ | |
924 | if (internal->is_alternate) | |
925 | continue; | |
926 | ||
927 | if (b->write != NULL) | |
fe0c6d4e | 928 | error = b->write(b, oid, data, len, type); |
f6f72d7e VM |
929 | } |
930 | ||
e172cf08 | 931 | if (!error || error == GIT_PASSTHROUGH) |
e1de726c | 932 | return 0; |
984ed6b6 | 933 | |
f063f578 RB |
934 | /* if no backends were able to write the object directly, we try a |
935 | * streaming write to the backends; just write the whole object into the | |
936 | * stream in one push | |
937 | */ | |
e1de726c RB |
938 | if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0) |
939 | return error; | |
f6f72d7e | 940 | |
090a07d2 CMN |
941 | stream->write(stream, data, len); |
942 | error = stream->finalize_write(stream, oid); | |
376e6c9f | 943 | git_odb_stream_free(stream); |
e1de726c RB |
944 | |
945 | return error; | |
f6f72d7e VM |
946 | } |
947 | ||
77b339f7 | 948 | static void hash_header(git_hash_ctx *ctx, git_off_t size, git_otype type) |
8380b39a CMN |
949 | { |
950 | char header[64]; | |
951 | int hdrlen; | |
952 | ||
953 | hdrlen = git_odb__format_object_header(header, sizeof(header), size, type); | |
954 | git_hash_update(ctx, header, hdrlen); | |
955 | } | |
956 | ||
e1de726c | 957 | int git_odb_open_wstream( |
77b339f7 | 958 | git_odb_stream **stream, git_odb *db, git_off_t size, git_otype type) |
a7c60cfc | 959 | { |
f063f578 | 960 | size_t i, writes = 0; |
7d7cd885 | 961 | int error = GIT_ERROR; |
7bd2f401 | 962 | git_hash_ctx *ctx = NULL; |
bed3229b | 963 | |
72a3fe42 | 964 | assert(stream && db); |
bed3229b | 965 | |
7d7cd885 | 966 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
d4b5a4e2 VM |
967 | backend_internal *internal = git_vector_get(&db->backends, i); |
968 | git_odb_backend *b = internal->backend; | |
969 | ||
970 | /* we don't write in alternates! */ | |
971 | if (internal->is_alternate) | |
972 | continue; | |
255a0dab | 973 | |
f063f578 RB |
974 | if (b->writestream != NULL) { |
975 | ++writes; | |
72a3fe42 | 976 | error = b->writestream(stream, b, size, type); |
f063f578 RB |
977 | } else if (b->write != NULL) { |
978 | ++writes; | |
d69d0185 | 979 | error = init_fake_wstream(stream, b, size, type); |
f063f578 | 980 | } |
72a3fe42 VM |
981 | } |
982 | ||
7bd2f401 ET |
983 | if (error < 0) { |
984 | if (error == GIT_PASSTHROUGH) | |
985 | error = 0; | |
986 | else if (!writes) | |
987 | error = git_odb__error_unsupported_in_backend("write object"); | |
988 | ||
989 | goto done; | |
990 | } | |
984ed6b6 | 991 | |
8380b39a CMN |
992 | ctx = git__malloc(sizeof(git_hash_ctx)); |
993 | GITERR_CHECK_ALLOC(ctx); | |
994 | ||
7bd2f401 ET |
995 | if ((error = git_hash_ctx_init(ctx)) < 0) |
996 | goto done; | |
8380b39a | 997 | |
8380b39a CMN |
998 | hash_header(ctx, size, type); |
999 | (*stream)->hash_ctx = ctx; | |
1000 | ||
031f3f80 | 1001 | (*stream)->declared_size = size; |
1002 | (*stream)->received_bytes = 0; | |
1003 | ||
7bd2f401 | 1004 | done: |
e1de726c | 1005 | return error; |
72a3fe42 VM |
1006 | } |
1007 | ||
031f3f80 | 1008 | static int git_odb_stream__invalid_length( |
1009 | const git_odb_stream *stream, | |
1010 | const char *action) | |
1011 | { | |
1012 | giterr_set(GITERR_ODB, | |
1013 | "Cannot %s - " | |
1014 | "Invalid length. %"PRIuZ" was expected. The " | |
1015 | "total size of the received chunks amounts to %"PRIuZ".", | |
1016 | action, stream->declared_size, stream->received_bytes); | |
1017 | ||
1018 | return -1; | |
1019 | } | |
1020 | ||
376e6c9f CMN |
1021 | int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len) |
1022 | { | |
8380b39a | 1023 | git_hash_update(stream->hash_ctx, buffer, len); |
031f3f80 | 1024 | |
1025 | stream->received_bytes += len; | |
1026 | ||
1027 | if (stream->received_bytes > stream->declared_size) | |
1028 | return git_odb_stream__invalid_length(stream, | |
1029 | "stream_write()"); | |
1030 | ||
376e6c9f CMN |
1031 | return stream->write(stream, buffer, len); |
1032 | } | |
1033 | ||
1034 | int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) | |
1035 | { | |
031f3f80 | 1036 | if (stream->received_bytes != stream->declared_size) |
1037 | return git_odb_stream__invalid_length(stream, | |
1038 | "stream_finalize_write()"); | |
1039 | ||
8380b39a | 1040 | git_hash_final(out, stream->hash_ctx); |
4047950f | 1041 | |
1042 | if (git_odb_exists(stream->backend->odb, out)) | |
1043 | return 0; | |
1044 | ||
fe0c6d4e | 1045 | return stream->finalize_write(stream, out); |
376e6c9f CMN |
1046 | } |
1047 | ||
1048 | int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len) | |
1049 | { | |
1050 | return stream->read(stream, buffer, len); | |
1051 | } | |
1052 | ||
1053 | void git_odb_stream_free(git_odb_stream *stream) | |
1054 | { | |
ae3b6d61 BR |
1055 | if (stream == NULL) |
1056 | return; | |
1057 | ||
c251f3bb | 1058 | git_hash_ctx_cleanup(stream->hash_ctx); |
8380b39a | 1059 | git__free(stream->hash_ctx); |
376e6c9f CMN |
1060 | stream->free(stream); |
1061 | } | |
1062 | ||
932d1baf | 1063 | int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid) |
72a3fe42 | 1064 | { |
f063f578 | 1065 | size_t i, reads = 0; |
72a3fe42 VM |
1066 | int error = GIT_ERROR; |
1067 | ||
1068 | assert(stream && db); | |
1069 | ||
1070 | for (i = 0; i < db->backends.length && error < 0; ++i) { | |
1071 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1072 | git_odb_backend *b = internal->backend; | |
1073 | ||
f063f578 RB |
1074 | if (b->readstream != NULL) { |
1075 | ++reads; | |
72a3fe42 | 1076 | error = b->readstream(stream, b, oid); |
f063f578 | 1077 | } |
adc0327a VM |
1078 | } |
1079 | ||
e172cf08 | 1080 | if (error == GIT_PASSTHROUGH) |
e1de726c | 1081 | error = 0; |
f063f578 RB |
1082 | if (error < 0 && !reads) |
1083 | error = git_odb__error_unsupported_in_backend("read object streamed"); | |
984ed6b6 | 1084 | |
e1de726c RB |
1085 | return error; |
1086 | } | |
1087 | ||
48e60ae7 | 1088 | int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_cb progress_cb, void *progress_payload) |
09cc0b92 | 1089 | { |
f063f578 | 1090 | size_t i, writes = 0; |
09cc0b92 ET |
1091 | int error = GIT_ERROR; |
1092 | ||
1093 | assert(out && db); | |
1094 | ||
1095 | for (i = 0; i < db->backends.length && error < 0; ++i) { | |
1096 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1097 | git_odb_backend *b = internal->backend; | |
1098 | ||
1099 | /* we don't write in alternates! */ | |
1100 | if (internal->is_alternate) | |
1101 | continue; | |
1102 | ||
f063f578 RB |
1103 | if (b->writepack != NULL) { |
1104 | ++writes; | |
0b33fca0 | 1105 | error = b->writepack(out, b, db, progress_cb, progress_payload); |
f063f578 | 1106 | } |
09cc0b92 ET |
1107 | } |
1108 | ||
1109 | if (error == GIT_PASSTHROUGH) | |
1110 | error = 0; | |
f063f578 RB |
1111 | if (error < 0 && !writes) |
1112 | error = git_odb__error_unsupported_in_backend("write pack"); | |
09cc0b92 ET |
1113 | |
1114 | return error; | |
1115 | } | |
1116 | ||
4a863c06 | 1117 | void *git_odb_backend_malloc(git_odb_backend *backend, size_t len) |
c49d328c | 1118 | { |
0e9f2fce | 1119 | GIT_UNUSED(backend); |
c49d328c PK |
1120 | return git__malloc(len); |
1121 | } | |
1122 | ||
4a863c06 VM |
1123 | int git_odb_refresh(struct git_odb *db) |
1124 | { | |
10c06114 | 1125 | size_t i; |
4a863c06 VM |
1126 | assert(db); |
1127 | ||
1128 | for (i = 0; i < db->backends.length; ++i) { | |
1129 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1130 | git_odb_backend *b = internal->backend; | |
1131 | ||
1132 | if (b->refresh != NULL) { | |
1133 | int error = b->refresh(b); | |
1134 | if (error < 0) | |
1135 | return error; | |
1136 | } | |
1137 | } | |
1138 | ||
1139 | return 0; | |
1140 | } | |
1141 | ||
282283ac | 1142 | int git_odb__error_notfound(const char *message, const git_oid *oid) |
e1de726c | 1143 | { |
282283ac RB |
1144 | if (oid != NULL) { |
1145 | char oid_str[GIT_OID_HEXSZ + 1]; | |
1146 | git_oid_tostr(oid_str, sizeof(oid_str), oid); | |
1147 | giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str); | |
1148 | } else | |
1149 | giterr_set(GITERR_ODB, "Object not found - %s", message); | |
1150 | ||
904b67e6 | 1151 | return GIT_ENOTFOUND; |
e1de726c RB |
1152 | } |
1153 | ||
1154 | int git_odb__error_ambiguous(const char *message) | |
1155 | { | |
1156 | giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message); | |
904b67e6 | 1157 | return GIT_EAMBIGUOUS; |
7b6e8067 RJ |
1158 | } |
1159 | ||
bc91347b | 1160 | int git_odb_init_backend(git_odb_backend *backend, unsigned int version) |
b9f81997 | 1161 | { |
bc91347b RB |
1162 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
1163 | backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT); | |
1164 | return 0; | |
b9f81997 | 1165 | } |