odb.c 20 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6 7
 */

8
#include "common.h"
9
#include <zlib.h>
10
#include "git2/object.h"
11
#include "fileops.h"
12
#include "hash.h"
13
#include "odb.h"
Vicent Marti committed
14
#include "delta-apply.h"
15
#include "filter.h"
16

17
#include "git2/odb_backend.h"
18
#include "git2/oid.h"
19

20 21
#define GIT_ALTERNATES_FILE "info/alternates"

22 23 24 25
/* TODO: is this correct? */
#define GIT_LOOSE_PRIORITY 2
#define GIT_PACKED_PRIORITY 1

26 27
#define GIT_ALTERNATES_MAX_DEPTH 5

28 29 30 31 32 33 34
typedef struct
{
	git_odb_backend *backend;
	int priority;
	int is_alternate;
} backend_internal;

35 36
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);

37
int git_odb__format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
38
{
Vicent Marti committed
39
	const char *type_str = git_object_type2string(obj_type);
40
	int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
41
	assert(len > 0 && len <= (int)n);
42 43 44
	return len+1;
}

45
int git_odb__hashobj(git_oid *id, git_rawobj *obj)
46 47
{
	git_buf_vec vec[2];
48 49
	char header[64];
	int hdrlen;
50

51
	assert(id && obj);
52

53
	if (!git_object_typeisloose(obj->type))
54
		return -1;
55
	if (!obj->data && obj->len != 0)
56
		return -1;
57

58
	hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type);
59

60
	vec[0].data = header;
Vicent Marti committed
61
	vec[0].len = hdrlen;
62
	vec[1].data = obj->data;
Vicent Marti committed
63
	vec[1].len = obj->len;
64 65 66

	git_hash_vec(id, vec, 2);

67
	return 0;
68 69
}

70

Vicent Marti committed
71
static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)
Ramsay Jones committed
72
{
Vicent Marti committed
73 74
	git_odb_object *object = git__malloc(sizeof(git_odb_object));
	memset(object, 0x0, sizeof(git_odb_object));
Ramsay Jones committed
75

Vicent Marti committed
76 77
	git_oid_cpy(&object->cached.oid, oid);
	memcpy(&object->raw, source, sizeof(git_rawobj));
Ramsay Jones committed
78

Vicent Marti committed
79
	return object;
80 81
}

Vicent Marti committed
82
static void free_odb_object(void *o)
83
{
Vicent Marti committed
84
	git_odb_object *object = (git_odb_object *)o;
85

Vicent Marti committed
86
	if (object != NULL) {
87 88
		git__free(object->raw.data);
		git__free(object);
Vicent Marti committed
89 90
	}
}
91

92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
const git_oid *git_odb_object_id(git_odb_object *object)
{
	return &object->cached.oid;
}

const void *git_odb_object_data(git_odb_object *object)
{
	return object->raw.data;
}

size_t git_odb_object_size(git_odb_object *object)
{
	return object->raw.len;
}

git_otype git_odb_object_type(git_odb_object *object)
{
	return object->raw.type;
}

112
void git_odb_object_free(git_odb_object *object)
Vicent Marti committed
113
{
114 115 116
	if (object == NULL)
		return;

Vicent Marti committed
117 118
	git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
}
119

120
int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
Vicent Marti committed
121
{
122
	int hdr_len;
Vicent Marti committed
123
	char hdr[64], buffer[2048];
124
	git_hash_ctx ctx;
125
	ssize_t read_len = 0;
126
	int error = 0;
Vicent Marti committed
127

128 129 130 131 132
	if (!git_object_typeisloose(type)) {
		giterr_set(GITERR_INVALID, "Invalid object type for hash");
		return -1;
	}

133 134
	if ((error = git_hash_ctx_init(&ctx)) < 0)
		return -1;
Vicent Marti committed
135

136
	hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);
137

138
	if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
139
		goto done;
Vicent Marti committed
140

Vicent Marti committed
141
	while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
142
		if ((error = git_hash_update(&ctx, buffer, read_len)) < 0)
143 144
			goto done;

Vicent Marti committed
145 146 147
		size -= read_len;
	}

