Commit f335b42c by Vicent Marti

Fix segmentation fault when freeing a repository

Disable garbage collection of cross-references to prevent
double-freeing. Internal reference management is now done
with a separate method.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent e06dd9b6
...@@ -132,7 +132,7 @@ int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *pa ...@@ -132,7 +132,7 @@ int git_blob_writefile(git_oid *written_id, git_repository *repo, const char *pa
/* FIXME: maybe we don't want to free this already? /* FIXME: maybe we don't want to free this already?
* the user may want to access it again */ * the user may want to access it again */
git_object_close((git_object *)blob); GIT_OBJECT_DECREF(repo, blob);
return GIT_SUCCESS; return GIT_SUCCESS;
} }
...@@ -48,7 +48,7 @@ static void clear_parents(git_commit *commit) ...@@ -48,7 +48,7 @@ static void clear_parents(git_commit *commit)
for (i = 0; i < commit->parents.length; ++i) { for (i = 0; i < commit->parents.length; ++i) {
git_commit *parent = git_vector_get(&commit->parents, i); git_commit *parent = git_vector_get(&commit->parents, i);
git_object_close((git_object *)parent); GIT_OBJECT_DECREF(commit->object.repo, parent);
} }
git_vector_clear(&commit->parents); git_vector_clear(&commit->parents);
...@@ -62,7 +62,7 @@ void git_commit__free(git_commit *commit) ...@@ -62,7 +62,7 @@ void git_commit__free(git_commit *commit)
git_signature_free(commit->author); git_signature_free(commit->author);
git_signature_free(commit->committer); git_signature_free(commit->committer);
git_object_close((git_object *)commit->tree); GIT_OBJECT_DECREF(commit->object.repo, commit->tree);
free(commit->message); free(commit->message);
free(commit->message_short); free(commit->message_short);
...@@ -130,7 +130,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int ...@@ -130,7 +130,7 @@ int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int
if ((error = git__parse_oid(&oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS) if ((error = git__parse_oid(&oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
return error; return error;
git_object_close((git_object *)commit->tree); GIT_OBJECT_DECREF(commit->object.repo, commit->tree);
if ((error = git_object_lookup((git_object **)&commit->tree, commit->object.repo, &oid, GIT_OBJ_TREE)) < GIT_SUCCESS) if ((error = git_object_lookup((git_object **)&commit->tree, commit->object.repo, &oid, GIT_OBJ_TREE)) < GIT_SUCCESS)
return error; return error;
...@@ -249,7 +249,7 @@ const git_tree *git_commit_tree(git_commit *commit) ...@@ -249,7 +249,7 @@ const git_tree *git_commit_tree(git_commit *commit)
if (!commit->object.in_memory && commit->tree == NULL) if (!commit->object.in_memory && commit->tree == NULL)
git_commit__parse_full(commit); git_commit__parse_full(commit);
GIT_OBJECT_INCREF(commit->tree); GIT_OBJECT_INCREF(commit->object.repo, commit->tree);
return commit->tree; return commit->tree;
} }
...@@ -283,7 +283,7 @@ git_commit *git_commit_parent(git_commit *commit, unsigned int n) ...@@ -283,7 +283,7 @@ git_commit *git_commit_parent(git_commit *commit, unsigned int n)
assert(commit); assert(commit);
parent = git_vector_get(&commit->parents, n); parent = git_vector_get(&commit->parents, n);
GIT_OBJECT_INCREF(parent); GIT_OBJECT_INCREF(commit->object.repo, parent);
return parent; return parent;
} }
...@@ -293,8 +293,8 @@ void git_commit_set_tree(git_commit *commit, git_tree *tree) ...@@ -293,8 +293,8 @@ void git_commit_set_tree(git_commit *commit, git_tree *tree)
commit->object.modified = 1; commit->object.modified = 1;
CHECK_FULL_PARSE(); CHECK_FULL_PARSE();
git_object_close((git_object *)commit->tree); GIT_OBJECT_DECREF(commit->object.repo, commit->tree);
GIT_OBJECT_INCREF(tree); GIT_OBJECT_INCREF(commit->object.repo, tree);
commit->tree = tree; commit->tree = tree;
} }
...@@ -353,6 +353,6 @@ int git_commit_add_parent(git_commit *commit, git_commit *new_parent) ...@@ -353,6 +353,6 @@ int git_commit_add_parent(git_commit *commit, git_commit *new_parent)
CHECK_FULL_PARSE(); CHECK_FULL_PARSE();
commit->object.modified = 1; commit->object.modified = 1;
GIT_OBJECT_INCREF(new_parent); GIT_OBJECT_INCREF(commit->object.repo, new_parent);
return git_vector_insert(&commit->parents, new_parent); return git_vector_insert(&commit->parents, new_parent);
} }
...@@ -277,7 +277,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o ...@@ -277,7 +277,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
object = git_hashtable_lookup(repo->objects, id); object = git_hashtable_lookup(repo->objects, id);
if (object != NULL) { if (object != NULL) {
*object_out = object; *object_out = object;
GIT_OBJECT_INCREF(object); GIT_OBJECT_INCREF(repo, object);
return GIT_SUCCESS; return GIT_SUCCESS;
} }
...@@ -330,7 +330,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o ...@@ -330,7 +330,7 @@ int git_object_lookup(git_object **object_out, git_repository *repo, const git_o
git_object__source_close(object); git_object__source_close(object);
git_hashtable_insert(repo->objects, &object->id, object); git_hashtable_insert(repo->objects, &object->id, object);
GIT_OBJECT_INCREF(object); GIT_OBJECT_INCREF(repo, object);
*object_out = object; *object_out = object;
return GIT_SUCCESS; return GIT_SUCCESS;
} }
...@@ -384,15 +384,6 @@ void git_object__free(git_object *object) ...@@ -384,15 +384,6 @@ void git_object__free(git_object *object)
git_object__source_close(object); git_object__source_close(object);
if (object->repo != NULL) {
if (object->in_memory) {
int idx = git_vector_search(&object->repo->memory_objects, object);
git_vector_remove(&object->repo->memory_objects, idx);
} else {
git_hashtable_remove(object->repo->objects, &object->id);
}
}
switch (object->source.raw.type) { switch (object->source.raw.type) {
case GIT_OBJ_COMMIT: case GIT_OBJ_COMMIT:
git_commit__free((git_commit *)object); git_commit__free((git_commit *)object);
...@@ -421,8 +412,18 @@ void git_object_close(git_object *object) ...@@ -421,8 +412,18 @@ void git_object_close(git_object *object)
if (object == NULL) if (object == NULL)
return; return;
if (--object->refcount <= 0) if (--object->refcount <= 0) {
if (object->repo != NULL) {
if (object->in_memory) {
int idx = git_vector_search(&object->repo->memory_objects, object);
git_vector_remove(&object->repo->memory_objects, idx);
} else {
git_hashtable_remove(object->repo->objects, &object->id);
}
}
git_object__free(object); git_object__free(object);
}
} }
const git_oid *git_object_id(const git_object *obj) const git_oid *git_object_id(const git_object *obj)
......
...@@ -216,6 +216,7 @@ static git_repository *repository_alloc() ...@@ -216,6 +216,7 @@ static git_repository *repository_alloc()
return NULL; return NULL;
} }
repo->gc_enabled = 1;
return repo; return repo;
} }
...@@ -387,11 +388,7 @@ void git_repository_free(git_repository *repo) ...@@ -387,11 +388,7 @@ void git_repository_free(git_repository *repo)
if (repo == NULL) if (repo == NULL)
return; return;
/* Increment the refcount of all the objects in the repository repo->gc_enabled = 0;
* to prevent freeing dependencies */
GIT_HASHTABLE_FOREACH(repo->objects, _unused, object,
GIT_OBJECT_INCREF(object);
);
/* force free all the objects */ /* force free all the objects */
GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, GIT_HASHTABLE_FOREACH(repo->objects, _unused, object,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "git2/oid.h" #include "git2/oid.h"
#include "git2/odb.h" #include "git2/odb.h"
#include "git2/repository.h" #include "git2/repository.h"
#include "git2/object.h"
#include "hashtable.h" #include "hashtable.h"
#include "index.h" #include "index.h"
...@@ -44,7 +45,7 @@ struct git_repository { ...@@ -44,7 +45,7 @@ struct git_repository {
char *path_odb; char *path_odb;
char *path_workdir; char *path_workdir;
unsigned is_bare:1; unsigned is_bare:1, gc_enabled:1;
}; };
int git_object__source_open(git_object *object); int git_object__source_open(git_object *object);
...@@ -60,12 +61,19 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len); ...@@ -60,12 +61,19 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len);
int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header); int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid); int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid);
#define GIT_OBJECT_INCREF(ob) git_object__incref((git_object *)(ob)) #define GIT_OBJECT_INCREF(repo, ob) git_object__incref((repo), (git_object *)(ob))
#define GIT_OBJECT_DECREF(repo, ob) git_object__decref((repo), (git_object *)(ob))
GIT_INLINE(void) git_object__incref(struct git_object *object) GIT_INLINE(void) git_object__incref(git_repository *repo, struct git_object *object)
{ {
if (object) if (repo && repo->gc_enabled && object)
object->refcount++; object->refcount++;
} }
GIT_INLINE(void) git_object__decref(git_repository *repo, struct git_object *object)
{
if (repo && repo->gc_enabled && object)
git_object_close(object);
}
#endif #endif
...@@ -114,7 +114,7 @@ static git_revwalk_commit *commit_to_walkcommit(git_revwalk *walk, git_commit *c ...@@ -114,7 +114,7 @@ static git_revwalk_commit *commit_to_walkcommit(git_revwalk *walk, git_commit *c
memset(commit, 0x0, sizeof(git_revwalk_commit)); memset(commit, 0x0, sizeof(git_revwalk_commit));
commit->commit_object = commit_object; commit->commit_object = commit_object;
GIT_OBJECT_INCREF(commit_object); GIT_OBJECT_INCREF(walk->repo, commit_object);
git_hashtable_insert(walk->commits, commit_object, commit); git_hashtable_insert(walk->commits, commit_object, commit);
...@@ -230,7 +230,7 @@ int git_revwalk_next(git_commit **commit, git_revwalk *walk) ...@@ -230,7 +230,7 @@ int git_revwalk_next(git_commit **commit, git_revwalk *walk)
while ((next = walk->next(&walk->iterator)) != NULL) { while ((next = walk->next(&walk->iterator)) != NULL) {
if (!next->uninteresting) { if (!next->uninteresting) {
*commit = next->commit_object; *commit = next->commit_object;
GIT_OBJECT_INCREF(*commit); GIT_OBJECT_INCREF(walk->repo, *commit);
return GIT_SUCCESS; return GIT_SUCCESS;
} }
} }
...@@ -248,7 +248,7 @@ void git_revwalk_reset(git_revwalk *walk) ...@@ -248,7 +248,7 @@ void git_revwalk_reset(git_revwalk *walk)
assert(walk); assert(walk);
GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, { GIT_HASHTABLE_FOREACH(walk->commits, _unused, commit, {
git_object_close((git_object *)commit->commit_object); GIT_OBJECT_DECREF(walk->repo, commit->commit_object);
git_revwalk_list_clear(&commit->parents); git_revwalk_list_clear(&commit->parents);
free(commit); free(commit);
}); });
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
void git_tag__free(git_tag *tag) void git_tag__free(git_tag *tag)
{ {
git_signature_free(tag->tagger); git_signature_free(tag->tagger);
git_object_close(tag->target); GIT_OBJECT_DECREF(tag->object.repo, tag->target);
free(tag->message); free(tag->message);
free(tag->tag_name); free(tag->tag_name);
free(tag); free(tag);
...@@ -49,7 +49,7 @@ const git_oid *git_tag_id(git_tag *c) ...@@ -49,7 +49,7 @@ const git_oid *git_tag_id(git_tag *c)
const git_object *git_tag_target(git_tag *t) const git_object *git_tag_target(git_tag *t)
{ {
assert(t); assert(t);
GIT_OBJECT_INCREF(t->target); GIT_OBJECT_INCREF(t->object.repo, t->target);
return t->target; return t->target;
} }
...@@ -57,8 +57,8 @@ void git_tag_set_target(git_tag *tag, git_object *target) ...@@ -57,8 +57,8 @@ void git_tag_set_target(git_tag *tag, git_object *target)
{ {
assert(tag && target); assert(tag && target);
git_object_close(tag->target); GIT_OBJECT_DECREF(tag->object.repo, tag->target);
GIT_OBJECT_INCREF(target); GIT_OBJECT_INCREF(tag->object.repo, target);
tag->object.modified = 1; tag->object.modified = 1;
tag->target = target; tag->target = target;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment