2 * Copyright (C) 2009-2011 the libgit2 contributors
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.
10 #include "git2/object.h"
14 #include "delta-apply.h"
16 #include "git2/odb_backend.h"
18 #define GIT_ALTERNATES_FILE "info/alternates"
20 /* TODO: is this correct? */
21 #define GIT_LOOSE_PRIORITY 2
22 #define GIT_PACKED_PRIORITY 1
26 git_odb_backend
*backend
;
31 static int format_object_header(char *hdr
, size_t n
, size_t obj_len
, git_otype obj_type
)
33 const char *type_str
= git_object_type2string(obj_type
);
34 int len
= p_snprintf(hdr
, n
, "%s %"PRIuZ
, type_str
, obj_len
);
36 if (len
< 0 || ((size_t) len
) >= n
)
37 return git__throw(GIT_ERROR
, "Cannot format object header. Length is out of bounds");
42 int git_odb__hash_obj(git_oid
*id
, git_rawobj
*obj
)
50 if (!git_object_typeisloose(obj
->type
))
51 return git__throw(GIT_ERROR
, "Failed to hash object. Wrong object type");
53 if (!obj
->data
&& obj
->len
!= 0)
54 return git__throw(GIT_ERROR
, "Failed to hash object. No data given");
56 if ((hdrlen
= format_object_header(header
, sizeof(header
), obj
->len
, obj
->type
)) < 0)
57 return git__rethrow(hdrlen
, "Failed to hash object");
61 vec
[1].data
= obj
->data
;
62 vec
[1].len
= obj
->len
;
64 git_hash_vec(id
, vec
, 2);
70 static git_odb_object
*new_odb_object(const git_oid
*oid
, git_rawobj
*source
)
72 git_odb_object
*object
= git__malloc(sizeof(git_odb_object
));
73 memset(object
, 0x0, sizeof(git_odb_object
));
75 git_oid_cpy(&object
->cached
.oid
, oid
);
76 memcpy(&object
->raw
, source
, sizeof(git_rawobj
));
81 static void free_odb_object(void *o
)
83 git_odb_object
*object
= (git_odb_object
*)o
;
86 free(object
->raw
.data
);
91 const git_oid
*git_odb_object_id(git_odb_object
*object
)
93 return &object
->cached
.oid
;
96 const void *git_odb_object_data(git_odb_object
*object
)
98 return object
->raw
.data
;
101 size_t git_odb_object_size(git_odb_object
*object
)
103 return object
->raw
.len
;
106 git_otype
git_odb_object_type(git_odb_object
*object
)
108 return object
->raw
.type
;
111 void git_odb_object_close(git_odb_object
*object
)
113 git_cached_obj_decref((git_cached_obj
*)object
, &free_odb_object
);
116 int git_odb_hashfile(git_oid
*out
, const char *path
, git_otype type
)
119 char hdr
[64], buffer
[2048];
123 if ((fd
= p_open(path
, O_RDONLY
)) < 0)
124 return git__throw(GIT_ENOTFOUND
, "Could not open '%s'", path
);
126 if ((size
= git_futils_filesize(fd
)) < 0 || !git__is_sizet(size
)) {
128 return git__throw(GIT_EOSERR
, "'%s' appears to be corrupted", path
);
131 hdr_len
= format_object_header(hdr
, sizeof(hdr
), (size_t)size
, type
);
133 return git__throw(GIT_ERROR
, "Failed to format blob header. Length is out of bounds");
135 ctx
= git_hash_new_ctx();
137 git_hash_update(ctx
, hdr
, hdr_len
);
142 read_len
= read(fd
, buffer
, sizeof(buffer
));
146 git_hash_free_ctx(ctx
);
147 return git__throw(GIT_EOSERR
, "Can't read full file '%s'", path
);
150 git_hash_update(ctx
, buffer
, read_len
);
156 git_hash_final(out
, ctx
);
157 git_hash_free_ctx(ctx
);
162 int git_odb_hash(git_oid
*id
, const void *data
, size_t len
, git_otype type
)
168 raw
.data
= (void *)data
;
172 return git_odb__hash_obj(id
, &raw
);
180 git_odb_stream stream
;
182 size_t size
, written
;
186 static int fake_wstream__fwrite(git_oid
*oid
, git_odb_stream
*_stream
)
188 fake_wstream
*stream
= (fake_wstream
*)_stream
;
189 return _stream
->backend
->write(oid
, _stream
->backend
, stream
->buffer
, stream
->size
, stream
->type
);
192 static int fake_wstream__write(git_odb_stream
*_stream
, const char *data
, size_t len
)
194 fake_wstream
*stream
= (fake_wstream
*)_stream
;
196 if (stream
->written
+ len
> stream
->size
)
199 memcpy(stream
->buffer
+ stream
->written
, data
, len
);
200 stream
->written
+= len
;
204 static void fake_wstream__free(git_odb_stream
*_stream
)
206 fake_wstream
*stream
= (fake_wstream
*)_stream
;
208 free(stream
->buffer
);
212 static int init_fake_wstream(git_odb_stream
**stream_p
, git_odb_backend
*backend
, size_t size
, git_otype type
)
214 fake_wstream
*stream
;
216 stream
= git__calloc(1, sizeof(fake_wstream
));
222 stream
->buffer
= git__malloc(size
);
223 if (stream
->buffer
== NULL
) {
228 stream
->stream
.backend
= backend
;
229 stream
->stream
.read
= NULL
; /* read only */
230 stream
->stream
.write
= &fake_wstream__write
;
231 stream
->stream
.finalize_write
= &fake_wstream__fwrite
;
232 stream
->stream
.free
= &fake_wstream__free
;
233 stream
->stream
.mode
= GIT_STREAM_WRONLY
;
235 *stream_p
= (git_odb_stream
*)stream
;
239 /***********************************************************
241 * OBJECT DATABASE PUBLIC API
243 * Public calls for the ODB functionality
245 ***********************************************************/
247 static int backend_sort_cmp(const void *a
, const void *b
)
249 const backend_internal
*backend_a
= (const backend_internal
*)(a
);
250 const backend_internal
*backend_b
= (const backend_internal
*)(b
);
252 if (backend_a
->is_alternate
== backend_b
->is_alternate
)
253 return (backend_b
->priority
- backend_a
->priority
);
255 return backend_a
->is_alternate
? 1 : -1;
258 int git_odb_new(git_odb
**out
)
262 git_odb
*db
= git__calloc(1, sizeof(*db
));
266 error
= git_cache_init(&db
->cache
, GIT_DEFAULT_CACHE_SIZE
, &free_odb_object
);
267 if (error
< GIT_SUCCESS
) {
269 return git__rethrow(error
, "Failed to create object database");
272 if ((error
= git_vector_init(&db
->backends
, 4, backend_sort_cmp
)) < GIT_SUCCESS
) {
274 return git__rethrow(error
, "Failed to create object database");
281 static int add_backend_internal(git_odb
*odb
, git_odb_backend
*backend
, int priority
, int is_alternate
)
283 backend_internal
*internal
;
285 assert(odb
&& backend
);
287 if (backend
->odb
!= NULL
&& backend
->odb
!= odb
)
288 return git__throw(GIT_EBUSY
, "The backend is already owned by another ODB");
290 internal
= git__malloc(sizeof(backend_internal
));
291 if (internal
== NULL
)
294 internal
->backend
= backend
;
295 internal
->priority
= priority
;
296 internal
->is_alternate
= is_alternate
;
298 if (git_vector_insert(&odb
->backends
, internal
) < 0) {
303 git_vector_sort(&odb
->backends
);
304 internal
->backend
->odb
= odb
;
308 int git_odb_add_backend(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
310 return add_backend_internal(odb
, backend
, priority
, 0);
313 int git_odb_add_alternate(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
315 return add_backend_internal(odb
, backend
, priority
, 1);
318 static int add_default_backends(git_odb
*db
, const char *objects_dir
, int as_alternates
)
320 git_odb_backend
*loose
, *packed
;
323 /* add the loose object backend */
324 error
= git_odb_backend_loose(&loose
, objects_dir
);
325 if (error
< GIT_SUCCESS
)
328 error
= add_backend_internal(db
, loose
, GIT_LOOSE_PRIORITY
, as_alternates
);
329 if (error
< GIT_SUCCESS
)
330 return git__rethrow(error
, "Failed to add backend");
332 /* add the packed file backend */
333 error
= git_odb_backend_pack(&packed
, objects_dir
);
334 if (error
< GIT_SUCCESS
)
337 error
= add_backend_internal(db
, packed
, GIT_PACKED_PRIORITY
, as_alternates
);
338 if (error
< GIT_SUCCESS
)
339 return git__rethrow(error
, "Failed to add backend");
344 static int load_alternates(git_odb
*odb
, const char *objects_dir
)
346 char alternates_path
[GIT_PATH_MAX
];
347 char *buffer
, *alternate
;
349 git_fbuffer alternates_buf
= GIT_FBUFFER_INIT
;
352 git_path_join(alternates_path
, objects_dir
, GIT_ALTERNATES_FILE
);
354 if (git_futils_exists(alternates_path
) < GIT_SUCCESS
)
357 if (git_futils_readbuffer(&alternates_buf
, alternates_path
) < GIT_SUCCESS
)
358 return git__throw(GIT_EOSERR
, "Failed to add backend. Can't read alternates");
360 buffer
= (char *)alternates_buf
.data
;
363 /* add each alternate as a new backend; one alternate per line */
364 while ((alternate
= git__strtok(&buffer
, "\r\n")) != NULL
) {
365 char full_path
[GIT_PATH_MAX
];
367 if (*alternate
== '\0' || *alternate
== '#')
370 /* relative path: build based on the current `objects` folder */
371 if (*alternate
== '.') {
372 git_path_join(full_path
, objects_dir
, alternate
);
373 alternate
= full_path
;
376 if ((error
= add_default_backends(odb
, alternate
, 1)) < GIT_SUCCESS
)
380 git_futils_freebuffer(&alternates_buf
);
381 if (error
< GIT_SUCCESS
)
382 return git__rethrow(error
, "Failed to load alternates");
386 int git_odb_open(git_odb
**out
, const char *objects_dir
)
391 assert(out
&& objects_dir
);
395 if ((error
= git_odb_new(&db
)) < 0)
396 return git__rethrow(error
, "Failed to open ODB");
398 if ((error
= add_default_backends(db
, objects_dir
, 0)) < GIT_SUCCESS
)
401 if ((error
= load_alternates(db
, objects_dir
)) < GIT_SUCCESS
)
409 return error
; /* error already set - pass through */
412 void git_odb_close(git_odb
*db
)
419 for (i
= 0; i
< db
->backends
.length
; ++i
) {
420 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
421 git_odb_backend
*backend
= internal
->backend
;
423 if (backend
->free
) backend
->free(backend
);
429 git_vector_free(&db
->backends
);
430 git_cache_free(&db
->cache
);
434 int git_odb_exists(git_odb
*db
, const git_oid
*id
)
436 git_odb_object
*object
;
442 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
443 git_odb_object_close(object
);
447 for (i
= 0; i
< db
->backends
.length
&& !found
; ++i
) {
448 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
449 git_odb_backend
*b
= internal
->backend
;
451 if (b
->exists
!= NULL
)
452 found
= b
->exists(b
, id
);
458 int git_odb_read_header(size_t *len_p
, git_otype
*type_p
, git_odb
*db
, const git_oid
*id
)
461 int error
= GIT_ENOTFOUND
;
462 git_odb_object
*object
;
466 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
467 *len_p
= object
->raw
.len
;
468 *type_p
= object
->raw
.type
;
469 git_odb_object_close(object
);
473 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
474 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
475 git_odb_backend
*b
= internal
->backend
;
477 if (b
->read_header
!= NULL
)
478 error
= b
->read_header(len_p
, type_p
, b
, id
);
481 if (error
== GIT_EPASSTHROUGH
)
485 * no backend could read only the header.
486 * try reading the whole object and freeing the contents
489 if ((error
= git_odb_read(&object
, db
, id
)) < GIT_SUCCESS
)
490 return error
; /* error already set - pass through */
492 *len_p
= object
->raw
.len
;
493 *type_p
= object
->raw
.type
;
494 git_odb_object_close(object
);
500 int git_odb_read(git_odb_object
**out
, git_odb
*db
, const git_oid
*id
)
503 int error
= GIT_ENOTFOUND
;
506 assert(out
&& db
&& id
);
508 *out
= git_cache_get(&db
->cache
, id
);
512 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
513 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
514 git_odb_backend
*b
= internal
->backend
;
517 error
= b
->read(&raw
.data
, &raw
.len
, &raw
.type
, b
, id
);
520 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
) {
521 *out
= git_cache_try_store(&db
->cache
, new_odb_object(id
, &raw
));
525 return git__rethrow(error
, "Failed to read object");
528 int git_odb_read_prefix(git_odb_object
**out
, git_odb
*db
, const git_oid
*short_id
, unsigned int len
)
531 int error
= GIT_ENOTFOUND
;
538 if (len
< GIT_OID_MINPREFIXLEN
)
539 return git__throw(GIT_EAMBIGUOUSOIDPREFIX
, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN
);
541 if (len
> GIT_OID_HEXSZ
)
544 if (len
== GIT_OID_HEXSZ
) {
545 *out
= git_cache_get(&db
->cache
, short_id
);
550 for (i
= 0; i
< db
->backends
.length
&& found
< 2; ++i
) {
551 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
552 git_odb_backend
*b
= internal
->backend
;
554 if (b
->read
!= NULL
) {
555 error
= b
->read_prefix(&full_oid
, &raw
.data
, &raw
.len
, &raw
.type
, b
, short_id
, len
);
561 case GIT_EPASSTHROUGH
:
563 case GIT_EAMBIGUOUSOIDPREFIX
:
564 return git__rethrow(error
, "Failed to read object. Ambiguous sha1 prefix");
566 return git__rethrow(error
, "Failed to read object");
572 *out
= git_cache_try_store(&db
->cache
, new_odb_object(&full_oid
, &raw
));
573 } else if (found
> 1) {
574 return git__throw(GIT_EAMBIGUOUSOIDPREFIX
, "Failed to read object. Ambiguous sha1 prefix");
576 return git__throw(GIT_ENOTFOUND
, "Failed to read object. Object not found");
582 int git_odb_write(git_oid
*oid
, git_odb
*db
, const void *data
, size_t len
, git_otype type
)
585 int error
= GIT_ERROR
;
586 git_odb_stream
*stream
;
590 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
591 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
592 git_odb_backend
*b
= internal
->backend
;
594 /* we don't write in alternates! */
595 if (internal
->is_alternate
)
598 if (b
->write
!= NULL
)
599 error
= b
->write(oid
, b
, data
, len
, type
);
602 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
605 /* if no backends were able to write the object directly, we try a streaming
606 * write to the backends; just write the whole object into the stream in one
609 if ((error
= git_odb_open_wstream(&stream
, db
, len
, type
)) == GIT_SUCCESS
) {
610 stream
->write(stream
, data
, len
);
611 error
= stream
->finalize_write(oid
, stream
);
612 stream
->free(stream
);
616 return git__rethrow(error
, "Failed to write object");
619 int git_odb_open_wstream(git_odb_stream
**stream
, git_odb
*db
, size_t size
, git_otype type
)
622 int error
= GIT_ERROR
;
624 assert(stream
&& db
);
626 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
627 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
628 git_odb_backend
*b
= internal
->backend
;
630 /* we don't write in alternates! */
631 if (internal
->is_alternate
)
634 if (b
->writestream
!= NULL
)
635 error
= b
->writestream(stream
, b
, size
, type
);
636 else if (b
->write
!= NULL
)
637 error
= init_fake_wstream(stream
, b
, size
, type
);
640 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
643 return git__rethrow(error
, "Failed to open write stream");
646 int git_odb_open_rstream(git_odb_stream
**stream
, git_odb
*db
, const git_oid
*oid
)
649 int error
= GIT_ERROR
;
651 assert(stream
&& db
);
653 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
654 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
655 git_odb_backend
*b
= internal
->backend
;
657 if (b
->readstream
!= NULL
)
658 error
= b
->readstream(stream
, b
, oid
);
661 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
664 return git__rethrow(error
, "Failed to open read stream");