]>
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 alternate
[GIT_PATH_MAX
];
327 gitfo_buf alternates_buf
= GITFO_BUF_INIT
;
330 git__joinpath(alternates_path
, objects_dir
, GIT_ALTERNATES_FILE
);
332 if (gitfo_exists(alternates_path
) < GIT_SUCCESS
)
335 if (gitfo_read_file(&alternates_buf
, alternates_path
) < GIT_SUCCESS
)
336 return git__throw(GIT_EOSERR
, "Failed to add backend. Can't read alternates");
338 buffer
= (char *)alternates_buf
.data
;
341 /* add each alternate as a new backend; one alternate per line */
342 while ((error
== GIT_SUCCESS
) && (buffer
= git__strtok(alternate
, buffer
, "\r\n")) != NULL
)
343 error
= add_default_backends(odb
, alternate
, 1);
345 gitfo_free_buf(&alternates_buf
);
346 if (error
< GIT_SUCCESS
)
347 return git__rethrow(error
, "Failed to load alternates");
351 int git_odb_open(git_odb
**out
, const char *objects_dir
)
356 assert(out
&& objects_dir
);
360 if ((error
= git_odb_new(&db
)) < 0)
361 return git__rethrow(error
, "Failed to open ODB");
363 if ((error
= add_default_backends(db
, objects_dir
, 0)) < GIT_SUCCESS
)
366 if ((error
= load_alternates(db
, objects_dir
)) < GIT_SUCCESS
)
374 return error
; /* error already set - pass through */
377 void git_odb_close(git_odb
*db
)
384 for (i
= 0; i
< db
->backends
.length
; ++i
) {
385 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
386 git_odb_backend
*backend
= internal
->backend
;
388 if (backend
->free
) backend
->free(backend
);
394 git_vector_free(&db
->backends
);
395 git_cache_free(&db
->cache
);
399 int git_odb_exists(git_odb
*db
, const git_oid
*id
)
401 git_odb_object
*object
;
407 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
408 git_odb_object_close(object
);
412 for (i
= 0; i
< db
->backends
.length
&& !found
; ++i
) {
413 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
414 git_odb_backend
*b
= internal
->backend
;
416 if (b
->exists
!= NULL
)
417 found
= b
->exists(b
, id
);
423 int git_odb_read_header(size_t *len_p
, git_otype
*type_p
, git_odb
*db
, const git_oid
*id
)
426 int error
= GIT_ENOTFOUND
;
427 git_odb_object
*object
;
431 if ((object
= git_cache_get(&db
->cache
, id
)) != NULL
) {
432 *len_p
= object
->raw
.len
;
433 *type_p
= object
->raw
.type
;
434 git_odb_object_close(object
);
438 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
439 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
440 git_odb_backend
*b
= internal
->backend
;
442 if (b
->read_header
!= NULL
)
443 error
= b
->read_header(len_p
, type_p
, b
, id
);
447 * no backend could read only the header.
448 * try reading the whole object and freeing the contents
451 if ((error
= git_odb_read(&object
, db
, id
)) < GIT_SUCCESS
)
452 return error
; /* error already set - pass through */
454 *len_p
= object
->raw
.len
;
455 *type_p
= object
->raw
.type
;
456 git_odb_object_close(object
);
462 int git_odb_read(git_odb_object
**out
, git_odb
*db
, const git_oid
*id
)
465 int error
= GIT_ENOTFOUND
;
468 assert(out
&& db
&& id
);
470 *out
= git_cache_get(&db
->cache
, id
);
474 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
475 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
476 git_odb_backend
*b
= internal
->backend
;
479 error
= b
->read(&raw
.data
, &raw
.len
, &raw
.type
, b
, id
);
482 if (error
== GIT_SUCCESS
) {
483 *out
= git_cache_try_store(&db
->cache
, new_odb_object(id
, &raw
));
486 if (error
< GIT_SUCCESS
)
487 return git__rethrow(error
, "Failed to read object");
491 int git_odb_write(git_oid
*oid
, git_odb
*db
, const void *data
, size_t len
, git_otype type
)
494 int error
= GIT_ERROR
;
498 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
499 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
500 git_odb_backend
*b
= internal
->backend
;
502 /* we don't write in alternates! */
503 if (internal
->is_alternate
)
506 if (b
->write
!= NULL
)
507 error
= b
->write(oid
, b
, data
, len
, type
);
510 /* if no backends were able to write the object directly, we try a streaming
511 * write to the backends; just write the whole object into the stream in one
513 if (error
< GIT_SUCCESS
) {
514 git_odb_stream
*stream
;
516 if ((error
= git_odb_open_wstream(&stream
, db
, len
, type
)) == GIT_SUCCESS
) {
517 stream
->write(stream
, data
, len
);
518 error
= stream
->finalize_write(oid
, stream
);
519 stream
->free(stream
);
523 return (error
== GIT_SUCCESS
) ? GIT_SUCCESS
:
524 git__rethrow(error
, "Failed to write object");
527 int git_odb_open_wstream(git_odb_stream
**stream
, git_odb
*db
, size_t size
, git_otype type
)
530 int error
= GIT_ERROR
;
532 assert(stream
&& db
);
534 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
535 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
536 git_odb_backend
*b
= internal
->backend
;
538 /* we don't write in alternates! */
539 if (internal
->is_alternate
)
542 if (b
->writestream
!= NULL
)
543 error
= b
->writestream(stream
, b
, size
, type
);
544 else if (b
->write
!= NULL
)
545 error
= init_fake_wstream(stream
, b
, size
, type
);
548 return (error
== GIT_SUCCESS
) ? GIT_SUCCESS
:
549 git__rethrow(error
, "Failed to open write stream");
552 int git_odb_open_rstream(git_odb_stream
**stream
, git_odb
*db
, const git_oid
*oid
)
555 int error
= GIT_ERROR
;
557 assert(stream
&& db
);
559 for (i
= 0; i
< db
->backends
.length
&& error
< 0; ++i
) {
560 backend_internal
*internal
= git_vector_get(&db
->backends
, i
);
561 git_odb_backend
*b
= internal
->backend
;
563 if (b
->readstream
!= NULL
)
564 error
= b
->readstream(stream
, b
, oid
);
567 return (error
== GIT_SUCCESS
) ? GIT_SUCCESS
:
568 git__rethrow(error
, "Failed to open read stream");