]>
git.proxmox.com Git - libgit2.git/blob - src/odb.c
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "git2/zlib.h"
28 #include "git2/object.h"
32 #include "delta-apply.h"
34 #include "git2/odb_backend.h"
36 #define GIT_ALTERNATES_FILE "info/alternates"
38 /* TODO: is this correct? */
39 #define GIT_LOOSE_PRIORITY 2
40 #define GIT_PACKED_PRIORITY 1
44 git_odb_backend
*backend
;
49 static int format_object_header(char *hdr
, size_t n
, git_rawobj
*obj
)
51 const char *type_str
= git_object_type2string(obj
->type
);
52 int len
= snprintf(hdr
, n
, "%s %"PRIuZ
, type_str
, obj
->len
);
54 assert(len
> 0); /* otherwise snprintf() is broken */
55 assert(((size_t) len
) < n
); /* otherwise the caller is broken! */
57 if (len
< 0 || ((size_t) len
) >= n
)
58 return git__throw(GIT_ERROR
, "Cannot format object header. Length is out of bounds");
62 int git_odb__hash_obj(git_oid
*id
, char *hdr
, size_t n
, int *len
, git_rawobj
*obj
)
67 assert(id
&& hdr
&& len
&& obj
);
69 if (!git_object_typeisloose(obj
->type
))
70 return git__throw(GIT_ERROR
, "Failed to hash object. Wrong object type");
72 if (!obj
->data
&& obj
->len
!= 0)
73 return git__throw(GIT_ERROR
, "Failed to hash object. No data given");
75 if ((hdrlen
= format_object_header(hdr
, n
, obj
)) < 0)
76 return git__rethrow(hdrlen
, "Failed to hash object");
82 vec
[1].data
= obj
->data
;
83 vec
[1].len
= obj
->len
;
85 git_hash_vec(id
, vec
, 2);
91 static git_odb_object
*new_odb_object(const git_oid
*oid
, git_rawobj
*source
)
93 git_odb_object
*object
= git__malloc(sizeof(git_odb_object
));
94 memset(object
, 0x0, sizeof(git_odb_object
));
96 git_oid_cpy(&object
->cached
.oid
, oid
);
97 memcpy(&object
->raw
, source
, sizeof(git_rawobj
));
102 static void free_odb_object(void *o
)
104 git_odb_object
*object
= (git_odb_object
*)o
;
106 if (object
!= NULL
) {
107 free(object
->raw
.data
);
112 const git_oid
*git_odb_object_id(git_odb_object
*object
)
114 return &object
->cached
.oid
;
117 const void *git_odb_object_data(git_odb_object
*object
)
119 return object
->raw
.data
;
122 size_t git_odb_object_size(git_odb_object
*object
)
124 return object
->raw
.len
;
127 git_otype
git_odb_object_type(git_odb_object
*object
)
129 return object
->raw
.type
;
132 void git_odb_object_close(git_odb_object
*object
)
134 git_cached_obj_decref((git_cached_obj
*)object
, &free_odb_object
);
137 int git_odb_hash(git_oid
*id
, const void *data
, size_t len
, git_otype type
)
145 raw
.data
= (void *)data
;
149 return git_odb__hash_obj(id
, hdr
, sizeof(hdr
), &hdrlen
, &raw
);
157 git_odb_stream stream
;
159 size_t size
, written
;
163 static int fake_wstream__fwrite(git_oid
*oid
, git_odb_stream
*_stream
)
165 fake_wstream
*stream
= (fake_wstream
*)_stream
;
166 return _stream
->backend
->write(oid
, _stream
->backend
, stream
->buffer
, stream
->size
, stream
->type
);
169 static int fake_wstream__write(git_odb_stream
*_stream
, const char *data
, size_t len
)
171 fake_wstream
*stream
= (fake_wstream
*)_stream
;
173 if (stream
->written
+ len
> stream
->size
)
176 memcpy(stream
->buffer
+ stream
->written
, data
, len
);
177 stream
->written
+= len
;
181 static void fake_wstream__free(git_odb_stream
*_stream
)
183 fake_wstream
*stream
= (fake_wstream
*)_stream
;
185 free(stream
->buffer
);
189 static int init_fake_wstream(git_odb_stream
**stream_p
, git_odb_backend
*backend
, size_t size
, git_otype type
)
191 fake_wstream
*stream
;
193 stream
= git__calloc(1, sizeof(fake_wstream
));
199 stream
->buffer
= git__malloc(size
);
200 if (stream
->buffer
== NULL
) {
205 stream
->stream
.backend
= backend
;
206 stream
->stream
.read
= NULL
; /* read only */
207 stream
->stream
.write
= &fake_wstream__write
;
208 stream
->stream
.finalize_write
= &fake_wstream__fwrite
;
209 stream
->stream
.free
= &fake_wstream__free
;
210 stream
->stream
.mode
= GIT_STREAM_WRONLY
;
212 *stream_p
= (git_odb_stream
*)stream
;
216 /***********************************************************
218 * OBJECT DATABASE PUBLIC API
220 * Public calls for the ODB functionality
222 ***********************************************************/
224 static int backend_sort_cmp(const void *a
, const void *b
)
226 const backend_internal
*backend_a
= *(const backend_internal
**)(a
);
227 const backend_internal
*backend_b
= *(const backend_internal
**)(b
);
229 if (backend_a
->is_alternate
== backend_b
->is_alternate
)
230 return (backend_b
->priority
- backend_a
->priority
);
232 return backend_a
->is_alternate
? 1 : -1;
235 int git_odb_new(git_odb
**out
)
239 git_odb
*db
= git__calloc(1, sizeof(*db
));
243 error
= git_cache_init(&db
->cache
, GIT_DEFAULT_CACHE_SIZE
, &free_odb_object
);
244 if (error
< GIT_SUCCESS
) {
246 return git__rethrow(error
, "Failed to create object database");
249 if ((error
= git_vector_init(&db
->backends
, 4, backend_sort_cmp
)) < GIT_SUCCESS
) {
251 return git__rethrow(error
, "Failed to create object database");
258 static int add_backend_internal(git_odb
*odb
, git_odb_backend
*backend
, int priority
, int is_alternate
)
260 backend_internal
*internal
;
262 assert(odb
&& backend
);
264 if (backend
->odb
!= NULL
&& backend
->odb
!= odb
)
265 return git__throw(GIT_EBUSY
, "The backend is already owned by another ODB");
267 internal
= git__malloc(sizeof(backend_internal
));
268 if (internal
== NULL
)
271 internal
->backend
= backend
;
272 internal
->priority
= priority
;
273 internal
->is_alternate
= is_alternate
;
275 if (git_vector_insert(&odb
->backends
, internal
) < 0) {
280 git_vector_sort(&odb
->backends
);
281 internal
->backend
->odb
= odb
;
285 int git_odb_add_backend(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
287 return add_backend_internal(odb
, backend
, priority
, 0);
290 int git_odb_add_alternate(git_odb
*odb
, git_odb_backend
*backend
, int priority
)
292 return add_backend_internal(odb
, backend
, priority
, 1);
295 static int add_default_backends(git_odb
*db
, const char *objects_dir
, int as_alternates
)
297 git_odb_backend
*loose
, *packed
;
300 /* add the loose object backend */
301 error
= git_odb_backend_loose(&loose
, objects_dir
);
302 if (error
< GIT_SUCCESS
)
305 error
= add_backend_internal(db
, loose
, GIT_LOOSE_PRIORITY
, as_alternates
);
306 if (error
< GIT_SUCCESS
)
307 return git__rethrow(error
, "Failed to add backend");
309 /* add the packed file backend */
310 error
= git_odb_backend_pack(&packed
, objects_dir
);
311 if (error
< GIT_SUCCESS
)
314 error
= add_backend_internal(db
, packed
, GIT_PACKED_PRIORITY
, as_alternates
);
315 if (error
< GIT_SUCCESS
)
316 return git__rethrow(error
, "Failed to add backend");
321 static int load_alternates(git_odb
*odb
, const char *objects_dir
)
323 char alternates_path
[GIT_PATH_MAX
];
324 char *buffer
, *alternate
;
326 gitfo_buf alternates_buf
= GITFO_BUF_INIT
;
329 git__joinpath(alternates_path
, objects_dir
, GIT_ALTERNATES_FILE
);
331 if (gitfo_exists(alternates_path
) < GIT_SUCCESS
)
334 if (gitfo_read_file(&alternates_buf
, alternates_path
) < GIT_SUCCESS
)
335 return git__throw(GIT_EOSERR
, "Failed to add backend. Can't read alternates");
337 buffer
= (char *)alternates_buf
.data
;
340 /* add each alternate as a new backend; one alternate per line */
341 while ((alternate
= git__strtok(&buffer
, "\r\n")) != NULL
) {
342 char full_path
[GIT_PATH_MAX
];
344 if (*alternate
== '\0' || *alternate
== '#')
347 /* relative path: build based on the current `objects` folder */
348 if (*alternate
== '.') {
349 git__joinpath(full_path
, objects_dir
, alternate
);
350 alternate
= full_path
;
353 if ((error
= add_default_backends(odb
, alternate
, 1)) < GIT_SUCCESS
)
357 gitfo_free_buf(&alternates_buf
);
358 if (error
< GIT_SUCCESS
)
359 return git__rethrow(error
, "Failed to load alternates");
363 int git_odb_open(git_odb
**out
, const char *objects_dir
)
368 assert(out
&& objects_dir
);
372 if ((error
= git_odb_new(&db
)) < 0)
373 return git__rethrow(error
, "Failed to open ODB");
375 if ((error
= add_default_backends(db
, objects_dir
, 0)) < GIT_SUCCESS
)
378 if ((error
= load_alternates(db
, objects_dir
)) < GIT_SUCCESS
)
386 return error
; /* error already set - pass through */
389 void git_odb_close(git_odb
*db
)
396 for (i
= 0; i
< db
->backends
.length
; ++i
) {
397 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
398 git_odb_backend
*backend
= internal
->backend
;
400 if (backend
->free
) backend
->free(backend
);
406 git_vector_free(&db
->backends
);
407 git_cache_free(&db
->cache
);
411 int git_odb_exists(git_odb
*db
, const git_oid
*id
)
413 git_odb_object
*object
;
419 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
420 git_odb_object_close(object
);
424 for (i
= 0; i
< db
->backends
.length
&& !found
; ++i
) {
425 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
426 git_odb_backend
*b
= internal
->backend
;
428 if (b
->exists
!= NULL
)
429 found
= b
->exists(b
, id
);
435 int git_odb_read_header(size_t *len_p
, git_otype
*type_p
, git_odb
*db
, const git_oid
*id
)
438 int error
= GIT_ENOTFOUND
;
439 git_odb_object
*object
;
443 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
444 *len_p
= object
->raw
.len
;
445 *type_p
= object
->raw
.type
;
446 git_odb_object_close(object
);
450 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
451 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
452 git_odb_backend
*b
= internal
->backend
;
454 if (b
->read_header
!= NULL
)
455 error
= b
->read_header(len_p
, type_p
, b
, id
);
458 if (error
== GIT_EPASSTHROUGH
)
462 * no backend could read only the header.
463 * try reading the whole object and freeing the contents
466 if ((error
= git_odb_read(&object
, db
, id
)) < GIT_SUCCESS
)
467 return error
; /* error already set - pass through */
469 *len_p
= object
->raw
.len
;
470 *type_p
= object
->raw
.type
;
471 git_odb_object_close(object
);
477 int git_odb_read(git_odb_object
**out
, git_odb
*db
, const git_oid
*id
)
480 int error
= GIT_ENOTFOUND
;
483 assert(out
&& db
&& id
);
485 *out
= git_cache_get(&db
->cache
, id
);
489 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
490 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
491 git_odb_backend
*b
= internal
->backend
;
494 error
= b
->read(&raw
.data
, &raw
.len
, &raw
.type
, b
, id
);
497 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
) {
498 *out
= git_cache_try_store(&db
->cache
, new_odb_object(id
, &raw
));
502 return git__rethrow(error
, "Failed to read object");
505 int git_odb_read_prefix(git_odb_object
**out
, git_odb
*db
, const git_oid
*short_id
, unsigned int len
)
508 int error
= GIT_ENOTFOUND
;
515 if (len
< GIT_OID_MINPREFIXLEN
)
516 return git__throw(GIT_EAMBIGUOUSOIDPREFIX
, "Failed to lookup object. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN
);
518 if (len
> GIT_OID_HEXSZ
)
521 if (len
== GIT_OID_HEXSZ
) {
522 *out
= git_cache_get(&db
->cache
, short_id
);
527 for (i
= 0; i
< db
->backends
.length
&& found
< 2; ++i
) {
528 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
529 git_odb_backend
*b
= internal
->backend
;
531 if (b
->read
!= NULL
) {
532 error
= b
->read_prefix(&full_oid
, &raw
.data
, &raw
.len
, &raw
.type
, b
, short_id
, len
);
538 case GIT_EPASSTHROUGH
:
540 case GIT_EAMBIGUOUSOIDPREFIX
:
541 return git__rethrow(error
, "Failed to read object. Ambiguous sha1 prefix");
543 return git__rethrow(error
, "Failed to read object");
549 *out
= git_cache_try_store(&db
->cache
, new_odb_object(&full_oid
, &raw
));
550 } else if (found
> 1) {
551 return git__throw(GIT_EAMBIGUOUSOIDPREFIX
, "Failed to read object. Ambiguous sha1 prefix");
553 return git__throw(GIT_ENOTFOUND
, "Failed to read object. Object not found");
559 int git_odb_write(git_oid
*oid
, git_odb
*db
, const void *data
, size_t len
, git_otype type
)
562 int error
= GIT_ERROR
;
563 git_odb_stream
*stream
;
567 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
568 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
569 git_odb_backend
*b
= internal
->backend
;
571 /* we don't write in alternates! */
572 if (internal
->is_alternate
)
575 if (b
->write
!= NULL
)
576 error
= b
->write(oid
, b
, data
, len
, type
);
579 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
582 /* if no backends were able to write the object directly, we try a streaming
583 * write to the backends; just write the whole object into the stream in one
586 if ((error
= git_odb_open_wstream(&stream
, db
, len
, type
)) == GIT_SUCCESS
) {
587 stream
->write(stream
, data
, len
);
588 error
= stream
->finalize_write(oid
, stream
);
589 stream
->free(stream
);
593 return git__rethrow(error
, "Failed to write object");
596 int git_odb_open_wstream(git_odb_stream
**stream
, git_odb
*db
, size_t size
, git_otype type
)
599 int error
= GIT_ERROR
;
601 assert(stream
&& db
);
603 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
604 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
605 git_odb_backend
*b
= internal
->backend
;
607 /* we don't write in alternates! */
608 if (internal
->is_alternate
)
611 if (b
->writestream
!= NULL
)
612 error
= b
->writestream(stream
, b
, size
, type
);
613 else if (b
->write
!= NULL
)
614 error
= init_fake_wstream(stream
, b
, size
, type
);
617 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
620 return git__rethrow(error
, "Failed to open write stream");
623 int git_odb_open_rstream(git_odb_stream
**stream
, git_odb
*db
, const git_oid
*oid
)
626 int error
= GIT_ERROR
;
628 assert(stream
&& db
);
630 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
631 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
632 git_odb_backend
*b
= internal
->backend
;
634 if (b
->readstream
!= NULL
)
635 error
= b
->readstream(stream
, b
, oid
);
638 if (error
== GIT_EPASSTHROUGH
|| error
== GIT_SUCCESS
)
641 return git__rethrow(error
, "Failed to open read stream");