Vicent Marti committed
148 149 150 151 152
	/* If p_read returned an error code, the read obviously failed.
	 * If size is not zero, the file was truncated after we originally
	 * stat'd it, so we consider this a read failure too */
	if (read_len < 0 || size > 0) {
		giterr_set(GITERR_OS, "Error reading file for hashing");
153 154 155
		error = -1;

		goto done;
Vicent Marti committed
156 157 158
		return -1;
	}

159
	error = git_hash_final(out, &ctx);
Vicent Marti committed
160

161
done:
162
	git_hash_ctx_cleanup(&ctx);
163
	return error;
Vicent Marti committed
164 165
}

166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
int git_odb__hashfd_filtered(
	git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
{
	int error;
	git_buf raw = GIT_BUF_INIT;
	git_buf filtered = GIT_BUF_INIT;

	if (!filters || !filters->length)
		return git_odb__hashfd(out, fd, size, type);

	/* size of data is used in header, so we have to read the whole file
	 * into memory to apply filters before beginning to calculate the hash
	 */

	if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
		error = git_filters_apply(&filtered, &raw, filters);

	git_buf_free(&raw);

	if (!error)
		error = git_odb_hash(out, filtered.ptr, filtered.size, type);

	git_buf_free(&filtered);

	return error;
}

193 194 195 196
int git_odb__hashlink(git_oid *out, const char *path)
{
	struct stat st;
	git_off_t size;
197
	int result;
198

199
	if (git_path_lstat(path, &st) < 0)
200
		return -1;
201 202 203

	size = st.st_size;

204 205 206 207
	if (!git__is_sizet(size)) {
		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
		return -1;
	}
208 209 210 211 212

	if (S_ISLNK(st.st_mode)) {
		char *link_data;
		ssize_t read_len;

213
		link_data = git__malloc((size_t)(size + 1));
214
		GITERR_CHECK_ALLOC(link_data);
215

216 217
		read_len = p_readlink(path, link_data, (size_t)size);
		link_data[size] = '\0';
218 219 220 221
		if (read_len != (ssize_t)size) {
			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
			return -1;
		}
222

223
		result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
224
		git__free(link_data);
225
	} else {
226 227 228 229
		int fd = git_futils_open_ro(path);
		if (fd < 0)
			return -1;
		result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
230 231 232
		p_close(fd);
	}

233
	return result;
234 235
}

236 237 238
int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
{
	git_off_t size;
239 240
	int result, fd = git_futils_open_ro(path);
	if (fd < 0)
241
		return fd;
242 243

	if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
244
		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
245
		p_close(fd);
246
		return -1;
247 248
	}

249
	result = git_odb__hashfd(out, fd, (size_t)size, type);
250
	p_close(fd);
251
	return result;
252 253
}

Vicent Marti committed
254 255 256
int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
{
	git_rawobj raw;
257

Vicent Marti committed
258
	assert(id);
259

Vicent Marti committed
260 261 262
	raw.data = (void *)data;
	raw.len = len;
	raw.type = type;
263

264
	return git_odb__hashobj(id, &raw);
265 266
}

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
/**
 * FAKE WSTREAM
 */

typedef struct {
	git_odb_stream stream;
	char *buffer;
	size_t size, written;
	git_otype type;
} fake_wstream;

static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream)
{
	fake_wstream *stream = (fake_wstream *)_stream;
	return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type);
}

static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
{
	fake_wstream *stream = (fake_wstream *)_stream;

288
	if (stream->written + len > stream->size)
289
		return -1;
290 291 292

	memcpy(stream->buffer + stream->written, data, len);
	stream->written += len;
293
	return 0;
294 295 296 297 298 299
}

static void fake_wstream__free(git_odb_stream *_stream)
{
	fake_wstream *stream = (fake_wstream *)_stream;

300 301
	git__free(stream->buffer);
	git__free(stream);
302 303 304 305 306 307 308
}

