]>
Commit | Line | Data |
---|---|---|
e52ed7a5 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
e52ed7a5 | 3 | * |
bb742ede VM |
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. | |
e52ed7a5 | 6 | */ |
e52ed7a5 VM |
7 | #include "git2/object.h" |
8 | ||
9 | #include "common.h" | |
10 | #include "repository.h" | |
11 | ||
12 | #include "commit.h" | |
13 | #include "tree.h" | |
14 | #include "blob.h" | |
15 | #include "tag.h" | |
16 | ||
f2dddf52 | 17 | bool git_object__strict_input_validation = true; |
22a19f5b | 18 | |
78606263 | 19 | typedef struct { |
87d9869f | 20 | const char *str; /* type name string */ |
e52ed7a5 | 21 | size_t size; /* size in bytes of the object structure */ |
78606263 | 22 | |
3f27127d | 23 | int (*parse)(void *self, git_odb_object *obj); |
78606263 RB |
24 | void (*free)(void *self); |
25 | } git_object_def; | |
26 | ||
27 | static git_object_def git_objects_table[] = { | |
e52ed7a5 | 28 | /* 0 = GIT_OBJ__EXT1 */ |
3f27127d | 29 | { "", 0, NULL, NULL }, |
e52ed7a5 VM |
30 | |
31 | /* 1 = GIT_OBJ_COMMIT */ | |
3f27127d | 32 | { "commit", sizeof(git_commit), git_commit__parse, git_commit__free }, |
e52ed7a5 VM |
33 | |
34 | /* 2 = GIT_OBJ_TREE */ | |
3f27127d | 35 | { "tree", sizeof(git_tree), git_tree__parse, git_tree__free }, |
e52ed7a5 VM |
36 | |
37 | /* 3 = GIT_OBJ_BLOB */ | |
3f27127d | 38 | { "blob", sizeof(git_blob), git_blob__parse, git_blob__free }, |
e52ed7a5 VM |
39 | |
40 | /* 4 = GIT_OBJ_TAG */ | |
3f27127d | 41 | { "tag", sizeof(git_tag), git_tag__parse, git_tag__free }, |
e52ed7a5 VM |
42 | |
43 | /* 5 = GIT_OBJ__EXT2 */ | |
3f27127d | 44 | { "", 0, NULL, NULL }, |
e52ed7a5 | 45 | /* 6 = GIT_OBJ_OFS_DELTA */ |
3f27127d | 46 | { "OFS_DELTA", 0, NULL, NULL }, |
e52ed7a5 | 47 | /* 7 = GIT_OBJ_REF_DELTA */ |
3f27127d | 48 | { "REF_DELTA", 0, NULL, NULL }, |
e52ed7a5 VM |
49 | }; |
50 | ||
c6ac28fd RB |
51 | int git_object__from_odb_object( |
52 | git_object **object_out, | |
53 | git_repository *repo, | |
54 | git_odb_object *odb_obj, | |
55 | git_otype type) | |
56 | { | |
57 | int error; | |
78606263 RB |
58 | size_t object_size; |
59 | git_object_def *def; | |
c6ac28fd RB |
60 | git_object *object = NULL; |
61 | ||
78606263 RB |
62 | assert(object_out); |
63 | *object_out = NULL; | |
64 | ||
65 | /* Validate type match */ | |
8842c75f VM |
66 | if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) { |
67 | giterr_set(GITERR_INVALID, | |
68 | "The requested type does not match the type in the ODB"); | |
c6ac28fd RB |
69 | return GIT_ENOTFOUND; |
70 | } | |
71 | ||
78606263 RB |
72 | if ((object_size = git_object__size(odb_obj->cached.type)) == 0) { |
73 | giterr_set(GITERR_INVALID, "The requested type is invalid"); | |
74 | return GIT_ENOTFOUND; | |
75 | } | |
76 | ||
77 | /* Allocate and initialize base object */ | |
78 | object = git__calloc(1, object_size); | |
79 | GITERR_CHECK_ALLOC(object); | |
c6ac28fd | 80 | |
c6ac28fd | 81 | git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid); |
cf7850a4 | 82 | object->cached.type = odb_obj->cached.type; |
78606263 | 83 | object->cached.size = odb_obj->cached.size; |
c6ac28fd RB |
84 | object->repo = repo; |
85 | ||
78606263 RB |
86 | /* Parse raw object data */ |
87 | def = &git_objects_table[odb_obj->cached.type]; | |
3f27127d | 88 | assert(def->free && def->parse); |
c6ac28fd | 89 | |
3f27127d | 90 | if ((error = def->parse(object, odb_obj)) < 0) |
78606263 | 91 | def->free(object); |
3f27127d RB |
92 | else |
93 | *object_out = git_cache_store_parsed(&repo->objects, object); | |
c6ac28fd | 94 | |
3f27127d | 95 | return error; |
c6ac28fd RB |
96 | } |
97 | ||
78606263 RB |
98 | void git_object__free(void *obj) |
99 | { | |
100 | git_otype type = ((git_object *)obj)->cached.type; | |
101 | ||
102 | if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) || | |
103 | !git_objects_table[type].free) | |
104 | git__free(obj); | |
105 | else | |
106 | git_objects_table[type].free(obj); | |
107 | } | |
108 | ||
9462c471 VM |
109 | int git_object_lookup_prefix( |
110 | git_object **object_out, | |
111 | git_repository *repo, | |
112 | const git_oid *id, | |
b8457baa | 113 | size_t len, |
9462c471 | 114 | git_otype type) |
5de079b8 VM |
115 | { |
116 | git_object *object = NULL; | |
9462c471 | 117 | git_odb *odb = NULL; |
e583334c | 118 | git_odb_object *odb_obj = NULL; |
e172cf08 | 119 | int error = 0; |
5de079b8 VM |
120 | |
121 | assert(repo && object_out && id); | |
122 | ||
8915a140 RB |
123 | if (len < GIT_OID_MINPREFIXLEN) { |
124 | giterr_set(GITERR_OBJECT, "Ambiguous lookup - OID prefix is too short"); | |
904b67e6 | 125 | return GIT_EAMBIGUOUS; |
8915a140 | 126 | } |
d0323a5f | 127 | |
9462c471 | 128 | error = git_repository_odb__weakptr(&odb, repo); |
e172cf08 | 129 | if (error < 0) |
9462c471 VM |
130 | return error; |
131 | ||
3d9ef2dc VM |
132 | if (len > GIT_OID_HEXSZ) |
133 | len = GIT_OID_HEXSZ; | |
bd1aa741 | 134 | |
3d9ef2dc | 135 | if (len == GIT_OID_HEXSZ) { |
5df18424 VM |
136 | git_cached_obj *cached = NULL; |
137 | ||
dd453c4d MP |
138 | /* We want to match the full id : we can first look up in the cache, |
139 | * since there is no need to check for non ambiguousity | |
140 | */ | |
5df18424 VM |
141 | cached = git_cache_get_any(&repo->objects, id); |
142 | if (cached != NULL) { | |
143 | if (cached->flags == GIT_CACHE_STORE_PARSED) { | |
144 | object = (git_object *)cached; | |
145 | ||
cf7850a4 | 146 | if (type != GIT_OBJ_ANY && type != object->cached.type) { |
5df18424 VM |
147 | git_object_free(object); |
148 | giterr_set(GITERR_INVALID, | |
149 | "The requested type does not match the type in ODB"); | |
150 | return GIT_ENOTFOUND; | |
151 | } | |
152 | ||
153 | *object_out = object; | |
154 | return 0; | |
155 | } else if (cached->flags == GIT_CACHE_STORE_RAW) { | |
156 | odb_obj = (git_odb_object *)cached; | |
157 | } else { | |
158 | assert(!"Wrong caching type in the global object cache"); | |
7d3ec3ca | 159 | } |
5df18424 VM |
160 | } else { |
161 | /* Object was not found in the cache, let's explore the backends. | |
162 | * We could just use git_odb_read_unique_short_oid, | |
163 | * it is the same cost for packed and loose object backends, | |
164 | * but it may be much more costly for sqlite and hiredis. | |
165 | */ | |
166 | error = git_odb_read(&odb_obj, odb, id); | |
dd453c4d | 167 | } |
dd453c4d MP |
168 | } else { |
169 | git_oid short_oid; | |
170 | ||
171 | /* We copy the first len*4 bits from id and fill the remaining with 0s */ | |
172 | memcpy(short_oid.id, id->id, (len + 1) / 2); | |
173 | if (len % 2) | |
174 | short_oid.id[len / 2] &= 0xF0; | |
3d9ef2dc | 175 | memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2); |
dd453c4d | 176 | |
3d9ef2dc | 177 | /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have |
dd453c4d MP |
178 | * 2 options : |
179 | * - We always search in the cache first. If we find that short oid is | |
87d9869f VM |
180 | * ambiguous, we can stop. But in all the other cases, we must then |
181 | * explore all the backends (to find an object if there was match, | |
182 | * or to check that oid is not ambiguous if we have found 1 match in | |
183 | * the cache) | |
dd453c4d MP |
184 | * - We never explore the cache, go right to exploring the backends |
185 | * We chose the latter : we explore directly the backends. | |
186 | */ | |
9462c471 | 187 | error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len); |
5de079b8 VM |
188 | } |
189 | ||
3aa351ea | 190 | if (error < 0) |
282283ac | 191 | return error; |
5de079b8 | 192 | |
c6ac28fd | 193 | error = git_object__from_odb_object(object_out, repo, odb_obj, type); |
5de079b8 | 194 | |
45e79e37 | 195 | git_odb_object_free(odb_obj); |
72a3fe42 | 196 | |
c6ac28fd | 197 | return error; |
5de079b8 VM |
198 | } |
199 | ||
dd453c4d | 200 | int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) { |
d0323a5f | 201 | return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type); |
dd453c4d MP |
202 | } |
203 | ||
45e79e37 | 204 | void git_object_free(git_object *object) |
48c27f86 VM |
205 | { |
206 | if (object == NULL) | |
207 | return; | |
208 | ||
5df18424 | 209 | git_cached_obj_decref(object); |
48c27f86 VM |
210 | } |
211 | ||
17cdf252 | 212 | const git_oid *git_object_id(const git_object *obj) |
e52ed7a5 VM |
213 | { |
214 | assert(obj); | |
72a3fe42 | 215 | return &obj->cached.oid; |
e52ed7a5 VM |
216 | } |
217 | ||
17cdf252 | 218 | git_otype git_object_type(const git_object *obj) |
e52ed7a5 VM |
219 | { |
220 | assert(obj); | |
cf7850a4 | 221 | return obj->cached.type; |
e52ed7a5 VM |
222 | } |
223 | ||
17cdf252 | 224 | git_repository *git_object_owner(const git_object *obj) |
e52ed7a5 VM |
225 | { |
226 | assert(obj); | |
227 | return obj->repo; | |
228 | } | |
229 | ||
230 | const char *git_object_type2string(git_otype type) | |
231 | { | |
232 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
233 | return ""; | |
234 | ||
235 | return git_objects_table[type].str; | |
236 | } | |
237 | ||
238 | git_otype git_object_string2type(const char *str) | |
239 | { | |
240 | size_t i; | |
241 | ||
242 | if (!str || !*str) | |
243 | return GIT_OBJ_BAD; | |
244 | ||
245 | for (i = 0; i < ARRAY_SIZE(git_objects_table); i++) | |
246 | if (!strcmp(str, git_objects_table[i].str)) | |
247 | return (git_otype)i; | |
248 | ||
249 | return GIT_OBJ_BAD; | |
250 | } | |
251 | ||
252 | int git_object_typeisloose(git_otype type) | |
253 | { | |
254 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
255 | return 0; | |
256 | ||
78606263 | 257 | return (git_objects_table[type].size > 0) ? 1 : 0; |
e52ed7a5 VM |
258 | } |
259 | ||
260 | size_t git_object__size(git_otype type) | |
261 | { | |
262 | if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table)) | |
263 | return 0; | |
264 | ||
265 | return git_objects_table[type].size; | |
266 | } | |
267 | ||
db9be945 | 268 | static int dereference_object(git_object **dereferenced, git_object *obj) |
269 | { | |
270 | git_otype type = git_object_type(obj); | |
271 | ||
272 | switch (type) { | |
273 | case GIT_OBJ_COMMIT: | |
274 | return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj); | |
db9be945 | 275 | |
276 | case GIT_OBJ_TAG: | |
277 | return git_tag_target(dereferenced, (git_tag*)obj); | |
d8057a5b RB |
278 | |
279 | case GIT_OBJ_BLOB: | |
d8057a5b | 280 | case GIT_OBJ_TREE: |
753e17b0 | 281 | return GIT_EPEEL; |
db9be945 | 282 | |
283 | default: | |
bc05f30c | 284 | return GIT_EINVALIDSPEC; |
db9be945 | 285 | } |
286 | } | |
287 | ||
bc05f30c | 288 | static int peel_error(int error, const git_oid *oid, git_otype type) |
289 | { | |
290 | const char *type_name; | |
291 | char hex_oid[GIT_OID_HEXSZ + 1]; | |
292 | ||
293 | type_name = git_object_type2string(type); | |
294 | ||
295 | git_oid_fmt(hex_oid, oid); | |
296 | hex_oid[GIT_OID_HEXSZ] = '\0'; | |
297 | ||
298 | giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be " | |
299 | "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type); | |
300 | ||
301 | return error; | |
302 | } | |
303 | ||
753e17b0 CMN |
304 | static int check_type_combination(git_otype type, git_otype target) |
305 | { | |
306 | if (type == target) | |
307 | return 0; | |
308 | ||
309 | switch (type) { | |
310 | case GIT_OBJ_BLOB: | |
311 | case GIT_OBJ_TREE: | |
312 | /* a blob or tree can never be peeled to anything but themselves */ | |
313 | return GIT_EINVALIDSPEC; | |
314 | break; | |
315 | case GIT_OBJ_COMMIT: | |
316 | /* a commit can only be peeled to a tree */ | |
317 | if (target != GIT_OBJ_TREE && target != GIT_OBJ_ANY) | |
318 | return GIT_EINVALIDSPEC; | |
319 | break; | |
320 | case GIT_OBJ_TAG: | |
321 | /* a tag may point to anything, so we let anything through */ | |
322 | break; | |
323 | default: | |
324 | return GIT_EINVALIDSPEC; | |
325 | } | |
326 | ||
327 | return 0; | |
328 | } | |
329 | ||
db9be945 | 330 | int git_object_peel( |
d8057a5b | 331 | git_object **peeled, |
cfbe4be3 | 332 | const git_object *object, |
d8057a5b | 333 | git_otype target_type) |
db9be945 | 334 | { |
335 | git_object *source, *deref = NULL; | |
bc05f30c | 336 | int error; |
337 | ||
d8057a5b | 338 | assert(object && peeled); |
db9be945 | 339 | |
0cce210a RB |
340 | assert(target_type == GIT_OBJ_TAG || |
341 | target_type == GIT_OBJ_COMMIT || | |
342 | target_type == GIT_OBJ_TREE || | |
343 | target_type == GIT_OBJ_BLOB || | |
344 | target_type == GIT_OBJ_ANY); | |
8915a140 | 345 | |
753e17b0 CMN |
346 | if ((error = check_type_combination(git_object_type(object), target_type)) < 0) |
347 | return peel_error(error, git_object_id(object), target_type); | |
348 | ||
349 | if (git_object_type(object) == target_type) | |
350 | return git_object_dup(peeled, (git_object *)object); | |
351 | ||
cfbe4be3 | 352 | source = (git_object *)object; |
db9be945 | 353 | |
bc05f30c | 354 | while (!(error = dereference_object(&deref, source))) { |
db9be945 | 355 | |
356 | if (source != object) | |
357 | git_object_free(source); | |
358 | ||
359 | if (git_object_type(deref) == target_type) { | |
360 | *peeled = deref; | |
361 | return 0; | |
362 | } | |
363 | ||
d8057a5b RB |
364 | if (target_type == GIT_OBJ_ANY && |
365 | git_object_type(deref) != git_object_type(object)) | |
366 | { | |
367 | *peeled = deref; | |
368 | return 0; | |
369 | } | |
370 | ||
db9be945 | 371 | source = deref; |
372 | deref = NULL; | |
373 | } | |
374 | ||
db9be945 | 375 | if (source != object) |
376 | git_object_free(source); | |
d8057a5b | 377 | |
db9be945 | 378 | git_object_free(deref); |
bc05f30c | 379 | |
380 | if (error) | |
381 | error = peel_error(error, git_object_id(object), target_type); | |
382 | ||
383 | return error; | |
db9be945 | 384 | } |
613d5eb9 | 385 | |
575a54db VM |
386 | int git_object_dup(git_object **dest, git_object *source) |
387 | { | |
388 | git_cached_obj_incref(source); | |
389 | *dest = source; | |
390 | return 0; | |
391 | } | |
ceab4e26 BS |
392 | |
393 | int git_object_lookup_bypath( | |
394 | git_object **out, | |
395 | const git_object *treeish, | |
396 | const char *path, | |
397 | git_otype type) | |
398 | { | |
399 | int error = -1; | |
400 | git_tree *tree = NULL; | |
401 | git_tree_entry *entry = NULL; | |
ceab4e26 BS |
402 | |
403 | assert(out && treeish && path); | |
404 | ||
6e9afb97 | 405 | if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0 || |
ba02079f | 406 | (error = git_tree_entry_bypath(&entry, tree, path)) < 0) |
ceab4e26 BS |
407 | { |
408 | goto cleanup; | |
409 | } | |
410 | ||
ba02079f BS |
411 | if (type != GIT_OBJ_ANY && git_tree_entry_type(entry) != type) |
412 | { | |
ceab4e26 BS |
413 | giterr_set(GITERR_OBJECT, |
414 | "object at path '%s' is not of the asked-for type %d", | |
415 | path, type); | |
416 | error = GIT_EINVALIDSPEC; | |
ba02079f | 417 | goto cleanup; |
ceab4e26 BS |
418 | } |
419 | ||
ba02079f BS |
420 | error = git_tree_entry_to_object(out, git_object_owner(treeish), entry); |
421 | ||
ceab4e26 BS |
422 | cleanup: |
423 | git_tree_entry_free(entry); | |
424 | git_tree_free(tree); | |
425 | return error; | |
426 | } | |
13f7ecd7 RB |
427 | |
428 | int git_object_short_id(git_buf *out, const git_object *obj) | |
429 | { | |
430 | git_repository *repo; | |
431 | int len = GIT_ABBREV_DEFAULT, error; | |
432 | git_oid id = {{0}}; | |
433 | git_odb *odb; | |
434 | ||
435 | assert(out && obj); | |
436 | ||
437 | git_buf_sanitize(out); | |
438 | repo = git_object_owner(obj); | |
439 | ||
440 | if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0) | |
441 | return error; | |
442 | ||
443 | if ((error = git_repository_odb(&odb, repo)) < 0) | |
444 | return error; | |
445 | ||
446 | while (len < GIT_OID_HEXSZ) { | |
447 | /* set up short oid */ | |
448 | memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2); | |
449 | if (len & 1) | |
450 | id.id[len / 2] &= 0xf0; | |
451 | ||
452 | error = git_odb_exists_prefix(NULL, odb, &id, len); | |
453 | if (error != GIT_EAMBIGUOUS) | |
454 | break; | |
455 | ||
456 | giterr_clear(); | |
457 | len++; | |
458 | } | |
459 | ||
460 | if (!error && !(error = git_buf_grow(out, len + 1))) { | |
461 | git_oid_tostr(out->ptr, len + 1, &id); | |
462 | out->size = len; | |
463 | } | |
464 | ||
465 | git_odb_free(odb); | |
466 | ||
467 | return error; | |
468 | } | |
469 | ||
3ef01e77 ET |
470 | bool git_object__is_valid( |
471 | git_repository *repo, const git_oid *id, git_otype expected_type) | |
472 | { | |
473 | git_odb *odb; | |
474 | git_otype actual_type; | |
475 | size_t len; | |
476 | int error; | |
477 | ||
478 | if (!git_object__strict_input_validation) | |
479 | return true; | |
480 | ||
481 | if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || | |
482 | (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0) | |
483 | return false; | |
484 | ||
485 | if (expected_type != GIT_OBJ_ANY && expected_type != actual_type) { | |
486 | giterr_set(GITERR_INVALID, | |
487 | "the requested type does not match the type in the ODB"); | |
488 | return false; | |
489 | } | |
490 | ||
491 | return true; | |
492 | } | |
493 |