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