File Coverage

deps/libgit2/src/object.c
Criterion Covered Total %
statement 130 233 55.7
branch 74 176 42.0
condition n/a
subroutine n/a
pod n/a
total 204 409 49.8


line stmt bran cond sub pod time code
1             /*
2             * Copyright (C) the libgit2 contributors. All rights reserved.
3             *
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.
6             */
7              
8             #include "object.h"
9              
10             #include "git2/object.h"
11              
12             #include "repository.h"
13              
14             #include "commit.h"
15             #include "hash.h"
16             #include "tree.h"
17             #include "blob.h"
18             #include "oid.h"
19             #include "tag.h"
20              
21             bool git_object__strict_input_validation = true;
22              
23             extern int git_odb_hash(git_oid *out, const void *data, size_t len, git_object_t type);
24             size_t git_object__size(git_object_t type);
25              
26             typedef struct {
27             const char *str; /* type name string */
28             size_t size; /* size in bytes of the object structure */
29              
30             int (*parse)(void *self, git_odb_object *obj);
31             int (*parse_raw)(void *self, const char *data, size_t size);
32             void (*free)(void *self);
33             } git_object_def;
34              
35             static git_object_def git_objects_table[] = {
36             /* 0 = GIT_OBJECT__EXT1 */
37             { "", 0, NULL, NULL, NULL },
38              
39             /* 1 = GIT_OBJECT_COMMIT */
40             { "commit", sizeof(git_commit), git_commit__parse, git_commit__parse_raw, git_commit__free },
41              
42             /* 2 = GIT_OBJECT_TREE */
43             { "tree", sizeof(git_tree), git_tree__parse, git_tree__parse_raw, git_tree__free },
44              
45             /* 3 = GIT_OBJECT_BLOB */
46             { "blob", sizeof(git_blob), git_blob__parse, git_blob__parse_raw, git_blob__free },
47              
48             /* 4 = GIT_OBJECT_TAG */
49             { "tag", sizeof(git_tag), git_tag__parse, git_tag__parse_raw, git_tag__free },
50              
51             /* 5 = GIT_OBJECT__EXT2 */
52             { "", 0, NULL, NULL, NULL },
53             /* 6 = GIT_OBJECT_OFS_DELTA */
54             { "OFS_DELTA", 0, NULL, NULL, NULL },
55             /* 7 = GIT_OBJECT_REF_DELTA */
56             { "REF_DELTA", 0, NULL, NULL, NULL },
57             };
58              
59 0           int git_object__from_raw(
60             git_object **object_out,
61             const char *data,
62             size_t size,
63             git_object_t type)
64             {
65             git_object_def *def;
66             git_object *object;
67             size_t object_size;
68             int error;
69              
70 0 0         assert(object_out);
71 0           *object_out = NULL;
72              
73             /* Validate type match */
74 0 0         if (type != GIT_OBJECT_BLOB && type != GIT_OBJECT_TREE && type != GIT_OBJECT_COMMIT && type != GIT_OBJECT_TAG) {
    0          
    0          
    0          
75 0           git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
76 0           return GIT_ENOTFOUND;
77             }
78              
79 0 0         if ((object_size = git_object__size(type)) == 0) {
80 0           git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
81 0           return GIT_ENOTFOUND;
82             }
83              
84             /* Allocate and initialize base object */
85 0           object = git__calloc(1, object_size);
86 0 0         GIT_ERROR_CHECK_ALLOC(object);
87 0           object->cached.flags = GIT_CACHE_STORE_PARSED;
88 0           object->cached.type = type;
89 0 0         if ((error = git_odb_hash(&object->cached.oid, data, size, type)) < 0)
90 0           return error;
91              
92             /* Parse raw object data */
93 0           def = &git_objects_table[type];
94 0 0         assert(def->free && def->parse_raw);
    0          
95              
96 0 0         if ((error = def->parse_raw(object, data, size)) < 0) {
97 0           def->free(object);
98 0           return error;
99             }
100              
101 0           git_cached_obj_incref(object);
102 0           *object_out = object;
103              
104 0           return 0;
105             }
106              
107 379           int git_object__from_odb_object(
108             git_object **object_out,
109             git_repository *repo,
110             git_odb_object *odb_obj,
111             git_object_t type)
112             {
113             int error;
114             size_t object_size;
115             git_object_def *def;
116 379           git_object *object = NULL;
117              
118 379 50         assert(object_out);
119 379           *object_out = NULL;
120              
121             /* Validate type match */
122 379 100         if (type != GIT_OBJECT_ANY && type != odb_obj->cached.type) {
    50          
123 0           git_error_set(GIT_ERROR_INVALID,
124             "the requested type does not match the type in the ODB");
125 0           return GIT_ENOTFOUND;
126             }
127              
128 379 50         if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
129 0           git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
130 0           return GIT_ENOTFOUND;
131             }
132              
133             /* Allocate and initialize base object */
134 379           object = git__calloc(1, object_size);
135 379 50         GIT_ERROR_CHECK_ALLOC(object);
136              
137 379           git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
138 379           object->cached.type = odb_obj->cached.type;
139 379           object->cached.size = odb_obj->cached.size;
140 379           object->repo = repo;
141              
142             /* Parse raw object data */
143 379           def = &git_objects_table[odb_obj->cached.type];
144 379 50         assert(def->free && def->parse);
    50          