static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, size_t size, git_otype type)
{
	fake_wstream *stream;

	stream = git__calloc(1, sizeof(fake_wstream));
309
	GITERR_CHECK_ALLOC(stream);
310 311 312 313 314

	stream->size = size;
	stream->type = type;
	stream->buffer = git__malloc(size);
	if (stream->buffer == NULL) {
315
		git__free(stream);
316
		return -1;
317 318 319 320 321 322 323 324 325 326
	}

	stream->stream.backend = backend;
	stream->stream.read = NULL; /* read only */
	stream->stream.write = &fake_wstream__write;
	stream->stream.finalize_write = &fake_wstream__fwrite;
	stream->stream.free = &fake_wstream__free;
	stream->stream.mode = GIT_STREAM_WRONLY;

	*stream_p = (git_odb_stream *)stream;
327
	return 0;
328
}
329

330 331 332 333 334 335 336
/***********************************************************
 *
 * OBJECT DATABASE PUBLIC API
 *
 * Public calls for the ODB functionality
 *
 ***********************************************************/
337

338
static int backend_sort_cmp(const void *a, const void *b)
339
{
340 341
	const backend_internal *backend_a = (const backend_internal *)(a);
	const backend_internal *backend_b = (const backend_internal *)(b);
342 343 344

	if (backend_a->is_alternate == backend_b->is_alternate)
		return (backend_b->priority - backend_a->priority);
345

346
	return backend_a->is_alternate ? 1 : -1;
347 348
}

349
int git_odb_new(git_odb **out)
350
{
351
	git_odb *db = git__calloc(1, sizeof(*db));
352
	GITERR_CHECK_ALLOC(db);
353

354 355 356
	if (git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object) < 0 ||
		git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
	{
357
		git__free(db);
358
		return -1;
359
	}
360

361
	*out = db;
362
	GIT_REFCOUNT_INC(db);
363
	return 0;
364 365
}

366
static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
Ramsay Jones committed
367
{
368 369
	backend_internal *internal;

370
	assert(odb && backend);
Ramsay Jones committed
371

Ben Straub committed
372
	GITERR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend");
373

374 375
	/* Check if the backend is already owned by another ODB */
	assert(!backend->odb || backend->odb == odb);
Ramsay Jones committed
376

377
	internal = git__malloc(sizeof(backend_internal));
378
	GITERR_CHECK_ALLOC(internal);
379 380 381 382

	internal->backend = backend;
	internal->priority = priority;
	internal->is_alternate = is_alternate;
Ramsay Jones committed
383

384
	if (git_vector_insert(&odb->backends, internal) < 0) {
385
		git__free(internal);
386
		return -1;
387
	}
Ramsay Jones committed
388

389
	git_vector_sort(&odb->backends);
390
	internal->backend->odb = odb;
391
	return 0;
Ramsay Jones committed
392 393
}

394 395 396 397 398 399 400 401 402 403
int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
{
	return add_backend_internal(odb, backend, priority, 0);
}

int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
{
	return add_backend_internal(odb, backend, priority, 1);
}

404
static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth)
405 406 407 408
{
	git_odb_backend *loose, *packed;

	/* add the loose object backend */
409 410 411
	if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
		add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
		return -1;
412 413

	/* add the packed file backend */
414 415 416
	if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
		add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
		return -1;
417

418
	return load_alternates(db, objects_dir, alternate_depth);
419 420
}

421
static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth)
422
{
423
	git_buf alternates_path = GIT_BUF_INIT;
424
	git_buf alternates_buf = GIT_BUF_INIT;
425 426
	char *buffer;
	const char *alternate;
427
	int result = 0;
428

429 430 431 432 433
	/* Git reports an error, we just ignore anything deeper */
	if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) {
		return 0;
	}

434 435
	if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
		return -1;
436

437
	if (git_path_exists(alternates_path.ptr) == false) {
438
		git_buf_free(&alternates_path);
439
		return 0;
440
	}
441

442
	if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
443
		git_buf_free(&alternates_path);
444
		return -1;
445
	}
446

447
	buffer = (char *)alternates_buf.ptr;
448 449

	/* add each alternate as a new backend; one alternate per line */
450 451 452 453
	while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
		if (*alternate == '\0' || *alternate == '#')
			continue;

