Commit 58519018 by Vicent Marti

Fix internal memory management on the library

String mememory is now managed in a much more sane manner.

Fixes include:

	- git_person email and name is no longer limited to 64 characters
	- git_tree_entry filename is no longer limited to 255 characters
	- raw objects are properly opened & closed the minimum amount of
	times required for parsing
	- unit tests no longer leak
	- removed 5 other misc memory leaks as reported by Valgrind
	- tree writeback no longer segfaults on rare ocassions

The git_person struct is no longer public. It is now managed by the
library, and getter methods are in place to access its internal
attributes.

Signed-off-by: Vicent Marti <tanoku@gmail.com>
parent 2d16373c
...@@ -23,11 +23,14 @@ ...@@ -23,11 +23,14 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include "git/common.h"
#include "git/odb.h"
#include "git/repository.h"
#include "common.h" #include "common.h"
#include "commit.h" #include "commit.h"
#include "revwalk.h" #include "revwalk.h"
#include "git/odb.h" #include "person.h"
#include "git/repository.h"
#define COMMIT_BASIC_PARSE 0x0 #define COMMIT_BASIC_PARSE 0x0
#define COMMIT_FULL_PARSE 0x1 #define COMMIT_FULL_PARSE 0x1
...@@ -56,8 +59,9 @@ void git_commit__free(git_commit *commit) ...@@ -56,8 +59,9 @@ void git_commit__free(git_commit *commit)
{ {
clear_parents(commit); clear_parents(commit);
free(commit->author); git_person__free(commit->author);
free(commit->committer); git_person__free(commit->committer);
free(commit->message); free(commit->message);
free(commit->message_short); free(commit->message_short);
free(commit); free(commit);
...@@ -73,146 +77,12 @@ const git_oid *git_commit_id(git_commit *c) ...@@ -73,146 +77,12 @@ const git_oid *git_commit_id(git_commit *c)
return git_object_id((git_object *)c); return git_object_id((git_object *)c);
} }
int git_commit__parse(git_commit *commit)
{
const int close_db_object = 1;
int error = 0;
if ((error = git_object__source_open((git_object *)commit)) < 0)
return error;
error = git_commit__parse_buffer(commit,
commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_BASIC_PARSE);
if (close_db_object)
git_object__source_close((git_object *)commit);
return error;
}
int git_commit__parse_full(git_commit *commit)
{
int error;
if (commit->full_parse)
return 0;
if (git_object__source_open((git_object *)commit) < 0)
return GIT_ERROR;
error = git_commit__parse_buffer(commit,
commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_FULL_PARSE);
git_object__source_close((git_object *)commit);
commit->full_parse = 1;
return error;
}
git_commit *git_commit_lookup(git_repository *repo, const git_oid *id) git_commit *git_commit_lookup(git_repository *repo, const git_oid *id)
{ {
return (git_commit *)git_repository_lookup(repo, id, GIT_OBJ_COMMIT); return (git_commit *)git_repository_lookup(repo, id, GIT_OBJ_COMMIT);
} }
int git__parse_person(git_person *person, char **buffer_out,
const char *buffer_end, const char *header)
{
const size_t header_len = strlen(header);
int i;
char *buffer = *buffer_out;
char *line_end, *name, *email;
line_end = memchr(buffer, '\n', buffer_end - buffer);
if (!line_end)
return GIT_EOBJCORRUPTED;
if (buffer + (header_len + 1) > line_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, header, header_len) != 0)
return GIT_EOBJCORRUPTED;
buffer += header_len;
/* Parse name field */
for (i = 0, name = person->name;
i < 64 && buffer < line_end && *buffer != '<';
++i)
*name++ = *buffer++;
*(name - 1) = 0;
while (buffer < line_end && *buffer != '<')
buffer++;
if (++buffer >= line_end)
return GIT_EOBJCORRUPTED;
/* Parse email field */
for (i = 0, email = person->email;
i < 64 && buffer < line_end && *buffer != '>';
++i)
*email++ = *buffer++;
*email = 0;
while (buffer < line_end && *buffer != '>')
buffer++;
if (++buffer >= line_end)
return GIT_EOBJCORRUPTED;
person->time = strtol(buffer, &buffer, 10);
if (person->time == 0)
return GIT_EOBJCORRUPTED;
*buffer_out = (line_end + 1);
return 0;
}
int git__write_person(git_odb_source *src, const char *header, const git_person *person)
{
return git__source_printf(src, "%s %s <%s> %u\n", header, person->name, person->email, person->time);
}
int git__parse_oid(git_oid *oid, char **buffer_out,
const char *buffer_end, const char *header)
{
const size_t sha_len = GIT_OID_HEXSZ;
const size_t header_len = strlen(header);
char *buffer = *buffer_out;
if (buffer + (header_len + sha_len + 1) > buffer_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, header, header_len) != 0)
return GIT_EOBJCORRUPTED;
if (buffer[header_len + sha_len] != '\n')
return GIT_EOBJCORRUPTED;
if (git_oid_mkstr(oid, buffer + header_len) < 0)
return GIT_EOBJCORRUPTED;
*buffer_out = buffer + (header_len + sha_len + 1);
return 0;
}
int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid)
{
char hex_oid[41];
git_oid_fmt(hex_oid, oid);
hex_oid[40] = 0;
return git__source_printf(src, "%s %s\n", header, hex_oid);
}
int git_commit__writeback(git_commit *commit, git_odb_source *src) int git_commit__writeback(git_commit *commit, git_odb_source *src)
{ {
git_commit_parents *parent; git_commit_parents *parent;
...@@ -232,12 +102,12 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src) ...@@ -232,12 +102,12 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src)
if (commit->author == NULL) if (commit->author == NULL)
return GIT_ERROR; return GIT_ERROR;
git__write_person(src, "author", commit->author); git_person__write(src, "author", commit->author);
if (commit->committer == NULL) if (commit->committer == NULL)
return GIT_ERROR; return GIT_ERROR;
git__write_person(src, "committer", commit->committer); git_person__write(src, "committer", commit->committer);
if (commit->message != NULL) if (commit->message != NULL)
git__source_printf(src, "\n%s", commit->message); git__source_printf(src, "\n%s", commit->message);
...@@ -245,13 +115,12 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src) ...@@ -245,13 +115,12 @@ int git_commit__writeback(git_commit *commit, git_odb_source *src)
return GIT_SUCCESS; return GIT_SUCCESS;
} }
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags) int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags)
{ {
char *buffer = (char *)data; char *buffer = (char *)data;
const char *buffer_end = (char *)data + len; const char *buffer_end = (char *)data + len;
git_oid oid; git_oid oid;
git_person person;
if (git__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0) if (git__parse_oid(&oid, &buffer, buffer_end, "tree ") < 0)
return GIT_EOBJCORRUPTED; return GIT_EOBJCORRUPTED;
...@@ -279,29 +148,31 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigne ...@@ -279,29 +148,31 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigne
commit->parents = node; commit->parents = node;
} }
if (git__parse_person(&person, &buffer, buffer_end, "author ") < 0)
return GIT_EOBJCORRUPTED;
if (parse_flags & COMMIT_FULL_PARSE) { if (parse_flags & COMMIT_FULL_PARSE) {
if (commit->author) if (commit->author)
free(commit->author); git_person__free(commit->author);
commit->author = git__malloc(sizeof(git_person)); commit->author = git__malloc(sizeof(git_person));
memcpy(commit->author, &person, sizeof(git_person)); if (git_person__parse(commit->author, &buffer, buffer_end, "author ") < 0)
} return GIT_EOBJCORRUPTED;
if (git__parse_person(&person, &buffer, buffer_end, "committer ") < 0) } else {
return GIT_EOBJCORRUPTED; if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
return GIT_EOBJCORRUPTED;
commit->commit_time = person.time; buffer++;
}
if (parse_flags & COMMIT_FULL_PARSE) { /* Always parse the committer; we need the commit time */
if (commit->committer) if (commit->committer)
free(commit->committer); git_person__free(commit->committer);
commit->committer = git__malloc(sizeof(git_person)); commit->committer = git__malloc(sizeof(git_person));
memcpy(commit->committer, &person, sizeof(git_person)); if (git_person__parse(commit->committer, &buffer, buffer_end, "committer ") < 0)
} return GIT_EOBJCORRUPTED;
commit->commit_time = commit->committer->time;
/* parse commit message */ /* parse commit message */
while (buffer <= buffer_end && *buffer == '\n') while (buffer <= buffer_end && *buffer == '\n')
...@@ -329,9 +200,38 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigne ...@@ -329,9 +200,38 @@ int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigne
return 0; return 0;
} }
int git_commit__parse(git_commit *commit)
{
assert(commit && commit->object.source.open);
return commit_parse_buffer(commit,
commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_BASIC_PARSE);
}
int git_commit__parse_full(git_commit *commit)
{
int error;
if (commit->full_parse)
return 0;
if (git_object__source_open((git_object *)commit) < 0)
return GIT_ERROR;
error = commit_parse_buffer(commit,
commit->object.source.raw.data, commit->object.source.raw.len, COMMIT_FULL_PARSE);
git_object__source_close((git_object *)commit);
commit->full_parse = 1;
return error;
}
#define GIT_COMMIT_GETTER(_rvalue, _name) \ #define GIT_COMMIT_GETTER(_rvalue, _name) \
const _rvalue git_commit_##_name(git_commit *commit) \ const _rvalue git_commit_##_name(git_commit *commit) \
{\ {\
assert(commit); \
if (commit->_name) \ if (commit->_name) \
return commit->_name; \ return commit->_name; \
git_commit__parse_full(commit); \ git_commit__parse_full(commit); \
...@@ -346,6 +246,8 @@ GIT_COMMIT_GETTER(char *, message_short) ...@@ -346,6 +246,8 @@ GIT_COMMIT_GETTER(char *, message_short)
time_t git_commit_time(git_commit *commit) time_t git_commit_time(git_commit *commit)
{ {
assert(commit);
if (commit->commit_time) if (commit->commit_time)
return commit->commit_time; return commit->commit_time;
...@@ -355,26 +257,27 @@ time_t git_commit_time(git_commit *commit) ...@@ -355,26 +257,27 @@ time_t git_commit_time(git_commit *commit)
void git_commit_set_tree(git_commit *commit, git_tree *tree) void git_commit_set_tree(git_commit *commit, git_tree *tree)
{ {
assert(commit && tree);
commit->object.modified = 1; commit->object.modified = 1;
commit->tree = tree; commit->tree = tree;
} }
void git_commit_set_author(git_commit *commit, const git_person *author) void git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time)
{ {
assert(commit && name && email);
commit->object.modified = 1; commit->object.modified = 1;
if (commit->author == NULL)
commit->author = git__malloc(sizeof(git_person));
memcpy(commit->author, author, sizeof(git_person)); git_person__free(commit->author);
commit->author = git_person__new(name, email, time);
} }
void git_commit_set_committer(git_commit *commit, const git_person *committer) void git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time)
{ {
assert(commit && name && email);
commit->object.modified = 1; commit->object.modified = 1;
if (commit->committer == NULL)
commit->committer = git__malloc(sizeof(git_person));
memcpy(commit->committer, committer, sizeof(git_person)); git_person__free(commit->committer);
commit->committer = git_person__new(name, email, time);
} }
void git_commit_set_message(git_commit *commit, const char *message) void git_commit_set_message(git_commit *commit, const char *message)
...@@ -409,3 +312,4 @@ void git_commit_add_parent(git_commit *commit, git_commit *new_parent) ...@@ -409,3 +312,4 @@ void git_commit_add_parent(git_commit *commit, git_commit *new_parent)
commit->parents = node; commit->parents = node;
} }
...@@ -31,14 +31,7 @@ struct git_commit { ...@@ -31,14 +31,7 @@ struct git_commit {
void git_commit__free(git_commit *c); void git_commit__free(git_commit *c);
int git_commit__parse(git_commit *commit); int git_commit__parse(git_commit *commit);
int git_commit__parse_full(git_commit *commit); int git_commit__parse_full(git_commit *commit);
int git_commit__parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags);
int git_commit__writeback(git_commit *commit, git_odb_source *src); int git_commit__writeback(git_commit *commit, git_odb_source *src);
int git__parse_oid(git_oid *oid, char **buffer_out, const char *buffer_end, const char *header);
int git__parse_person(git_person *person, 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_person(git_odb_source *src, const char *header, const git_person *person);
#endif #endif
...@@ -108,16 +108,20 @@ GIT_EXTERN(void) git_commit_set_message(git_commit *commit, const char *message) ...@@ -108,16 +108,20 @@ GIT_EXTERN(void) git_commit_set_message(git_commit *commit, const char *message)
/** /**
* Set the committer of a commit * Set the committer of a commit
* @param commit the commit object * @param commit the commit object
* @param committer the new committer * @param name name of the new committer
* @param email email of the new committer
* @param time time when the committer committed the commit
*/ */
GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const git_person *committer); GIT_EXTERN(void) git_commit_set_committer(git_commit *commit, const char *name, const char *email, time_t time);
/** /**
* Set the author of a commit * Set the author of a commit
* @param commit the commit object * @param commit the commit object
* @param author the new author * @param name name of the new author
* @param email email of the new author
* @param time time when the author created the commit
*/ */
GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const git_person *author); GIT_EXTERN(void) git_commit_set_author(git_commit *commit, const char *name, const char *email, time_t time);
/** /**
* Set the tree which is pointed to by a commit * Set the tree which is pointed to by a commit
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define INCLUDE_git_common_h__ #define INCLUDE_git_common_h__
#include "thread-utils.h" #include "thread-utils.h"
#include <time.h>
#ifdef __cplusplus #ifdef __cplusplus
# define GIT_BEGIN_DECL extern "C" { # define GIT_BEGIN_DECL extern "C" {
...@@ -94,12 +95,13 @@ typedef struct git_repository git_repository; ...@@ -94,12 +95,13 @@ typedef struct git_repository git_repository;
/** Representation of a generic object in a repository */ /** Representation of a generic object in a repository */
typedef struct git_object git_object; typedef struct git_object git_object;
/** Parsed representation of a person */ /** Parsed representation of a person */
typedef struct git_person { typedef struct git_person git_person;
char name[64]; /**< Full name */
char email[64]; /**< Email address */ const char *git_person_name(git_person *person);
time_t time; /**< Time when this person committed the change */ const char *git_person_email(git_person *person);
} git_person; time_t git_person_time(git_person *person);
/** @} */ /** @} */
GIT_END_DECL GIT_END_DECL
......
...@@ -100,9 +100,11 @@ GIT_EXTERN(void) git_tag_set_name(git_tag *tag, const char *name); ...@@ -100,9 +100,11 @@ GIT_EXTERN(void) git_tag_set_name(git_tag *tag, const char *name);
/** /**
* Set the tagger of a tag * Set the tagger of a tag
* @param tag The tag to modify * @param tag The tag to modify
* @param tagger the new tagger for the tag * @param name the name of the new tagger
* @param email the email of the new tagger
* @param time the time when the tag was created
*/ */
GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const git_person *tagger); GIT_EXTERN(void) git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time);
/** /**
* Set the message of a tag * Set the message of a tag
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "common.h" #include "common.h"
#include "git/oid.h" #include "git/oid.h"
#include "repository.h"
#include <string.h> #include <string.h>
static signed char from_hex[] = { static signed char from_hex[] = {
...@@ -116,3 +117,37 @@ char *git_oid_to_string(char *out, size_t n, const git_oid *oid) ...@@ -116,3 +117,37 @@ char *git_oid_to_string(char *out, size_t n, const git_oid *oid)
return out; return out;
} }
int git__parse_oid(git_oid *oid, char **buffer_out,
const char *buffer_end, const char *header)
{
const size_t sha_len = GIT_OID_HEXSZ;
const size_t header_len = strlen(header);
char *buffer = *buffer_out;
if (buffer + (header_len + sha_len + 1) > buffer_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, header, header_len) != 0)
return GIT_EOBJCORRUPTED;
if (buffer[header_len + sha_len] != '\n')
return GIT_EOBJCORRUPTED;
if (git_oid_mkstr(oid, buffer + header_len) < 0)
return GIT_EOBJCORRUPTED;
*buffer_out = buffer + (header_len + sha_len + 1);
return 0;
}
int git__write_oid(git_odb_source *src, const char *header, const git_oid *oid)
{
char hex_oid[41];
git_oid_fmt(hex_oid, oid);
hex_oid[40] = 0;
return git__source_printf(src, "%s %s\n", header, hex_oid);
}
/*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "common.h"
#include "person.h"
#include "repository.h"
#include "git/common.h"
void git_person__free(git_person *person)
{
if (person == NULL)
return;
free(person->name);
free(person->email);
free(person);
}
git_person *git_person__new(const char *name, const char *email, time_t time)
{
git_person *p;
if ((p = git__malloc(sizeof(git_person))) == NULL)
goto cleanup;
p->name = git__strdup(name);
p->email = git__strdup(email);
p->time = time;
if (p->name == NULL || p->email == NULL)
goto cleanup;
return p;
cleanup:
git_person__free(p);
return NULL;
}
const char *git_person_name(git_person *person)
{
return person->name;
}
const char *git_person_email(git_person *person)
{
return person->email;
}
time_t git_person_time(git_person *person)
{
return person->time;
}
int git_person__parse(git_person *person, char **buffer_out,
const char *buffer_end, const char *header)
{
const size_t header_len = strlen(header);
int name_length, email_length;
char *buffer = *buffer_out;
char *line_end, *name_end, *email_end;
memset(person, 0x0, sizeof(git_person));
line_end = memchr(buffer, '\n', buffer_end - buffer);
if (!line_end)
return GIT_EOBJCORRUPTED;
if (buffer + (header_len + 1) > line_end)
return GIT_EOBJCORRUPTED;
if (memcmp(buffer, header, header_len) != 0)
return GIT_EOBJCORRUPTED;
buffer += header_len;
/* Parse name */
if ((name_end = memchr(buffer, '<', buffer_end - buffer)) == NULL)
return GIT_EOBJCORRUPTED;
name_length = name_end - buffer - 1;
person->name = git__malloc(name_length + 1);
memcpy(person->name, buffer, name_length);
person->name[name_length] = 0;
buffer = name_end + 1;
if (buffer >= line_end)
return GIT_EOBJCORRUPTED;
/* Parse email */
if ((email_end = memchr(buffer, '>', buffer_end - buffer)) == NULL)
return GIT_EOBJCORRUPTED;
email_length = email_end - buffer;
person->email = git__malloc(email_length + 1);
memcpy(person->email, buffer, email_length);
person->email[email_length] = 0;
buffer = email_end + 1;
if (buffer >= line_end)
return GIT_EOBJCORRUPTED;
person->time = strtol(buffer, &buffer, 10);
if (person->time == 0)
return GIT_EOBJCORRUPTED;
*buffer_out = (line_end + 1);
return 0;
}
int git_person__write(git_odb_source *src, const char *header, const git_person *person)
{
return git__source_printf(src, "%s %s <%s> %u\n", header, person->name, person->email, person->time);
}
#ifndef INCLUDE_person_h__
#define INCLUDE_person_h__
#include "git/common.h"
#include "repository.h"
#include <time.h>
/** Parsed representation of a person */
struct git_person {
char *name; /**< Full name */
char *email; /**< Email address */
time_t time; /**< Time when this person committed the change */
};
void git_person__free(git_person *person);
git_person *git_person__new(const char *name, const char *email, time_t time);
int git_person__parse(git_person *person, char **buffer_out, const char *buffer_end, const char *header);
int git_person__write(git_odb_source *src, const char *header, const git_person *person);
#endif
...@@ -32,6 +32,8 @@ ...@@ -32,6 +32,8 @@
static const int default_table_size = 32; static const int default_table_size = 32;
static const double max_load_factor = 0.65; static const double max_load_factor = 0.65;
static const int OBJECT_BASE_SIZE = 4096;
static const size_t object_sizes[] = { static const size_t object_sizes[] = {
0, 0,
sizeof(git_commit), sizeof(git_commit),
...@@ -171,14 +173,12 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len) ...@@ -171,14 +173,12 @@ int git__source_write(git_odb_source *source, const void *bytes, size_t len)
static void prepare_write(git_object *object) static void prepare_write(git_object *object)
{ {
const size_t base_size = 4096; /* 4Kb base size */
if (object->source.write_ptr != NULL || object->source.open) if (object->source.write_ptr != NULL || object->source.open)
git_object__source_close(object); git_object__source_close(object);
/* TODO: proper size calculation */ /* TODO: proper size calculation */
object->source.raw.data = git__malloc(base_size); object->source.raw.data = git__malloc(OBJECT_BASE_SIZE);
object->source.raw.len = base_size; object->source.raw.len = OBJECT_BASE_SIZE;
object->source.write_ptr = object->source.raw.data; object->source.write_ptr = object->source.raw.data;
object->source.written_bytes = 0; object->source.written_bytes = 0;
...@@ -193,11 +193,9 @@ static int write_back(git_object *object) ...@@ -193,11 +193,9 @@ static int write_back(git_object *object)
assert(object); assert(object);
if (!object->source.open) assert(object->source.open);
return GIT_ERROR;
assert(object->modified); assert(object->modified);
object->source.raw.len = object->source.written_bytes; object->source.raw.len = object->source.written_bytes;
git_obj_hash(&new_id, &object->source.raw); git_obj_hash(&new_id, &object->source.raw);
...@@ -230,9 +228,6 @@ int git_object__source_open(git_object *object) ...@@ -230,9 +228,6 @@ int git_object__source_open(git_object *object)
if (object->source.open) if (object->source.open)
git_object__source_close(object); git_object__source_close(object);
if (object->source.open)
return GIT_SUCCESS;
error = git_odb_read(&object->source.raw, object->repo->db, &object->id); error = git_odb_read(&object->source.raw, object->repo->db, &object->id);
if (error < 0) if (error < 0)
return error; return error;
...@@ -245,7 +240,7 @@ void git_object__source_close(git_object *object) ...@@ -245,7 +240,7 @@ void git_object__source_close(git_object *object)
{ {
assert(object); assert(object);
if (!object->source.open) { if (object->source.open) {
git_obj_close(&object->source.raw); git_obj_close(&object->source.raw);
object->source.open = 0; object->source.open = 0;
} }
...@@ -294,8 +289,8 @@ void git_object_free(git_object *object) ...@@ -294,8 +289,8 @@ void git_object_free(git_object *object)
{ {
assert(object); assert(object);
git_object__source_close(object);
git_hashtable_remove(object->repo->objects, &object->id); git_hashtable_remove(object->repo->objects, &object->id);
git_obj_close(&object->source.raw);
switch (object->source.raw.type) { switch (object->source.raw.type) {
case GIT_OBJ_COMMIT: case GIT_OBJ_COMMIT:
...@@ -372,6 +367,7 @@ git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_o ...@@ -372,6 +367,7 @@ git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_o
{ {
git_object *object = NULL; git_object *object = NULL;
git_rawobj obj_file; git_rawobj obj_file;
int error = 0;
assert(repo); assert(repo);
...@@ -397,43 +393,35 @@ git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_o ...@@ -397,43 +393,35 @@ git_object *git_repository_lookup(git_repository *repo, const git_oid *id, git_o
/* Initialize parent object */ /* Initialize parent object */
git_oid_cpy(&object->id, id); git_oid_cpy(&object->id, id);
object->repo = repo; object->repo = repo;
object->source.open = 1;
memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj)); memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
object->source.open = 1;
switch (type) { switch (type) {
case GIT_OBJ_COMMIT: case GIT_OBJ_COMMIT:
if (git_commit__parse((git_commit *)object) < 0) { error = git_commit__parse((git_commit *)object);
free(object);
return NULL;
}
break; break;
case GIT_OBJ_TREE: case GIT_OBJ_TREE:
if (git_tree__parse((git_tree *)object) < 0) { error = git_tree__parse((git_tree *)object);
free(object);
return NULL;
}
break; break;
case GIT_OBJ_TAG: case GIT_OBJ_TAG:
if (git_tag__parse((git_tag *)object) < 0) { error = git_tag__parse((git_tag *)object);
free(object);
return NULL;
}
break; break;
case GIT_OBJ_BLOB:
default: default:
/* blobs get no parsing */ /* blobs get no parsing */
break; break;
} }
git_obj_close(&object->source.raw); if (error < 0) {
object->source.open = 0; git_object_free(object);
return NULL;
}
git_object__source_close(object);
git_hashtable_insert(repo->objects, &object->id, object); git_hashtable_insert(repo->objects, &object->id, object);
return object; return object;
} }
...@@ -34,4 +34,7 @@ void git_object__source_close(git_object *object); ...@@ -34,4 +34,7 @@ void git_object__source_close(git_object *object);
int git__source_printf(git_odb_source *source, const char *format, ...); int git__source_printf(git_odb_source *source, const char *format, ...);
int git__source_write(git_odb_source *source, const void *bytes, size_t len); 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__write_oid(git_odb_source *src, const char *header, const git_oid *oid);
#endif #endif
...@@ -26,15 +26,16 @@ ...@@ -26,15 +26,16 @@
#include "common.h" #include "common.h"
#include "commit.h" #include "commit.h"
#include "tag.h" #include "tag.h"
#include "person.h"
#include "revwalk.h" #include "revwalk.h"
#include "git/odb.h" #include "git/odb.h"
#include "git/repository.h" #include "git/repository.h"
void git_tag__free(git_tag *tag) void git_tag__free(git_tag *tag)
{ {
git_person__free(tag->tagger);
free(tag->message); free(tag->message);
free(tag->tag_name); free(tag->tag_name);
free(tag->tagger);
free(tag); free(tag);
} }
...@@ -50,6 +51,7 @@ const git_oid *git_tag_id(git_tag *c) ...@@ -50,6 +51,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);
return t->target; return t->target;
} }
...@@ -64,6 +66,7 @@ void git_tag_set_target(git_tag *tag, git_object *target) ...@@ -64,6 +66,7 @@ void git_tag_set_target(git_tag *tag, git_object *target)
git_otype git_tag_type(git_tag *t) git_otype git_tag_type(git_tag *t)
{ {
assert(t);
return t->type; return t->type;
} }
...@@ -77,6 +80,7 @@ void git_tag_set_type(git_tag *tag, git_otype type) ...@@ -77,6 +80,7 @@ void git_tag_set_type(git_tag *tag, git_otype type)
const char *git_tag_name(git_tag *t) const char *git_tag_name(git_tag *t)
{ {
assert(t);
return t->tag_name; return t->tag_name;
} }
...@@ -98,19 +102,18 @@ const git_person *git_tag_tagger(git_tag *t) ...@@ -98,19 +102,18 @@ const git_person *git_tag_tagger(git_tag *t)
return t->tagger; return t->tagger;
} }
void git_tag_set_tagger(git_tag *tag, const git_person *tagger) void git_tag_set_tagger(git_tag *tag, const char *name, const char *email, time_t time)
{ {
assert(tag && tagger); assert(tag && name && email);
tag->object.modified = 1; tag->object.modified = 1;
if (tag->tagger == NULL)
tag->tagger = git__malloc(sizeof(git_person));
memcpy(tag->tagger, tagger, sizeof(git_person)); git_person__free(tag->tagger);
tag->tagger = git_person__new(name, email, time);
} }
const char *git_tag_message(git_tag *t) const char *git_tag_message(git_tag *t)
{ {
assert(t);
return t->message; return t->message;
} }
...@@ -194,11 +197,11 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end) ...@@ -194,11 +197,11 @@ static int parse_tag_buffer(git_tag *tag, char *buffer, const char *buffer_end)
buffer = search + 1; buffer = search + 1;
if (tag->tagger != NULL) if (tag->tagger != NULL)
free(tag->tagger); git_person__free(tag->tagger);
tag->tagger = git__malloc(sizeof(git_person)); tag->tagger = git__malloc(sizeof(git_person));
if (git__parse_person(tag->tagger, &buffer, buffer_end, "tagger ") != 0) if (git_person__parse(tag->tagger, &buffer, buffer_end, "tagger ") != 0)
return GIT_EOBJCORRUPTED; return GIT_EOBJCORRUPTED;
text_len = buffer_end - ++buffer; text_len = buffer_end - ++buffer;
...@@ -221,7 +224,7 @@ int git_tag__writeback(git_tag *tag, git_odb_source *src) ...@@ -221,7 +224,7 @@ int git_tag__writeback(git_tag *tag, git_odb_source *src)
git__write_oid(src, "object", git_object_id(tag->target)); git__write_oid(src, "object", git_object_id(tag->target));
git__source_printf(src, "type %s\n", git_obj_type_to_string(tag->type)); git__source_printf(src, "type %s\n", git_obj_type_to_string(tag->type));
git__source_printf(src, "tag %s\n", tag->tag_name); git__source_printf(src, "tag %s\n", tag->tag_name);
git__write_person(src, "tagger", tag->tagger); git_person__write(src, "tagger", tag->tagger);
if (tag->message != NULL) if (tag->message != NULL)
git__source_printf(src, "\n%s", tag->message); git__source_printf(src, "\n%s", tag->message);
...@@ -232,16 +235,8 @@ int git_tag__writeback(git_tag *tag, git_odb_source *src) ...@@ -232,16 +235,8 @@ int git_tag__writeback(git_tag *tag, git_odb_source *src)
int git_tag__parse(git_tag *tag) int git_tag__parse(git_tag *tag)
{ {
int error = 0; assert(tag && tag->object.source.open);
return parse_tag_buffer(tag, tag->object.source.raw.data, tag->object.source.raw.data + tag->object.source.raw.len);
error = git_object__source_open((git_object *)tag);
if (error < 0)
return error;
error = parse_tag_buffer(tag, tag->object.source.raw.data, tag->object.source.raw.data + tag->object.source.raw.len);
git_object__source_close((git_object *)tag);
return error;
} }
git_tag *git_tag_lookup(git_repository *repo, const git_oid *id) git_tag *git_tag_lookup(git_repository *repo, const git_oid *id)
......
...@@ -70,17 +70,26 @@ static void entry_resort(git_tree *tree) ...@@ -70,17 +70,26 @@ static void entry_resort(git_tree *tree)
qsort(tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_sort_cmp); qsort(tree->entries, tree->entry_count, sizeof(git_tree_entry *), entry_sort_cmp);
} }
static void free_tree_entries(git_tree *tree)
void git_tree__free(git_tree *tree)
{ {
size_t i; size_t i;
for (i = 0; i < tree->entry_count; ++i) if (tree == NULL)
return;
for (i = 0; i < tree->entry_count; ++i) {
free(tree->entries[i]->filename);
free(tree->entries[i]); free(tree->entries[i]);
}
free(tree->entries); free(tree->entries);
}
void git_tree__free(git_tree *tree)
{
free_tree_entries(tree);
free(tree); free(tree);
} }
...@@ -111,7 +120,8 @@ void git_tree_entry_set_name(git_tree_entry *entry, const char *name) ...@@ -111,7 +120,8 @@ void git_tree_entry_set_name(git_tree_entry *entry, const char *name)
{ {
assert(entry && entry->owner); assert(entry && entry->owner);
strncpy(entry->filename, name, GIT_TREE_MAX_FILENAME); free(entry->filename);
entry->filename = git__strdup(name);
entry_resort(entry->owner); entry_resort(entry->owner);
entry->owner->object.modified = 1; entry->owner->object.modified = 1;
} }
...@@ -188,7 +198,7 @@ int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename, ...@@ -188,7 +198,7 @@ int git_tree_add_entry(git_tree *tree, const git_oid *id, const char *filename,
memset(entry, 0x0, sizeof(git_tree_entry)); memset(entry, 0x0, sizeof(git_tree_entry));
strncpy(entry->filename, filename, GIT_TREE_MAX_FILENAME); entry->filename = git__strdup(filename);
git_oid_cpy(&entry->oid, id); git_oid_cpy(&entry->oid, id);
entry->attr = attributes; entry->attr = attributes;
entry->owner = tree; entry->owner = tree;
...@@ -212,6 +222,7 @@ int git_tree_remove_entry_byindex(git_tree *tree, int idx) ...@@ -212,6 +222,7 @@ int git_tree_remove_entry_byindex(git_tree *tree, int idx)
remove_ptr = tree->entries[idx]; remove_ptr = tree->entries[idx];
tree->entries[idx] = tree->entries[--tree->entry_count]; tree->entries[idx] = tree->entries[--tree->entry_count];
free(remove_ptr->filename);
free(remove_ptr); free(remove_ptr);
entry_resort(tree); entry_resort(tree);
...@@ -237,6 +248,7 @@ int git_tree_remove_entry_byname(git_tree *tree, const char *filename) ...@@ -237,6 +248,7 @@ int git_tree_remove_entry_byname(git_tree *tree, const char *filename)
int git_tree__writeback(git_tree *tree, git_odb_source *src) int git_tree__writeback(git_tree *tree, git_odb_source *src)
{ {
size_t i; size_t i;
char filemode[8];
assert(tree && src); assert(tree && src);
...@@ -249,38 +261,23 @@ int git_tree__writeback(git_tree *tree, git_odb_source *src) ...@@ -249,38 +261,23 @@ int git_tree__writeback(git_tree *tree, git_odb_source *src)
git_tree_entry *entry; git_tree_entry *entry;
entry = tree->entries[i]; entry = tree->entries[i];
git__source_printf(src, "%06o %s\0", entry->attr, entry->filename); sprintf(filemode, "%06o ", entry->attr);
git__source_write(src, filemode, strlen(filemode));
git__source_write(src, entry->filename, strlen(entry->filename) + 1);
git__source_write(src, entry->oid.id, GIT_OID_RAWSZ); git__source_write(src, entry->oid.id, GIT_OID_RAWSZ);
} }
return GIT_SUCCESS; return GIT_SUCCESS;
} }
int git_tree__parse(git_tree *tree) static int tree_parse_buffer(git_tree *tree, char *buffer, char *buffer_end)
{ {
static const size_t avg_entry_size = 40; static const size_t avg_entry_size = 40;
int error = 0; int error = 0;
char *buffer, *buffer_end;
assert(!tree->object.in_memory);
if (tree->entries != NULL) { free_tree_entries(tree);
size_t i;
for (i = 0; i < tree->entry_count; ++i)
free(tree->entries[i]);
free(tree->entries);
}
error = git_object__source_open((git_object *)tree);
if (error < 0)
return error;
buffer = tree->object.source.raw.data;
buffer_end = buffer + tree->object.source.raw.len;
tree->entry_count = 0; tree->entry_count = 0;
tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1; tree->array_size = (tree->object.source.raw.len / avg_entry_size) + 1;
...@@ -312,7 +309,12 @@ int git_tree__parse(git_tree *tree) ...@@ -312,7 +309,12 @@ int git_tree__parse(git_tree *tree)
break; break;
} }
strncpy(entry->filename, buffer, GIT_TREE_MAX_FILENAME); if (memchr(buffer, 0, buffer_end - buffer) == NULL) {
error = GIT_EOBJCORRUPTED;
break;
}
entry->filename = git__strdup(buffer);
while (buffer < buffer_end && *buffer != 0) while (buffer < buffer_end && *buffer != 0)
buffer++; buffer++;
...@@ -323,6 +325,19 @@ int git_tree__parse(git_tree *tree) ...@@ -323,6 +325,19 @@ int git_tree__parse(git_tree *tree)
buffer += GIT_OID_RAWSZ; buffer += GIT_OID_RAWSZ;
} }
git_object__source_close((git_object *)tree);
return error; return error;
} }
int git_tree__parse(git_tree *tree)
{
char *buffer, *buffer_end;
assert(tree && tree->object.source.open);
assert(!tree->object.in_memory);
buffer = tree->object.source.raw.data;
buffer_end = buffer + tree->object.source.raw.len;
return tree_parse_buffer(tree, buffer, buffer_end);
}
...@@ -4,11 +4,9 @@ ...@@ -4,11 +4,9 @@
#include <git/tree.h> #include <git/tree.h>
#include "repository.h" #include "repository.h"
#define GIT_TREE_MAX_FILENAME 255
struct git_tree_entry { struct git_tree_entry {
unsigned int attr; unsigned int attr;
char filename[GIT_TREE_MAX_FILENAME]; char *filename;
git_oid oid; git_oid oid;
git_tree *owner; git_tree *owner;
......
#include "test_lib.h" #include "test_lib.h"
#include "test_helpers.h" #include "test_helpers.h"
#include "commit.h" #include "commit.h"
#include "person.h"
#include <git/odb.h> #include <git/odb.h>
#include <git/commit.h> #include <git/commit.h>
#include <git/revwalk.h> #include <git/revwalk.h>
...@@ -127,18 +128,20 @@ BEGIN_TEST(parse_person_test) ...@@ -127,18 +128,20 @@ BEGIN_TEST(parse_person_test)
#define TEST_PERSON_PASS(_string, _header, _name, _email, _time) { \ #define TEST_PERSON_PASS(_string, _header, _name, _email, _time) { \
char *ptr = _string; \ char *ptr = _string; \
size_t len = strlen(_string);\ size_t len = strlen(_string);\
git_person person; \ git_person person = {NULL, NULL, 0}; \
must_pass(git__parse_person(&person, &ptr, ptr + len, _header));\ must_pass(git_person__parse(&person, &ptr, ptr + len, _header));\
must_be_true(strncmp(_name, person.name, 63) == 0);\ must_be_true(strcmp(_name, person.name) == 0);\
must_be_true(strncmp(_email, person.email, 63) == 0);\ must_be_true(strcmp(_email, person.email) == 0);\
must_be_true(_time == person.time);\ must_be_true(_time == person.time);\
free(person.name); free(person.email);\
} }
#define TEST_PERSON_FAIL(_string, _header) { \ #define TEST_PERSON_FAIL(_string, _header) { \
char *ptr = _string; \ char *ptr = _string; \
size_t len = strlen(_string);\ size_t len = strlen(_string);\
git_person person; \ git_person person = {NULL, NULL, 0}; \
must_fail(git__parse_person(&person, &ptr, ptr + len, _header));\ must_fail(git_person__parse(&person, &ptr, ptr + len, _header));\
free(person.name); free(person.email);\
} }
TEST_PERSON_PASS( TEST_PERSON_PASS(
...@@ -213,6 +216,9 @@ BEGIN_TEST(parse_person_test) ...@@ -213,6 +216,9 @@ BEGIN_TEST(parse_person_test)
END_TEST END_TEST
/* External declaration for testing the buffer parsing method */
int commit_parse_buffer(git_commit *commit, void *data, size_t len, unsigned int parse_flags);
BEGIN_TEST(parse_buffer_test) BEGIN_TEST(parse_buffer_test)
const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken); const int broken_commit_count = sizeof(test_commits_broken) / sizeof(*test_commits_broken);
const int working_commit_count = sizeof(test_commits_working) / sizeof(*test_commits_working); const int working_commit_count = sizeof(test_commits_working) / sizeof(*test_commits_working);
...@@ -232,7 +238,7 @@ BEGIN_TEST(parse_buffer_test) ...@@ -232,7 +238,7 @@ BEGIN_TEST(parse_buffer_test)
memset(commit, 0x0, sizeof(git_commit)); memset(commit, 0x0, sizeof(git_commit));
commit->object.repo = repo; commit->object.repo = repo;
must_fail(git_commit__parse_buffer( must_fail(commit_parse_buffer(
commit, commit,
test_commits_broken[i], test_commits_broken[i],
strlen(test_commits_broken[i]), strlen(test_commits_broken[i]),
...@@ -248,7 +254,7 @@ BEGIN_TEST(parse_buffer_test) ...@@ -248,7 +254,7 @@ BEGIN_TEST(parse_buffer_test)
memset(commit, 0x0, sizeof(git_commit)); memset(commit, 0x0, sizeof(git_commit));
commit->object.repo = repo; commit->object.repo = repo;
must_pass(git_commit__parse_buffer( must_pass(commit_parse_buffer(
commit, commit,
test_commits_working[i], test_commits_working[i],
strlen(test_commits_working[i]), strlen(test_commits_working[i]),
...@@ -259,5 +265,6 @@ BEGIN_TEST(parse_buffer_test) ...@@ -259,5 +265,6 @@ BEGIN_TEST(parse_buffer_test)
} }
git_repository_free(repo); git_repository_free(repo);
git_odb_close(db);
END_TEST END_TEST
#include "test_lib.h" #include "test_lib.h"
#include "test_helpers.h" #include "test_helpers.h"
#include "commit.h" #include "commit.h"
#include "person.h"
#include <git/odb.h> #include <git/odb.h>
#include <git/commit.h> #include <git/commit.h>
...@@ -28,6 +29,7 @@ BEGIN_TEST(query_details_test) ...@@ -28,6 +29,7 @@ BEGIN_TEST(query_details_test)
repo = git_repository_alloc(db); repo = git_repository_alloc(db);
must_be_true(repo != NULL); must_be_true(repo != NULL);
for (i = 0; i < commit_count; ++i) { for (i = 0; i < commit_count; ++i) {
git_oid id; git_oid id;
git_commit *commit; git_commit *commit;
......
...@@ -17,6 +17,9 @@ static const char *commit_ids[] = { ...@@ -17,6 +17,9 @@ static const char *commit_ids[] = {
}; };
static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
#define COMMITTER_NAME "Vicent Marti"
#define COMMITTER_EMAIL "vicent@github.com"
BEGIN_TEST(writenew_test) BEGIN_TEST(writenew_test)
git_odb *db; git_odb *db;
git_repository *repo; git_repository *repo;
...@@ -25,9 +28,6 @@ BEGIN_TEST(writenew_test) ...@@ -25,9 +28,6 @@ BEGIN_TEST(writenew_test)
git_oid id; git_oid id;
/* char hex_oid[41]; */ /* char hex_oid[41]; */
git_person author = {"Vicent Marti", "vicent@github.com", 123456789};
git_person committer = {"Vicent Marti", "vicent@github.com", 987654321};
must_pass(git_odb_open(&db, odb_dir)); must_pass(git_odb_open(&db, odb_dir));
repo = git_repository_alloc(db); repo = git_repository_alloc(db);
...@@ -45,8 +45,8 @@ BEGIN_TEST(writenew_test) ...@@ -45,8 +45,8 @@ BEGIN_TEST(writenew_test)
git_commit_add_parent(commit, parent); git_commit_add_parent(commit, parent);
/* Set other attributes */ /* Set other attributes */
git_commit_set_committer(commit, &committer); git_commit_set_committer(commit, COMMITTER_NAME, COMMITTER_EMAIL, 123456789);
git_commit_set_author(commit, &author); git_commit_set_author(commit, COMMITTER_NAME, COMMITTER_EMAIL, 987654321);
git_commit_set_message(commit, git_commit_set_message(commit,
"This commit has been created in memory\n\ "This commit has been created in memory\n\
This is a commit created in memory and it will be written back to disk\n"); This is a commit created in memory and it will be written back to disk\n");
...@@ -73,6 +73,9 @@ This is a commit created in memory and it will be written back to disk\n"); ...@@ -73,6 +73,9 @@ This is a commit created in memory and it will be written back to disk\n");
must_pass(remove_loose_object(odb_dir, (git_object *)commit)); must_pass(remove_loose_object(odb_dir, (git_object *)commit));
//git_person_free(&author);
//git_person_free(&committer);
git_repository_free(repo); git_repository_free(repo);
git_odb_close(db); git_odb_close(db);
......
...@@ -38,6 +38,8 @@ BEGIN_TEST(tree_in_memory_add_test) ...@@ -38,6 +38,8 @@ BEGIN_TEST(tree_in_memory_add_test)
must_pass(git_object_write((git_object *)tree)); must_pass(git_object_write((git_object *)tree));
must_pass(remove_loose_object(odb_dir, (git_object *)tree)); must_pass(remove_loose_object(odb_dir, (git_object *)tree));
git_object_free((git_object *)tree);
git_repository_free(repo); git_repository_free(repo);
git_odb_close(db); git_odb_close(db);
END_TEST END_TEST
...@@ -91,6 +93,8 @@ BEGIN_TEST(tree_add_entry_test) ...@@ -91,6 +93,8 @@ BEGIN_TEST(tree_add_entry_test)
*/ */
must_pass(remove_loose_object(odb_dir, (git_object *)tree)); must_pass(remove_loose_object(odb_dir, (git_object *)tree));
git_object_free((git_object *)tree);
git_repository_free(repo); git_repository_free(repo);
git_odb_close(db); git_odb_close(db);
......
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