File Coverage

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