454 455 456 457 458 459
		/*
		 * Relative path: build based on the current `objects`
		 * folder. However, relative paths are only allowed in
		 * the current repository.
		 */
		if (*alternate == '.' && !alternate_depth) {
460
			if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
461 462
				break;
			alternate = git_buf_cstr(&alternates_path);
463 464
		}

465
		if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0)
466 467
			break;
	}
468

469
	git_buf_free(&alternates_path);
470 471
	git_buf_free(&alternates_buf);

472
	return result;
473
}
Ramsay Jones committed
474

475 476 477 478 479
int git_odb_add_disk_alternate(git_odb *odb, const char *path)
{
	return add_default_backends(odb, path, 1, 0);
}

480
int git_odb_open(git_odb **out, const char *objects_dir)
Ramsay Jones committed
481
{
482
	git_odb *db;
Ramsay Jones committed
483

484 485 486 487
	assert(out && objects_dir);

	*out = NULL;

488 489
	if (git_odb_new(&db) < 0)
		return -1;
Ramsay Jones committed
490

491
	if (add_default_backends(db, objects_dir, 0, 0) < 0) {
492 493 494
		git_odb_free(db);
		return -1;
	}
Ramsay Jones committed
495

496
	*out = db;
497
	return 0;
498
}
Ramsay Jones committed
499

500
static void odb_free(git_odb *db)
501
{
502
	unsigned int i;
503

504
	for (i = 0; i < db->backends.length; ++i) {
505 506
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *backend = internal->backend;
507

508
		if (backend->free) backend->free(backend);
509
		else git__free(backend);
510

511
		git__free(internal);
512 513
	}

514
	git_vector_free(&db->backends);
Vicent Marti committed
515
	git_cache_free(&db->cache);
516
	git__free(db);
517 518
}

519 520 521 522 523 524 525 526
void git_odb_free(git_odb *db)
{
	if (db == NULL)
		return;

	GIT_REFCOUNT_DEC(db, odb_free);
}

527
int git_odb_exists(git_odb *db, const git_oid *id)
528
{
Vicent Marti committed
529
	git_odb_object *object;
530
	unsigned int i;
531
	bool found = false;
532
	bool refreshed = false;
533

534
	assert(db && id);
535

Vicent Marti committed
536
	if ((object = git_cache_get(&db->cache, id)) != NULL) {
537
		git_odb_object_free(object);
538
		return (int)true;
Vicent Marti committed
539 540
	}

541
attempt_lookup:
542
	for (i = 0; i < db->backends.length && !found; ++i) {
543 544
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;
545

546 547
		if (b->exists != NULL)
			found = b->exists(b, id);
548 549
	}

550 551 552 553 554 555 556 557 558 559
	if (!found && !refreshed) {
		if (git_odb_refresh(db) < 0) {
			giterr_clear();
			return (int)false;
		}

		refreshed = true;
		goto attempt_lookup;
	}

560
	return (int)found;
561 562
}

Vicent Marti committed
563
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
564
{
565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	int error;
	git_odb_object *object;

	error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);

	if (object)
		git_odb_object_free(object);

	return error;
}

int git_odb__read_header_or_object(
	git_odb_object **out, size_t *len_p, git_otype *type_p,
	git_odb *db, const git_oid *id)
{
580
	unsigned int i;
581
	int error = GIT_ENOTFOUND;
Vicent Marti committed
582
	git_odb_object *object;
583

584
	assert(db && id && out && len_p && type_p);
Vicent Marti committed
585 586 587 588

	if ((object = git_cache_get(&db->cache, id)) != NULL) {
		*len_p = object->raw.len;
		*type_p = object->raw.type;
589
		*out = object;
590
		return 0;
Vicent Marti committed
591
	}
592

593 594
	*out = NULL;

595
	for (i = 0; i < db->backends.length && error < 0; ++i) {
596 597
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;
598

599
		if (b->read_header != NULL)
Vicent Marti committed
600
			error = b->read_header(len_p, type_p, b, id);
601 602
	}

603
	if (!error || error == GIT_PASSTHROUGH)
604
		return 0;
Vicent Marti committed
605

606 607 608 609
	/*
	 * no backend could read only the header.
	 * try reading the whole object and freeing the contents
	 */
610 611
	if ((error = git_odb_read(&object, db, id)) < 0)
		return error; /* error already set - pass along */
612

613 614
	*len_p = object->raw.len;
	*type_p = object->raw.type;
615 616
	*out = object;

617
	return 0;
618 619
}

