]> git.proxmox.com Git - libgit2.git/blob - src/object.c
Add range checking around cache opts
[libgit2.git] / src / object.c
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 #include <stdarg.h>
8
9 #include "git2/object.h"
10
11 #include "common.h"
12 #include "repository.h"
13
14 #include "commit.h"
15 #include "tree.h"
16 #include "blob.h"
17 #include "tag.h"
18
19 static const int OBJECT_BASE_SIZE = 4096;
20
21 static struct {
22 const char *str; /* type name string */
23 int loose; /* valid loose object type flag */
24 size_t size; /* size in bytes of the object structure */
25 } git_objects_table[] = {
26 /* 0 = GIT_OBJ__EXT1 */
27 { "", 0, 0},
28
29 /* 1 = GIT_OBJ_COMMIT */
30 { "commit", 1, sizeof(struct git_commit)},
31
32 /* 2 = GIT_OBJ_TREE */
33 { "tree", 1, sizeof(struct git_tree) },
34
35 /* 3 = GIT_OBJ_BLOB */
36 { "blob", 1, sizeof(struct git_blob) },
37
38 /* 4 = GIT_OBJ_TAG */
39 { "tag", 1, sizeof(struct git_tag) },
40
41 /* 5 = GIT_OBJ__EXT2 */
42 { "", 0, 0 },
43
44 /* 6 = GIT_OBJ_OFS_DELTA */
45 { "OFS_DELTA", 0, 0 },
46
47 /* 7 = GIT_OBJ_REF_DELTA */
48 { "REF_DELTA", 0, 0 }
49 };
50
51 static int create_object(git_object **object_out, git_otype type)
52 {
53 git_object *object = NULL;
54
55 assert(object_out);
56
57 *object_out = NULL;
58
59 switch (type) {
60 case GIT_OBJ_COMMIT:
61 case GIT_OBJ_TAG:
62 case GIT_OBJ_BLOB:
63 case GIT_OBJ_TREE:
64 object = git__malloc(git_object__size(type));
65 GITERR_CHECK_ALLOC(object);
66 memset(object, 0x0, git_object__size(type));
67 break;
68
69 default:
70 giterr_set(GITERR_INVALID, "The given type is invalid");
71 return -1;
72 }
73
74 *object_out = object;
75 return 0;
76 }
77
78 int git_object__from_odb_object(
79 git_object **object_out,
80 git_repository *repo,
81 git_odb_object *odb_obj,
82 git_otype type)
83 {
84 int error;
85 git_object *object = NULL;
86
87 if (type != GIT_OBJ_ANY && type != odb_obj->cached.type) {
88 giterr_set(GITERR_INVALID,
89 "The requested type does not match the type in the ODB");
90 return GIT_ENOTFOUND;
91 }
92
93 if ((error = create_object(&object, odb_obj->cached.type)) < 0)
94 return error;
95
96 /* Initialize parent object */
97 git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
98 object->cached.size = odb_obj->cached.size;
99 object->cached.type = odb_obj->cached.type;
100 object->repo = repo;
101
102 switch (object->cached.type) {
103 case GIT_OBJ_COMMIT:
104 error = git_commit__parse((git_commit *)object, odb_obj);
105 break;
106
107 case GIT_OBJ_TREE:
108 error = git_tree__parse((git_tree *)object, odb_obj);
109 break;
110
111 case GIT_OBJ_TAG:
112 error = git_tag__parse((git_tag *)object, odb_obj);
113 break;
114
115 case GIT_OBJ_BLOB:
116 error = git_blob__parse((git_blob *)object, odb_obj);
117 break;
118
119 default:
120 break;
121 }
122
123 if (error < 0) {
124 git_object__free(object);
125 return error;
126 }
127
128 *object_out = git_cache_store_parsed(&repo->objects, object);
129 return 0;
130 }
131
132 int git_object_lookup_prefix(
133 git_object **object_out,
134 git_repository *repo,
135 const git_oid *id,
136 size_t len,
137 git_otype type)
138 {
139 git_object *object = NULL;
140 git_odb *odb = NULL;
141 git_odb_object *odb_obj;
142 int error = 0;
143
144 assert(repo && object_out && id);
145
146 if (len < GIT_OID_MINPREFIXLEN)
147 return GIT_EAMBIGUOUS;
148
149 error = git_repository_odb__weakptr(&odb, repo);
150 if (error < 0)
151 return error;
152
153 if (len > GIT_OID_HEXSZ)
154 len = GIT_OID_HEXSZ;
155
156 if (len == GIT_OID_HEXSZ) {
157 git_cached_obj *cached = NULL;
158
159 /* We want to match the full id : we can first look up in the cache,
160 * since there is no need to check for non ambiguousity
161 */
162 cached = git_cache_get_any(&repo->objects, id);
163 if (cached != NULL) {
164 if (cached->flags == GIT_CACHE_STORE_PARSED) {
165 object = (git_object *)cached;
166
167 if (type != GIT_OBJ_ANY && type != object->cached.type) {
168 git_object_free(object);
169 giterr_set(GITERR_INVALID,
170 "The requested type does not match the type in ODB");
171 return GIT_ENOTFOUND;
172 }
173
174 *object_out = object;
175 return 0;
176 } else if (cached->flags == GIT_CACHE_STORE_RAW) {
177 odb_obj = (git_odb_object *)cached;
178 } else {
179 assert(!"Wrong caching type in the global object cache");
180 }
181 } else {
182 /* Object was not found in the cache, let's explore the backends.
183 * We could just use git_odb_read_unique_short_oid,
184 * it is the same cost for packed and loose object backends,
185 * but it may be much more costly for sqlite and hiredis.
186 */
187 error = git_odb_read(&odb_obj, odb, id);
188 }
189 } else {
190 git_oid short_oid;
191
192 /* We copy the first len*4 bits from id and fill the remaining with 0s */
193 memcpy(short_oid.id, id->id, (len + 1) / 2);
194 if (len % 2)
195 short_oid.id[len / 2] &= 0xF0;
196 memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);
197
198 /* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
199 * 2 options :
200 * - We always search in the cache first. If we find that short oid is
201 * ambiguous, we can stop. But in all the other cases, we must then
202 * explore all the backends (to find an object if there was match,
203 * or to check that oid is not ambiguous if we have found 1 match in
204 * the cache)
205 * - We never explore the cache, go right to exploring the backends
206 * We chose the latter : we explore directly the backends.
207 */
208 error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
209 }
210
211 if (error < 0)
212 return error;
213
214 error = git_object__from_odb_object(object_out, repo, odb_obj, type);
215
216 git_odb_object_free(odb_obj);
217
218 return error;
219 }
220
221 int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
222 return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
223 }
224
225 void git_object__free(void *_obj)
226 {
227 git_object *object = (git_object *)_obj;
228
229 assert(object);
230
231 switch (object->cached.type) {
232 case GIT_OBJ_COMMIT:
233 git_commit__free((git_commit *)object);
234 break;
235
236 case GIT_OBJ_TREE:
237 git_tree__free((git_tree *)object);
238 break;
239
240 case GIT_OBJ_TAG:
241 git_tag__free((git_tag *)object);
242 break;
243
244 case GIT_OBJ_BLOB:
245 git_blob__free((git_blob *)object);
246 break;
247
248 default:
249 git__free(object);
250 break;
251 }
252 }
253
254 void git_object_free(git_object *object)
255 {
256 if (object == NULL)
257 return;
258
259 git_cached_obj_decref(object);
260 }
261
262 const git_oid *git_object_id(const git_object *obj)
263 {
264 assert(obj);
265 return &obj->cached.oid;
266 }
267
268 git_otype git_object_type(const git_object *obj)
269 {
270 assert(obj);
271 return obj->cached.type;
272 }
273
274 git_repository *git_object_owner(const git_object *obj)
275 {
276 assert(obj);
277 return obj->repo;
278 }
279
280 const char *git_object_type2string(git_otype type)
281 {
282 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
283 return "";
284
285 return git_objects_table[type].str;
286 }
287
288 git_otype git_object_string2type(const char *str)
289 {
290 size_t i;
291
292 if (!str || !*str)
293 return GIT_OBJ_BAD;
294
295 for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
296 if (!strcmp(str, git_objects_table[i].str))
297 return (git_otype)i;
298
299 return GIT_OBJ_BAD;
300 }
301
302 int git_object_typeisloose(git_otype type)
303 {
304 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
305 return 0;
306
307 return git_objects_table[type].loose;
308 }
309
310 size_t git_object__size(git_otype type)
311 {
312 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
313 return 0;
314
315 return git_objects_table[type].size;
316 }
317
318 static int dereference_object(git_object **dereferenced, git_object *obj)
319 {
320 git_otype type = git_object_type(obj);
321
322 switch (type) {
323 case GIT_OBJ_COMMIT:
324 return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
325
326 case GIT_OBJ_TAG:
327 return git_tag_target(dereferenced, (git_tag*)obj);
328
329 case GIT_OBJ_BLOB:
330 return GIT_ENOTFOUND;
331
332 case GIT_OBJ_TREE:
333 return GIT_EAMBIGUOUS;
334
335 default:
336 return GIT_EINVALIDSPEC;
337 }
338 }
339
340 static int peel_error(int error, const git_oid *oid, git_otype type)
341 {
342 const char *type_name;
343 char hex_oid[GIT_OID_HEXSZ + 1];
344
345 type_name = git_object_type2string(type);
346
347 git_oid_fmt(hex_oid, oid);
348 hex_oid[GIT_OID_HEXSZ] = '\0';
349
350 giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be "
351 "successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type);
352
353 return error;
354 }
355
356 int git_object_peel(
357 git_object **peeled,
358 const git_object *object,
359 git_otype target_type)
360 {
361 git_object *source, *deref = NULL;
362 int error;
363
364 if (target_type != GIT_OBJ_TAG &&
365 target_type != GIT_OBJ_COMMIT &&
366 target_type != GIT_OBJ_TREE &&
367 target_type != GIT_OBJ_BLOB &&
368 target_type != GIT_OBJ_ANY)
369 return GIT_EINVALIDSPEC;
370
371 assert(object && peeled);
372
373 if (git_object_type(object) == target_type)
374 return git_object_dup(peeled, (git_object *)object);
375
376 source = (git_object *)object;
377
378 while (!(error = dereference_object(&deref, source))) {
379
380 if (source != object)
381 git_object_free(source);
382
383 if (git_object_type(deref) == target_type) {
384 *peeled = deref;
385 return 0;
386 }
387
388 if (target_type == GIT_OBJ_ANY &&
389 git_object_type(deref) != git_object_type(object))
390 {
391 *peeled = deref;
392 return 0;
393 }
394
395 source = deref;
396 deref = NULL;
397 }
398
399 if (source != object)
400 git_object_free(source);
401
402 git_object_free(deref);
403
404 if (error)
405 error = peel_error(error, git_object_id(object), target_type);
406
407 return error;
408 }
409
410 int git_object_dup(git_object **dest, git_object *source)
411 {
412 git_cached_obj_incref(source);
413 *dest = source;
414 return 0;
415 }