object.c 11.1 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6 7 8 9 10 11 12 13 14
 */
#include "git2/object.h"

#include "common.h"
#include "repository.h"

#include "commit.h"
#include "tree.h"
#include "blob.h"
15
#include "oid.h"
16 17
#include "tag.h"

18
bool git_object__strict_input_validation = true;
19

20
typedef struct {
Vicent Marti committed
21
	const char	*str;	/* type name string */
22
	size_t		size;	/* size in bytes of the object structure */
23

24
	int  (*parse)(void *self, git_odb_object *obj);
25 26 27 28
	void (*free)(void *self);
} git_object_def;

static git_object_def git_objects_table[] = {
29
	/* 0 = GIT_OBJ__EXT1 */
30
	{ "", 0, NULL, NULL },
31 32

	/* 1 = GIT_OBJ_COMMIT */
33
	{ "commit", sizeof(git_commit), git_commit__parse, git_commit__free },
34 35

	/* 2 = GIT_OBJ_TREE */
36
	{ "tree", sizeof(git_tree), git_tree__parse, git_tree__free },
37 38

	/* 3 = GIT_OBJ_BLOB */
39
	{ "blob", sizeof(git_blob), git_blob__parse, git_blob__free },
40 41

	/* 4 = GIT_OBJ_TAG */
42
	{ "tag", sizeof(git_tag), git_tag__parse, git_tag__free },
43 44

	/* 5 = GIT_OBJ__EXT2 */
45
	{ "", 0, NULL, NULL },
46
	/* 6 = GIT_OBJ_OFS_DELTA */
47
	{ "OFS_DELTA", 0, NULL, NULL },
48
	/* 7 = GIT_OBJ_REF_DELTA */
49
	{ "REF_DELTA", 0, NULL, NULL },
50 51
};

52 53 54 55 56 57 58
int git_object__from_odb_object(
	git_object **object_out,
	git_repository *repo,
	git_odb_object *odb_obj,
	git_otype type)
{
	int error;
59 60
	size_t object_size;
	git_object_def *def;
61 62
	git_object *object = NULL;

63 64 65 66
	assert(object_out);
	*object_out = NULL;

	/* Validate type match */
Vicent Marti committed
67 68 69
	if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) {
		giterr_set(GITERR_INVALID,
			"The requested type does not match the type in the ODB");
70 71 72
		return GIT_ENOTFOUND;
	}

73 74 75 76 77 78 79 80
	if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
		giterr_set(GITERR_INVALID, "The requested type is invalid");
		return GIT_ENOTFOUND;
	}

	/* Allocate and initialize base object */
	object = git__calloc(1, object_size);
	GITERR_CHECK_ALLOC(object);
81 82

	git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
Vicent Marti committed
83
	object->cached.type = odb_obj->cached.type;
84
	object->cached.size = odb_obj->cached.size;
85 86
	object->repo = repo;

87 88
	/* Parse raw object data */
	def = &git_objects_table[odb_obj->cached.type];
89
	assert(def->free && def->parse);
90

91
	if ((error = def->parse(object, odb_obj)) < 0)
92
		def->free(object);
93 94
	else
		*object_out = git_cache_store_parsed(&repo->objects, object);
95

96
	return error;
97 98
}

99 100 101 102 103 104 105 106 107 108 109
void git_object__free(void *obj)
{
	git_otype type = ((git_object *)obj)->cached.type;

	if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) ||
		!git_objects_table[type].free)
		git__free(obj);
	else
		git_objects_table[type].free(obj);
}

110 111 112 113
int git_object_lookup_prefix(
	git_object **object_out,
	git_repository *repo,
	const git_oid *id,
114
	size_t len,
115
	git_otype type)