Vicent Marti committed
620
int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
621
{
622
	unsigned int i;
Vicent Marti committed
623
	int error;
Vicent Marti committed
624
	bool refreshed = false;
Vicent Marti committed
625
	git_rawobj raw;
626

627
	assert(out && db && id);
628

Vicent Marti committed
629 630 631 632 633
	if (db->backends.length == 0) {
		giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded");
		return GIT_ENOTFOUND;
	}

Vicent Marti committed
634 635
	*out = git_cache_get(&db->cache, id);
	if (*out != NULL)
636
		return 0;
Vicent Marti committed
637

Vicent Marti committed
638
attempt_lookup:
Vicent Marti committed
639
	error = GIT_ENOTFOUND;
Vicent Marti committed
640

641
	for (i = 0; i < db->backends.length && error < 0; ++i) {
642 643
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;
644

645
		if (b->read != NULL)
Vicent Marti committed
646 647 648
			error = b->read(&raw.data, &raw.len, &raw.type, b, id);
	}

Vicent Marti committed
649 650 651 652 653 654 655
	if (error == GIT_ENOTFOUND && !refreshed) {
		if ((error = git_odb_refresh(db)) < 0)
			return error;

		refreshed = true;
		goto attempt_lookup;
	}
656

657
	if (error && error != GIT_PASSTHROUGH)
658
		return error;
659

660 661
	*out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
	return 0;
662 663
}