145              
146 379 50         if ((error = def->parse(object, odb_obj)) < 0)
147 0           def->free(object);
148             else
149 379           *object_out = git_cache_store_parsed(&repo->objects, object);
150              
151 379           return error;
152             }
153              
154 372           void git_object__free(void *obj)
155             {
156 372           git_object_t type = ((git_object *)obj)->cached.type;
157              
158 372 50         if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) ||
    50          
    50          
159 372           !git_objects_table[type].free)
160 0           git__free(obj);
161             else
162 372           git_objects_table[type].free(obj);
163 372           }
164              
165 1692           int git_object_lookup_prefix(
166             git_object **object_out,
167             git_repository *repo,
168             const git_oid *id,
169             size_t len,
170             git_object_t type)
171             {
172 1692           git_object *object = NULL;
173 1692           git_odb *odb = NULL;
174 1692           git_odb_object *odb_obj = NULL;
175 1692           int error = 0;
176              
177 1692 50         assert(repo && object_out && id);
    50          
    50          
178              
179 1692 50         if (len < GIT_OID_MINPREFIXLEN) {
180 0           git_error_set(GIT_ERROR_OBJECT, "ambiguous lookup - OID prefix is too short");
181 0           return GIT_EAMBIGUOUS;
182             }
183              
184 1692           error = git_repository_odb__weakptr(&odb, repo);
185 1692 50         if (error < 0)
186 0           return error;
187              
188 1692 50         if (len > GIT_OID_HEXSZ)
189 0           len = GIT_OID_HEXSZ;
190              
191 1692 100         if (len == GIT_OID_HEXSZ) {
192 1678           git_cached_obj *cached = NULL;
193              
194             /* We want to match the full id : we can first look up in the cache,
195             * since there is no need to check for non ambiguousity
196             */
197 1678           cached = git_cache_get_any(&repo->objects, id);
198 1678 100         if (cached != NULL) {
199 1309 100         if (cached->flags == GIT_CACHE_STORE_PARSED) {
200 1304           object = (git_object *)cached;
201              
202 1304 100         if (type != GIT_OBJECT_ANY && type != object->cached.type) {
    50          
203 0           git_object_free(object);
204 0           git_error_set(GIT_ERROR_INVALID,
205             "the requested type does not match the type in the ODB");
206 0           return GIT_ENOTFOUND;
207             }
208              
209 1304           *object_out = object;
210 1304           return 0;
211 5 50         } else if (cached->flags == GIT_CACHE_STORE_RAW) {
212 5           odb_obj = (git_odb_object *)cached;
213             } else {
214 0           assert(!"Wrong caching type in the global object cache");
215             }
216             } else {
217             /* Object was not found in the cache, let's explore the backends.
218             * We could just use git_odb_read_unique_short_oid,
219             * it is the same cost for packed and loose object backends,
220             * but it may be much more costly for sqlite and hiredis.
221             */
222 374           error = git_odb_read(&odb_obj, odb, id);
223             }
224             } else {
225 14           git_oid short_oid = {{ 0 }};
226              
227 14           git_oid__cpy_prefix(&short_oid, id, len);
228              
229             /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
230             * 2 options :
231             * - We always search in the cache first. If we find that short oid is
232             * ambiguous, we can stop. But in all the other cases, we must then
233             * explore all the backends (to find an object if there was match,
234             * or to check that oid is not ambiguous if we have found 1 match in
235             * the cache)
236             * - We never explore the cache, go right to exploring the backends
237             * We chose the latter : we explore directly the backends.
238             */
239 14           error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
240             }
241              
242 388 100         if (error < 0)
243 9           return error;
244              
245 379           error = git_object__from_odb_object(object_out, repo, odb_obj, type);
246              
247 379           git_odb_object_free(odb_obj);
248              
249 1692           return error;
250             }
251              
252 1661           int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_object_t type) {
253 1661           return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
254             }
255              
256 8085           void git_object_free(git_object *object)
257             {
258 8085 100         if (object == NULL)
259 5380           return;
260              
261 2705           git_cached_obj_decref(object);
262             }
263              
264 938           const git_oid *git_object_id(const git_object *obj)
265             {
266 938 50         assert(obj);
267 938           return &obj->cached.oid;
268             }
269              
270 1048           git_object_t git_object_type(const git_object *obj)
271             {
272 1048 50         assert(obj);
273 1048           return obj->cached.type;
274             }
275              
276 586           git_repository *git_object_owner(const git_object *obj)
277             {
278 586 50         assert(obj);
279 586           return obj->repo;
280             }
281              
282 1404           const char *git_object_type2string(git_object_t type)
283             {
284 1404 50         if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
    50          
285 0           return "";
286              
287 1404           return git_objects_table[type].str;
288             }
289              
290 0           git_object_t git_object_string2type(const char *str)
291             {
292 0 0         if (!str)
293 0           return GIT_OBJECT_INVALID;
294              
295 0           return git_object_stringn2type(str, strlen(str));
296             }
297              
298 1140           git_object_t git_object_stringn2type(const char *str, size_t len)
299             {
300             size_t i;
301              
302 1140 50         if (!str || !len || !*str)
    50          
    50          
303 0           return GIT_OBJECT_INVALID;
304              
305 3540 50         for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
306 5940           if (*git_objects_table[i].str &&
307 2400           !git__prefixncmp(str, len, git_objects_table[i].str))
308 1140           return (git_object_t)i;
309              
310 0           return GIT_OBJECT_INVALID;
311             }
312              
313 2260           int git_object_typeisloose(git_object_t type)
314             {
315 2260 50         if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
    50          
316 0           return 0;
317              
318 2260           return (git_objects_table[type].size > 0) ? 1 : 0;
319             }
320              
321 379           size_t git_object__size(git_object_t type)
322             {
323 379 50         if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
    50          
324 0           return 0;
325              
326 379           return git_objects_table[type].size;
327             }
328              
329 170           static int dereference_object(git_object **dereferenced, git_object *obj)
330             {
331 170           git_object_t type = git_object_type(obj);
332              
333 170           switch (type) {
334             case GIT_OBJECT_COMMIT:
335 170           return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
336              
337             case GIT_OBJECT_TAG:
338 0           return git_tag_target(dereferenced, (git_tag*)obj);
339              
340             case GIT_OBJECT_BLOB:
341             case GIT_OBJECT_TREE:
342 0           return GIT_EPEEL;
343              
344             default:
345 0           return GIT_EINVALIDSPEC;
346             }
347             }
348              
349 0           static int peel_error(int error, const git_oid *oid, git_object_t type)
350             {
351             const char *type_name;
352             char hex_oid[GIT_OID_HEXSZ + 1];
353              
354 0           type_name = git_object_type2string(type);
355              
356 0           git_oid_fmt(hex_oid, oid);
357 0           hex_oid[GIT_OID_HEXSZ] = '\0';
358              
359 0           git_error_set(GIT_ERROR_OBJECT, "the git_object of id '%s' can not be "
360             "successfully peeled into a %s (git_object_t=%i).", hex_oid, type_name, type);
361              
362 0           return error;
363             }
364              
365 278           static int check_type_combination(git_object_t type, git_object_t target)
366             {
367 278 100         if (type == target)
368 108           return 0;
369              
370 170           switch (type) {
371             case GIT_OBJECT_BLOB:
372             case GIT_OBJECT_TREE:
373             /* a blob or tree can never be peeled to anything but themselves */
374 0           return GIT_EINVALIDSPEC;
375             break;
376             case GIT_OBJECT_COMMIT:
377             /* a commit can only be peeled to a tree */
378 170 50         if (target != GIT_OBJECT_TREE && target != GIT_OBJECT_ANY)
    0          
379 0           return GIT_EINVALIDSPEC;
380 170           break;
381             case GIT_OBJECT_TAG:
382             /* a tag may point to anything, so we let anything through */
383 0           break;
384             default:
385 0           return GIT_EINVALIDSPEC;
386             }
387              
388 170           return 0;
389             }
390              
391 278           int git_object_peel(
392             git_object **peeled,
393             const git_object *object,
394             git_object_t target_type)
395             {
396 278           git_object *source, *deref = NULL;
397             int error;
398              
399 278 50         assert(object && peeled);
    50          
400              
401 278 50         assert(target_type == GIT_OBJECT_TAG ||
    100          
    50          
    0          
    0          
402             target_type == GIT_OBJECT_COMMIT ||
403             target_type == GIT_OBJECT_TREE ||
404             target_type == GIT_OBJECT_BLOB ||
405             target_type == GIT_OBJECT_ANY);
406              
407 278 50         if ((error = check_type_combination(git_object_type(object), target_type)) < 0)
408 0           return peel_error(error, git_object_id(object), target_type);
409              
410 278 100         if (git_object_type(object) == target_type)
411 108           return git_object_dup(peeled, (git_object *)object);
412              
413 170           source = (git_object *)object;
414              
415 170 50         while (!(error = dereference_object(&deref, source))) {
416              
417 170 50         if (source != object)
418 0           git_object_free(source);
419              
420 170 50         if (git_object_type(deref) == target_type) {
421 170           *peeled = deref;
422 170           return 0;
423             }
424              
425 0           if (target_type == GIT_OBJECT_ANY &&
426 0           git_object_type(deref) != git_object_type(object))
427             {
428 0           *peeled = deref;
429 0           return 0;
430             }
431              
432 0           source = deref;
433 0           deref = NULL;
434             }
435              
436 0 0         if (source != object)
437 0           git_object_free(source);
438              
439 0           git_object_free(deref);
440              
441 0 0         if (error)
442 0           error = peel_error(error, git_object_id(object), target_type);
443              
444 278           return error;
445             }
446              
447 1037           int git_object_dup(git_object **dest, git_object *source)
448             {
449 1037           git_cached_obj_incref(source);
450 1037           *dest = source;
451 1037           return 0;
452             }
453              
454 5           int git_object_lookup_bypath(
455             git_object **out,
456             const git_object *treeish,
457             const char *path,
458             git_object_t type)
459             {
460 5           int error = -1;
461 5           git_tree *tree = NULL;
462 5           git_tree_entry *entry = NULL;
463              
464 5 50         assert(out && treeish && path);
    50          
    50          
465              
466 5 50         if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJECT_TREE)) < 0 ||
    100          
