]> git.proxmox.com Git - libgit2.git/blame - src/odb.c
Include stacktrace summary in memory leak output.
[libgit2.git] / src / odb.c
CommitLineData
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
33typedef 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
41static 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
51static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
52
77b339f7 53int 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 61int 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 88static 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 102void 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
110const git_oid *git_odb_object_id(git_odb_object *object)
111{
112 return &object->cached.oid;
113}
114
115const void *git_odb_object_data(git_odb_object *object)
116{
8842c75f 117 return object->buffer;
1881f078
VM
118}
119
120size_t git_odb_object_size(git_odb_object *object)
121{
8842c75f 122 return object->cached.size;
1881f078
VM
123}
124
125git_otype git_odb_object_type(git_odb_object *object)
126{
8842c75f 127 return object->cached.type;
1881f078
VM
128}
129
98fec8a9
VM
130int 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 137void 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 145int 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 185done:
603bee07 186 git_hash_ctx_cleanup(&ctx);
d6fb0924 187 return error;
c52736fa
VM
188}
189
60b9d3fc 190int 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
219int 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
265int 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
283int 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
300typedef struct {
301 git_odb_stream stream;
302 char *buffer;
303 size_t size, written;
304 git_otype type;
305} fake_wstream;
306
fe0c6d4e 307static 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
313static 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
325static 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 333static 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 372static 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 383int 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
399static 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
430int 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
435int 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
440size_t git_odb_num_backends(git_odb *odb)
441{
442 assert(odb);
443 return odb->backends.length;
444}
445
f063f578
RB
446static 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
454int 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
470static 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 517static 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
570int 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 575int 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 595static 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
616void git_odb_free(git_odb *db)
617{
618 if (db == NULL)
619 return;
620
621 GIT_REFCOUNT_DEC(db, odb_free);
622}
623
7d7cd885 624int 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
648int 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 708int 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
721int 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
765static 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 }};
767static 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
770static 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 787int 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 826int 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 891int 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
906int 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 948static 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 957int 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 1004done:
e1de726c 1005 return error;
72a3fe42
VM
1006}
1007
031f3f80 1008static 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
1021int 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
1034int 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
1048int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len)
1049{
1050 return stream->read(stream, buffer, len);
1051}
1052
1053void 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 1063int 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 1088int 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 1117void *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
1123int 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 1142int 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
1154int 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 1160int 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}