664
int git_odb_read_prefix(
665
	git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
666 667
{
	unsigned int i;
668
	int error = GIT_ENOTFOUND;
669
	git_oid found_full_oid = {{0}};
670
	git_rawobj raw;
671
	void *data = NULL;
Vicent Marti committed
672
	bool found = false, refreshed = false;
673

Vicent Marti committed
674
	assert(out && db);
675

676
	if (len < GIT_OID_MINPREFIXLEN)
677
		return git_odb__error_ambiguous("prefix length too short");
Vicent Marti committed
678

679 680 681 682 683
	if (len > GIT_OID_HEXSZ)
		len = GIT_OID_HEXSZ;

	if (len == GIT_OID_HEXSZ) {
		*out = git_cache_get(&db->cache, short_id);
Vicent Marti committed
684
		if (*out != NULL)
685
			return 0;
686 687
	}

Vicent Marti committed
688
attempt_lookup:
689
	for (i = 0; i < db->backends.length; ++i) {
690 691 692
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

Vicent Marti committed
693
		if (b->read_prefix != NULL) {
694
			git_oid full_oid;
Vicent Marti committed
695
			error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
696
			if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
697 698 699
				continue;

			if (error)
700
				return error;
701

702 703
			git__free(data);
			data = raw.data;
Vicent Marti committed
704

705 706
			if (found && git_oid_cmp(&full_oid, &found_full_oid))
				return git_odb__error_ambiguous("multiple matches for prefix");
Vicent Marti committed
707

708 709
			found_full_oid = full_oid;
			found = true;
710 711 712
		}
	}

Vicent Marti committed
713 714 715 716 717 718 719 720
	if (!found && !refreshed) {
		if ((error = git_odb_refresh(db)) < 0)
			return error;

		refreshed = true;
		goto attempt_lookup;
	}

721
	if (!found)
Russell Belfer committed
722
		return git_odb__error_notfound("no match for prefix", short_id);
723

724
	*out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
725
	return 0;
726 727
}

Ben Straub committed
728
int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
729 730 731
{
	unsigned int i;
	backend_internal *internal;
732

733 734
	git_vector_foreach(&db->backends, i, internal) {
		git_odb_backend *b = internal->backend;
Ben Straub committed
735
		int error = b->foreach(b, cb, payload);
736 737
		if (error < 0)
			return error;
738 739 740 741 742
	}

	return 0;
}

743 744
int git_odb_write(
	git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
745 746 747
{
	unsigned int i;
	int error = GIT_ERROR;
Vicent Marti committed
748
	git_odb_stream *stream;
749 750 751

	assert(oid && db);

752 753 754 755
	git_odb_hash(oid, data, len, type);
	if (git_odb_exists(db, oid))
		return 0;

756 757 758 759 760 761 762 763 764 765 766 767
	for (i = 0; i < db->backends.length && error < 0; ++i) {
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

		/* we don't write in alternates! */
		if (internal->is_alternate)
			continue;

		if (b->write != NULL)
			error = b->write(oid, b, data, len, type);
	}

768
	if (!error || error == GIT_PASSTHROUGH)
769
		return 0;
Vicent Marti committed
770

771 772 773 774
	/* if no backends were able to write the object directly, we try a streaming
	 * write to the backends; just write the whole object into the stream in one
	 * push */

775 776
	if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
		return error;
777

778 779 780 781 782
	stream->write(stream, data, len);
	error = stream->finalize_write(oid, stream);
	stream->free(stream);

	return error;
783 784
}

785 786
int git_odb_open_wstream(
	git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
787
{
788 789
	unsigned int i;
	int error = GIT_ERROR;
790

Vicent Marti committed
791
	assert(stream && db);
792

793
	for (i = 0; i < db->backends.length && error < 0; ++i) {
794 795 796 797 798 799
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

		/* we don't write in alternates! */
		if (internal->is_alternate)
			continue;
800

Vicent Marti committed
801 802
		if (b->writestream != NULL)
			error = b->writestream(stream, b, size, type);
803 804
		else if (b->write != NULL)
			error = init_fake_wstream(stream, b, size, type);
Vicent Marti committed
805 806
	}

807
	if (error == GIT_PASSTHROUGH)
808
		error = 0;
Vicent Marti committed
809

810
	return error;
Vicent Marti committed
811 812
}

813
int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
Vicent Marti committed
814 815 816 817 818 819 820 821 822 823 824 825
{
	unsigned int i;
	int error = GIT_ERROR;

	assert(stream && db);

	for (i = 0; i < db->backends.length && error < 0; ++i) {
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

		if (b->readstream != NULL)
			error = b->readstream(stream, b, oid);
826 827
	}

828
	if (error == GIT_PASSTHROUGH)
829
		error = 0;
Vicent Marti committed
830

831 832 833
	return error;
}

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload)
{
	unsigned int i;
	int error = GIT_ERROR;

	assert(out && db);

	for (i = 0; i < db->backends.length && error < 0; ++i) {
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

		/* we don't write in alternates! */
		if (internal->is_alternate)
			continue;

		if (b->writepack != NULL)
			error = b->writepack(out, b, progress_cb, progress_payload);
	}

	if (error == GIT_PASSTHROUGH)
		error = 0;

	return error;
}

Vicent Marti committed
859
void *git_odb_backend_malloc(git_odb_backend *backend, size_t len)
860
{
861
	GIT_UNUSED(backend);
862 863 864
	return git__malloc(len);
}

Vicent Marti committed
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
int git_odb_refresh(struct git_odb *db)
{
	unsigned int i;
	assert(db);

	for (i = 0; i < db->backends.length; ++i) {
		backend_internal *internal = git_vector_get(&db->backends, i);
		git_odb_backend *b = internal->backend;

		if (b->refresh != NULL) {
			int error = b->refresh(b);
			if (error < 0)
				return error;
		}
	}

	return 0;
}

Russell Belfer committed
884
int git_odb__error_notfound(const char *message, const git_oid *oid)
885
{
Russell Belfer committed
886 887 888 889 890 891 892
	if (oid != NULL) {
		char oid_str[GIT_OID_HEXSZ + 1];
		git_oid_tostr(oid_str, sizeof(oid_str), oid);
		giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
	} else
		giterr_set(GITERR_ODB, "Object not found - %s", message);

893
	return GIT_ENOTFOUND;
894 895 896 897 898
}

int git_odb__error_ambiguous(const char *message)
{
	giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
899
	return GIT_EAMBIGUOUS;
900 901
}