467 5           (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
468             {
469             goto cleanup;
470             }
471              
472 4 50         if (type != GIT_OBJECT_ANY && git_tree_entry_type(entry) != type)
    50          
473             {
474 0           git_error_set(GIT_ERROR_OBJECT,
475             "object at path '%s' is not of the asked-for type %d",
476             path, type);
477 0           error = GIT_EINVALIDSPEC;
478 0           goto cleanup;
479             }
480              
481 4           error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);
482              
483             cleanup:
484 5           git_tree_entry_free(entry);
485 5           git_tree_free(tree);
486 5           return error;
487             }
488              
489 0           int git_object_short_id(git_buf *out, const git_object *obj)
490             {
491             git_repository *repo;
492 0           int len = GIT_ABBREV_DEFAULT, error;
493 0           git_oid id = {{0}};
494             git_odb *odb;
495              
496 0 0         assert(out && obj);
    0          
497              
498 0           git_buf_sanitize(out);
499 0           repo = git_object_owner(obj);
500              
501 0 0         if ((error = git_repository__configmap_lookup(&len, repo, GIT_CONFIGMAP_ABBREV)) < 0)
502 0           return error;
503              
504 0 0         if ((error = git_repository_odb(&odb, repo)) < 0)
505 0           return error;
506              
507 0 0         while (len < GIT_OID_HEXSZ) {
508             /* set up short oid */
509 0           memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2);
510 0 0         if (len & 1)
511 0           id.id[len / 2] &= 0xf0;
512              
513 0           error = git_odb_exists_prefix(NULL, odb, &id, len);
514 0 0         if (error != GIT_EAMBIGUOUS)
515 0           break;
516              
517 0           git_error_clear();
518 0           len++;
519             }
520              
521 0 0         if (!error && !(error = git_buf_grow(out, len + 1))) {
    0          
522 0           git_oid_tostr(out->ptr, len + 1, &id);
523 0           out->size = len;
524             }
525              
526 0           git_odb_free(odb);
527              
528 0           return error;
529             }
530              
531 327           bool git_object__is_valid(
532             git_repository *repo, const git_oid *id, git_object_t expected_type)
533             {
534             git_odb *odb;
535             git_object_t actual_type;
536             size_t len;
537             int error;
538              
539 327 50         if (!git_object__strict_input_validation)
540 0           return true;
541              
542 327 50         if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
    50          
543 327           (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
544 0           return false;
545              
546 327 100         if (expected_type != GIT_OBJECT_ANY && expected_type != actual_type) {
    50          
547 0           git_error_set(GIT_ERROR_INVALID,
548             "the requested type does not match the type in the ODB");
549 0           return false;
550             }
551              
552 327           return true;
553             }