]> git.proxmox.com Git - libgit2.git/blob - src/object.c
Fix segmentation fault when freeing a repository
[libgit2.git] / src / object.c
1 /*
2 * This file is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License, version 2,
4 * as published by the Free Software Foundation.
5 *
6 * In addition to the permissions in the GNU General Public License,
7 * the authors give you unlimited permission to link the compiled
8 * version of this file into combinations with other programs,
9 * and to distribute those combinations without any restriction
10 * coming from the use of this file. (The General Public License
11 * restrictions do apply in other respects; for example, they cover
12 * modification of the file, and distribution when not linked into
13 * a combined executable.)
14 *
15 * This file is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; see the file COPYING. If not, write to
22 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25 #include <stdarg.h>
26
27 #include "git2/object.h"
28
29 #include "common.h"
30 #include "repository.h"
31
32 #include "commit.h"
33 #include "tree.h"
34 #include "blob.h"
35 #include "tag.h"
36
37 static const int OBJECT_BASE_SIZE = 4096;
38
39 static struct {
40 const char *str; /* type name string */
41 int loose; /* valid loose object type flag */
42 size_t size; /* size in bytes of the object structure */
43 } git_objects_table[] = {
44 /* 0 = GIT_OBJ__EXT1 */
45 { "", 0, 0},
46
47 /* 1 = GIT_OBJ_COMMIT */
48 { "commit", 1, sizeof(struct git_commit)},
49
50 /* 2 = GIT_OBJ_TREE */
51 { "tree", 1, sizeof(struct git_tree) },
52
53 /* 3 = GIT_OBJ_BLOB */
54 { "blob", 1, sizeof(struct git_blob) },
55
56 /* 4 = GIT_OBJ_TAG */
57 { "tag", 1, sizeof(struct git_tag) },
58
59 /* 5 = GIT_OBJ__EXT2 */
60 { "", 0, 0 },
61
62 /* 6 = GIT_OBJ_OFS_DELTA */
63 { "OFS_DELTA", 0, 0 },
64
65 /* 7 = GIT_OBJ_REF_DELTA */
66 { "REF_DELTA", 0, 0 }
67 };
68
69 /*
70 * Object source methods
71 *
72 * Abstract buffer methods that allow the writeback system
73 * to prepare the contents of any git file in-memory before
74 * writing them to disk.
75 */
76 static int source_resize(git_odb_source *src)
77 {
78 size_t write_offset, new_size;
79 void *new_data;
80
81 write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data);
82
83 new_size = src->raw.len * 2;
84 if ((new_data = git__malloc(new_size)) == NULL)
85 return GIT_ENOMEM;
86
87 memcpy(new_data, src->raw.data, src->written_bytes);
88 free(src->raw.data);
89
90 src->raw.data = new_data;
91 src->raw.len = new_size;
92 src->write_ptr = (char *)new_data + write_offset;
93
94 return GIT_SUCCESS;
95 }
96
97 int git__source_printf(git_odb_source *source, const char *format, ...)
98 {
99 va_list arglist;
100 int len;
101
102 assert(source->open && source->write_ptr);
103
104 va_start(arglist, format);
105
106 len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
107
108 while (source->written_bytes + len >= source->raw.len) {
109 if (source_resize(source) < GIT_SUCCESS)
110 return GIT_ENOMEM;
111
112 len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
113 }
114
115 source->write_ptr = (char *)source->write_ptr + len;
116 source->written_bytes += len;
117
118 return GIT_SUCCESS;
119 }
120
121 int git__source_write(git_odb_source *source, const void *bytes, size_t len)
122 {
123 assert(source);
124
125 assert(source->open && source->write_ptr);
126
127 while (source->written_bytes + len >= source->raw.len) {
128 if (source_resize(source) < GIT_SUCCESS)
129 return GIT_ENOMEM;
130 }
131
132 memcpy(source->write_ptr, bytes, len);
133 source->write_ptr = (char *)source->write_ptr + len;
134 source->written_bytes += len;
135
136 return GIT_SUCCESS;
137 }
138
139 static void prepare_write(git_object *object)
140 {
141 if (object->source.write_ptr != NULL || object->source.open)
142 git_object__source_close(object);
143
144 /* TODO: proper size calculation */
145 object->source.raw.data = git__malloc(OBJECT_BASE_SIZE);
146 object->source.raw.len = OBJECT_BASE_SIZE;
147
148 object->source.write_ptr = object->source.raw.data;
149 object->source.written_bytes = 0;
150
151 object->source.open = 1;
152 }
153
154 static int write_back(git_object *object)
155 {
156 int error;
157 git_oid new_id;
158
159 assert(object);
160
161 assert(object->source.open);
162 assert(object->modified);
163
164 object->source.raw.len = object->source.written_bytes;
165
166 if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < GIT_SUCCESS)
167 return error;
168
169 if (object->in_memory) {
170 int idx = git_vector_search(&object->repo->memory_objects, object);
171 git_vector_remove(&object->repo->memory_objects, idx);
172 } else {
173 git_hashtable_remove(object->repo->objects, &object->id);
174 }
175
176 git_oid_cpy(&object->id, &new_id);
177 git_hashtable_insert(object->repo->objects, &object->id, object);
178
179 object->source.write_ptr = NULL;
180 object->source.written_bytes = 0;
181
182 object->modified = 0;
183 object->in_memory = 0;
184
185 git_object__source_close(object);
186 return GIT_SUCCESS;
187 }
188
189 int git_object__source_open(git_object *object)
190 {
191 int error;
192
193 assert(object && !object->in_memory);
194
195 if (object->source.open)
196 git_object__source_close(object);
197
198 error = git_odb_read(&object->source.raw, object->repo->db, &object->id);
199 if (error < GIT_SUCCESS)
200 return error;
201
202 object->source.open = 1;
203 return GIT_SUCCESS;
204 }
205
206 void git_object__source_close(git_object *object)
207 {
208 assert(object);
209
210 if (object->source.open) {
211 git_rawobj_close(&object->source.raw);
212 object->source.open = 0;
213 }
214 }
215
216 static int create_object(git_object **object_out, git_otype type)
217 {
218 git_object *object = NULL;
219
220 assert(object_out);
221
222 *object_out = NULL;
223
224 switch (type) {
225 case GIT_OBJ_COMMIT:
226 case GIT_OBJ_TAG:
227 case GIT_OBJ_BLOB:
228 object = git__malloc(git_object__size(type));
229 if (object == NULL)
230 return GIT_ENOMEM;
231 memset(object, 0x0, git_object__size(type));
232 break;
233
234 case GIT_OBJ_TREE:
235 object = (git_object *)git_tree__new();
236 if (object == NULL)
237 return GIT_ENOMEM;
238 break;
239
240 default:
241 return GIT_EINVALIDTYPE;
242 }
243
244 *object_out = object;
245 return GIT_SUCCESS;
246 }
247
248 int git_object_new(git_object **object_out, git_repository *repo, git_otype type)
249 {
250 git_object *object = NULL;
251 int error;
252
253 assert(object_out && repo);
254
255 if ((error = create_object(&object, type)) < GIT_SUCCESS)
256 return error;
257
258 object->repo = repo;
259 object->in_memory = 1;
260 object->modified = 1;
261
262 object->source.raw.type = type;
263
264 object->refcount++;
265 *object_out = object;
266 return GIT_SUCCESS;
267 }
268
269 int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
270 {
271 git_object *object = NULL;
272 git_rawobj obj_file;
273 int error = GIT_SUCCESS;
274
275 assert(repo && object_out && id);
276
277 object = git_hashtable_lookup(repo->objects, id);
278 if (object != NULL) {
279 *object_out = object;
280 GIT_OBJECT_INCREF(repo, object);
281 return GIT_SUCCESS;
282 }
283
284 error = git_odb_read(&obj_file, repo->db, id);
285 if (error < GIT_SUCCESS)
286 return error;
287
288 if (type != GIT_OBJ_ANY && type != obj_file.type) {
289 git_rawobj_close(&obj_file);
290 return GIT_EINVALIDTYPE;
291 }
292
293 type = obj_file.type;
294
295 if ((error = create_object(&object, type)) < GIT_SUCCESS)
296 return error;
297
298 /* Initialize parent object */
299 git_oid_cpy(&object->id, id);
300 object->repo = repo;
301 memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
302 object->source.open = 1;
303
304 switch (type) {
305 case GIT_OBJ_COMMIT:
306 error = git_commit__parse((git_commit *)object);
307 break;
308
309 case GIT_OBJ_TREE:
310 error = git_tree__parse((git_tree *)object);
311 break;
312
313 case GIT_OBJ_TAG:
314 error = git_tag__parse((git_tag *)object);
315 break;
316
317 case GIT_OBJ_BLOB:
318 error = git_blob__parse((git_blob *)object);
319 break;
320
321 default:
322 break;
323 }
324
325 if (error < GIT_SUCCESS) {
326 git_object__free(object);
327 return error;
328 }
329
330 git_object__source_close(object);
331 git_hashtable_insert(repo->objects, &object->id, object);
332
333 GIT_OBJECT_INCREF(repo, object);
334 *object_out = object;
335 return GIT_SUCCESS;
336 }
337
338 int git_object_write(git_object *object)
339 {
340 int error;
341 git_odb_source *source;
342
343 assert(object);
344
345 if (object->modified == 0)
346 return GIT_SUCCESS;
347
348 prepare_write(object);
349 source = &object->source;
350
351 switch (source->raw.type) {
352 case GIT_OBJ_COMMIT:
353 error = git_commit__writeback((git_commit *)object, source);
354 break;
355
356 case GIT_OBJ_TREE:
357 error = git_tree__writeback((git_tree *)object, source);
358 break;
359
360 case GIT_OBJ_TAG:
361 error = git_tag__writeback((git_tag *)object, source);
362 break;
363
364 case GIT_OBJ_BLOB:
365 error = git_blob__writeback((git_blob *)object, source);
366 break;
367
368 default:
369 error = GIT_ERROR;
370 break;
371 }
372
373 if (error < GIT_SUCCESS) {
374 git_object__source_close(object);
375 return error;
376 }
377
378 return write_back(object);
379 }
380
381 void git_object__free(git_object *object)
382 {
383 assert(object);
384
385 git_object__source_close(object);
386
387 switch (object->source.raw.type) {
388 case GIT_OBJ_COMMIT:
389 git_commit__free((git_commit *)object);
390 break;
391
392 case GIT_OBJ_TREE:
393 git_tree__free((git_tree *)object);
394 break;
395
396 case GIT_OBJ_TAG:
397 git_tag__free((git_tag *)object);
398 break;
399
400 case GIT_OBJ_BLOB:
401 git_blob__free((git_blob *)object);
402 break;
403
404 default:
405 free(object);
406 break;
407 }
408 }
409
410 void git_object_close(git_object *object)
411 {
412 if (object == NULL)
413 return;
414
415 if (--object->refcount <= 0) {
416 if (object->repo != NULL) {
417 if (object->in_memory) {
418 int idx = git_vector_search(&object->repo->memory_objects, object);
419 git_vector_remove(&object->repo->memory_objects, idx);
420 } else {
421 git_hashtable_remove(object->repo->objects, &object->id);
422 }
423 }
424
425 git_object__free(object);
426 }
427 }
428
429 const git_oid *git_object_id(const git_object *obj)
430 {
431 assert(obj);
432
433 if (obj->in_memory)
434 return NULL;
435
436 return &obj->id;
437 }
438
439 git_otype git_object_type(const git_object *obj)
440 {
441 assert(obj);
442 return obj->source.raw.type;
443 }
444
445 git_repository *git_object_owner(const git_object *obj)
446 {
447 assert(obj);
448 return obj->repo;
449 }
450
451 const char *git_object_type2string(git_otype type)
452 {
453 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
454 return "";
455
456 return git_objects_table[type].str;
457 }
458
459 git_otype git_object_string2type(const char *str)
460 {
461 size_t i;
462
463 if (!str || !*str)
464 return GIT_OBJ_BAD;
465
466 for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
467 if (!strcmp(str, git_objects_table[i].str))
468 return (git_otype)i;
469
470 return GIT_OBJ_BAD;
471 }
472
473 int git_object_typeisloose(git_otype type)
474 {
475 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
476 return 0;
477
478 return git_objects_table[type].loose;
479 }
480
481 size_t git_object__size(git_otype type)
482 {
483 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
484 return 0;
485
486 return git_objects_table[type].size;
487 }
488