116 117
{
	git_object *object = NULL;
118
	git_odb *odb = NULL;
119
	git_odb_object *odb_obj = NULL;
120
	int error = 0;
121 122 123

	assert(repo && object_out && id);

124 125
	if (len < GIT_OID_MINPREFIXLEN) {
		giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short");
126
		return GIT_EAMBIGUOUS;
127
	}
Vicent Marti committed
128

129
	error = git_repository_odb__weakptr(&odb, repo);
130
	if (error < 0)
131 132
		return error;

133 134
	if (len > GIT_OID_HEXSZ)
		len = GIT_OID_HEXSZ;
135

136
	if (len == GIT_OID_HEXSZ) {
137 138
		git_cached_obj *cached = NULL;

139 140 141
		/* We want to match the full id : we can first look up in the cache,
		 * since there is no need to check for non ambiguousity
		 */
142 143 144 145 146
		cached = git_cache_get_any(&repo->objects, id);
		if (cached != NULL) {
			if (cached->flags == GIT_CACHE_STORE_PARSED) {
				object = (git_object *)cached;

Vicent Marti committed
147
				if (type != GIT_OBJ_ANY && type != object->cached.type) {
148 149 150 151 152 153 154 155 156 157 158 159
					git_object_free(object);
					giterr_set(GITERR_INVALID,
						"The requested type does not match the type in ODB");
					return GIT_ENOTFOUND;
				}

				*object_out = object;
				return 0;
			} else if (cached->flags == GIT_CACHE_STORE_RAW) {
				odb_obj = (git_odb_object *)cached;
			} else {
				assert(!"Wrong caching type in the global object cache");
160
			}
161 162 163 164 165 166 167
		} else {
			/* Object was not found in the cache, let's explore the backends.
			 * We could just use git_odb_read_unique_short_oid,
			 * it is the same cost for packed and loose object backends,
			 * but it may be much more costly for sqlite and hiredis.
			 */
			error = git_odb_read(&odb_obj, odb, id);
168 169
		}
	} else {
170
		git_oid short_oid = {{ 0 }};
171

172
		git_oid__cpy_prefix(&short_oid, id, len);
173

174
		/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
175 176
		 * 2 options :
		 * - We always search in the cache first. If we find that short oid is
Vicent Marti committed
177 178 179 180
		 *	ambiguous, we can stop. But in all the other cases, we must then
		 *	explore all the backends (to find an object if there was match,
		 *	or to check that oid is not ambiguous if we have found 1 match in
		 *	the cache)
181 182 183
		 * - We never explore the cache, go right to exploring the backends
		 * We chose the latter : we explore directly the backends.
		 */
184
		error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
185 186
	}

187
	if (error < 0)
Russell Belfer committed
188
		return error;
189

190
	error = git_object__from_odb_object(object_out, repo, odb_obj, type);
191

192
	git_odb_object_free(odb_obj);
Vicent Marti committed
193

194
	return error;
195 196
}

197
int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
Vicent Marti committed
198
	return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
199 200
}

201
void git_object_free(git_object *object)
202 203 204 205
{
	if (object == NULL)
		return;

206
	git_cached_obj_decref(object);
207 208
}

209
const git_oid *git_object_id(const git_object *obj)
210 211
{
	assert(obj);
Vicent Marti committed
212
	return &obj->cached.oid;
213 214
}

215
git_otype git_object_type(const git_object *obj)
216 217
{
	assert(obj);
Vicent Marti committed
218
	return obj->cached.type;
219 220
}

221
git_repository *git_object_owner(const git_object *obj)
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
{
	assert(obj);
	return obj->repo;
}

const char *git_object_type2string(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return "";

	return git_objects_table[type].str;
}

git_otype git_object_string2type(const char *str)
{
	size_t i;

	if (!str || !*str)
		return GIT_OBJ_BAD;

	for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
		if (!strcmp(str, git_objects_table[i].str))
			return (git_otype)i;

	return GIT_OBJ_BAD;
}

int git_object_typeisloose(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return 0;

254
	return (git_objects_table[type].size > 0) ? 1 : 0;
255 256 257 258 259 260 261 262 263 264
}

size_t git_object__size(git_otype type)
{
	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
		return 0;

	return git_objects_table[type].size;
}

265 266 267 268 269 270 271 272 273 274
static int dereference_object(git_object **dereferenced, git_object *obj)
{
	git_otype type = git_object_type(obj);

	switch (type) {
	case GIT_OBJ_COMMIT:
		return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);

	case GIT_OBJ_TAG:
		return git_tag_target(dereferenced, (git_tag*)obj);
275 276 277

	case GIT_OBJ_BLOB:
	case GIT_OBJ_TREE:
278
		return GIT_EPEEL;
279 280

	default:
281
		return GIT_EINVALIDSPEC;
282 283 284
	}
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
static int peel_error(int error, const git_oid *oid, git_otype type)
{
	const char *type_name;
	char hex_oid[GIT_OID_HEXSZ + 1];

	type_name = git_object_type2string(type);

	git_oid_fmt(hex_oid, oid);
	hex_oid[GIT_OID_HEXSZ] = '\0';

	giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be "
		"successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type);

	return error;
}

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
static int check_type_combination(git_otype type, git_otype target)
{
	if (type == target)
		return 0;

	switch (type) {
	case GIT_OBJ_BLOB:
	case GIT_OBJ_TREE:
		/* a blob or tree can never be peeled to anything but themselves */
		return GIT_EINVALIDSPEC;
		break;
	case GIT_OBJ_COMMIT:
		/* a commit can only be peeled to a tree */
		if (target != GIT_OBJ_TREE && target != GIT_OBJ_ANY)
			return GIT_EINVALIDSPEC;
		break;
	case GIT_OBJ_TAG:
		/* a tag may point to anything, so we let anything through */
		break;
	default:
		return GIT_EINVALIDSPEC;
	}

	return 0;
}

