]>
git.proxmox.com Git - libgit2.git/blob - src/odb.c
2 * Copyright (C) 2009-2012 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
);
35 assert(len
> 0 && len
<= (int)n
);
39 int git_odb__hashobj(git_oid
*id
, git_rawobj
*obj
)
47 if (!git_object_typeisloose(obj
->type
))
49 if (!obj
->data
&& obj
->len
!= 0)
52 hdrlen
= format_object_header(header
, sizeof(header
), obj
->len
, obj
->type
);
56 vec
[1].data
= obj
->data
;
57 vec
[1].len
= obj
->len
;
59 git_hash_vec(id
, vec
, 2);
65 static git_odb_object
*new_odb_object(const git_oid
*oid
, git_rawobj
*source
)
67 git_odb_object
*object
= git__malloc(sizeof(git_odb_object
));
68 memset(object
, 0x0, sizeof(git_odb_object
));
70 git_oid_cpy(&object
->cached
.oid
, oid
);
71 memcpy(&object
->raw
, source
, sizeof(git_rawobj
));
76 static void free_odb_object(void *o
)
78 git_odb_object
*object
= (git_odb_object
*)o
;
81 git__free(object
->raw
.data
);
86 const git_oid
*git_odb_object_id(git_odb_object
*object
)
88 return &object
->cached
.oid
;
91 const void *git_odb_object_data(git_odb_object
*object
)
93 return object
->raw
.data
;
96 size_t git_odb_object_size(git_odb_object
*object
)
98 return object
->raw
.len
;
101 git_otype
git_odb_object_type(git_odb_object
*object
)
103 return object
->raw
.type
;
106 void git_odb_object_free(git_odb_object
*object
)
108 git_cached_obj_decref((git_cached_obj
*)object
, &free_odb_object
);
111 int git_odb__hashfd(git_oid
*out
, git_file fd
, size_t size
, git_otype type
)
114 char hdr
[64], buffer
[2048];
117 hdr_len
= format_object_header(hdr
, sizeof(hdr
), size
, type
);
119 ctx
= git_hash_new_ctx();
121 git_hash_update(ctx
, hdr
, hdr_len
);
124 ssize_t read_len
= read(fd
, buffer
, sizeof(buffer
));
127 git_hash_free_ctx(ctx
);
128 giterr_set(GITERR_OS
, "Error reading file");
132 git_hash_update(ctx
, buffer
, read_len
);
136 git_hash_final(out
, ctx
);
137 git_hash_free_ctx(ctx
);
142 int git_odb__hashlink(git_oid
*out
, const char *path
)
148 if (git_path_lstat(path
, &st
) < 0)
153 if (!git__is_sizet(size
)) {
154 giterr_set(GITERR_OS
, "File size overflow for 32-bit systems");
158 if (S_ISLNK(st
.st_mode
)) {
162 link_data
= git__malloc((size_t)size
);
163 GITERR_CHECK_ALLOC(link_data
);
165 read_len
= p_readlink(path
, link_data
, (size_t)(size
+ 1));
166 if (read_len
!= (ssize_t
)size
) {
167 giterr_set(GITERR_OS
, "Failed to read symlink data for '%s'", path
);
171 result
= git_odb_hash(out
, link_data
, (size_t)size
, GIT_OBJ_BLOB
);
172 git__free(link_data
);
174 int fd
= git_futils_open_ro(path
);
177 result
= git_odb__hashfd(out
, fd
, (size_t)size
, GIT_OBJ_BLOB
);
184 int git_odb_hashfile(git_oid
*out
, const char *path
, git_otype type
)
187 int result
, fd
= git_futils_open_ro(path
);
191 if ((size
= git_futils_filesize(fd
)) < 0 || !git__is_sizet(size
)) {
192 giterr_set(GITERR_OS
, "File size overflow for 32-bit systems");
197 result
= git_odb__hashfd(out
, fd
, (size_t)size
, type
);
202 int git_odb_hash(git_oid
*id
, const void *data
, size_t len
, git_otype type
)
208 raw
.data
= (void *)data
;
212 return git_odb__hashobj(id
, &raw
);
220 git_odb_stream stream
;
222 size_t size
, written
;
226 static int fake_wstream__fwrite(git_oid
*oid
, git_odb_stream
*_stream
)
228 fake_wstream
*stream
= (fake_wstream
*)_stream
;
229 return _stream
->backend
->write(oid
, _stream
->backend
, stream
->buffer
, stream
->size
, stream
->type
);
232 static int fake_wstream__write(git_odb_stream
*_stream
, const char *data
, size_t len
)
234 fake_wstream
*stream
= (fake_wstream
*)_stream
;
236 if (stream
->written
+ len
> stream
->size
)
239 memcpy(stream
->buffer
+ stream
->written
, data
, len
);
240 stream
->written
+= len
;
244 static void fake_wstream__free(git_odb_stream
*_stream
)
246 fake_wstream
*stream
= (fake_wstream
*)_stream
;
248 git__free(stream
->buffer
);
252 static int init_fake_wstream(git_odb_stream
**stream_p
, git_odb_backend
*backend
, size_t size
, git_otype type
)
254 fake_wstream
*stream
;
256 stream
= git__calloc(1, sizeof(fake_wstream
));
257 GITERR_CHECK_ALLOC(stream
);
261 stream
->buffer
= git__malloc(size
);
262 if (stream
->buffer
== NULL
) {
267 stream
->stream
.backend
= backend
;
268 stream
->stream
.read
= NULL
; /* read only */
269 stream
->stream
.write
= &fake_wstream__write
;
270 stream
->stream
.finalize_write
= &fake_wstream__fwrite
;
271 stream
->stream
.free
= &fake_wstream__free
;
272 stream
->stream
.mode
= GIT_STREAM_WRONLY
;
274 *stream_p
= (git_odb_stream
*)stream
;
278 /***********************************************************
280 * OBJECT DATABASE PUBLIC API
282 * Public calls for the ODB functionality
284 ***********************************************************/
286 static int backend_sort_cmp(const void *a
, const void *b
)
288 const backend_internal
*backend_a
= (const backend_internal
*)(a
);
289 const backend_internal
*backend_b
= (const backend_internal
*)(b
);
291 if (backend_a
->is_alternate
== backend_b
->is_alternate
)
292 return (backend_b
->priority
- backend_a
->priority
);
294 return backend_a
->is_alternate
? 1 : -1;
297 int git_odb_new(git_odb
**out
)
299 git_odb
*db
= git__calloc(1, sizeof(*db
));
300 GITERR_CHECK_ALLOC(db
);
302 if (git_cache_init(&db
->cache
, GIT_DEFAULT_CACHE_SIZE
, &free_odb_object
) < 0 ||
303 git_vector_init(&db
->backends
, 4, backend_sort_cmp
) < 0)
310 GIT_REFCOUNT_INC(db
);
314 static int add_backend_internal(git_odb
*odb
, git_odb_backend
*backend
, int priority
, int is_alternate
)
316 backend_internal
*internal
;
318 assert(odb
&& backend
);
320 /* Check if the backend is already owned by another ODB */
321 assert(!backend
->odb
|| backend
->odb
== odb
);
323 internal
= git__malloc(sizeof(backend_internal
));
324 GITERR_CHECK_ALLOC(internal
);
326 internal
->backend
= backend
;
327 internal
->priority
= priority
;
328 internal
->is_alternate
= is_alternate
;
330 if (git_vector_insert(&odb
->backends
, internal
) < 0) {
335 git_vector_sort(&odb
->backends
);
336 internal
->backend
->odb
= odb
;
340 int git_odb_add_backend(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
342 return add_backend_internal(odb
, backend
, priority
, 0);
345 int git_odb_add_alternate(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
347 return add_backend_internal(odb
, backend
, priority
, 1);
350 static int add_default_backends(git_odb
*db
, const char *objects_dir
, int as_alternates
)
352 git_odb_backend
*loose
, *packed
;
354 /* add the loose object backend */
355 if (git_odb_backend_loose(&loose
, objects_dir
, -1, 0) < 0 ||
356 add_backend_internal(db
, loose
, GIT_LOOSE_PRIORITY
, as_alternates
) < 0)
359 /* add the packed file backend */
360 if (git_odb_backend_pack(&packed
, objects_dir
) < 0 ||
361 add_backend_internal(db
, packed
, GIT_PACKED_PRIORITY
, as_alternates
) < 0)
367 static int load_alternates(git_odb
*odb
, const char *objects_dir
)
369 git_buf alternates_path
= GIT_BUF_INIT
;
370 git_buf alternates_buf
= GIT_BUF_INIT
;
372 const char *alternate
;
375 if (git_buf_joinpath(&alternates_path
, objects_dir
, GIT_ALTERNATES_FILE
) < 0)
378 if (git_path_exists(alternates_path
.ptr
) == false) {
379 git_buf_free(&alternates_path
);
383 if (git_futils_readbuffer(&alternates_buf
, alternates_path
.ptr
) < 0) {
384 git_buf_free(&alternates_path
);
388 buffer
= (char *)alternates_buf
.ptr
;
390 /* add each alternate as a new backend; one alternate per line */
391 while ((alternate
= git__strtok(&buffer
, "\r\n")) != NULL
) {
392 if (*alternate
== '\0' || *alternate
== '#')
395 /* relative path: build based on the current `objects` folder */
396 if (*alternate
== '.') {
397 if ((result
= git_buf_joinpath(&alternates_path
, objects_dir
, alternate
)) < 0)
399 alternate
= git_buf_cstr(&alternates_path
);
402 if ((result
= add_default_backends(odb
, alternate
, 1)) < 0)
406 git_buf_free(&alternates_path
);
407 git_buf_free(&alternates_buf
);
412 int git_odb_open(git_odb
**out
, const char *objects_dir
)
416 assert(out
&& objects_dir
);
420 if (git_odb_new(&db
) < 0)
423 if (add_default_backends(db
, objects_dir
, 0) < 0 ||
424 load_alternates(db
, objects_dir
) < 0)
434 static void odb_free(git_odb
*db
)
438 for (i
= 0; i
< db
->backends
.length
; ++i
) {
439 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
440 git_odb_backend
*backend
= internal
->backend
;
442 if (backend
->free
) backend
->free(backend
);
443 else git__free(backend
);
448 git_vector_free(&db
->backends
);
449 git_cache_free(&db
->cache
);
453 void git_odb_free(git_odb
*db
)
458 GIT_REFCOUNT_DEC(db
, odb_free
);
461 int git_odb_exists(git_odb
*db
, const git_oid
*id
)
463 git_odb_object
*object
;
469 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
470 git_odb_object_free(object
);
474 for (i
= 0; i
< db
->backends
.length
&& !found
; ++i
) {
475 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
476 git_odb_backend
*b
= internal
->backend
;
478 if (b
->exists
!= NULL
)
479 found
= b
->exists(b
, id
);
485 int git_odb_read_header(size_t *len_p
, git_otype
*type_p
, git_odb
*db
, const git_oid
*id
)
488 int error
= GIT_ENOTFOUND
;
489 git_odb_object
*object
;
493 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
494 *len_p
= object
->raw
.len
;
495 *type_p
= object
->raw
.type
;
496 git_odb_object_free(object
);
500 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
501 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
502 git_odb_backend
*b
= internal
->backend
;
504 if (b
->read_header
!= NULL
)
505 error
= b
->read_header(len_p
, type_p
, b
, id
);
508 if (!error
|| error
== GIT_EPASSTHROUGH
)
512 * no backend could read only the header.
513 * try reading the whole object and freeing the contents
515 if ((error
= git_odb_read(&object
, db
, id
)) < 0)
516 return error
; /* error already set - pass along */
518 *len_p
= object
->raw
.len
;
519 *type_p
= object
->raw
.type
;
520 git_odb_object_free(object
);
524 int git_odb_read(git_odb_object
**out
, git_odb
*db
, const git_oid
*id
)
527 int error
= GIT_ENOTFOUND
;
530 assert(out
&& db
&& id
);
532 *out
= git_cache_get(&db
->cache
, id
);
536 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
537 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
538 git_odb_backend
*b
= internal
->backend
;
541 error
= b
->read(&raw
.data
, &raw
.len
, &raw
.type
, b
, id
);
544 /* TODO: If no backends are configured, this returns GIT_ENOTFOUND but
545 * will never have called giterr_set().
548 if (error
&& error
!= GIT_EPASSTHROUGH
)
551 *out
= git_cache_try_store(&db
->cache
, new_odb_object(id
, &raw
));
555 int git_odb_read_prefix(
556 git_odb_object
**out
, git_odb
*db
, const git_oid
*short_id
, unsigned int len
)
559 int error
= GIT_ENOTFOUND
;
566 if (len
< GIT_OID_MINPREFIXLEN
)
567 return git_odb__error_ambiguous("prefix length too short");
569 if (len
> GIT_OID_HEXSZ
)
572 if (len
== GIT_OID_HEXSZ
) {
573 *out
= git_cache_get(&db
->cache
, short_id
);
578 for (i
= 0; i
< db
->backends
.length
&& found
< 2; ++i
) {
579 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
580 git_odb_backend
*b
= internal
->backend
;
582 if (b
->read
!= NULL
) {
583 error
= b
->read_prefix(&full_oid
, &raw
.data
, &raw
.len
, &raw
.type
, b
, short_id
, len
);
586 else if (error
!= GIT_ENOTFOUND
&& error
!= GIT_EPASSTHROUGH
)
592 return git_odb__error_notfound("no match for prefix");
594 return git_odb__error_ambiguous("multiple matches for prefix");
596 *out
= git_cache_try_store(&db
->cache
, new_odb_object(&full_oid
, &raw
));
601 git_oid
*oid
, git_odb
*db
, const void *data
, size_t len
, git_otype type
)
604 int error
= GIT_ERROR
;
605 git_odb_stream
*stream
;
609 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
610 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
611 git_odb_backend
*b
= internal
->backend
;
613 /* we don't write in alternates! */
614 if (internal
->is_alternate
)
617 if (b
->write
!= NULL
)
618 error
= b
->write(oid
, b
, data
, len
, type
);
621 if (!error
|| error
== GIT_EPASSTHROUGH
)
624 /* if no backends were able to write the object directly, we try a streaming
625 * write to the backends; just write the whole object into the stream in one
628 if ((error
= git_odb_open_wstream(&stream
, db
, len
, type
)) != 0)
631 stream
->write(stream
, data
, len
);
632 error
= stream
->finalize_write(oid
, stream
);
633 stream
->free(stream
);
638 int git_odb_open_wstream(
639 git_odb_stream
**stream
, git_odb
*db
, size_t size
, git_otype type
)
642 int error
= GIT_ERROR
;
644 assert(stream
&& db
);
646 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
647 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
648 git_odb_backend
*b
= internal
->backend
;
650 /* we don't write in alternates! */
651 if (internal
->is_alternate
)
654 if (b
->writestream
!= NULL
)
655 error
= b
->writestream(stream
, b
, size
, type
);
656 else if (b
->write
!= NULL
)
657 error
= init_fake_wstream(stream
, b
, size
, type
);
660 if (error
== GIT_EPASSTHROUGH
)
666 int git_odb_open_rstream(git_odb_stream
**stream
, git_odb
*db
, const git_oid
*oid
)
669 int error
= GIT_ERROR
;
671 assert(stream
&& db
);
673 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
674 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
675 git_odb_backend
*b
= internal
->backend
;
677 if (b
->readstream
!= NULL
)
678 error
= b
->readstream(stream
, b
, oid
);
681 if (error
== GIT_EPASSTHROUGH
)
687 int git_odb__error_notfound(const char *message
)
689 giterr_set(GITERR_ODB
, "Object not found - %s", message
);
690 return GIT_ENOTFOUND
;
693 int git_odb__error_ambiguous(const char *message
)
695 giterr_set(GITERR_ODB
, "Ambiguous SHA1 prefix - %s", message
);
696 return GIT_EAMBIGUOUS
;