]>
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 | ||
eae0bfdc PP |
8 | #include "odb.h" |
9 | ||
0c3bae62 | 10 | #include <zlib.h> |
44908fe7 | 11 | #include "git2/object.h" |
83cc70d9 | 12 | #include "git2/sys/odb_backend.h" |
22a2d3d5 | 13 | #include "futils.h" |
c960d6a3 | 14 | #include "hash.h" |
6a2d2f8a | 15 | #include "delta.h" |
60b9d3fc | 16 | #include "filter.h" |
5df18424 | 17 | #include "repository.h" |
ac3d33df | 18 | #include "blob.h" |
e579e0f7 | 19 | #include "oid.h" |
c15648cb | 20 | |
44908fe7 | 21 | #include "git2/odb_backend.h" |
c07d9c95 | 22 | #include "git2/oid.h" |
6c04269c | 23 | #include "git2/oidarray.h" |
7b6e8067 | 24 | |
5a800efc VM |
25 | #define GIT_ALTERNATES_FILE "info/alternates" |
26 | ||
c25aa7cd PP |
27 | #define GIT_ALTERNATES_MAX_DEPTH 5 |
28 | ||
b0d7f329 CMN |
29 | /* |
30 | * We work under the assumption that most objects for long-running | |
31 | * operations will be packed | |
32 | */ | |
c25aa7cd PP |
33 | int git_odb__loose_priority = GIT_ODB_DEFAULT_LOOSE_PRIORITY; |
34 | int git_odb__packed_priority = GIT_ODB_DEFAULT_PACKED_PRIORITY; | |
85e7efa1 | 35 | |
35079f50 PS |
36 | bool git_odb__strict_hash_verification = true; |
37 | ||
d4b5a4e2 VM |
38 | typedef struct |
39 | { | |
40 | git_odb_backend *backend; | |
41 | int priority; | |
a29c6b5f VM |
42 | bool is_alternate; |
43 | ino_t disk_inode; | |
d4b5a4e2 VM |
44 | } backend_internal; |
45 | ||
5df18424 VM |
46 | static git_cache *odb_cache(git_odb *odb) |
47 | { | |
22a2d3d5 UG |
48 | git_repository *owner = GIT_REFCOUNT_OWNER(odb); |
49 | if (owner != NULL) { | |
5df18424 VM |
50 | return &owner->objects; |
51 | } | |
52 | ||
53 | return &odb->own_cache; | |
54 | } | |
f5e28202 | 55 | |
ac3d33df | 56 | static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id); |
85e7efa1 | 57 | static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth); |
eae0bfdc | 58 | static int error_null_oid(int error, const char *message); |
85e7efa1 | 59 | |
ac3d33df | 60 | static git_object_t odb_hardcoded_type(const git_oid *id) |
4416aa77 | 61 | { |
e579e0f7 | 62 | if (!git_oid_cmp(id, &git_oid__empty_tree_sha1)) |
ac3d33df | 63 | return GIT_OBJECT_TREE; |
4416aa77 | 64 | |
ac3d33df | 65 | return GIT_OBJECT_INVALID; |
4416aa77 VM |
66 | } |
67 | ||
eae0bfdc | 68 | static int odb_read_hardcoded(bool *found, git_rawobj *raw, const git_oid *id) |
4416aa77 | 69 | { |
ac3d33df | 70 | git_object_t type; |
eae0bfdc PP |
71 | |
72 | *found = false; | |
73 | ||
ac3d33df | 74 | if ((type = odb_hardcoded_type(id)) == GIT_OBJECT_INVALID) |
eae0bfdc | 75 | return 0; |
4416aa77 VM |
76 | |
77 | raw->type = type; | |
78 | raw->len = 0; | |
79 | raw->data = git__calloc(1, sizeof(uint8_t)); | |
ac3d33df | 80 | GIT_ERROR_CHECK_ALLOC(raw->data); |
eae0bfdc PP |
81 | |
82 | *found = true; | |
4416aa77 VM |
83 | return 0; |
84 | } | |
85 | ||
eae0bfdc PP |
86 | int git_odb__format_object_header( |
87 | size_t *written, | |
88 | char *hdr, | |
89 | size_t hdr_size, | |
22a2d3d5 | 90 | git_object_size_t obj_len, |
ac3d33df | 91 | git_object_t obj_type) |
c960d6a3 | 92 | { |
c52736fa | 93 | const char *type_str = git_object_type2string(obj_type); |
eae0bfdc PP |
94 | int hdr_max = (hdr_size > INT_MAX-2) ? (INT_MAX-2) : (int)hdr_size; |
95 | int len; | |
96 | ||
ac3d33df | 97 | len = p_snprintf(hdr, hdr_max, "%s %"PRId64, type_str, (int64_t)obj_len); |
eae0bfdc PP |
98 | |
99 | if (len < 0 || len >= hdr_max) { | |
ac3d33df | 100 | git_error_set(GIT_ERROR_OS, "object header creation failed"); |
eae0bfdc PP |
101 | return -1; |
102 | } | |
103 | ||
104 | *written = (size_t)(len + 1); | |
105 | return 0; | |
c960d6a3 RJ |
106 | } |
107 | ||
18e5b854 | 108 | int git_odb__hashobj(git_oid *id, git_rawobj *obj) |
c960d6a3 | 109 | { |
e579e0f7 | 110 | git_str_vec vec[2]; |
c85e08b1 | 111 | char header[64]; |
eae0bfdc PP |
112 | size_t hdrlen; |
113 | int error; | |
c960d6a3 | 114 | |
c25aa7cd PP |
115 | GIT_ASSERT_ARG(id); |
116 | GIT_ASSERT_ARG(obj); | |
c960d6a3 | 117 | |
eae0bfdc | 118 | if (!git_object_typeisloose(obj->type)) { |
ac3d33df | 119 | git_error_set(GIT_ERROR_INVALID, "invalid object type"); |
ae9e29fd | 120 | return -1; |
eae0bfdc | 121 | } |
8842c75f | 122 | |
eae0bfdc | 123 | if (!obj->data && obj->len != 0) { |
ac3d33df | 124 | git_error_set(GIT_ERROR_INVALID, "invalid object"); |
ae9e29fd | 125 | return -1; |
eae0bfdc | 126 | } |
c960d6a3 | 127 | |
eae0bfdc PP |
128 | if ((error = git_odb__format_object_header(&hdrlen, |
129 | header, sizeof(header), obj->len, obj->type)) < 0) | |
130 | return error; | |
c960d6a3 | 131 | |
c85e08b1 | 132 | vec[0].data = header; |
87d9869f | 133 | vec[0].len = hdrlen; |
c960d6a3 | 134 | vec[1].data = obj->data; |
87d9869f | 135 | vec[1].len = obj->len; |
c960d6a3 | 136 | |
e579e0f7 | 137 | return git_hash_vec(id->id, vec, 2, GIT_HASH_ALGORITHM_SHA1); |
c960d6a3 RJ |
138 | } |
139 | ||
d12299fe | 140 | |
78606263 | 141 | static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source) |
e17a3f56 | 142 | { |
78606263 | 143 | git_odb_object *object = git__calloc(1, sizeof(git_odb_object)); |
e17a3f56 | 144 | |
78606263 RB |
145 | if (object != NULL) { |
146 | git_oid_cpy(&object->cached.oid, oid); | |
147 | object->cached.type = source->type; | |
148 | object->cached.size = source->len; | |
149 | object->buffer = source->data; | |
150 | } | |
e17a3f56 | 151 | |
72a3fe42 | 152 | return object; |
3d3552e8 RJ |
153 | } |
154 | ||
78606263 | 155 | void git_odb_object__free(void *object) |
3d3552e8 | 156 | { |
72a3fe42 | 157 | if (object != NULL) { |
78606263 | 158 | git__free(((git_odb_object *)object)->buffer); |
3286c408 | 159 | git__free(object); |
72a3fe42 VM |
160 | } |
161 | } | |
3d3552e8 | 162 | |
1881f078 VM |
163 | const git_oid *git_odb_object_id(git_odb_object *object) |
164 | { | |
165 | return &object->cached.oid; | |
166 | } | |
167 | ||
168 | const void *git_odb_object_data(git_odb_object *object) | |
169 | { | |
8842c75f | 170 | return object->buffer; |
1881f078 VM |
171 | } |
172 | ||
173 | size_t git_odb_object_size(git_odb_object *object) | |
174 | { | |
8842c75f | 175 | return object->cached.size; |
1881f078 VM |
176 | } |
177 | ||
ac3d33df | 178 | git_object_t git_odb_object_type(git_odb_object *object) |
1881f078 | 179 | { |
8842c75f | 180 | return object->cached.type; |
1881f078 VM |
181 | } |
182 | ||
98fec8a9 VM |
183 | int git_odb_object_dup(git_odb_object **dest, git_odb_object *source) |
184 | { | |
185 | git_cached_obj_incref(source); | |
186 | *dest = source; | |
187 | return 0; | |
188 | } | |
189 | ||
45e79e37 | 190 | void git_odb_object_free(git_odb_object *object) |
72a3fe42 | 191 | { |
edca6c8f MS |
192 | if (object == NULL) |
193 | return; | |
194 | ||
5df18424 | 195 | git_cached_obj_decref(object); |
72a3fe42 | 196 | } |
3d3552e8 | 197 | |
ac3d33df | 198 | int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_object_t type) |
c52736fa | 199 | { |
eae0bfdc | 200 | size_t hdr_len; |
7dd22538 | 201 | char hdr[64], buffer[FILEIO_BUFSIZE]; |
603bee07 | 202 | git_hash_ctx ctx; |
addc9be4 | 203 | ssize_t read_len = 0; |
d6fb0924 | 204 | int error = 0; |
c52736fa | 205 | |
a13fb55a | 206 | if (!git_object_typeisloose(type)) { |
ac3d33df | 207 | git_error_set(GIT_ERROR_INVALID, "invalid object type for hash"); |
a13fb55a RB |
208 | return -1; |
209 | } | |
210 | ||
e579e0f7 | 211 | if ((error = git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1)) < 0) |
eae0bfdc | 212 | return error; |
c52736fa | 213 | |
eae0bfdc PP |
214 | if ((error = git_odb__format_object_header(&hdr_len, hdr, |
215 | sizeof(hdr), size, type)) < 0) | |
216 | goto done; | |
d6fb0924 | 217 | |
603bee07 | 218 | if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0) |
d6fb0924 | 219 | goto done; |
c52736fa | 220 | |
c859184b | 221 | while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) { |
603bee07 | 222 | if ((error = git_hash_update(&ctx, buffer, read_len)) < 0) |
d6fb0924 ET |
223 | goto done; |
224 | ||
c52736fa VM |
225 | size -= read_len; |
226 | } | |
227 | ||
c859184b VM |
228 | /* If p_read returned an error code, the read obviously failed. |
229 | * If size is not zero, the file was truncated after we originally | |
230 | * stat'd it, so we consider this a read failure too */ | |
231 | if (read_len < 0 || size > 0) { | |
ac3d33df | 232 | git_error_set(GIT_ERROR_OS, "error reading file for hashing"); |
d6fb0924 ET |
233 | error = -1; |
234 | ||
235 | goto done; | |
c859184b VM |
236 | } |
237 | ||
e579e0f7 | 238 | error = git_hash_final(out->id, &ctx); |
c52736fa | 239 | |
d6fb0924 | 240 | done: |
603bee07 | 241 | git_hash_ctx_cleanup(&ctx); |
d6fb0924 | 242 | return error; |
c52736fa VM |
243 | } |
244 | ||
60b9d3fc | 245 | int git_odb__hashfd_filtered( |
ac3d33df | 246 | git_oid *out, git_file fd, size_t size, git_object_t type, git_filter_list *fl) |
60b9d3fc RB |
247 | { |
248 | int error; | |
e579e0f7 | 249 | git_str raw = GIT_STR_INIT; |
60b9d3fc | 250 | |
85d54812 | 251 | if (!fl) |
60b9d3fc RB |
252 | return git_odb__hashfd(out, fd, size, type); |
253 | ||
254 | /* size of data is used in header, so we have to read the whole file | |
255 | * into memory to apply filters before beginning to calculate the hash | |
256 | */ | |
257 | ||
2a7d224f | 258 | if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) { |
e579e0f7 | 259 | git_str post = GIT_STR_INIT; |
60b9d3fc | 260 | |
c25aa7cd | 261 | error = git_filter_list__convert_buf(&post, fl, &raw); |
60b9d3fc | 262 | |
2a7d224f RB |
263 | if (!error) |
264 | error = git_odb_hash(out, post.ptr, post.size, type); | |
265 | ||
e579e0f7 | 266 | git_str_dispose(&post); |
2a7d224f | 267 | } |
60b9d3fc RB |
268 | |
269 | return error; | |
270 | } | |
271 | ||
f19e3ca2 VM |
272 | int git_odb__hashlink(git_oid *out, const char *path) |
273 | { | |
274 | struct stat st; | |
f1453c59 | 275 | int size; |
ae9e29fd | 276 | int result; |
f19e3ca2 | 277 | |
e579e0f7 | 278 | if (git_fs_path_lstat(path, &st) < 0) |
ae9e29fd | 279 | return -1; |
f19e3ca2 | 280 | |
f1453c59 | 281 | if (!git__is_int(st.st_size) || (int)st.st_size < 0) { |
ac3d33df | 282 | git_error_set(GIT_ERROR_FILESYSTEM, "file size overflow for 32-bit systems"); |
ae9e29fd RB |
283 | return -1; |
284 | } | |
f19e3ca2 | 285 | |
f1453c59 | 286 | size = (int)st.st_size; |
15d54fdd | 287 | |
f19e3ca2 VM |
288 | if (S_ISLNK(st.st_mode)) { |
289 | char *link_data; | |
f1453c59 ET |
290 | int read_len; |
291 | size_t alloc_size; | |
f19e3ca2 | 292 | |
ac3d33df | 293 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, size, 1); |
f1453c59 | 294 | link_data = git__malloc(alloc_size); |
ac3d33df | 295 | GIT_ERROR_CHECK_ALLOC(link_data); |
f19e3ca2 | 296 | |
15d54fdd | 297 | read_len = p_readlink(path, link_data, size); |
c25aa7cd | 298 | if (read_len == -1) { |
ac3d33df | 299 | git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", path); |
c6451624 | 300 | git__free(link_data); |
ae9e29fd RB |
301 | return -1; |
302 | } | |
c25aa7cd PP |
303 | GIT_ASSERT(read_len <= size); |
304 | link_data[read_len] = '\0'; | |
f19e3ca2 | 305 | |
c25aa7cd | 306 | result = git_odb_hash(out, link_data, read_len, GIT_OBJECT_BLOB); |
2bc8fa02 | 307 | git__free(link_data); |
60b9d3fc | 308 | } else { |
ae9e29fd RB |
309 | int fd = git_futils_open_ro(path); |
310 | if (fd < 0) | |
311 | return -1; | |
ac3d33df | 312 | result = git_odb__hashfd(out, fd, size, GIT_OBJECT_BLOB); |
f19e3ca2 VM |
313 | p_close(fd); |
314 | } | |
315 | ||
ae9e29fd | 316 | return result; |
f19e3ca2 VM |
317 | } |
318 | ||
ac3d33df | 319 | int git_odb_hashfile(git_oid *out, const char *path, git_object_t type) |
18e5b854 | 320 | { |
22a2d3d5 UG |
321 | uint64_t size; |
322 | int fd, error = 0; | |
323 | ||
324 | if ((fd = git_futils_open_ro(path)) < 0) | |
e1de726c | 325 | return fd; |
18e5b854 | 326 | |
22a2d3d5 UG |
327 | if ((error = git_futils_filesize(&size, fd)) < 0) |
328 | goto done; | |
329 | ||
330 | if (!git__is_sizet(size)) { | |
ac3d33df | 331 | git_error_set(GIT_ERROR_OS, "file size overflow for 32-bit systems"); |
22a2d3d5 UG |
332 | error = -1; |
333 | goto done; | |
18e5b854 VM |
334 | } |
335 | ||
22a2d3d5 UG |
336 | error = git_odb__hashfd(out, fd, (size_t)size, type); |
337 | ||
338 | done: | |
18e5b854 | 339 | p_close(fd); |
22a2d3d5 | 340 | return error; |
18e5b854 VM |
341 | } |
342 | ||
ac3d33df | 343 | int git_odb_hash(git_oid *id, const void *data, size_t len, git_object_t type) |
72a3fe42 | 344 | { |
72a3fe42 | 345 | git_rawobj raw; |
3d3552e8 | 346 | |
c25aa7cd | 347 | GIT_ASSERT_ARG(id); |
3d3552e8 | 348 | |
72a3fe42 VM |
349 | raw.data = (void *)data; |
350 | raw.len = len; | |
351 | raw.type = type; | |
3d3552e8 | 352 | |
18e5b854 | 353 | return git_odb__hashobj(id, &raw); |
3d3552e8 RJ |
354 | } |
355 | ||
d69d0185 VM |
356 | /** |
357 | * FAKE WSTREAM | |
358 | */ | |
359 | ||
360 | typedef struct { | |
361 | git_odb_stream stream; | |
362 | char *buffer; | |
363 | size_t size, written; | |
ac3d33df | 364 | git_object_t type; |
d69d0185 VM |
365 | } fake_wstream; |
366 | ||
fe0c6d4e | 367 | static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid) |
d69d0185 VM |
368 | { |
369 | fake_wstream *stream = (fake_wstream *)_stream; | |
fe0c6d4e | 370 | return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type); |
d69d0185 VM |
371 | } |
372 | ||
373 | static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len) | |
374 | { | |
375 | fake_wstream *stream = (fake_wstream *)_stream; | |
376 | ||
c25aa7cd | 377 | GIT_ASSERT(stream->written + len <= stream->size); |
d69d0185 VM |
378 | |
379 | memcpy(stream->buffer + stream->written, data, len); | |
380 | stream->written += len; | |
ae9e29fd | 381 | return 0; |
d69d0185 VM |
382 | } |
383 | ||
384 | static void fake_wstream__free(git_odb_stream *_stream) | |
385 | { | |
386 | fake_wstream *stream = (fake_wstream *)_stream; | |
387 | ||
3286c408 VM |
388 | git__free(stream->buffer); |
389 | git__free(stream); | |
d69d0185 VM |
390 | } |
391 | ||
22a2d3d5 | 392 | static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_object_size_t size, git_object_t type) |
d69d0185 VM |
393 | { |
394 | fake_wstream *stream; | |
ac3d33df | 395 | size_t blobsize; |
d69d0185 | 396 | |
ac3d33df JK |
397 | GIT_ERROR_CHECK_BLOBSIZE(size); |
398 | blobsize = (size_t)size; | |
77b339f7 | 399 | |
d69d0185 | 400 | stream = git__calloc(1, sizeof(fake_wstream)); |
ac3d33df | 401 | GIT_ERROR_CHECK_ALLOC(stream); |
d69d0185 | 402 | |
ac3d33df | 403 | stream->size = blobsize; |
d69d0185 | 404 | stream->type = type; |
ac3d33df | 405 | stream->buffer = git__malloc(blobsize); |
d69d0185 | 406 | if (stream->buffer == NULL) { |
3286c408 | 407 | git__free(stream); |
ae9e29fd | 408 | return -1; |
d69d0185 VM |
409 | } |
410 | ||
411 | stream->stream.backend = backend; | |
412 | stream->stream.read = NULL; /* read only */ | |
413 | stream->stream.write = &fake_wstream__write; | |
414 | stream->stream.finalize_write = &fake_wstream__fwrite; | |
415 | stream->stream.free = &fake_wstream__free; | |
416 | stream->stream.mode = GIT_STREAM_WRONLY; | |
417 | ||
418 | *stream_p = (git_odb_stream *)stream; | |
ae9e29fd | 419 | return 0; |
d69d0185 | 420 | } |
3d3552e8 | 421 | |
7d7cd885 VM |
422 | /*********************************************************** |
423 | * | |
424 | * OBJECT DATABASE PUBLIC API | |
425 | * | |
426 | * Public calls for the ODB functionality | |
427 | * | |
428 | ***********************************************************/ | |
3d3552e8 | 429 | |
39e1032c | 430 | static int backend_sort_cmp(const void *a, const void *b) |
7d7cd885 | 431 | { |
de18f276 VM |
432 | const backend_internal *backend_a = (const backend_internal *)(a); |
433 | const backend_internal *backend_b = (const backend_internal *)(b); | |
d4b5a4e2 | 434 | |
a0a1b19a VM |
435 | if (backend_b->priority == backend_a->priority) { |
436 | if (backend_a->is_alternate) | |
437 | return -1; | |
438 | if (backend_b->is_alternate) | |
439 | return 1; | |
440 | return 0; | |
441 | } | |
442 | return (backend_b->priority - backend_a->priority); | |
3d3552e8 RJ |
443 | } |
444 | ||
7d7cd885 | 445 | int git_odb_new(git_odb **out) |
3d3552e8 | 446 | { |
7d7cd885 | 447 | git_odb *db = git__calloc(1, sizeof(*db)); |
ac3d33df | 448 | GIT_ERROR_CHECK_ALLOC(db); |
3d3552e8 | 449 | |
c25aa7cd PP |
450 | if (git_mutex_init(&db->lock) < 0) { |
451 | git__free(db); | |
452 | return -1; | |
453 | } | |
6147f643 | 454 | if (git_cache_init(&db->own_cache) < 0) { |
c25aa7cd | 455 | git_mutex_free(&db->lock); |
6147f643 PP |
456 | git__free(db); |
457 | return -1; | |
458 | } | |
459 | if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) { | |
22a2d3d5 | 460 | git_cache_dispose(&db->own_cache); |
c25aa7cd | 461 | git_mutex_free(&db->lock); |
3286c408 | 462 | git__free(db); |
ae9e29fd | 463 | return -1; |
7d7cd885 | 464 | } |
3d3552e8 | 465 | |
7d7cd885 | 466 | *out = db; |
9462c471 | 467 | GIT_REFCOUNT_INC(db); |
ae9e29fd | 468 | return 0; |
3d3552e8 RJ |
469 | } |
470 | ||
a29c6b5f VM |
471 | static int add_backend_internal( |
472 | git_odb *odb, git_odb_backend *backend, | |
473 | int priority, bool is_alternate, ino_t disk_inode) | |
e17a3f56 | 474 | { |
d4b5a4e2 VM |
475 | backend_internal *internal; |
476 | ||
c25aa7cd PP |
477 | GIT_ASSERT_ARG(odb); |
478 | GIT_ASSERT_ARG(backend); | |
e17a3f56 | 479 | |
ac3d33df | 480 | GIT_ERROR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend"); |
55f6f21b | 481 | |
998f7b3d | 482 | /* Check if the backend is already owned by another ODB */ |
c25aa7cd | 483 | GIT_ASSERT(!backend->odb || backend->odb == odb); |
e17a3f56 | 484 | |
d4b5a4e2 | 485 | internal = git__malloc(sizeof(backend_internal)); |
ac3d33df | 486 | GIT_ERROR_CHECK_ALLOC(internal); |
d4b5a4e2 VM |
487 | |
488 | internal->backend = backend; | |
489 | internal->priority = priority; | |
490 | internal->is_alternate = is_alternate; | |
a29c6b5f | 491 | internal->disk_inode = disk_inode; |
e17a3f56 | 492 | |
c25aa7cd PP |
493 | if (git_mutex_lock(&odb->lock) < 0) { |
494 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
495 | return -1; | |
496 | } | |
d4b5a4e2 | 497 | if (git_vector_insert(&odb->backends, internal) < 0) { |
c25aa7cd | 498 | git_mutex_unlock(&odb->lock); |
3286c408 | 499 | git__free(internal); |
ae9e29fd | 500 | return -1; |
d4b5a4e2 | 501 | } |
7d7cd885 | 502 | git_vector_sort(&odb->backends); |
d4b5a4e2 | 503 | internal->backend->odb = odb; |
c25aa7cd | 504 | git_mutex_unlock(&odb->lock); |
ae9e29fd | 505 | return 0; |
e17a3f56 RJ |
506 | } |
507 | ||
d4b5a4e2 VM |
508 | int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority) |
509 | { | |
a29c6b5f | 510 | return add_backend_internal(odb, backend, priority, false, 0); |
d4b5a4e2 VM |
511 | } |
512 | ||
513 | int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority) | |
514 | { | |
a29c6b5f | 515 | return add_backend_internal(odb, backend, priority, true, 0); |
d4b5a4e2 VM |
516 | } |
517 | ||
83cc70d9 RB |
518 | size_t git_odb_num_backends(git_odb *odb) |
519 | { | |
c25aa7cd PP |
520 | size_t length; |
521 | bool locked = true; | |
522 | ||
523 | GIT_ASSERT_ARG(odb); | |
524 | ||
525 | if (git_mutex_lock(&odb->lock) < 0) { | |
526 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
527 | locked = false; | |
528 | } | |
529 | length = odb->backends.length; | |
530 | if (locked) | |
531 | git_mutex_unlock(&odb->lock); | |
532 | return length; | |
83cc70d9 RB |
533 | } |
534 | ||
f063f578 RB |
535 | static int git_odb__error_unsupported_in_backend(const char *action) |
536 | { | |
ac3d33df | 537 | git_error_set(GIT_ERROR_ODB, |
909d5494 | 538 | "cannot %s - unsupported in the loaded odb backends", action); |
f063f578 RB |
539 | return -1; |
540 | } | |
541 | ||
542 | ||
83cc70d9 RB |
543 | int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos) |
544 | { | |
545 | backend_internal *internal; | |
c25aa7cd | 546 | int error; |
83cc70d9 | 547 | |
c25aa7cd PP |
548 | GIT_ASSERT_ARG(out); |
549 | GIT_ASSERT_ARG(odb); | |
550 | ||
551 | ||
552 | if ((error = git_mutex_lock(&odb->lock)) < 0) { | |
553 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
554 | return error; | |
555 | } | |
83cc70d9 RB |
556 | internal = git_vector_get(&odb->backends, pos); |
557 | ||
c25aa7cd PP |
558 | if (!internal || !internal->backend) { |
559 | git_mutex_unlock(&odb->lock); | |
560 | ||
561 | git_error_set(GIT_ERROR_ODB, "no ODB backend loaded at index %" PRIuZ, pos); | |
562 | return GIT_ENOTFOUND; | |
83cc70d9 | 563 | } |
c25aa7cd PP |
564 | *out = internal->backend; |
565 | git_mutex_unlock(&odb->lock); | |
83cc70d9 | 566 | |
c25aa7cd | 567 | return 0; |
83cc70d9 RB |
568 | } |
569 | ||
1c04a96b | 570 | int git_odb__add_default_backends( |
a29c6b5f VM |
571 | git_odb *db, const char *objects_dir, |
572 | bool as_alternates, int alternate_depth) | |
5a800efc | 573 | { |
c25aa7cd | 574 | size_t i = 0; |
a29c6b5f | 575 | struct stat st; |
c8a4e8a5 | 576 | ino_t inode; |
5a800efc | 577 | git_odb_backend *loose, *packed; |
5a800efc | 578 | |
4ef2c79c VM |
579 | /* TODO: inodes are not really relevant on Win32, so we need to find |
580 | * a cross-platform workaround for this */ | |
c8a4e8a5 ET |
581 | #ifdef GIT_WIN32 |
582 | GIT_UNUSED(i); | |
c25aa7cd | 583 | GIT_UNUSED(&st); |
c8a4e8a5 ET |
584 | |
585 | inode = 0; | |
586 | #else | |
a29c6b5f | 587 | if (p_stat(objects_dir, &st) < 0) { |
dfec726b | 588 | if (as_alternates) |
22a2d3d5 | 589 | /* this should warn */ |
dfec726b VM |
590 | return 0; |
591 | ||
ac3d33df | 592 | git_error_set(GIT_ERROR_ODB, "failed to load object database in '%s'", objects_dir); |
a29c6b5f VM |
593 | return -1; |
594 | } | |
595 | ||
c8a4e8a5 ET |
596 | inode = st.st_ino; |
597 | ||
c25aa7cd PP |
598 | if (git_mutex_lock(&db->lock) < 0) { |
599 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
600 | return -1; | |
601 | } | |
a29c6b5f VM |
602 | for (i = 0; i < db->backends.length; ++i) { |
603 | backend_internal *backend = git_vector_get(&db->backends, i); | |
c25aa7cd PP |
604 | if (backend->disk_inode == inode) { |
605 | git_mutex_unlock(&db->lock); | |
a29c6b5f | 606 | return 0; |
c25aa7cd | 607 | } |
a29c6b5f | 608 | } |
c25aa7cd | 609 | git_mutex_unlock(&db->lock); |
4ef2c79c | 610 | #endif |
f063f578 | 611 | |
5a800efc | 612 | /* add the loose object backend */ |
1c04a96b | 613 | if (git_odb_backend_loose(&loose, objects_dir, -1, db->do_fsync, 0, 0) < 0 || |
c25aa7cd | 614 | add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0) |
ae9e29fd | 615 | return -1; |
5a800efc VM |
616 | |
617 | /* add the packed file backend */ | |
ae9e29fd | 618 | if (git_odb_backend_pack(&packed, objects_dir) < 0 || |
c25aa7cd PP |
619 | add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0) |
620 | return -1; | |
621 | ||
622 | if (git_mutex_lock(&db->lock) < 0) { | |
623 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
624 | return -1; | |
625 | } | |
626 | if (!db->cgraph && git_commit_graph_new(&db->cgraph, objects_dir, false) < 0) { | |
627 | git_mutex_unlock(&db->lock); | |
ae9e29fd | 628 | return -1; |
c25aa7cd PP |
629 | } |
630 | git_mutex_unlock(&db->lock); | |
5a800efc | 631 | |
85e7efa1 | 632 | return load_alternates(db, objects_dir, alternate_depth); |
5a800efc VM |
633 | } |
634 | ||
85e7efa1 | 635 | static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth) |
5a800efc | 636 | { |
e579e0f7 MB |
637 | git_str alternates_path = GIT_STR_INIT; |
638 | git_str alternates_buf = GIT_STR_INIT; | |
97769280 | 639 | char *buffer; |
97769280 | 640 | const char *alternate; |
ae9e29fd | 641 | int result = 0; |
5a800efc | 642 | |
85e7efa1 | 643 | /* Git reports an error, we just ignore anything deeper */ |
f063f578 | 644 | if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) |
85e7efa1 | 645 | return 0; |
85e7efa1 | 646 | |
e579e0f7 | 647 | if (git_str_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0) |
ae9e29fd | 648 | return -1; |
5a800efc | 649 | |
e579e0f7 MB |
650 | if (git_fs_path_exists(alternates_path.ptr) == false) { |
651 | git_str_dispose(&alternates_path); | |
ae9e29fd | 652 | return 0; |
97769280 | 653 | } |
5a800efc | 654 | |
ae9e29fd | 655 | if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) { |
e579e0f7 | 656 | git_str_dispose(&alternates_path); |
ae9e29fd | 657 | return -1; |
97769280 | 658 | } |
5a800efc | 659 | |
13224ea4 | 660 | buffer = (char *)alternates_buf.ptr; |
5a800efc VM |
661 | |
662 | /* add each alternate as a new backend; one alternate per line */ | |
0291b5b7 | 663 | while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) { |
0291b5b7 VM |
664 | if (*alternate == '\0' || *alternate == '#') |
665 | continue; | |
666 | ||
85e7efa1 CMN |
667 | /* |
668 | * Relative path: build based on the current `objects` | |
669 | * folder. However, relative paths are only allowed in | |
670 | * the current repository. | |
671 | */ | |
672 | if (*alternate == '.' && !alternate_depth) { | |
e579e0f7 | 673 | if ((result = git_str_joinpath(&alternates_path, objects_dir, alternate)) < 0) |
97769280 | 674 | break; |
e579e0f7 | 675 | alternate = git_str_cstr(&alternates_path); |
0291b5b7 VM |
676 | } |
677 | ||
1c04a96b | 678 | if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0) |
0291b5b7 VM |
679 | break; |
680 | } | |
5a800efc | 681 | |
e579e0f7 MB |
682 | git_str_dispose(&alternates_path); |
683 | git_str_dispose(&alternates_buf); | |
13224ea4 | 684 | |
ae9e29fd | 685 | return result; |
5a800efc | 686 | } |
e17a3f56 | 687 | |
9507a434 VM |
688 | int git_odb_add_disk_alternate(git_odb *odb, const char *path) |
689 | { | |
1c04a96b | 690 | return git_odb__add_default_backends(odb, path, true, 0); |
9507a434 VM |
691 | } |
692 | ||
c25aa7cd PP |
693 | int git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph) |
694 | { | |
695 | int error = 0; | |
696 | ||
697 | GIT_ASSERT_ARG(odb); | |
698 | ||
699 | if ((error = git_mutex_lock(&odb->lock)) < 0) { | |
700 | git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock"); | |
701 | return error; | |
702 | } | |
703 | git_commit_graph_free(odb->cgraph); | |
704 | odb->cgraph = cgraph; | |
705 | git_mutex_unlock(&odb->lock); | |
706 | ||
707 | return error; | |
708 | } | |
709 | ||
7d7cd885 | 710 | int git_odb_open(git_odb **out, const char *objects_dir) |
e17a3f56 | 711 | { |
7d7cd885 | 712 | git_odb *db; |
e17a3f56 | 713 | |
c25aa7cd PP |
714 | GIT_ASSERT_ARG(out); |
715 | GIT_ASSERT_ARG(objects_dir); | |
5a800efc VM |
716 | |
717 | *out = NULL; | |
718 | ||
ae9e29fd RB |
719 | if (git_odb_new(&db) < 0) |
720 | return -1; | |
e17a3f56 | 721 | |
1c04a96b | 722 | if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) { |
ae9e29fd RB |
723 | git_odb_free(db); |
724 | return -1; | |
725 | } | |
e17a3f56 | 726 | |
7d7cd885 | 727 | *out = db; |
ae9e29fd | 728 | return 0; |
7d7cd885 | 729 | } |
e17a3f56 | 730 | |
1c04a96b ET |
731 | int git_odb__set_caps(git_odb *odb, int caps) |
732 | { | |
733 | if (caps == GIT_ODB_CAP_FROM_OWNER) { | |
22a2d3d5 | 734 | git_repository *repo = GIT_REFCOUNT_OWNER(odb); |
1c04a96b ET |
735 | int val; |
736 | ||
737 | if (!repo) { | |
ac3d33df | 738 | git_error_set(GIT_ERROR_ODB, "cannot access repository to set odb caps"); |
1c04a96b ET |
739 | return -1; |
740 | } | |
741 | ||
22a2d3d5 | 742 | if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FSYNCOBJECTFILES)) |
1c04a96b ET |
743 | odb->do_fsync = !!val; |
744 | } | |
745 | ||
746 | return 0; | |
747 | } | |
748 | ||
9462c471 | 749 | static void odb_free(git_odb *db) |
a7c60cfc | 750 | { |
10c06114 | 751 | size_t i; |
c25aa7cd | 752 | bool locked = true; |
a7c60cfc | 753 | |
c25aa7cd PP |
754 | if (git_mutex_lock(&db->lock) < 0) { |
755 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
756 | locked = false; | |
757 | } | |
7d7cd885 | 758 | for (i = 0; i < db->backends.length; ++i) { |
d4b5a4e2 VM |
759 | backend_internal *internal = git_vector_get(&db->backends, i); |
760 | git_odb_backend *backend = internal->backend; | |
a7c60cfc | 761 | |
d3b29fb9 | 762 | backend->free(backend); |
d4b5a4e2 | 763 | |
3286c408 | 764 | git__free(internal); |
a7c60cfc | 765 | } |
c25aa7cd PP |
766 | if (locked) |
767 | git_mutex_unlock(&db->lock); | |
a7c60cfc | 768 | |
c25aa7cd | 769 | git_commit_graph_free(db->cgraph); |
7d7cd885 | 770 | git_vector_free(&db->backends); |
22a2d3d5 | 771 | git_cache_dispose(&db->own_cache); |
c25aa7cd | 772 | git_mutex_free(&db->lock); |
f658dc43 | 773 | |
6de9b2ee | 774 | git__memzero(db, sizeof(*db)); |
3286c408 | 775 | git__free(db); |
608d33fa RJ |
776 | } |
777 | ||
9462c471 VM |
778 | void git_odb_free(git_odb *db) |
779 | { | |
780 | if (db == NULL) | |
781 | return; | |
782 | ||
783 | GIT_REFCOUNT_DEC(db, odb_free); | |
784 | } | |
785 | ||
8f09a98e ET |
786 | static int odb_exists_1( |
787 | git_odb *db, | |
788 | const git_oid *id, | |
789 | bool only_refreshed) | |
608d33fa | 790 | { |
10c06114 | 791 | size_t i; |
ae9e29fd | 792 | bool found = false; |
c25aa7cd | 793 | int error; |
608d33fa | 794 | |
c25aa7cd PP |
795 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
796 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
797 | return error; | |
798 | } | |
7d7cd885 | 799 | for (i = 0; i < db->backends.length && !found; ++i) { |
d4b5a4e2 VM |
800 | backend_internal *internal = git_vector_get(&db->backends, i); |
801 | git_odb_backend *b = internal->backend; | |
a7c60cfc | 802 | |
43820f20 VM |
803 | if (only_refreshed && !b->refresh) |
804 | continue; | |
805 | ||
b1a6c316 | 806 | if (b->exists != NULL) |
66566516 | 807 | found = (bool)b->exists(b, id); |
608d33fa | 808 | } |
c25aa7cd | 809 | git_mutex_unlock(&db->lock); |
608d33fa | 810 | |
ae9e29fd | 811 | return (int)found; |
2cdc4544 RJ |
812 | } |
813 | ||
c25aa7cd PP |
814 | int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *db) |
815 | { | |
816 | int error = 0; | |
817 | git_commit_graph_file *result = NULL; | |
818 | ||
819 | if ((error = git_mutex_lock(&db->lock)) < 0) { | |
820 | git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock"); | |
821 | return error; | |
822 | } | |
823 | if (!db->cgraph) { | |
824 | error = GIT_ENOTFOUND; | |
825 | goto done; | |
826 | } | |
827 | error = git_commit_graph_get_file(&result, db->cgraph); | |
828 | if (error) | |
829 | goto done; | |
830 | *out = result; | |
831 | ||
832 | done: | |
833 | git_mutex_unlock(&db->lock); | |
834 | return error; | |
835 | } | |
836 | ||
8f09a98e ET |
837 | static int odb_freshen_1( |
838 | git_odb *db, | |
839 | const git_oid *id, | |
840 | bool only_refreshed) | |
841 | { | |
842 | size_t i; | |
843 | bool found = false; | |
c25aa7cd | 844 | int error; |
8f09a98e | 845 | |
c25aa7cd PP |
846 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
847 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
848 | return error; | |
849 | } | |
8f09a98e ET |
850 | for (i = 0; i < db->backends.length && !found; ++i) { |
851 | backend_internal *internal = git_vector_get(&db->backends, i); | |
852 | git_odb_backend *b = internal->backend; | |
853 | ||
854 | if (only_refreshed && !b->refresh) | |
855 | continue; | |
856 | ||
857 | if (b->freshen != NULL) | |
858 | found = !b->freshen(b, id); | |
859 | else if (b->exists != NULL) | |
860 | found = b->exists(b, id); | |
861 | } | |
c25aa7cd | 862 | git_mutex_unlock(&db->lock); |
8f09a98e ET |
863 | |
864 | return (int)found; | |
865 | } | |
866 | ||
52d03f37 | 867 | int git_odb__freshen(git_odb *db, const git_oid *id) |
8f09a98e | 868 | { |
c25aa7cd PP |
869 | GIT_ASSERT_ARG(db); |
870 | GIT_ASSERT_ARG(id); | |
8f09a98e ET |
871 | |
872 | if (odb_freshen_1(db, id, false)) | |
873 | return 1; | |
874 | ||
875 | if (!git_odb_refresh(db)) | |
876 | return odb_freshen_1(db, id, true); | |
877 | ||
878 | /* Failed to refresh, hence not found */ | |
879 | return 0; | |
880 | } | |
881 | ||
43820f20 | 882 | int git_odb_exists(git_odb *db, const git_oid *id) |
e579e0f7 MB |
883 | { |
884 | return git_odb_exists_ext(db, id, 0); | |
885 | } | |
886 | ||
887 | int git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags) | |
f5753999 | 888 | { |
43820f20 | 889 | git_odb_object *object; |
f5753999 | 890 | |
c25aa7cd PP |
891 | GIT_ASSERT_ARG(db); |
892 | GIT_ASSERT_ARG(id); | |
f5753999 | 893 | |
22a2d3d5 | 894 | if (git_oid_is_zero(id)) |
eae0bfdc PP |
895 | return 0; |
896 | ||
43820f20 VM |
897 | if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { |
898 | git_odb_object_free(object); | |
6c04269c | 899 | return 1; |
f5753999 RB |
900 | } |
901 | ||
43820f20 VM |
902 | if (odb_exists_1(db, id, false)) |
903 | return 1; | |
904 | ||
e579e0f7 | 905 | if (!(flags & GIT_ODB_LOOKUP_NO_REFRESH) && !git_odb_refresh(db)) |
43820f20 VM |
906 | return odb_exists_1(db, id, true); |
907 | ||
908 | /* Failed to refresh, hence not found */ | |
909 | return 0; | |
910 | } | |
911 | ||
912 | static int odb_exists_prefix_1(git_oid *out, git_odb *db, | |
913 | const git_oid *key, size_t len, bool only_refreshed) | |
914 | { | |
915 | size_t i; | |
916 | int error = GIT_ENOTFOUND, num_found = 0; | |
917 | git_oid last_found = {{0}}, found; | |
89499078 | 918 | |
c25aa7cd PP |
919 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
920 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
921 | return error; | |
922 | } | |
923 | error = GIT_ENOTFOUND; | |
f5753999 RB |
924 | for (i = 0; i < db->backends.length; ++i) { |
925 | backend_internal *internal = git_vector_get(&db->backends, i); | |
926 | git_odb_backend *b = internal->backend; | |
927 | ||
43820f20 VM |
928 | if (only_refreshed && !b->refresh) |
929 | continue; | |
930 | ||
f5753999 RB |
931 | if (!b->exists_prefix) |
932 | continue; | |
933 | ||
43820f20 | 934 | error = b->exists_prefix(&found, b, key, len); |
f5753999 RB |
935 | if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) |
936 | continue; | |
c25aa7cd PP |
937 | if (error) { |
938 | git_mutex_unlock(&db->lock); | |
f5753999 | 939 | return error; |
c25aa7cd | 940 | } |
f5753999 RB |
941 | |
942 | /* make sure found item doesn't introduce ambiguity */ | |
943 | if (num_found) { | |
c25aa7cd PP |
944 | if (git_oid__cmp(&last_found, &found)) { |
945 | git_mutex_unlock(&db->lock); | |
f5753999 | 946 | return git_odb__error_ambiguous("multiple matches for prefix"); |
c25aa7cd | 947 | } |
f5753999 RB |
948 | } else { |
949 | git_oid_cpy(&last_found, &found); | |
950 | num_found++; | |
951 | } | |
952 | } | |
c25aa7cd | 953 | git_mutex_unlock(&db->lock); |
f5753999 RB |
954 | |
955 | if (!num_found) | |
43820f20 VM |
956 | return GIT_ENOTFOUND; |
957 | ||
f5753999 RB |
958 | if (out) |
959 | git_oid_cpy(out, &last_found); | |
960 | ||
89499078 | 961 | return 0; |
f5753999 RB |
962 | } |
963 | ||
43820f20 VM |
964 | int git_odb_exists_prefix( |
965 | git_oid *out, git_odb *db, const git_oid *short_id, size_t len) | |
966 | { | |
967 | int error; | |
eae0bfdc | 968 | git_oid key = {{0}}; |
43820f20 | 969 | |
c25aa7cd PP |
970 | GIT_ASSERT_ARG(db); |
971 | GIT_ASSERT_ARG(short_id); | |
43820f20 VM |
972 | |
973 | if (len < GIT_OID_MINPREFIXLEN) | |
974 | return git_odb__error_ambiguous("prefix length too short"); | |
43820f20 | 975 | |
6c04269c | 976 | if (len >= GIT_OID_HEXSZ) { |
43820f20 VM |
977 | if (git_odb_exists(db, short_id)) { |
978 | if (out) | |
979 | git_oid_cpy(out, short_id); | |
980 | return 0; | |
981 | } else { | |
e10144ae ET |
982 | return git_odb__error_notfound( |
983 | "no match for id prefix", short_id, len); | |
43820f20 VM |
984 | } |
985 | } | |
986 | ||
6c04269c | 987 | git_oid__cpy_prefix(&key, short_id, len); |
43820f20 VM |
988 | |
989 | error = odb_exists_prefix_1(out, db, &key, len, false); | |
990 | ||
991 | if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) | |
992 | error = odb_exists_prefix_1(out, db, &key, len, true); | |
993 | ||
994 | if (error == GIT_ENOTFOUND) | |
e10144ae | 995 | return git_odb__error_notfound("no match for id prefix", &key, len); |
43820f20 VM |
996 | |
997 | return error; | |
998 | } | |
999 | ||
4b1f0f79 | 1000 | int git_odb_expand_ids( |
6c04269c | 1001 | git_odb *db, |
62484f52 ET |
1002 | git_odb_expand_id *ids, |
1003 | size_t count) | |
6c04269c | 1004 | { |
4416aa77 | 1005 | size_t i; |
6c04269c | 1006 | |
c25aa7cd PP |
1007 | GIT_ASSERT_ARG(db); |
1008 | GIT_ASSERT_ARG(ids); | |
6c04269c | 1009 | |
62484f52 ET |
1010 | for (i = 0; i < count; i++) { |
1011 | git_odb_expand_id *query = &ids[i]; | |
9a786650 | 1012 | int error = GIT_EAMBIGUOUS; |
6c04269c | 1013 | |
e78d2ac9 | 1014 | if (!query->type) |
ac3d33df | 1015 | query->type = GIT_OBJECT_ANY; |
e78d2ac9 VM |
1016 | |
1017 | /* if we have a short OID, expand it first */ | |
1018 | if (query->length >= GIT_OID_MINPREFIXLEN && query->length < GIT_OID_HEXSZ) { | |
1019 | git_oid actual_id; | |
1020 | ||
1021 | error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false); | |
1022 | if (!error) { | |
1023 | git_oid_cpy(&query->id, &actual_id); | |
1024 | query->length = GIT_OID_HEXSZ; | |
1025 | } | |
6c04269c ET |
1026 | } |
1027 | ||
9a786650 | 1028 | /* |
e78d2ac9 VM |
1029 | * now we ought to have a 40-char OID, either because we've expanded it |
1030 | * or because the user passed a full OID. Ensure its type is right. | |
6c04269c | 1031 | */ |
e78d2ac9 | 1032 | if (query->length >= GIT_OID_HEXSZ) { |
ac3d33df | 1033 | git_object_t actual_type; |
6c04269c | 1034 | |
e78d2ac9 VM |
1035 | error = odb_otype_fast(&actual_type, db, &query->id); |
1036 | if (!error) { | |
ac3d33df | 1037 | if (query->type != GIT_OBJECT_ANY && query->type != actual_type) |
e78d2ac9 VM |
1038 | error = GIT_ENOTFOUND; |
1039 | else | |
1040 | query->type = actual_type; | |
1041 | } | |
1042 | } | |
4b1f0f79 | 1043 | |
9a786650 | 1044 | switch (error) { |
e78d2ac9 | 1045 | /* no errors, so we've successfully expanded the OID */ |
9a786650 | 1046 | case 0: |
e78d2ac9 | 1047 | continue; |
9a786650 VM |
1048 | |
1049 | /* the object is missing or ambiguous */ | |
1050 | case GIT_ENOTFOUND: | |
1051 | case GIT_EAMBIGUOUS: | |
62484f52 ET |
1052 | memset(&query->id, 0, sizeof(git_oid)); |
1053 | query->length = 0; | |
1054 | query->type = 0; | |
9a786650 VM |
1055 | break; |
1056 | ||
1057 | /* something went very wrong with the ODB; bail hard */ | |
1058 | default: | |
1059 | return error; | |
6c04269c ET |
1060 | } |
1061 | } | |
1062 | ||
ac3d33df | 1063 | git_error_clear(); |
9a786650 | 1064 | return 0; |
6c04269c ET |
1065 | } |
1066 | ||
ac3d33df | 1067 | int git_odb_read_header(size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id) |
c6ac28fd RB |
1068 | { |
1069 | int error; | |
1070 | git_odb_object *object; | |
1071 | ||
1072 | error = git_odb__read_header_or_object(&object, len_p, type_p, db, id); | |
1073 | ||
1074 | if (object) | |
1075 | git_odb_object_free(object); | |
1076 | ||
1077 | return error; | |
1078 | } | |
1079 | ||
4416aa77 | 1080 | static int odb_read_header_1( |
ac3d33df | 1081 | size_t *len_p, git_object_t *type_p, git_odb *db, |
4416aa77 VM |
1082 | const git_oid *id, bool only_refreshed) |
1083 | { | |
1084 | size_t i; | |
ac3d33df | 1085 | git_object_t ht; |
1bbcb2b2 VM |
1086 | bool passthrough = false; |
1087 | int error; | |
4416aa77 | 1088 | |
ac3d33df | 1089 | if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJECT_INVALID) { |
4416aa77 VM |
1090 | *type_p = ht; |
1091 | *len_p = 0; | |
1092 | return 0; | |
1093 | } | |
1094 | ||
c25aa7cd PP |
1095 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1096 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1097 | return error; | |
1098 | } | |
1bbcb2b2 | 1099 | for (i = 0; i < db->backends.length; ++i) { |
4416aa77 VM |
1100 | backend_internal *internal = git_vector_get(&db->backends, i); |
1101 | git_odb_backend *b = internal->backend; | |
1102 | ||
1103 | if (only_refreshed && !b->refresh) | |
1104 | continue; | |
1105 | ||
1bbcb2b2 VM |
1106 | if (!b->read_header) { |
1107 | passthrough = true; | |
1108 | continue; | |
1109 | } | |
1110 | ||
1111 | error = b->read_header(len_p, type_p, b, id); | |
1112 | ||
1113 | switch (error) { | |
1114 | case GIT_PASSTHROUGH: | |
1115 | passthrough = true; | |
1116 | break; | |
1117 | case GIT_ENOTFOUND: | |
1118 | break; | |
1119 | default: | |
c25aa7cd | 1120 | git_mutex_unlock(&db->lock); |
1bbcb2b2 VM |
1121 | return error; |
1122 | } | |
4416aa77 | 1123 | } |
c25aa7cd | 1124 | git_mutex_unlock(&db->lock); |
4416aa77 | 1125 | |
1bbcb2b2 | 1126 | return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND; |
4416aa77 VM |
1127 | } |
1128 | ||
c6ac28fd | 1129 | int git_odb__read_header_or_object( |
ac3d33df | 1130 | git_odb_object **out, size_t *len_p, git_object_t *type_p, |
c6ac28fd | 1131 | git_odb *db, const git_oid *id) |
a7c60cfc | 1132 | { |
904b67e6 | 1133 | int error = GIT_ENOTFOUND; |
72a3fe42 | 1134 | git_odb_object *object; |
a7c60cfc | 1135 | |
c25aa7cd PP |
1136 | GIT_ASSERT_ARG(db); |
1137 | GIT_ASSERT_ARG(id); | |
1138 | GIT_ASSERT_ARG(out); | |
1139 | GIT_ASSERT_ARG(len_p); | |
1140 | GIT_ASSERT_ARG(type_p); | |
72a3fe42 | 1141 | |
eae0bfdc PP |
1142 | *out = NULL; |
1143 | ||
22a2d3d5 | 1144 | if (git_oid_is_zero(id)) |
eae0bfdc PP |
1145 | return error_null_oid(GIT_ENOTFOUND, "cannot read object"); |
1146 | ||
5df18424 | 1147 | if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { |
8842c75f VM |
1148 | *len_p = object->cached.size; |
1149 | *type_p = object->cached.type; | |
c6ac28fd | 1150 | *out = object; |
ae9e29fd | 1151 | return 0; |
72a3fe42 | 1152 | } |
608d33fa | 1153 | |
4416aa77 | 1154 | error = odb_read_header_1(len_p, type_p, db, id, false); |
c6ac28fd | 1155 | |
4416aa77 VM |
1156 | if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) |
1157 | error = odb_read_header_1(len_p, type_p, db, id, true); | |
608d33fa | 1158 | |
4416aa77 VM |
1159 | if (error == GIT_ENOTFOUND) |
1160 | return git_odb__error_notfound("cannot read header for", id, GIT_OID_HEXSZ); | |
608d33fa | 1161 | |
4416aa77 VM |
1162 | /* we found the header; return early */ |
1163 | if (!error) | |
ae9e29fd | 1164 | return 0; |
984ed6b6 | 1165 | |
4416aa77 VM |
1166 | if (error == GIT_PASSTHROUGH) { |
1167 | /* | |
1168 | * no backend has header-reading functionality | |
1169 | * so try using `git_odb_read` instead | |
1170 | */ | |
1171 | error = git_odb_read(&object, db, id); | |
1172 | if (!error) { | |
1173 | *len_p = object->cached.size; | |
1174 | *type_p = object->cached.type; | |
1175 | *out = object; | |
1176 | } | |
e1ac0101 | 1177 | } |
4416aa77 VM |
1178 | |
1179 | return error; | |
e1ac0101 CMN |
1180 | } |
1181 | ||
43820f20 VM |
1182 | static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id, |
1183 | bool only_refreshed) | |
a7c60cfc | 1184 | { |
43820f20 | 1185 | size_t i; |
72a3fe42 | 1186 | git_rawobj raw; |
78606263 | 1187 | git_odb_object *object; |
28a0741f | 1188 | git_oid hashed; |
43820f20 | 1189 | bool found = false; |
7776db51 | 1190 | int error = 0; |
a7c60cfc | 1191 | |
eae0bfdc PP |
1192 | if (!only_refreshed) { |
1193 | if ((error = odb_read_hardcoded(&found, &raw, id)) < 0) | |
1194 | return error; | |
1195 | } | |
4a863c06 | 1196 | |
c25aa7cd PP |
1197 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1198 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1199 | return error; | |
1200 | } | |
43820f20 | 1201 | for (i = 0; i < db->backends.length && !found; ++i) { |
d4b5a4e2 VM |
1202 | backend_internal *internal = git_vector_get(&db->backends, i); |
1203 | git_odb_backend *b = internal->backend; | |
7d7cd885 | 1204 | |
43820f20 VM |
1205 | if (only_refreshed && !b->refresh) |
1206 | continue; | |
1207 | ||
b1a6c316 | 1208 | if (b->read != NULL) { |
28a0741f | 1209 | error = b->read(&raw.data, &raw.len, &raw.type, b, id); |
43820f20 VM |
1210 | if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND) |
1211 | continue; | |
1212 | ||
c25aa7cd PP |
1213 | if (error < 0) { |
1214 | git_mutex_unlock(&db->lock); | |
43820f20 | 1215 | return error; |
c25aa7cd | 1216 | } |
43820f20 VM |
1217 | |
1218 | found = true; | |
f063f578 | 1219 | } |
72a3fe42 | 1220 | } |
c25aa7cd | 1221 | git_mutex_unlock(&db->lock); |
72a3fe42 | 1222 | |
43820f20 VM |
1223 | if (!found) |
1224 | return GIT_ENOTFOUND; | |
7d7cd885 | 1225 | |
35079f50 PS |
1226 | if (git_odb__strict_hash_verification) { |
1227 | if ((error = git_odb_hash(&hashed, raw.data, raw.len, raw.type)) < 0) | |
1228 | goto out; | |
28a0741f | 1229 | |
35079f50 PS |
1230 | if (!git_oid_equal(id, &hashed)) { |
1231 | error = git_odb__error_mismatch(id, &hashed); | |
1232 | goto out; | |
1233 | } | |
28a0741f PS |
1234 | } |
1235 | ||
ac3d33df | 1236 | git_error_clear(); |
eae0bfdc PP |
1237 | if ((object = odb_object__alloc(id, &raw)) == NULL) { |
1238 | error = -1; | |
28a0741f | 1239 | goto out; |
eae0bfdc | 1240 | } |
78606263 RB |
1241 | |
1242 | *out = git_cache_store_raw(odb_cache(db), object); | |
28a0741f PS |
1243 | |
1244 | out: | |
1245 | if (error) | |
1246 | git__free(raw.data); | |
1247 | return error; | |
2cdc4544 RJ |
1248 | } |
1249 | ||
43820f20 VM |
1250 | int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) |
1251 | { | |
1252 | int error; | |
1253 | ||
c25aa7cd PP |
1254 | GIT_ASSERT_ARG(out); |
1255 | GIT_ASSERT_ARG(db); | |
1256 | GIT_ASSERT_ARG(id); | |
43820f20 | 1257 | |
22a2d3d5 | 1258 | if (git_oid_is_zero(id)) |
eae0bfdc PP |
1259 | return error_null_oid(GIT_ENOTFOUND, "cannot read object"); |
1260 | ||
43820f20 VM |
1261 | *out = git_cache_get_raw(odb_cache(db), id); |
1262 | if (*out != NULL) | |
1263 | return 0; | |
1264 | ||
1265 | error = odb_read_1(out, db, id, false); | |
1266 | ||
1267 | if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) | |
1268 | error = odb_read_1(out, db, id, true); | |
1269 | ||
1270 | if (error == GIT_ENOTFOUND) | |
e10144ae | 1271 | return git_odb__error_notfound("no match for id", id, GIT_OID_HEXSZ); |
43820f20 VM |
1272 | |
1273 | return error; | |
1274 | } | |
1275 | ||
ac3d33df | 1276 | static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id) |
4416aa77 VM |
1277 | { |
1278 | git_odb_object *object; | |
1279 | size_t _unused; | |
1280 | int error; | |
1281 | ||
22a2d3d5 | 1282 | if (git_oid_is_zero(id)) |
eae0bfdc PP |
1283 | return error_null_oid(GIT_ENOTFOUND, "cannot get object type"); |
1284 | ||
4416aa77 VM |
1285 | if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) { |
1286 | *type_p = object->cached.type; | |
6147f643 | 1287 | git_odb_object_free(object); |
4416aa77 VM |
1288 | return 0; |
1289 | } | |
eae0bfdc | 1290 | |
4416aa77 VM |
1291 | error = odb_read_header_1(&_unused, type_p, db, id, false); |
1292 | ||
1293 | if (error == GIT_PASSTHROUGH) { | |
1294 | error = odb_read_1(&object, db, id, false); | |
1295 | if (!error) | |
1296 | *type_p = object->cached.type; | |
1297 | git_odb_object_free(object); | |
1298 | } | |
1299 | ||
1300 | return error; | |
1301 | } | |
1302 | ||
43820f20 VM |
1303 | static int read_prefix_1(git_odb_object **out, git_odb *db, |
1304 | const git_oid *key, size_t len, bool only_refreshed) | |
dd453c4d | 1305 | { |
10c06114 | 1306 | size_t i; |
7776db51 | 1307 | int error = 0; |
43820f20 | 1308 | git_oid found_full_oid = {{0}}; |
14109620 | 1309 | git_rawobj raw = {0}; |
c06e0003 | 1310 | void *data = NULL; |
b1a6c316 | 1311 | bool found = false; |
78606263 | 1312 | git_odb_object *object; |
dd453c4d | 1313 | |
c25aa7cd PP |
1314 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1315 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1316 | return error; | |
1317 | } | |
24634c6f | 1318 | for (i = 0; i < db->backends.length; ++i) { |
dd453c4d MP |
1319 | backend_internal *internal = git_vector_get(&db->backends, i); |
1320 | git_odb_backend *b = internal->backend; | |
1321 | ||
43820f20 VM |
1322 | if (only_refreshed && !b->refresh) |
1323 | continue; | |
1324 | ||
b1a6c316 | 1325 | if (b->read_prefix != NULL) { |
24634c6f | 1326 | git_oid full_oid; |
43820f20 | 1327 | error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len); |
cb3010c5 ET |
1328 | |
1329 | if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) { | |
1330 | error = 0; | |
24634c6f | 1331 | continue; |
cb3010c5 | 1332 | } |
24634c6f | 1333 | |
c25aa7cd PP |
1334 | if (error) { |
1335 | git_mutex_unlock(&db->lock); | |
14109620 | 1336 | goto out; |
c25aa7cd | 1337 | } |
24634c6f | 1338 | |
c06e0003 CMN |
1339 | git__free(data); |
1340 | data = raw.data; | |
4a863c06 | 1341 | |
e54cfb9b | 1342 | if (found && git_oid__cmp(&full_oid, &found_full_oid)) { |
e579e0f7 | 1343 | git_str buf = GIT_STR_INIT; |
14109620 | 1344 | |
e579e0f7 | 1345 | git_str_printf(&buf, "multiple matches for prefix: %s", |
14109620 | 1346 | git_oid_tostr_s(&full_oid)); |
e579e0f7 | 1347 | git_str_printf(&buf, " %s", |
14109620 PS |
1348 | git_oid_tostr_s(&found_full_oid)); |
1349 | ||
1350 | error = git_odb__error_ambiguous(buf.ptr); | |
e579e0f7 | 1351 | git_str_dispose(&buf); |
c25aa7cd | 1352 | git_mutex_unlock(&db->lock); |
14109620 | 1353 | goto out; |
e54cfb9b | 1354 | } |
4a863c06 | 1355 | |
24634c6f HWN |
1356 | found_full_oid = full_oid; |
1357 | found = true; | |
dd453c4d MP |
1358 | } |
1359 | } | |
c25aa7cd | 1360 | git_mutex_unlock(&db->lock); |
dd453c4d | 1361 | |
24634c6f | 1362 | if (!found) |
43820f20 | 1363 | return GIT_ENOTFOUND; |
dd453c4d | 1364 | |
e0973bc0 PS |
1365 | if (git_odb__strict_hash_verification) { |
1366 | git_oid hash; | |
1367 | ||
1368 | if ((error = git_odb_hash(&hash, raw.data, raw.len, raw.type)) < 0) | |
1369 | goto out; | |
1370 | ||
1371 | if (!git_oid_equal(&found_full_oid, &hash)) { | |
1372 | error = git_odb__error_mismatch(&found_full_oid, &hash); | |
1373 | goto out; | |
1374 | } | |
1375 | } | |
1376 | ||
eae0bfdc PP |
1377 | if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) { |
1378 | error = -1; | |
14109620 | 1379 | goto out; |
eae0bfdc | 1380 | } |
78606263 RB |
1381 | |
1382 | *out = git_cache_store_raw(odb_cache(db), object); | |
14109620 PS |
1383 | |
1384 | out: | |
1385 | if (error) | |
1386 | git__free(raw.data); | |
1387 | ||
1388 | return error; | |
dd453c4d MP |
1389 | } |
1390 | ||
43820f20 VM |
1391 | int git_odb_read_prefix( |
1392 | git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len) | |
1393 | { | |
1394 | git_oid key = {{0}}; | |
1395 | int error; | |
1396 | ||
c25aa7cd PP |
1397 | GIT_ASSERT_ARG(out); |
1398 | GIT_ASSERT_ARG(db); | |
43820f20 VM |
1399 | |
1400 | if (len < GIT_OID_MINPREFIXLEN) | |
1401 | return git_odb__error_ambiguous("prefix length too short"); | |
1402 | ||
1403 | if (len > GIT_OID_HEXSZ) | |
1404 | len = GIT_OID_HEXSZ; | |
1405 | ||
1406 | if (len == GIT_OID_HEXSZ) { | |
1407 | *out = git_cache_get_raw(odb_cache(db), short_id); | |
1408 | if (*out != NULL) | |
1409 | return 0; | |
1410 | } | |
1411 | ||
6c04269c | 1412 | git_oid__cpy_prefix(&key, short_id, len); |
43820f20 VM |
1413 | |
1414 | error = read_prefix_1(out, db, &key, len, false); | |
1415 | ||
1416 | if (error == GIT_ENOTFOUND && !git_odb_refresh(db)) | |
1417 | error = read_prefix_1(out, db, &key, len, true); | |
1418 | ||
1419 | if (error == GIT_ENOTFOUND) | |
e10144ae | 1420 | return git_odb__error_notfound("no match for prefix", &key, len); |
43820f20 VM |
1421 | |
1422 | return error; | |
1423 | } | |
1424 | ||
2e76b5fc | 1425 | int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload) |
521aedad CMN |
1426 | { |
1427 | unsigned int i; | |
c25aa7cd | 1428 | git_vector backends = GIT_VECTOR_INIT; |
521aedad | 1429 | backend_internal *internal; |
c25aa7cd PP |
1430 | int error = 0; |
1431 | ||
1432 | /* Make a copy of the backends vector to invoke the callback without holding the lock. */ | |
1433 | if ((error = git_mutex_lock(&db->lock)) < 0) { | |
1434 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1435 | goto cleanup; | |
1436 | } | |
1437 | error = git_vector_dup(&backends, &db->backends, NULL); | |
1438 | git_mutex_unlock(&db->lock); | |
1439 | ||
1440 | if (error < 0) | |
1441 | goto cleanup; | |
5dca2010 | 1442 | |
c25aa7cd | 1443 | git_vector_foreach(&backends, i, internal) { |
521aedad | 1444 | git_odb_backend *b = internal->backend; |
c25aa7cd | 1445 | error = b->foreach(b, cb, payload); |
ac3d33df | 1446 | if (error != 0) |
c25aa7cd | 1447 | goto cleanup; |
521aedad CMN |
1448 | } |
1449 | ||
c25aa7cd PP |
1450 | cleanup: |
1451 | git_vector_free(&backends); | |
1452 | ||
1453 | return error; | |
521aedad CMN |
1454 | } |
1455 | ||
e1de726c | 1456 | int git_odb_write( |
ac3d33df | 1457 | git_oid *oid, git_odb *db, const void *data, size_t len, git_object_t type) |
f6f72d7e | 1458 | { |
10c06114 | 1459 | size_t i; |
22a2d3d5 | 1460 | int error; |
984ed6b6 | 1461 | git_odb_stream *stream; |
f6f72d7e | 1462 | |
c25aa7cd PP |
1463 | GIT_ASSERT_ARG(oid); |
1464 | GIT_ASSERT_ARG(db); | |
f6f72d7e | 1465 | |
22a2d3d5 UG |
1466 | if ((error = git_odb_hash(oid, data, len, type)) < 0) |
1467 | return error; | |
eae0bfdc | 1468 | |
22a2d3d5 | 1469 | if (git_oid_is_zero(oid)) |
eae0bfdc PP |
1470 | return error_null_oid(GIT_EINVALID, "cannot write object"); |
1471 | ||
52d03f37 | 1472 | if (git_odb__freshen(db, oid)) |
4d185dd9 DMB |
1473 | return 0; |
1474 | ||
c25aa7cd PP |
1475 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1476 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1477 | return error; | |
1478 | } | |
22a2d3d5 | 1479 | for (i = 0, error = GIT_ERROR; i < db->backends.length && error < 0; ++i) { |
f6f72d7e VM |
1480 | backend_internal *internal = git_vector_get(&db->backends, i); |
1481 | git_odb_backend *b = internal->backend; | |
1482 | ||
1483 | /* we don't write in alternates! */ | |
1484 | if (internal->is_alternate) | |
1485 | continue; | |
1486 | ||
1487 | if (b->write != NULL) | |
fe0c6d4e | 1488 | error = b->write(b, oid, data, len, type); |
f6f72d7e | 1489 | } |
c25aa7cd | 1490 | git_mutex_unlock(&db->lock); |
f6f72d7e | 1491 | |
e172cf08 | 1492 | if (!error || error == GIT_PASSTHROUGH) |
e1de726c | 1493 | return 0; |
984ed6b6 | 1494 | |
f063f578 RB |
1495 | /* if no backends were able to write the object directly, we try a |
1496 | * streaming write to the backends; just write the whole object into the | |
1497 | * stream in one push | |
1498 | */ | |
e1de726c RB |
1499 | if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0) |
1500 | return error; | |
f6f72d7e | 1501 | |
e579e0f7 MB |
1502 | if ((error = stream->write(stream, data, len)) == 0) |
1503 | error = stream->finalize_write(stream, oid); | |
e1de726c | 1504 | |
e579e0f7 | 1505 | git_odb_stream_free(stream); |
e1de726c | 1506 | return error; |
f6f72d7e VM |
1507 | } |
1508 | ||
22a2d3d5 | 1509 | static int hash_header(git_hash_ctx *ctx, git_object_size_t size, git_object_t type) |
8380b39a CMN |
1510 | { |
1511 | char header[64]; | |
eae0bfdc PP |
1512 | size_t hdrlen; |
1513 | int error; | |
8380b39a | 1514 | |
eae0bfdc PP |
1515 | if ((error = git_odb__format_object_header(&hdrlen, |
1516 | header, sizeof(header), size, type)) < 0) | |
1517 | return error; | |
1518 | ||
1519 | return git_hash_update(ctx, header, hdrlen); | |
8380b39a CMN |
1520 | } |
1521 | ||
e1de726c | 1522 | int git_odb_open_wstream( |
22a2d3d5 | 1523 | git_odb_stream **stream, git_odb *db, git_object_size_t size, git_object_t type) |
a7c60cfc | 1524 | { |
f063f578 | 1525 | size_t i, writes = 0; |
7d7cd885 | 1526 | int error = GIT_ERROR; |
7bd2f401 | 1527 | git_hash_ctx *ctx = NULL; |
bed3229b | 1528 | |
c25aa7cd PP |
1529 | GIT_ASSERT_ARG(stream); |
1530 | GIT_ASSERT_ARG(db); | |
bed3229b | 1531 | |
c25aa7cd PP |
1532 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1533 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1534 | return error; | |
1535 | } | |
1536 | error = GIT_ERROR; | |
7d7cd885 | 1537 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
d4b5a4e2 VM |
1538 | backend_internal *internal = git_vector_get(&db->backends, i); |
1539 | git_odb_backend *b = internal->backend; | |
1540 | ||
1541 | /* we don't write in alternates! */ | |
1542 | if (internal->is_alternate) | |
1543 | continue; | |
255a0dab | 1544 | |
f063f578 RB |
1545 | if (b->writestream != NULL) { |
1546 | ++writes; | |
72a3fe42 | 1547 | error = b->writestream(stream, b, size, type); |
f063f578 RB |
1548 | } else if (b->write != NULL) { |
1549 | ++writes; | |
d69d0185 | 1550 | error = init_fake_wstream(stream, b, size, type); |
f063f578 | 1551 | } |
72a3fe42 | 1552 | } |
c25aa7cd | 1553 | git_mutex_unlock(&db->lock); |
72a3fe42 | 1554 | |
7bd2f401 ET |
1555 | if (error < 0) { |
1556 | if (error == GIT_PASSTHROUGH) | |
1557 | error = 0; | |
1558 | else if (!writes) | |
1559 | error = git_odb__error_unsupported_in_backend("write object"); | |
1560 | ||
1561 | goto done; | |
1562 | } | |
984ed6b6 | 1563 | |
8380b39a | 1564 | ctx = git__malloc(sizeof(git_hash_ctx)); |
ac3d33df | 1565 | GIT_ERROR_CHECK_ALLOC(ctx); |
8380b39a | 1566 | |
e579e0f7 | 1567 | if ((error = git_hash_ctx_init(ctx, GIT_HASH_ALGORITHM_SHA1)) < 0 || |
eae0bfdc | 1568 | (error = hash_header(ctx, size, type)) < 0) |
7bd2f401 | 1569 | goto done; |
8380b39a | 1570 | |
8380b39a | 1571 | (*stream)->hash_ctx = ctx; |
031f3f80 | 1572 | (*stream)->declared_size = size; |
1573 | (*stream)->received_bytes = 0; | |
1574 | ||
7bd2f401 | 1575 | done: |
eae0bfdc PP |
1576 | if (error) |
1577 | git__free(ctx); | |
e1de726c | 1578 | return error; |
72a3fe42 VM |
1579 | } |
1580 | ||
031f3f80 | 1581 | static int git_odb_stream__invalid_length( |
1582 | const git_odb_stream *stream, | |
1583 | const char *action) | |
1584 | { | |
ac3d33df | 1585 | git_error_set(GIT_ERROR_ODB, |
909d5494 | 1586 | "cannot %s - " |
6c7cee42 RD |
1587 | "Invalid length. %"PRId64" was expected. The " |
1588 | "total size of the received chunks amounts to %"PRId64".", | |
8d93a11c | 1589 | action, stream->declared_size, stream->received_bytes); |
031f3f80 | 1590 | |
1591 | return -1; | |
1592 | } | |
1593 | ||
376e6c9f CMN |
1594 | int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len) |
1595 | { | |
8380b39a | 1596 | git_hash_update(stream->hash_ctx, buffer, len); |
031f3f80 | 1597 | |
1598 | stream->received_bytes += len; | |
1599 | ||
1600 | if (stream->received_bytes > stream->declared_size) | |
1601 | return git_odb_stream__invalid_length(stream, | |
1602 | "stream_write()"); | |
1603 | ||
376e6c9f CMN |
1604 | return stream->write(stream, buffer, len); |
1605 | } | |
1606 | ||
1607 | int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream) | |
1608 | { | |
031f3f80 | 1609 | if (stream->received_bytes != stream->declared_size) |
1610 | return git_odb_stream__invalid_length(stream, | |
1611 | "stream_finalize_write()"); | |
1612 | ||
e579e0f7 | 1613 | git_hash_final(out->id, stream->hash_ctx); |
4047950f | 1614 | |
52d03f37 | 1615 | if (git_odb__freshen(stream->backend->odb, out)) |
4047950f | 1616 | return 0; |
1617 | ||
fe0c6d4e | 1618 | return stream->finalize_write(stream, out); |
376e6c9f CMN |
1619 | } |
1620 | ||
1621 | int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len) | |
1622 | { | |
1623 | return stream->read(stream, buffer, len); | |
1624 | } | |
1625 | ||
1626 | void git_odb_stream_free(git_odb_stream *stream) | |
1627 | { | |
ae3b6d61 BR |
1628 | if (stream == NULL) |
1629 | return; | |
1630 | ||
c251f3bb | 1631 | git_hash_ctx_cleanup(stream->hash_ctx); |
8380b39a | 1632 | git__free(stream->hash_ctx); |
376e6c9f CMN |
1633 | stream->free(stream); |
1634 | } | |
1635 | ||
eae0bfdc PP |
1636 | int git_odb_open_rstream( |
1637 | git_odb_stream **stream, | |
1638 | size_t *len, | |
ac3d33df | 1639 | git_object_t *type, |
eae0bfdc PP |
1640 | git_odb *db, |
1641 | const git_oid *oid) | |
72a3fe42 | 1642 | { |
f063f578 | 1643 | size_t i, reads = 0; |
72a3fe42 VM |
1644 | int error = GIT_ERROR; |
1645 | ||
c25aa7cd PP |
1646 | GIT_ASSERT_ARG(stream); |
1647 | GIT_ASSERT_ARG(db); | |
72a3fe42 | 1648 | |
c25aa7cd PP |
1649 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1650 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1651 | return error; | |
1652 | } | |
1653 | error = GIT_ERROR; | |
72a3fe42 VM |
1654 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
1655 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1656 | git_odb_backend *b = internal->backend; | |
1657 | ||
f063f578 RB |
1658 | if (b->readstream != NULL) { |
1659 | ++reads; | |
eae0bfdc | 1660 | error = b->readstream(stream, len, type, b, oid); |
f063f578 | 1661 | } |
adc0327a | 1662 | } |
c25aa7cd | 1663 | git_mutex_unlock(&db->lock); |
adc0327a | 1664 | |
e172cf08 | 1665 | if (error == GIT_PASSTHROUGH) |
e1de726c | 1666 | error = 0; |
f063f578 RB |
1667 | if (error < 0 && !reads) |
1668 | error = git_odb__error_unsupported_in_backend("read object streamed"); | |
984ed6b6 | 1669 | |
e1de726c RB |
1670 | return error; |
1671 | } | |
1672 | ||
22a2d3d5 | 1673 | int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_progress_cb progress_cb, void *progress_payload) |
09cc0b92 | 1674 | { |
f063f578 | 1675 | size_t i, writes = 0; |
09cc0b92 ET |
1676 | int error = GIT_ERROR; |
1677 | ||
c25aa7cd PP |
1678 | GIT_ASSERT_ARG(out); |
1679 | GIT_ASSERT_ARG(db); | |
09cc0b92 | 1680 | |
c25aa7cd PP |
1681 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1682 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1683 | return error; | |
1684 | } | |
1685 | error = GIT_ERROR; | |
09cc0b92 ET |
1686 | for (i = 0; i < db->backends.length && error < 0; ++i) { |
1687 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1688 | git_odb_backend *b = internal->backend; | |
1689 | ||
1690 | /* we don't write in alternates! */ | |
1691 | if (internal->is_alternate) | |
1692 | continue; | |
1693 | ||
f063f578 RB |
1694 | if (b->writepack != NULL) { |
1695 | ++writes; | |
0b33fca0 | 1696 | error = b->writepack(out, b, db, progress_cb, progress_payload); |
f063f578 | 1697 | } |
09cc0b92 | 1698 | } |
c25aa7cd | 1699 | git_mutex_unlock(&db->lock); |
09cc0b92 ET |
1700 | |
1701 | if (error == GIT_PASSTHROUGH) | |
1702 | error = 0; | |
f063f578 RB |
1703 | if (error < 0 && !writes) |
1704 | error = git_odb__error_unsupported_in_backend("write pack"); | |
09cc0b92 ET |
1705 | |
1706 | return error; | |
1707 | } | |
1708 | ||
c25aa7cd PP |
1709 | int git_odb_write_multi_pack_index(git_odb *db) |
1710 | { | |
1711 | size_t i, writes = 0; | |
1712 | int error = GIT_ERROR; | |
1713 | ||
1714 | GIT_ASSERT_ARG(db); | |
1715 | ||
1716 | for (i = 0; i < db->backends.length && error < 0; ++i) { | |
1717 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1718 | git_odb_backend *b = internal->backend; | |
1719 | ||
1720 | /* we don't write in alternates! */ | |
1721 | if (internal->is_alternate) | |
1722 | continue; | |
1723 | ||
1724 | if (b->writemidx != NULL) { | |
1725 | ++writes; | |
1726 | error = b->writemidx(b); | |
1727 | } | |
1728 | } | |
1729 | ||
1730 | if (error == GIT_PASSTHROUGH) | |
1731 | error = 0; | |
1732 | if (error < 0 && !writes) | |
1733 | error = git_odb__error_unsupported_in_backend("write multi-pack-index"); | |
1734 | ||
1735 | return error; | |
1736 | } | |
1737 | ||
22a2d3d5 | 1738 | void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len) |
c49d328c | 1739 | { |
0e9f2fce | 1740 | GIT_UNUSED(backend); |
c49d328c PK |
1741 | return git__malloc(len); |
1742 | } | |
1743 | ||
22a2d3d5 UG |
1744 | #ifndef GIT_DEPRECATE_HARD |
1745 | void *git_odb_backend_malloc(git_odb_backend *backend, size_t len) | |
1746 | { | |
1747 | return git_odb_backend_data_alloc(backend, len); | |
1748 | } | |
1749 | #endif | |
1750 | ||
1751 | void git_odb_backend_data_free(git_odb_backend *backend, void *data) | |
1752 | { | |
1753 | GIT_UNUSED(backend); | |
1754 | git__free(data); | |
1755 | } | |
1756 | ||
4a863c06 VM |
1757 | int git_odb_refresh(struct git_odb *db) |
1758 | { | |
10c06114 | 1759 | size_t i; |
c25aa7cd PP |
1760 | int error; |
1761 | ||
1762 | GIT_ASSERT_ARG(db); | |
4a863c06 | 1763 | |
c25aa7cd PP |
1764 | if ((error = git_mutex_lock(&db->lock)) < 0) { |
1765 | git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock"); | |
1766 | return error; | |
1767 | } | |
4a863c06 VM |
1768 | for (i = 0; i < db->backends.length; ++i) { |
1769 | backend_internal *internal = git_vector_get(&db->backends, i); | |
1770 | git_odb_backend *b = internal->backend; | |
1771 | ||
1772 | if (b->refresh != NULL) { | |
1773 | int error = b->refresh(b); | |
c25aa7cd PP |
1774 | if (error < 0) { |
1775 | git_mutex_unlock(&db->lock); | |
4a863c06 | 1776 | return error; |
c25aa7cd | 1777 | } |
4a863c06 VM |
1778 | } |
1779 | } | |
c25aa7cd PP |
1780 | if (db->cgraph) |
1781 | git_commit_graph_refresh(db->cgraph); | |
1782 | git_mutex_unlock(&db->lock); | |
4a863c06 VM |
1783 | |
1784 | return 0; | |
1785 | } | |
1786 | ||
28a0741f PS |
1787 | int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual) |
1788 | { | |
1789 | char expected_oid[GIT_OID_HEXSZ + 1], actual_oid[GIT_OID_HEXSZ + 1]; | |
1790 | ||
1791 | git_oid_tostr(expected_oid, sizeof(expected_oid), expected); | |
1792 | git_oid_tostr(actual_oid, sizeof(actual_oid), actual); | |
1793 | ||
ac3d33df | 1794 | git_error_set(GIT_ERROR_ODB, "object hash mismatch - expected %s but got %s", |
28a0741f PS |
1795 | expected_oid, actual_oid); |
1796 | ||
1797 | return GIT_EMISMATCH; | |
1798 | } | |
1799 | ||
e10144ae ET |
1800 | int git_odb__error_notfound( |
1801 | const char *message, const git_oid *oid, size_t oid_len) | |
e1de726c | 1802 | { |
282283ac RB |
1803 | if (oid != NULL) { |
1804 | char oid_str[GIT_OID_HEXSZ + 1]; | |
2076d329 | 1805 | git_oid_tostr(oid_str, oid_len+1, oid); |
ac3d33df | 1806 | git_error_set(GIT_ERROR_ODB, "object not found - %s (%.*s)", |
901434b0 | 1807 | message, (int) oid_len, oid_str); |
282283ac | 1808 | } else |
ac3d33df | 1809 | git_error_set(GIT_ERROR_ODB, "object not found - %s", message); |
282283ac | 1810 | |
904b67e6 | 1811 | return GIT_ENOTFOUND; |
e1de726c RB |
1812 | } |
1813 | ||
eae0bfdc PP |
1814 | static int error_null_oid(int error, const char *message) |
1815 | { | |
ac3d33df | 1816 | git_error_set(GIT_ERROR_ODB, "odb: %s: null OID cannot exist", message); |
eae0bfdc PP |
1817 | return error; |
1818 | } | |
1819 | ||
e1de726c RB |
1820 | int git_odb__error_ambiguous(const char *message) |
1821 | { | |
ac3d33df | 1822 | git_error_set(GIT_ERROR_ODB, "ambiguous SHA1 prefix - %s", message); |
904b67e6 | 1823 | return GIT_EAMBIGUOUS; |
7b6e8067 RJ |
1824 | } |
1825 | ||
bc91347b | 1826 | int git_odb_init_backend(git_odb_backend *backend, unsigned int version) |
b9f81997 | 1827 | { |
bc91347b RB |
1828 | GIT_INIT_STRUCTURE_FROM_TEMPLATE( |
1829 | backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT); | |
1830 | return 0; | |
b9f81997 | 1831 | } |