327
int git_object_peel(
328
	git_object **peeled,
Vicent Marti committed
329
	const git_object *object,
330
	git_otype target_type)
331 332
{
	git_object *source, *deref = NULL;
333 334
	int error;

335
	assert(object && peeled);
336

337 338 339 340 341
	assert(target_type == GIT_OBJ_TAG ||
		target_type == GIT_OBJ_COMMIT ||
		target_type == GIT_OBJ_TREE ||
		target_type == GIT_OBJ_BLOB ||
		target_type == GIT_OBJ_ANY);
342

343 344 345 346 347 348
	if ((error = check_type_combination(git_object_type(object), target_type)) < 0)
		return peel_error(error, git_object_id(object), target_type);

	if (git_object_type(object) == target_type)
		return git_object_dup(peeled, (git_object *)object);

Vicent Marti committed
349
	source = (git_object *)object;
350

351
	while (!(error = dereference_object(&deref, source))) {
352 353 354 355 356 357 358 359 360

		if (source != object)
			git_object_free(source);

		if (git_object_type(deref) == target_type) {
			*peeled = deref;
			return 0;
		}

361 362 363 364 365 366 367
		if (target_type == GIT_OBJ_ANY &&
			git_object_type(deref) != git_object_type(object))
		{
			*peeled = deref;
			return 0;
		}

368 369 370 371 372 373
		source = deref;
		deref = NULL;
	}

	if (source != object)
		git_object_free(source);
374

375
	git_object_free(deref);
376 377 378 379 380

	if (error)
		error = peel_error(error, git_object_id(object), target_type);

	return error;
381
}
382

383 384 385 386 387 388
int git_object_dup(git_object **dest, git_object *source)
{
	git_cached_obj_incref(source);
	*dest = source;
	return 0;
}
Ben Straub committed
389 390 391 392 393 394 395 396 397 398 399 400 401

int git_object_lookup_bypath(
		git_object **out,
		const git_object *treeish,
		const char *path,
		git_otype type)
{
	int error = -1;
	git_tree *tree = NULL;
	git_tree_entry *entry = NULL;

	assert(out && treeish && path);

402
	if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0 ||
403
		 (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
Ben Straub committed
404 405 406 407
	{
		goto cleanup;
	}

408 409
	if (type != GIT_OBJ_ANY && git_tree_entry_type(entry) != type)
	{
Ben Straub committed
410 411 412 413
		giterr_set(GITERR_OBJECT,
				"object at path '%s' is not of the asked-for type %d",
				path, type);
		error = GIT_EINVALIDSPEC;
414
		goto cleanup;
Ben Straub committed
415 416
	}

417 418
	error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);

Ben Straub committed
419 420 421 422 423
cleanup:
	git_tree_entry_free(entry);
	git_tree_free(tree);
	return error;
}
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466

int git_object_short_id(git_buf *out, const git_object *obj)
{
	git_repository *repo;
	int len = GIT_ABBREV_DEFAULT, error;
	git_oid id = {{0}};
	git_odb *odb;

	assert(out && obj);

	git_buf_sanitize(out);
	repo = git_object_owner(obj);

	if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0)
		return error;

	if ((error = git_repository_odb(&odb, repo)) < 0)
		return error;

	while (len < GIT_OID_HEXSZ) {
		/* set up short oid */
		memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2);
		if (len & 1)
			id.id[len / 2] &= 0xf0;

		error = git_odb_exists_prefix(NULL, odb, &id, len);
		if (error != GIT_EAMBIGUOUS)
			break;

		giterr_clear();
		len++;
	}

	if (!error && !(error = git_buf_grow(out, len + 1))) {
		git_oid_tostr(out->ptr, len + 1, &id);
		out->size = len;
	}

	git_odb_free(odb);

	return error;
}

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
bool git_object__is_valid(
	git_repository *repo, const git_oid *id, git_otype expected_type)
{
	git_odb *odb;
	git_otype actual_type;
	size_t len;
	int error;

	if (!git_object__strict_input_validation)
		return true;

	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
		(error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
		return false;

	if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) {
		giterr_set(GITERR_INVALID,
			"the requested type does not match the type in the ODB");
		return false;
	}

	return true;
}