Commit 97769280 by Russell Belfer

Use git_buf for path storage instead of stack-based buffers

This converts virtually all of the places that allocate GIT_PATH_MAX
buffers on the stack for manipulating paths to use git_buf objects
instead.  The patch is pretty careful not to touch the public API
for libgit2, so there are a few places that still use GIT_PATH_MAX.

This extends and changes some details of the git_buf implementation
to add a couple of extra functions and to make error handling easier.

This includes serious alterations to all the path.c functions, and
several of the fileops.c ones, too.  Also, there are a number of new
functions that parallel existing ones except that use a git_buf
instead of a stack-based buffer (such as git_config_find_global_r
that exists alongsize git_config_find_global).

This also modifies the win32 version of p_realpath to allocate whatever
buffer size is needed to accommodate the realpath instead of hardcoding
a GIT_PATH_MAX limit, but that change needs to be tested still.
parent a22b14d3
......@@ -67,12 +67,13 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
{
int error, islnk;
int error = GIT_SUCCESS;
int islnk = 0;
int fd = 0;
char full_path[GIT_PATH_MAX];
git_buf full_path = GIT_BUF_INIT;
char buffer[2048];
git_off_t size;
git_odb_stream *stream;
git_odb_stream *stream = NULL;
struct stat st;
const char *workdir;
git_odb *odb;
......@@ -81,11 +82,14 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
if (workdir == NULL)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
git_path_join(full_path, workdir, path);
error = git_buf_joinpath(&full_path, workdir, path);
if (error < GIT_SUCCESS)
return error;
error = p_lstat(full_path, &st);
error = p_lstat(full_path.ptr, &st);
if (error < 0) {
return git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
goto cleanup;
}
islnk = S_ISLNK(st.st_mode);
......@@ -93,32 +97,30 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
error = git_repository_odb__weakptr(&odb, repo);
if (error < GIT_SUCCESS)
return error;
goto cleanup;
if (!islnk) {
if ((fd = p_open(full_path, O_RDONLY)) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path);
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr
);
goto cleanup;
}
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS) {
if (!islnk)
p_close(fd);
return git__rethrow(error, "Failed to create blob");
}
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
goto cleanup;
while (size > 0) {
ssize_t read_len;
if (!islnk)
read_len = p_read(fd, buffer, sizeof(buffer));
else
read_len = p_readlink(full_path, buffer, sizeof(buffer));
read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer));
if (read_len < 0) {
if (!islnk)
p_close(fd);
stream->free(stream);
return git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
goto cleanup;
}
stream->write(stream, buffer, read_len);
......@@ -126,10 +128,15 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat
}
error = stream->finalize_write(oid, stream);
cleanup:
if (stream)
stream->free(stream);
if (!islnk)
if (!islnk && fd)
p_close(fd);
git_buf_free(&full_path);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create blob");
return error == GIT_SUCCESS ? GIT_SUCCESS :
git__rethrow(error, "Failed to create blob");
}
......@@ -15,7 +15,8 @@ char git_buf_initbuf[1];
#define ENSURE_SIZE(b, d) \
if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return;
return GIT_ENOMEM;
void git_buf_init(git_buf *buf, size_t initial_size)
{
......@@ -29,6 +30,14 @@ void git_buf_init(git_buf *buf, size_t initial_size)
int git_buf_grow(git_buf *buf, size_t target_size)
{
int error = git_buf_try_grow(buf, target_size);
if (error != GIT_SUCCESS)
buf->asize = -1;
return error;
}
int git_buf_try_grow(git_buf *buf, size_t target_size)
{
char *new_ptr;
size_t new_size;
......@@ -55,10 +64,9 @@ int git_buf_grow(git_buf *buf, size_t target_size)
new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr) {
buf->asize = -1;
/* if realloc fails, return without modifying the git_buf */
if (!new_ptr)
return GIT_ENOMEM;
}
buf->asize = new_size;
buf->ptr = new_ptr;
......@@ -93,7 +101,12 @@ int git_buf_oom(const git_buf *buf)
return (buf->asize < 0);
}
void git_buf_set(git_buf *buf, const char *data, size_t len)
int git_buf_lasterror(const git_buf *buf)
{
return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
}
int git_buf_set(git_buf *buf, const char *data, size_t len)
{
if (len == 0 || data == NULL) {
git_buf_clear(buf);
......@@ -103,35 +116,38 @@ void git_buf_set(git_buf *buf, const char *data, size_t len)
buf->size = len;
buf->ptr[buf->size] = '\0';
}
return GIT_SUCCESS;
}
void git_buf_sets(git_buf *buf, const char *string)
int git_buf_sets(git_buf *buf, const char *string)
{
git_buf_set(buf, string, string ? strlen(string) : 0);
return git_buf_set(buf, string, string ? strlen(string) : 0);
}
void git_buf_putc(git_buf *buf, char c)
int git_buf_putc(git_buf *buf, char c)
{
ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
}
void git_buf_put(git_buf *buf, const char *data, size_t len)
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
memmove(buf->ptr + buf->size, data, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS;
}
void git_buf_puts(git_buf *buf, const char *string)
int git_buf_puts(git_buf *buf, const char *string)
{
assert(string);
git_buf_put(buf, string, strlen(string));
return git_buf_put(buf, string, strlen(string));
}
void git_buf_printf(git_buf *buf, const char *format, ...)
int git_buf_printf(git_buf *buf, const char *format, ...)
{
int len;
va_list arglist;
......@@ -145,16 +161,18 @@ void git_buf_printf(git_buf *buf, const char *format, ...)
if (len < 0) {
buf->asize = -1;
return;
return GIT_ENOMEM;
}
if (len + 1 <= buf->asize - buf->size) {
buf->size += len;
return;
break;
}
ENSURE_SIZE(buf, buf->size + len + 1);
}
return GIT_SUCCESS;
}
const char *git_buf_cstr(git_buf *buf)
......@@ -162,7 +180,7 @@ const char *git_buf_cstr(git_buf *buf)
return buf->ptr;
}
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf)
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
{
size_t copylen;
......@@ -190,6 +208,14 @@ void git_buf_consume(git_buf *buf, const char *end)
}
}
void git_buf_truncate(git_buf *buf, ssize_t len)
{
if (len < buf->size) {
buf->size = len;
buf->ptr[buf->size] = '\0';
}
}
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
{
git_buf t = *buf_a;
......@@ -197,7 +223,7 @@ void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
*buf_b = t;
}
char *git_buf_take_cstr(git_buf *buf)
char *git_buf_detach(git_buf *buf)
{
char *data = buf->ptr;
......@@ -209,18 +235,34 @@ char *git_buf_take_cstr(git_buf *buf)
return data;
}
void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
{
/* Make two passes to avoid multiple reallocation */
git_buf_free(buf);
if (ptr) {
buf->ptr = ptr;
buf->size = strlen(ptr);
if (asize)
buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
else /* pass 0 to fall back on strlen + 1 */
buf->asize = buf->size + 1;
} else {
git_buf_grow(buf, asize);
}
}
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
{
va_list ap;
int i;
int i, error = GIT_SUCCESS;
size_t total_size = 0;
char *out;
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
++total_size; /* space for initial separator */
/* Make two passes to avoid multiple reallocation */
va_start(ap, nbuf);
for (i = 0; i < nbuf; ++i) {
const char* segment;
......@@ -237,7 +279,10 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
}
va_end(ap);
ENSURE_SIZE(buf, buf->size + total_size + 1);
/* expand buffer if needed */
if (total_size > 0 &&
(error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
return error;
out = buf->ptr + buf->size;
......@@ -274,14 +319,17 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
/* set size based on num characters actually written */
buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
return error;
}
void git_buf_join(
int git_buf_join(
git_buf *buf,
char separator,
const char *str_a,
const char *str_b)
{
int error = GIT_SUCCESS;
size_t strlen_a = strlen(str_a);
size_t strlen_b = strlen(str_b);
int need_sep = 0;
......@@ -293,7 +341,9 @@ void git_buf_join(
need_sep = 1;
}
ENSURE_SIZE(buf, strlen_a + strlen_b + need_sep + 1);
error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1);
if (error < GIT_SUCCESS)
return error;
memmove(buf->ptr, str_a, strlen_a);
if (need_sep)
......@@ -302,4 +352,6 @@ void git_buf_join(
buf->size = strlen_a + strlen_b + need_sep;
buf->ptr[buf->size] = '\0';
return error;
}
......@@ -18,34 +18,87 @@ extern char git_buf_initbuf[];
#define GIT_BUF_INIT { git_buf_initbuf, 0, 0 }
/**
* Initialize a git_buf structure.
*
* For the cases where GIT_BUF_INIT cannot be used to do static
* initialization.
*/
void git_buf_init(git_buf *buf, size_t initial_size);
/**
* Grow the buffer to hold at least `target_size` bytes.
*
* If the allocation fails, this will return an error and the buffer
* will be marked as invalid for future operations. The existing
* contents of the buffer will be preserved however.
* @return GIT_SUCCESS or GIT_ENOMEM on failure
*/
int git_buf_grow(git_buf *buf, size_t target_size);
/**
* Attempt to grow the buffer to hold at least `target_size` bytes.
*
* This is just like `git_buf_grow` except that even if the allocation
* fails, the git_buf will still be left in a valid state.
*/
int git_buf_try_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
char *git_buf_detach(git_buf *buf);
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
/**
* Test if there have been any reallocation failures with this git_buf.
*
* Any function that writes to a git_buf can fail due to memory allocation
* issues. If one fails, the git_buf will be marked with an OOM error and
* further calls to modify the buffer will fail. You just check
* git_buf_oom() at the end of your sequence and it will be true if you ran
* out of memory at any point with that buffer.
* further calls to modify the buffer will fail. Check git_buf_oom() at the
* end of your sequence and it will be true if you ran out of memory at any
* point with that buffer.
* @return 0 if no error, 1 if allocation error.
*/
int git_buf_oom(const git_buf *buf);
void git_buf_set(git_buf *buf, const char *data, size_t len);
void git_buf_sets(git_buf *buf, const char *string);
void git_buf_putc(git_buf *buf, char c);
void git_buf_put(git_buf *buf, const char *data, size_t len);
void git_buf_puts(git_buf *buf, const char *string);
void git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
/**
* Just like git_buf_oom, except returns appropriate error code.
* @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
*/
int git_buf_lasterror(const git_buf *buf);
/*
* The functions below that return int values, will return GIT_ENOMEM
* if they fail to expand the git_buf when they are called, otherwise
* GIT_SUCCESS. Passing a git_buf that has failed an allocation will
* automatically return GIT_ENOMEM for all further calls. As a result,
* you can ignore the return code of these functions and call them in a
* series then just call git_buf_lasterror at the end.
*/
int git_buf_set(git_buf *buf, const char *data, size_t len);
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
void git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
void git_buf_truncate(git_buf *buf, ssize_t len);
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
/**
* Join two strings as paths, inserting a slash between as needed.
* @return error code or GIT_SUCCESS
*/
GIT_INLINE (int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
{
return git_buf_join(buf, '/', a, b);
}
const char *git_buf_cstr(git_buf *buf);
char *git_buf_take_cstr(git_buf *buf);
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf);
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
......
......@@ -129,7 +129,8 @@ int git_commit_create(
git_buf_puts(&commit, message);
if (git_buf_oom(&commit)) {
error = git__throw(GIT_ENOMEM, "Not enough memory to build the commit data");
error = git__throw(git_buf_lasterror(&commit),
"Not enough memory to build the commit data");
goto cleanup;
}
......
......@@ -330,9 +330,25 @@ int git_config_get_string(git_config *cfg, const char *name, const char **out)
int git_config_find_global(char *global_config_path)
{
const char *home;
git_buf path = GIT_BUF_INIT;
int error = git_config_find_global_r(&path);
if (error == GIT_SUCCESS) {
if (path.size > GIT_PATH_MAX)
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
else
git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
}
git_buf_free(&path);
home = getenv("HOME");
return error;
}
int git_config_find_global_r(git_buf *path)
{
int error;
const char *home = getenv("HOME");
#ifdef GIT_WIN32
if (home == NULL)
......@@ -342,10 +358,13 @@ int git_config_find_global(char *global_config_path)
if (home == NULL)
return git__throw(GIT_EOSERR, "Failed to open global config file. Cannot locate the user's home directory");
git_path_join(global_config_path, home, GIT_CONFIG_FILENAME);
if ((error = git_buf_joinpath(path, home, GIT_CONFIG_FILENAME)) < GIT_SUCCESS)
return error;
if (git_futils_exists(global_config_path) < GIT_SUCCESS)
if (git_futils_exists(path->ptr) < GIT_SUCCESS) {
git_buf_clear(path);
return git__throw(GIT_EOSERR, "Failed to open global config file. The file does not exist");
}
return GIT_SUCCESS;
}
......@@ -353,7 +372,7 @@ int git_config_find_global(char *global_config_path)
#if GIT_WIN32
static int win32_find_system(char *system_config_path)
static int win32_find_system(git_buf *system_config_path)
{
const wchar_t *query = L"%PROGRAMFILES%\\Git\\etc\\gitconfig";
wchar_t *apphome_utf16;
......@@ -378,25 +397,21 @@ static int win32_find_system(char *system_config_path)
apphome_utf8 = gitwin_from_utf16(apphome_utf16);
git__free(apphome_utf16);
if (strlen(apphome_utf8) >= GIT_PATH_MAX) {
git__free(apphome_utf8);
return git__throw(GIT_ESHORTBUFFER, "Path is too long");
}
git_buf_attach(system_config_path, apphome_utf8, 0);
strcpy(system_config_path, apphome_utf8);
git__free(apphome_utf8);
return GIT_SUCCESS;
}
#endif
int git_config_find_system(char *system_config_path)
int git_config_find_system_r(git_buf *system_config_path)
{
const char *etc = "/etc/gitconfig";
if (git_buf_sets(system_config_path, "/etc/gitconfig") < GIT_SUCCESS)
return git_buf_lasterror(system_config_path);
if (git_futils_exists(etc) == GIT_SUCCESS) {
memcpy(system_config_path, etc, strlen(etc) + 1);
if (git_futils_exists(system_config_path->ptr) == GIT_SUCCESS)
return GIT_SUCCESS;
}
git_buf_clear(system_config_path);
#if GIT_WIN32
return win32_find_system(system_config_path);
......@@ -405,6 +420,23 @@ int git_config_find_system(char *system_config_path)
#endif
}
int git_config_find_system(char *system_config_path)
{
git_buf path = GIT_BUF_INIT;
int error = git_config_find_system_r(&path);
if (error == GIT_SUCCESS) {
if (path.size > GIT_PATH_MAX)
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
else
git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
}
git_buf_free(&path);
return error;
}
int git_config_open_global(git_config **out)
{
int error;
......
......@@ -21,4 +21,7 @@ struct git_config {
git_vector files;
};
extern int git_config_find_global_r(git_buf *global_config_path);
extern int git_config_find_system_r(git_buf *system_config_path);
#endif
......@@ -115,25 +115,31 @@ int git_fetch_download_pack(char **out, git_remote *remote)
}
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
GIT_SOCKET fd, git_repository *repo)
int git_fetch__download_pack(
char **out,
const char *buffered,
size_t buffered_size,
GIT_SOCKET fd,
git_repository *repo)
{
git_filebuf file = GIT_FILEBUF_INIT;
int error;
char buff[1024], path[GIT_PATH_MAX];
char buff[1024];
git_buf path = GIT_BUF_INIT;
static const char suff[] = "/objects/pack/pack-received";
gitno_buffer buf;
git_path_join(path, repo->path_repository, suff);
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
if (memcmp(buffered, "PACK", strlen("PACK"))) {
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
}
error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
error = git_buf_joinpath(&path, repo->path_repository, suff);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -166,7 +172,7 @@ int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_s
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&file);
git_buf_free(&path);
return error;
}
......@@ -196,18 +196,19 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
/* If we are writing to a temp file */
if (flags & GIT_FILEBUF_TEMPORARY) {
char tmp_path[GIT_PATH_MAX];
git_buf tmp_path = GIT_BUF_INIT;
/* Open the file as temporary for locking */
file->fd = git_futils_mktmp(tmp_path, path);
file->fd = git_futils_mktmp(&tmp_path, path);
if (file->fd < 0) {
git_buf_free(&tmp_path);
error = GIT_EOSERR;
goto cleanup;
}
/* No original path */
file->path_original = NULL;
file->path_lock = git__strdup(tmp_path);
file->path_lock = git_buf_detach(&tmp_path);
if (file->path_lock == NULL) {
error = GIT_ENOMEM;
......
......@@ -10,35 +10,40 @@
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
{
int error = GIT_SUCCESS;
char target_folder_path[GIT_PATH_MAX];
int error;
git_buf target_folder = GIT_BUF_INIT;
error = git_path_dirname_r(target_folder_path, sizeof(target_folder_path), file_path);
if (error < GIT_SUCCESS)
error = git_path_dirname_r(&target_folder, file_path);
if (error < GIT_SUCCESS) {
git_buf_free(&target_folder);
return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
} else {
/* reset error */
error = GIT_SUCCESS;
}
/* Does the containing folder exist? */
if (git_futils_isdir(target_folder_path)) {
git_path_join(target_folder_path, target_folder_path, ""); /* Ensure there's a trailing slash */
if (git_futils_isdir(target_folder.ptr) != GIT_SUCCESS)
/* Let's create the tree structure */
error = git_futils_mkdir_r(target_folder_path, mode);
if (error < GIT_SUCCESS)
return error; /* The callee already takes care of setting the correct error message. */
}
error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
return GIT_SUCCESS;
git_buf_free(&target_folder);
return error;
}
int git_futils_mktmp(char *path_out, const char *filename)
int git_futils_mktmp(git_buf *path_out, const char *filename)
{
int fd;
strcpy(path_out, filename);
strcat(path_out, "_git2_XXXXXX");
git_buf_sets(path_out, filename);
git_buf_puts(path_out, "_git2_XXXXXX");
if (git_buf_oom(path_out))
return git__rethrow(git_buf_lasterror(path_out),
"Failed to create temporary file for %s", filename);
if ((fd = p_mkstemp(path_out)) < 0)
return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out);
if ((fd = p_mkstemp(path_out->ptr)) < 0)
return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
return fd;
}
......@@ -180,6 +185,14 @@ int git_futils_readbuffer(git_fbuffer *obj, const char *path)
return git_futils_readbuffer_updated(obj, path, NULL, NULL);
}
void git_futils_fbuffer_rtrim(git_fbuffer *obj)
{
unsigned char *buff = obj->data;
while (obj->len > 0 && isspace(buff[obj->len - 1]))
obj->len--;
buff[obj->len] = '\0';
}
void git_futils_freebuffer(git_fbuffer *obj)
{
assert(obj);
......@@ -215,76 +228,72 @@ GIT_INLINE(int) is_dot_or_dotdot(const char *name)
}
int git_futils_direach(
char *path,
size_t path_sz,
int (*fn)(void *, char *),
git_buf *path,
int (*fn)(void *, git_buf *),
void *arg)
{
size_t wd_len = strlen(path);
ssize_t wd_len;
DIR *dir;
struct dirent *de;
if (!wd_len || path_sz < wd_len + 2)
return git__throw(GIT_EINVALIDARGS, "Failed to process `%s` tree structure. Path is either empty or buffer size is too short", path);
while (path[wd_len - 1] == '/')
wd_len--;
path[wd_len++] = '/';
path[wd_len] = '\0';
if (git_path_to_dir(path) < GIT_SUCCESS)
return git_buf_lasterror(path);
dir = opendir(path);
wd_len = path->size;
dir = opendir(path->ptr);
if (!dir)
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path);
return git__throw(GIT_EOSERR, "Failed to process `%s` tree structure. An error occured while opening the directory", path->ptr);
while ((de = readdir(dir)) != NULL) {
size_t de_len;
int result;
if (is_dot_or_dotdot(de->d_name))
continue;
de_len = strlen(de->d_name);
if (path_sz < wd_len + de_len + 1) {
closedir(dir);
return git__throw(GIT_ERROR, "Failed to process `%s` tree structure. Buffer size is too short", path);
}
if (git_buf_puts(path, de->d_name) < GIT_SUCCESS)
return git_buf_lasterror(path);
strcpy(path + wd_len, de->d_name);
result = fn(arg, path);
if (result < GIT_SUCCESS) {
git_buf_truncate(path, wd_len); /* restore path */
if (result != GIT_SUCCESS) {
closedir(dir);
return result; /* The callee is reponsible for setting the correct error message */
}
if (result > 0) {
closedir(dir);
return result;
}
}
closedir(dir);
return GIT_SUCCESS;
}
int git_futils_mkdir_r(const char *path, const mode_t mode)
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
{
int error, root_path_offset;
git_buf make_path = GIT_BUF_INIT;
size_t start;
char *pp, *sp;
char *path_copy = git__strdup(path);
if (path_copy == NULL)
return GIT_ENOMEM;
if (base != NULL) {
start = strlen(base);
error = git_buf_joinpath(&make_path, base, path);
} else {
start = 0;
error = git_buf_puts(&make_path, path);
}
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to create `%s` tree structure", path);
error = GIT_SUCCESS;
pp = path_copy;
pp = make_path.ptr + start;
root_path_offset = git_path_root(pp);
root_path_offset = git_path_root(make_path.ptr);
if (root_path_offset > 0)
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
if (sp != pp && git_futils_isdir(path_copy) < GIT_SUCCESS) {
if (sp != pp && git_futils_isdir(make_path.ptr) < GIT_SUCCESS) {
*sp = 0;
error = p_mkdir(path_copy, mode);
error = p_mkdir(make_path.ptr, mode);
/* Do not choke while trying to recreate an existing directory */
if (errno == EEXIST)
......@@ -297,12 +306,12 @@ int git_futils_mkdir_r(const char *path, const mode_t mode)
}
if (*pp != '\0' && error == GIT_SUCCESS) {
error = p_mkdir(path, mode);
error = p_mkdir(make_path.ptr, mode);
if (errno == EEXIST)
error = GIT_SUCCESS;
}
git__free(path_copy);
git_buf_free(&make_path);
if (error < GIT_SUCCESS)
return git__throw(error, "Failed to recursively create `%s` tree structure", path);
......@@ -310,32 +319,34 @@ int git_futils_mkdir_r(const char *path, const mode_t mode)
return GIT_SUCCESS;
}
static int _rmdir_recurs_foreach(void *opaque, char *path)
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
{
int error = GIT_SUCCESS;
int force = *(int *)opaque;
if (git_futils_isdir(path) == GIT_SUCCESS) {
size_t root_size = strlen(path);
if ((error = git_futils_direach(path, GIT_PATH_MAX, _rmdir_recurs_foreach, opaque)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to remove directory `%s`", path);
path[root_size] = '\0';
return p_rmdir(path);
if (git_futils_isdir(path->ptr) == GIT_SUCCESS) {
error = git_futils_direach(path, _rmdir_recurs_foreach, opaque);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
return p_rmdir(path->ptr);
} else if (force) {
return p_unlink(path);
return p_unlink(path->ptr);
}
return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path);
return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
}
int git_futils_rmdir_r(const char *path, int force)
{
char p[GIT_PATH_MAX];
strncpy(p, path, GIT_PATH_MAX);
return _rmdir_recurs_foreach(&force, p);
int error;
git_buf p = GIT_BUF_INIT;
error = git_buf_sets(&p, path);
if (error == GIT_SUCCESS)
error = _rmdir_recurs_foreach(&force, &p);
git_buf_free(&p);
return error;
}
int git_futils_cmp_path(const char *name1, int len1, int isdir1,
......@@ -356,3 +367,39 @@ int git_futils_cmp_path(const char *name1, int len1, int isdir1,
return 0;
}
static int _check_dir_contents(
git_buf *dir,
const char *sub,
int append_on_success,
int (*predicate)(const char *))
{
int error = GIT_SUCCESS;
size_t dir_size = dir->size;
size_t sub_size = strlen(sub);
/* leave base valid even if we could not make space for subdir */
if ((error = git_buf_try_grow(dir, dir_size + sub_size + 2)) < GIT_SUCCESS)
return error;
/* save excursion */
git_buf_joinpath(dir, dir->ptr, sub);
error = (*predicate)(dir->ptr);
/* restore excursion */
if (!append_on_success || error != GIT_SUCCESS)
git_buf_truncate(dir, dir_size);
return error;
}
int git_futils_contains_dir(git_buf *base, const char *subdir, int append_if_exists)
{
return _check_dir_contents(base, subdir, append_if_exists, &git_futils_isdir);
}
int git_futils_contains_file(git_buf *base, const char *file, int append_if_exists)
{
return _check_dir_contents(base, file, append_if_exists, &git_futils_isfile);
}
......@@ -28,6 +28,7 @@ typedef struct { /* file io buffer */
extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
extern void git_futils_freebuffer(git_fbuffer *obj);
extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
/**
* File utils
......@@ -72,9 +73,25 @@ extern int git_futils_isdir(const char *path);
extern int git_futils_isfile(const char *path);
/**
* Check if the given path contains the given subdirectory.
*
* If `append_if_exists` is true, then the subdir will be appended to the
* parent path if it does exists.
*/
extern int git_futils_contains_dir(git_buf *parent, const char *subdir, int append_if_exists);
/**
* Check if the given path contains the given file
*
* If `append_if_exists` is true, then the filename will be appended to the
* parent path if it does exists.
*/
extern int git_futils_contains_file(git_buf *parent, const char *file, int append_if_exists);
/**
* Create a path recursively
*/
extern int git_futils_mkdir_r(const char *path, const mode_t mode);
extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
/**
* Create all the folders required to contain
......@@ -85,9 +102,11 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
extern int git_futils_rmdir_r(const char *path, int force);
/**
* Create and open a temporary file with a `_git2_` suffix
* Create and open a temporary file with a `_git2_` suffix.
* Writes the filename into path_out.
* @return On success, an open file descriptor, else an error code < 0.
*/
extern int git_futils_mktmp(char *path_out, const char *filename);
extern int git_futils_mktmp(git_buf *path_out, const char *filename);
/**
* Move a file on the filesystem, create the
......@@ -133,16 +152,14 @@ extern void git_futils_mmap_free(git_map *map);
*
* @param pathbuf buffer the function reads the initial directory
* path from, and updates with each successive entry's name.
* @param pathmax maximum allocation of pathbuf.
* @param fn function to invoke with each entry. The first arg is
* the input state and the second arg is pathbuf. The function
* may modify the pathbuf, but only by appending new text.
* @param state to pass to fn as the first arg.
*/
extern int git_futils_direach(
char *pathbuf,
size_t pathmax,
int (*fn)(void *, char *),
git_buf *pathbuf,
int (*fn)(void *, git_buf *),
void *state);
extern int git_futils_cmp_path(const char *name1, int len1, int isdir1,
......
......@@ -294,40 +294,30 @@ git_index_entry *git_index_get(git_index *index, unsigned int n)
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
{
git_index_entry *entry;
char full_path[GIT_PATH_MAX];
git_index_entry *entry = NULL;
struct stat st;
git_oid oid;
int error;
const char *workdir;
if (INDEX_OWNER(index) == NULL)
return git__throw(GIT_EBAREINDEX,
"Failed to initialize entry. Repository is bare");
workdir = git_repository_workdir(INDEX_OWNER(index));
if (workdir == NULL)
return git__throw(GIT_EBAREINDEX,
"Failed to initialize entry. Cannot resolved workdir");
git_path_join(full_path, workdir, rel_path);
if (p_lstat(full_path, &st) < 0)
return git__throw(GIT_ENOTFOUND,
"Failed to initialize entry. '%s' cannot be opened", full_path);
if (stage < 0 || stage > 3)
return git__throw(GIT_ERROR,
"Failed to initialize entry. Invalid stage %i", stage);
/* There is no need to validate the rel_path here, since it will be
* immediately validated by the call to git_blob_create_fromfile.
*/
/* write the blob to disk and get the oid */
if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to initialize index entry");
entry = git__malloc(sizeof(git_index_entry));
entry = git__calloc(1, sizeof(git_index_entry));
if (!entry)
return GIT_ENOMEM;
memset(entry, 0x0, sizeof(git_index_entry));
entry->ctime.seconds = (git_time_t)st.st_ctime;
entry->mtime.seconds = (git_time_t)st.st_mtime;
......
......@@ -151,28 +151,35 @@ cleanup:
return error;
}
static void index_path(char *path, git_indexer *idx)
static int index_path(git_buf *path, git_indexer *idx)
{
char *ptr;
const char prefix[] = "pack-", suffix[] = ".idx";
size_t slash = (size_t)path->size;
ptr = strrchr(path, '/') + 1;
/* search backwards for '/' */
while (slash > 0 && path->ptr[slash - 1] != '/')
slash--;
memcpy(ptr, prefix, strlen(prefix));
ptr += strlen(prefix);
git_oid_fmt(ptr, &idx->hash);
ptr += GIT_OID_HEXSZ;
memcpy(ptr, suffix, strlen(suffix) + 1);
if (git_buf_grow(path, slash + 1 + strlen(prefix) +
GIT_OID_HEXSZ + strlen(suffix) + 1) < GIT_SUCCESS)
return GIT_ENOMEM;
git_buf_truncate(path, slash + 1);
git_buf_puts(path, prefix);
git_oid_fmt(path->ptr + path->size, &idx->hash);
path->size += GIT_OID_HEXSZ;
git_buf_puts(path, suffix);
return git_buf_lasterror(path);
}
int git_indexer_write(git_indexer *idx)
{
git_mwindow *w = NULL;
int error;
size_t namelen;
unsigned int i, long_offsets = 0, left;
struct git_pack_idx_header hdr;
char filename[GIT_PATH_MAX];
git_buf filename = GIT_BUF_INIT;
struct entry *entry;
void *packfile_hash;
git_oid file_hash;
......@@ -180,16 +187,23 @@ int git_indexer_write(git_indexer *idx)
git_vector_sort(&idx->objects);
namelen = strlen(idx->pack->pack_name);
memcpy(filename, idx->pack->pack_name, namelen);
memcpy(filename + namelen - strlen("pack"), "idx", strlen("idx") + 1);
git_buf_sets(&filename, idx->pack->pack_name);
git_buf_truncate(&filename, filename.size - strlen("pack"));
git_buf_puts(&filename, "idx");
error = git_filebuf_open(&idx->file, filename, GIT_FILEBUF_HASH_CONTENTS);
if ((error = git_buf_lasterror(&filename)) < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);
if (error < GIT_SUCCESS)
goto cleanup;
/* Write out the header */
hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
hdr.idx_version = htonl(2);
error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));
if (error < GIT_SUCCESS)
goto cleanup;
/* Write out the fanout table */
for (i = 0; i < 256; ++i) {
......@@ -270,14 +284,18 @@ int git_indexer_write(git_indexer *idx)
goto cleanup;
/* Figure out what the final name should be */
index_path(filename, idx);
error = index_path(&filename, idx);
if (error < GIT_SUCCESS)
goto cleanup;
/* Commit file */
error = git_filebuf_commit_at(&idx->file, filename, GIT_PACK_FILE_MODE);
error = git_filebuf_commit_at(&idx->file, filename.ptr, GIT_PACK_FILE_MODE);
cleanup:
git_mwindow_free_all(&idx->pack->mwf);
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&idx->file);
git_buf_free(&filename);
return error;
}
......
......@@ -344,40 +344,47 @@ static int add_default_backends(git_odb *db, const char *objects_dir, int as_alt
static int load_alternates(git_odb *odb, const char *objects_dir)
{
char alternates_path[GIT_PATH_MAX];
char *buffer, *alternate;
git_buf alternates_path = GIT_BUF_INIT;
char *buffer;
git_fbuffer alternates_buf = GIT_FBUFFER_INIT;
const char *alternate;
int error;
git_path_join(alternates_path, objects_dir, GIT_ALTERNATES_FILE);
error = git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE);
if (error < GIT_SUCCESS)
return error;
if (git_futils_exists(alternates_path) < GIT_SUCCESS)
if (git_futils_exists(alternates_path.ptr) < GIT_SUCCESS) {
git_buf_free(&alternates_path);
return GIT_SUCCESS;
}
if (git_futils_readbuffer(&alternates_buf, alternates_path) < GIT_SUCCESS)
if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < GIT_SUCCESS) {
git_buf_free(&alternates_path);
return git__throw(GIT_EOSERR, "Failed to add backend. Can't read alternates");
}
buffer = (char *)alternates_buf.data;
error = GIT_SUCCESS;
/* add each alternate as a new backend; one alternate per line */
while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
char full_path[GIT_PATH_MAX];
if (*alternate == '\0' || *alternate == '#')
continue;
/* relative path: build based on the current `objects` folder */
if (*alternate == '.') {
git_path_join(full_path, objects_dir, alternate);
alternate = full_path;
error = git_buf_joinpath(&alternates_path, objects_dir, alternate);
if (error < GIT_SUCCESS)
break;
alternate = git_buf_cstr(&alternates_path);
}
if ((error = add_default_backends(odb, alternate, 1)) < GIT_SUCCESS)
break;
}
git_buf_free(&alternates_path);
git_futils_freebuffer(&alternates_buf);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to load alternates");
......
......@@ -26,7 +26,6 @@ typedef struct { /* object header data */
typedef struct {
git_odb_stream stream;
git_filebuf fbuf;
int finished;
} loose_writestream;
typedef struct loose_backend {
......@@ -51,31 +50,28 @@ typedef struct {
} loose_locate_object_state;
/***********************************************************
*
* MISCELANEOUS HELPER FUNCTIONS
*
***********************************************************/
static size_t object_file_name(char *name, size_t n, char *dir, const git_oid *id)
static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
{
size_t len = strlen(dir);
git_buf_sets(name, dir);
/* check length: 43 = 40 hex sha1 chars + 2 * '/' + '\0' */
if (len+43 > n)
return len+43;
/* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */
if (git_buf_grow(name, name->size + GIT_OID_HEXSZ + 3) < GIT_SUCCESS)
return GIT_ENOMEM;
/* the object dir: eg $GIT_DIR/objects */
strcpy(name, dir);
if (name[len-1] != '/')
name[len++] = '/';
git_path_to_dir(name);
/* loose object filename: aa/aaa... (41 bytes) */
git_oid_pathfmt(&name[len], id);
name[len+41] = '\0';
git_oid_pathfmt(name->ptr + name->size, id);
name->size += GIT_OID_HEXSZ + 1;
name->ptr[name->size] = '\0';
return 0;
return GIT_SUCCESS;
}
......@@ -384,18 +380,21 @@ static int inflate_disk_obj(git_rawobj *out, git_fbuffer *obj)
*
***********************************************************/
static int read_loose(git_rawobj *out, const char *loc)
static int read_loose(git_rawobj *out, git_buf *loc)
{
int error;
git_fbuffer obj = GIT_FBUFFER_INIT;
assert(out && loc);
if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
return error;
out->data = NULL;
out->len = 0;
out->type = GIT_OBJ_BAD;
if (git_futils_readbuffer(&obj, loc) < 0)
if (git_futils_readbuffer(&obj, loc->ptr) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to read loose object. File not found");
error = inflate_disk_obj(out, &obj);
......@@ -404,7 +403,7 @@ static int read_loose(git_rawobj *out, const char *loc)
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read loose object");
}
static int read_header_loose(git_rawobj *out, const char *loc)
static int read_header_loose(git_rawobj *out, git_buf *loc)
{
int error = GIT_SUCCESS, z_return = Z_ERRNO, read_bytes;
git_file fd;
......@@ -414,9 +413,12 @@ static int read_header_loose(git_rawobj *out, const char *loc)
assert(out && loc);
if ((error = git_buf_lasterror(loc)) < GIT_SUCCESS)
return error;
out->data = NULL;
if ((fd = p_open(loc, O_RDONLY)) < 0)
if ((fd = p_open(loc->ptr, O_RDONLY)) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to read loose object header. File not found");
init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
......@@ -456,33 +458,39 @@ cleanup:
return GIT_SUCCESS;
}
static int locate_object(char *object_location, loose_backend *backend, const git_oid *oid)
static int locate_object(
git_buf *object_location,
loose_backend *backend,
const git_oid *oid)
{
object_file_name(object_location, GIT_PATH_MAX, backend->objects_dir, oid);
return git_futils_exists(object_location);
int error = object_file_name(object_location, backend->objects_dir, oid);
if (error == GIT_SUCCESS)
error = git_futils_exists(git_buf_cstr(object_location));
return error;
}
/* Explore an entry of a directory and see if it matches a short oid */
static int fn_locate_object_short_oid(void *state, char *pathbuf) {
static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
loose_locate_object_state *sstate = (loose_locate_object_state *)state;
size_t pathbuf_len = strlen(pathbuf);
if (pathbuf_len - sstate->dir_len != GIT_OID_HEXSZ - 2) {
if (pathbuf->size - sstate->dir_len != GIT_OID_HEXSZ - 2) {
/* Entry cannot be an object. Continue to next entry */
return GIT_SUCCESS;
}
if (!git_futils_exists(pathbuf) && git_futils_isdir(pathbuf)) {
if (!git_futils_exists(pathbuf->ptr) && git_futils_isdir(pathbuf->ptr)) {
/* We are already in the directory matching the 2 first hex characters,
* compare the first ncmp characters of the oids */
if (!memcmp(sstate->short_oid + 2,
(unsigned char *)pathbuf + sstate->dir_len,
(unsigned char *)pathbuf->ptr + sstate->dir_len,
sstate->short_oid_len - 2)) {
if (!sstate->found) {
sstate->res_oid[0] = sstate->short_oid[0];
sstate->res_oid[1] = sstate->short_oid[1];
memcpy(sstate->res_oid+2, pathbuf+sstate->dir_len, GIT_OID_HEXSZ-2);
memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
}
sstate->found++;
}
......@@ -494,39 +502,50 @@ static int fn_locate_object_short_oid(void *state, char *pathbuf) {
}
/* Locate an object matching a given short oid */
static int locate_object_short_oid(char *object_location, git_oid *res_oid, loose_backend *backend, const git_oid *short_oid, unsigned int len)
static int locate_object_short_oid(
git_buf *object_location,
git_oid *res_oid,
loose_backend *backend,
const git_oid *short_oid,
unsigned int len)
{
char *objects_dir = backend->objects_dir;
size_t dir_len = strlen(objects_dir);
loose_locate_object_state state;
int error;
if (dir_len+43 > GIT_PATH_MAX)
return git__throw(GIT_ERROR, "Failed to locate object from short oid. Object path too long");
/* prealloc memory for OBJ_DIR/xx/ */
if ((error = git_buf_grow(object_location, dir_len + 5)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to locate object from short oid");
strcpy(object_location, objects_dir);
git_buf_sets(object_location, objects_dir);
git_path_to_dir(object_location);
/* Add a separator if not already there */
if (object_location[dir_len-1] != '/')
object_location[dir_len++] = '/';
/* save adjusted position at end of dir so it can be restored later */
dir_len = object_location->size;
/* Convert raw oid to hex formatted oid */
git_oid_fmt((char *)state.short_oid, short_oid);
/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
sprintf(object_location+dir_len, "%.2s/", state.short_oid);
error = git_buf_printf(object_location, "%.2s/", state.short_oid);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to locate object from short oid");
/* Check that directory exists */
if (git_futils_exists(object_location) || git_futils_isdir(object_location))
if (git_futils_exists(object_location->ptr) ||
git_futils_isdir(object_location->ptr))
return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
state.dir_len = dir_len+3;
state.dir_len = object_location->size;
state.short_oid_len = len;
state.found = 0;
/* Explore directory to find a unique object matching short_oid */
error = git_futils_direach(object_location, GIT_PATH_MAX, fn_locate_object_short_oid, &state);
if (error) {
error = git_futils_direach(object_location, fn_locate_object_short_oid, &state);
if (error)
return git__rethrow(error, "Failed to locate object from short oid");
}
if (!state.found) {
return git__throw(GIT_ENOTFOUND, "Failed to locate object from short oid. Object not found");
}
......@@ -538,7 +557,16 @@ static int locate_object_short_oid(char *object_location, git_oid *res_oid, loos
}
/* Update the location according to the oid obtained */
git_oid_pathfmt(object_location+dir_len, res_oid);
git_buf_truncate(object_location, dir_len);
error = git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2);
if (error)
return git__rethrow(error, "Failed to locate object from short oid");
git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
object_location->size += GIT_OID_HEXSZ + 1;
object_location->ptr[object_location->size] = '\0';
return GIT_SUCCESS;
}
......@@ -561,45 +589,49 @@ static int locate_object_short_oid(char *object_location, git_oid *res_oid, loos
static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
{
char object_path[GIT_PATH_MAX];
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
int error;
int error = GIT_SUCCESS;
assert(backend && oid);
raw.len = 0;
raw.type = GIT_OBJ_BAD;
if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS)
return error;
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend header. Object not found");
else if ((error = read_header_loose(&raw, &object_path)) == GIT_SUCCESS) {
*len_p = raw.len;
*type_p = raw.type;
return GIT_SUCCESS;
}
git_buf_free(&object_path);
return error;
}
static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
{
char object_path[GIT_PATH_MAX];
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
int error;
int error = GIT_SUCCESS;
assert(backend && oid);
if (locate_object(object_path, (loose_backend *)backend, oid) < 0)
return git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to read loose backend");
if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
error = git__throw(GIT_ENOTFOUND, "Failed to read loose backend. Object not found");
else if ((error = read_loose(&raw, &object_path)) == GIT_SUCCESS) {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
}
else {
git__rethrow(error, "Failed to read loose backend");
}
return GIT_SUCCESS;
git_buf_free(&object_path);
return error;
}
static int loose_backend__read_prefix(
......@@ -611,45 +643,52 @@ static int loose_backend__read_prefix(
const git_oid *short_oid,
unsigned int len)
{
int error = GIT_SUCCESS;
if (len < GIT_OID_MINPREFIXLEN)
return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
return git__throw(GIT_EAMBIGUOUSOIDPREFIX, "Failed to read loose "
"backend. Prefix length is lower than %d.", GIT_OID_MINPREFIXLEN);
if (len >= GIT_OID_HEXSZ) {
/* We can fall back to regular read method */
int error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
if (error == GIT_SUCCESS)
git_oid_cpy(out_oid, short_oid);
return error;
} else {
char object_path[GIT_PATH_MAX];
git_buf object_path = GIT_BUF_INIT;
git_rawobj raw;
int error;
assert(backend && short_oid);
if ((error = locate_object_short_oid(object_path, out_oid, (loose_backend *)backend, short_oid, len)) < 0) {
return git__rethrow(error, "Failed to read loose backend");
}
if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to read loose backend");
if ((error = locate_object_short_oid(&object_path, out_oid,
(loose_backend *)backend, short_oid, len)) < 0)
git__rethrow(error, "Failed to read loose backend");
else if ((error = read_loose(&raw, &object_path)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read loose backend");
else {
*buffer_p = raw.data;
*len_p = raw.len;
*type_p = raw.type;
}
return GIT_SUCCESS;
git_buf_free(&object_path);
}
return error;
}
static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
char object_path[GIT_PATH_MAX];
git_buf object_path = GIT_BUF_INIT;
int error;
assert(backend && oid);
return locate_object(object_path, (loose_backend *)backend, oid) == GIT_SUCCESS;
error = locate_object(&object_path, (loose_backend *)backend, oid);
git_buf_free(&object_path);
return (error == GIT_SUCCESS);
}
static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
......@@ -658,30 +697,39 @@ static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
loose_backend *backend = (loose_backend *)_stream->backend;
int error;
char final_path[GIT_PATH_MAX];
git_buf final_path = GIT_BUF_INIT;
if ((error = git_filebuf_hash(oid, &stream->fbuf)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write loose backend");
goto cleanup;
if (object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid))
return GIT_ENOMEM;
if ((error = object_file_name(&final_path, backend->objects_dir, oid)) < GIT_SUCCESS)
goto cleanup;
if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write loose backend");
if ((error = git_buf_lasterror(&final_path)) < GIT_SUCCESS)
goto cleanup;
stream->finished = 1;
if ((error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS)
goto cleanup;
/*
* Don't try to add an existing object to the repository. This
* is what git does and allows us to sidestep the fact that
* we're not allowed to overwrite a read-only file on Windows.
*/
if (git_futils_exists(final_path) == GIT_SUCCESS) {
if (git_futils_exists(final_path.ptr) == GIT_SUCCESS) {
git_filebuf_cleanup(&stream->fbuf);
return GIT_SUCCESS;
goto cleanup;
}
return git_filebuf_commit_at(&stream->fbuf, final_path, GIT_OBJECT_FILE_MODE);
error = git_filebuf_commit_at(&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
cleanup:
git_buf_free(&final_path);
if (error < GIT_SUCCESS)
git__rethrow(error, "Failed to write loose backend");
return error;
}
static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
......@@ -694,9 +742,7 @@ static void loose_backend__stream_free(git_odb_stream *_stream)
{
loose_writestream *stream = (loose_writestream *)_stream;
if (!stream->finished)
git_filebuf_cleanup(&stream->fbuf);
git__free(stream);
}
......@@ -718,7 +764,8 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
loose_backend *backend;
loose_writestream *stream;
char hdr[64], tmp_path[GIT_PATH_MAX];
char hdr[64];
git_buf tmp_path = GIT_BUF_INIT;
int hdrlen;
int error;
......@@ -742,33 +789,38 @@ static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_
stream->stream.free = &loose_backend__stream_free;
stream->stream.mode = GIT_STREAM_WRONLY;
git_path_join(tmp_path, backend->objects_dir, "tmp_object");
error = git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object");
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&stream->fbuf, tmp_path,
error = git_filebuf_open(&stream->fbuf, tmp_path.ptr,
GIT_FILEBUF_HASH_CONTENTS |
GIT_FILEBUF_TEMPORARY |
(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
if (error < GIT_SUCCESS) {
git__free(stream);
return git__rethrow(error, "Failed to create loose backend stream");
}
if (error < GIT_SUCCESS)
goto cleanup;
error = stream->stream.write((git_odb_stream *)stream, hdr, hdrlen);
if (error < GIT_SUCCESS) {
git_filebuf_cleanup(&stream->fbuf);
git__free(stream);
return git__rethrow(error, "Failed to create loose backend stream");
}
if (error < GIT_SUCCESS)
goto cleanup;
git_buf_free(&tmp_path);
*stream_out = (git_odb_stream *)stream;
return GIT_SUCCESS;
cleanup:
git_buf_free(&tmp_path);
git_filebuf_cleanup(&stream->fbuf);
git__free(stream);
return git__rethrow(error, "Failed to create loose backend stream");
}
static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
{
int error, header_len;
char final_path[GIT_PATH_MAX], header[64];
git_buf final_path = GIT_BUF_INIT;
char header[64];
git_filebuf fbuf = GIT_FILEBUF_INIT;
loose_backend *backend;
......@@ -781,30 +833,35 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v
return GIT_EOBJCORRUPTED;
}
git_path_join(final_path, backend->objects_dir, "tmp_object");
error = git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object");
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&fbuf, final_path,
error = git_filebuf_open(&fbuf, final_path.ptr,
GIT_FILEBUF_HASH_CONTENTS |
GIT_FILEBUF_TEMPORARY |
(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT));
if (error < GIT_SUCCESS)
return error;
goto cleanup;
git_filebuf_write(&fbuf, header, header_len);
git_filebuf_write(&fbuf, data, len);
git_filebuf_hash(oid, &fbuf);
if ((error = object_file_name(final_path, sizeof(final_path), backend->objects_dir, oid)) < GIT_SUCCESS)
error = object_file_name(&final_path, backend->objects_dir, oid);
if (error < GIT_SUCCESS)
goto cleanup;
if ((error = git_futils_mkpath2file(final_path, GIT_OBJECT_DIR_MODE)) < GIT_SUCCESS)
error = git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE);
if (error < GIT_SUCCESS)
goto cleanup;
return git_filebuf_commit_at(&fbuf, final_path, GIT_OBJECT_FILE_MODE);
error = git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&fbuf);
git_buf_free(&final_path);
return error;
}
......
......@@ -133,7 +133,7 @@ static int pack_window_contains(git_mwindow *win, off_t offset);
static int packfile_sort__cb(const void *a_, const void *b_);
static int packfile_load__cb(void *_data, char *path);
static int packfile_load__cb(void *_data, git_buf *path);
static int packfile_refresh_all(struct pack_backend *backend);
static int pack_entry_find(struct git_pack_entry *e,
......@@ -207,23 +207,23 @@ static int packfile_sort__cb(const void *a_, const void *b_)
static int packfile_load__cb(void *_data, char *path)
static int packfile_load__cb(void *_data, git_buf *path)
{
struct pack_backend *backend = (struct pack_backend *)_data;
struct git_pack_file *pack;
int error;
size_t i;
if (git__suffixcmp(path, ".idx") != 0)
if (git__suffixcmp(path->ptr, ".idx") != 0)
return GIT_SUCCESS; /* not an index */
for (i = 0; i < backend->packs.length; ++i) {
struct git_pack_file *p = git_vector_get(&backend->packs, i);
if (memcmp(p->pack_name, path, strlen(path) - strlen(".idx")) == 0)
if (memcmp(p->pack_name, path->ptr, path->size - strlen(".idx")) == 0)
return GIT_SUCCESS;
}
error = git_packfile_check(&pack, path);
error = git_packfile_check(&pack, path->ptr);
if (error == GIT_ENOTFOUND) {
/* ignore missing .pack file as git does */
return GIT_SUCCESS;
......@@ -250,11 +250,13 @@ static int packfile_refresh_all(struct pack_backend *backend)
return git__throw(GIT_ENOTFOUND, "Failed to refresh packfiles. Backend not found");
if (st.st_mtime != backend->pack_folder_mtime) {
char path[GIT_PATH_MAX];
strcpy(path, backend->pack_folder);
git_buf path = GIT_BUF_INIT;
git_buf_sets(&path, backend->pack_folder);
/* reload all packs */
error = git_futils_direach(path, GIT_PATH_MAX, packfile_load__cb, (void *)backend);
error = git_futils_direach(&path, packfile_load__cb, (void *)backend);
git_buf_free(&path);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to refresh packfiles");
......@@ -451,27 +453,25 @@ static void pack_backend__free(git_odb_backend *_backend)
int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
{
struct pack_backend *backend;
char path[GIT_PATH_MAX];
struct pack_backend *backend = NULL;
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
backend = git__calloc(1, sizeof(struct pack_backend));
if (backend == NULL)
return GIT_ENOMEM;
if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < GIT_SUCCESS) {
git__free(backend);
return GIT_ENOMEM;
}
error = git_vector_init(&backend->packs, 8, packfile_sort__cb);
if (error < GIT_SUCCESS)
goto cleanup;
git_path_join(path, objects_dir, "pack");
if (git_futils_isdir(path) == GIT_SUCCESS) {
backend->pack_folder = git__strdup(path);
backend->pack_folder_mtime = 0;
error = git_buf_joinpath(&path, objects_dir, "pack");
if (error < GIT_SUCCESS)
goto cleanup;
if (backend->pack_folder == NULL) {
git__free(backend);
return GIT_ENOMEM;
}
if (git_futils_isdir(git_buf_cstr(&path)) == GIT_SUCCESS) {
backend->pack_folder = git_buf_detach(&path);
backend->pack_folder_mtime = 0;
}
backend->parent.read = &pack_backend__read;
......@@ -481,5 +481,11 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
backend->parent.free = &pack_backend__free;
*backend_out = (git_odb_backend *)backend;
return GIT_SUCCESS;
cleanup:
if (error < GIT_SUCCESS)
git__free(backend);
git_buf_free(&path);
return error;
}
......@@ -16,7 +16,7 @@
* Based on the Android implementation, BSD licensed.
* Check http://android.git.kernel.org/
*/
int git_path_basename_r(char *buffer, size_t bufflen, const char *path)
int git_path_basename_r(git_buf *buffer, const char *path)
{
const char *endp, *startp;
int len, result;
......@@ -49,18 +49,13 @@ int git_path_basename_r(char *buffer, size_t bufflen, const char *path)
Exit:
result = len;
if (buffer == NULL) {
return result;
}
if (len > (int)bufflen-1) {
len = (int)bufflen-1;
result = GIT_ENOMEM;
}
if (len >= 0) {
memmove(buffer, startp, len);
buffer[len] = 0;
if (buffer != NULL) {
if (git_buf_set(buffer, startp, len) < GIT_SUCCESS)
return git__rethrow(git_buf_lasterror(buffer),
"Could not get basename of '%s'", path);
}
return result;
}
......@@ -68,7 +63,7 @@ Exit:
* Based on the Android implementation, BSD licensed.
* Check http://android.git.kernel.org/
*/
int git_path_dirname_r(char *buffer, size_t bufflen, const char *path)
int git_path_dirname_r(git_buf *buffer, const char *path)
{
const char *endp;
int result, len;
......@@ -114,59 +109,39 @@ int git_path_dirname_r(char *buffer, size_t bufflen, const char *path)
Exit:
result = len;
if (len+1 > GIT_PATH_MAX) {
return GIT_ENOMEM;
}
if (buffer == NULL)
return result;
if (len > (int)bufflen-1) {
len = (int)bufflen-1;
result = GIT_ENOMEM;
if (buffer != NULL) {
if (git_buf_set(buffer, path, len) < GIT_SUCCESS)
return git__rethrow(git_buf_lasterror(buffer),
"Could not get dirname of '%s'", path);
}
if (len >= 0) {
memmove(buffer, path, len);
buffer[len] = 0;
}
return result;
}
char *git_path_dirname(const char *path)
{
char *dname = NULL;
int len;
len = (path ? strlen(path) : 0) + 2;
dname = (char *)git__malloc(len);
if (dname == NULL)
return NULL;
git_buf buf = GIT_BUF_INIT;
char *dirname;
if (git_path_dirname_r(dname, len, path) < GIT_SUCCESS) {
git__free(dname);
return NULL;
}
git_path_dirname_r(&buf, path);
dirname = git_buf_detach(&buf);
git_buf_free(&buf); /* avoid memleak if error occurs */
return dname;
return dirname;
}
char *git_path_basename(const char *path)
{
char *bname = NULL;
int len;
git_buf buf = GIT_BUF_INIT;
char *basename;
len = (path ? strlen(path) : 0) + 2;
bname = (char *)git__malloc(len);
if (bname == NULL)
return NULL;
git_path_basename_r(&buf, path);
basename = git_buf_detach(&buf);
git_buf_free(&buf); /* avoid memleak if error occurs */
if (git_path_basename_r(bname, len, path) < GIT_SUCCESS) {
git__free(bname);
return NULL;
}
return bname;
return basename;
}
......@@ -188,39 +163,6 @@ const char *git_path_topdir(const char *path)
return &path[i + 1];
}
void git_path_join_n(char *buffer_out, int count, ...)
{
va_list ap;
int i;
char *buffer_start = buffer_out;
va_start(ap, count);
for (i = 0; i < count; ++i) {
const char *path;
int len;
path = va_arg(ap, const char *);
assert((i == 0) || path != buffer_start);
if (i > 0 && *path == '/' && buffer_out > buffer_start && buffer_out[-1] == '/')
path++;
if (!*path)
continue;
len = strlen(path);
memmove(buffer_out, path, len);
buffer_out = buffer_out + len;
if (i < count - 1 && buffer_out[-1] != '/')
*buffer_out++ = '/';
}
va_end(ap);
*buffer_out = '\0';
}
int git_path_root(const char *path)
{
int offset = 0;
......@@ -237,34 +179,61 @@ int git_path_root(const char *path)
return -1; /* Not a real error. Rather a signal than the path is not rooted */
}
int git_path_prettify(char *path_out, const char *path, const char *base)
int git_path_prettify(git_buf *path_out, const char *path, const char *base)
{
char *result;
char *result = NULL;
int error = GIT_SUCCESS;
git_buf_clear(path_out);
/* construct path if needed */
if (base != NULL && git_path_root(path) < 0) {
if ((error = git_buf_joinpath(path_out, base, path)) < GIT_SUCCESS)
return error;
path = path_out->ptr;
}
if (base == NULL || git_path_root(path) >= 0) {
result = p_realpath(path, path_out);
/* allow realpath to allocate the buffer */
if (path != NULL)
result = p_realpath(path, NULL);
if (result) {
error = git_buf_sets(path_out, result);
git__free(result);
} else {
char aux_path[GIT_PATH_MAX];
git_path_join(aux_path, base, path);
result = p_realpath(aux_path, path_out);
error = GIT_EOSERR;
}
return result ? GIT_SUCCESS : GIT_EOSERR;
return error;
}
int git_path_prettify_dir(char *path_out, const char *path, const char *base)
int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
{
size_t end;
int error = git_path_prettify(path_out, path, base);
if (git_path_prettify(path_out, path, base) < GIT_SUCCESS)
return GIT_EOSERR;
if (error == GIT_SUCCESS)
error = git_path_to_dir(path_out);
end = strlen(path_out);
return error;
}
if (end && path_out[end - 1] != '/') {
path_out[end] = '/';
path_out[end + 1] = '\0';
}
int git_path_to_dir(git_buf *path)
{
if (path->asize > 0 &&
path->size > 0 &&
path->ptr[path->size - 1] != '/')
git_buf_putc(path, '/');
return git_buf_lasterror(path);
}
void git_path_string_to_dir(char* path, size_t size)
{
size_t end = strlen(path);
return GIT_SUCCESS;
if (end && path[end - 1] != '/' && end < size) {
path[end] = '/';
path[end + 1] = '\0';
}
}
......@@ -8,6 +8,7 @@
#define INCLUDE_path_h__
#include "common.h"
#include "buffer.h"
/*
* The dirname() function shall take a pointer to a character string
......@@ -22,11 +23,13 @@
* The `git_path_dirname` implementation is thread safe. The returned
* string must be manually free'd.
*
* The `git_path_dirname_r` implementation expects a string allocated
* by the user with big enough size.
* The `git_path_dirname_r` implementation writes the dirname to a `git_buf`
* if the buffer pointer is not NULL.
* It returns an error code < 0 if there is an allocation error, otherwise
* the length of the dirname (which will be > 0).
*/
extern char *git_path_dirname(const char *path);
extern int git_path_dirname_r(char *buffer, size_t bufflen, const char *path);
extern int git_path_dirname_r(git_buf *buffer, const char *path);
/*
* This function returns the basename of the file, which is the last
......@@ -40,32 +43,22 @@ extern int git_path_dirname_r(char *buffer, size_t bufflen, const char *path);
* The `git_path_basename` implementation is thread safe. The returned
* string must be manually free'd.
*
* The `git_path_basename_r` implementation expects a string allocated
* by the user with big enough size.
* The `git_path_basename_r` implementation writes the basename to a `git_buf`.
* It returns an error code < 0 if there is an allocation error, otherwise
* the length of the basename (which will be >= 0).
*/
extern char *git_path_basename(const char *path);
extern int git_path_basename_r(char *buffer, size_t bufflen, const char *path);
extern int git_path_basename_r(git_buf *buffer, const char *path);
extern const char *git_path_topdir(const char *path);
/**
* Join two paths together. Takes care of properly fixing the
* middle slashes and everything
*
* The paths are joined together into buffer_out; this is expected
* to be an user allocated buffer of `GIT_PATH_MAX` size
*/
extern void git_path_join_n(char *buffer_out, int npath, ...);
GIT_INLINE(void) git_path_join(char *buffer_out, const char *path_a, const char *path_b)
{
git_path_join_n(buffer_out, 2, path_a, path_b);
}
extern int git_path_root(const char *path);
int git_path_root(const char *path);
extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);
int git_path_prettify(char *path_out, const char *path, const char *base);
int git_path_prettify_dir(char *path_out, const char *path, const char *base);
extern int git_path_to_dir(git_buf *path);
extern void git_path_string_to_dir(char* path, size_t size);
#ifdef GIT_WIN32
GIT_INLINE(void) git_path_mkposix(char *path)
......
......@@ -268,8 +268,7 @@ void git_pkt_free(git_pkt *pkt)
int git_pkt_buffer_flush(git_buf *buf)
{
git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
}
int git_pkt_send_flush(int s)
......@@ -291,9 +290,7 @@ static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps
git_buf_grow(buf, buf->size + len);
git_oid_fmt(oid, &head->oid);
git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
}
static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, GIT_SOCKET fd)
......@@ -401,8 +398,7 @@ int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
memset(oidhex, 0x0, sizeof(oidhex));
git_oid_fmt(oidhex, oid);
git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
}
int git_pkt_send_have(git_oid *oid, int fd)
......@@ -416,8 +412,7 @@ int git_pkt_send_have(git_oid *oid, int fd)
int git_pkt_buffer_done(git_buf *buf)
{
git_buf_puts(buf, pkt_done_str);
return git_buf_oom(buf) ? GIT_ENOMEM : GIT_SUCCESS;
return git_buf_puts(buf, pkt_done_str);
}
int git_pkt_send_done(int fd)
......
......@@ -35,7 +35,8 @@ int p_getcwd(char *buffer_out, size_t size)
git_path_mkposix(buffer_out);
git_path_join(buffer_out, buffer_out, ""); //Ensure the path ends with a trailing slash
git_path_string_to_dir(buffer_out, size); //Ensure the path ends with a trailing slash
return GIT_SUCCESS;
}
......
......@@ -51,7 +51,7 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_puts(&log, oid_new);
git_signature__writebuf(&log, " ", committer);
log.size--; /* drop LF */
git_buf_truncate(&log, log.size - 1); /* drop LF */
if (msg) {
if (strchr(msg, '\n')) {
......@@ -65,15 +65,21 @@ static int reflog_write(const char *log_path, const char *oid_old,
git_buf_putc(&log, '\n');
if ((error = git_buf_lasterror(&log)) < GIT_SUCCESS) {
git_buf_free(&log);
return git__rethrow(error, "Failed to write reflog. Memory allocation failure");
}
if ((error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND)) < GIT_SUCCESS) {
git_buf_free(&log);
return git__throw(GIT_ERROR, "Failed to write reflog. Cannot open reflog `%s`", log_path);
return git__rethrow(error, "Failed to write reflog. Cannot open reflog `%s`", log_path);
}
git_filebuf_write(&fbuf, log.ptr, log.size);
error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
git_buf_free(&log);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to write reflog");
}
......@@ -176,7 +182,7 @@ void git_reflog_free(git_reflog *reflog)
int git_reflog_read(git_reflog **reflog, git_reference *ref)
{
int error;
char log_path[GIT_PATH_MAX];
git_buf log_path = GIT_BUF_INIT;
git_fbuffer log_file = GIT_FBUFFER_INIT;
git_reflog *log = NULL;
......@@ -185,23 +191,28 @@ int git_reflog_read(git_reflog **reflog, git_reference *ref)
if ((error = reflog_init(&log, ref)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to read reflog. Cannot init reflog");
git_path_join_n(log_path, 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
if ((error = git_futils_readbuffer(&log_file, log_path)) < GIT_SUCCESS) {
git_reflog_free(log);
return git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path);
if ((error = git_futils_readbuffer(&log_file, log_path.ptr)) < GIT_SUCCESS) {
git__rethrow(error, "Failed to read reflog. Cannot read file `%s`", log_path.ptr);
goto cleanup;
}
error = reflog_parse(log, log_file.data, log_file.len);
git_futils_freebuffer(&log_file);
if (error == GIT_SUCCESS)
*reflog = log;
if ((error = reflog_parse(log, log_file.data, log_file.len)) < GIT_SUCCESS)
git__rethrow(error, "Failed to read reflog");
else
*reflog = log;
cleanup:
if (error != GIT_SUCCESS && log != NULL)
git_reflog_free(log);
git_futils_freebuffer(&log_file);
git_buf_free(&log_path);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to read reflog");
return error;
}
int git_reflog_write(git_reference *ref, const git_oid *oid_old,
......@@ -210,7 +221,7 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
int error;
char old[GIT_OID_HEXSZ+1];
char new[GIT_OID_HEXSZ+1];
char log_path[GIT_PATH_MAX];
git_buf log_path = GIT_BUF_INIT;
git_reference *r;
const git_oid *oid;
......@@ -220,65 +231,83 @@ int git_reflog_write(git_reference *ref, const git_oid *oid_old,
oid = git_reference_oid(r);
if (oid == NULL) {
git_reference_free(r);
return git__throw(GIT_ERROR,
error = git__throw(GIT_ERROR,
"Failed to write reflog. Cannot resolve reference `%s`", r->name);
git_reference_free(r);
return error;
}
git_reference_free(r);
git_oid_to_string(new, GIT_OID_HEXSZ+1, oid);
git_path_join_n(log_path, 3,
error = git_buf_join_n(&log_path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
git_reference_free(r);
if (git_futils_exists(log_path)) {
error = git_futils_mkpath2file(log_path, GIT_REFLOG_DIR_MODE);
if (git_futils_exists(log_path.ptr)) {
error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error,
git__rethrow(error,
"Failed to write reflog. Cannot create reflog directory");
} else if (git_futils_isfile(log_path)) {
return git__throw(GIT_ERROR,
"Failed to write reflog. `%s` is directory", log_path);
} else if (git_futils_isfile(log_path.ptr)) {
error = git__throw(GIT_ERROR,
"Failed to write reflog. `%s` is directory", log_path.ptr);
} else if (oid_old == NULL) {
return git__throw(GIT_ERROR,
error = git__throw(GIT_ERROR,
"Failed to write reflog. Old OID cannot be NULL for existing reference");
}
if (error < GIT_SUCCESS)
goto cleanup;
if (oid_old)
git_oid_to_string(old, GIT_OID_HEXSZ+1, oid_old);
git_oid_to_string(old, sizeof(old), oid_old);
else
p_snprintf(old, GIT_OID_HEXSZ+1, "%0*d", GIT_OID_HEXSZ, 0);
p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);
error = reflog_write(log_path.ptr, old, new, committer, msg);
return reflog_write(log_path, old, new, committer, msg);
cleanup:
git_buf_free(&log_path);
return error;
}
int git_reflog_rename(git_reference *ref, const char *new_name)
{
char old_path[GIT_PATH_MAX];
char new_path[GIT_PATH_MAX];
int error;
git_buf old_path = GIT_BUF_INIT;
git_buf new_path = GIT_BUF_INIT;
if (git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name) &&
git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, new_name))
error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path));
else
error = GIT_ENOMEM;
git_path_join_n(old_path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name);
git_path_join_n(new_path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, new_name);
git_buf_free(&old_path);
git_buf_free(&new_path);
return p_rename(old_path, new_path);
return error;
}
int git_reflog_delete(git_reference *ref)
{
char path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
git_buf path = GIT_BUF_INIT;
error = git_buf_join_n(&path, '/', 3,
ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
git_path_join_n(path, 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name);
if (error == GIT_SUCCESS && git_futils_exists(path.ptr) == 0)
error = p_unlink(path.ptr);
if (git_futils_exists(path))
return GIT_SUCCESS;
git_buf_free(&path);
return p_unlink(path);
return error;
}
unsigned int git_reflog_entrycount(git_reflog *reflog)
......
......@@ -88,9 +88,12 @@ void git_reference_free(git_reference *reference)
return;
git__free(reference->name);
reference->name = NULL;
if (reference->flags & GIT_REF_SYMBOLIC)
if (reference->flags & GIT_REF_SYMBOLIC) {
git__free(reference->target.symbolic);
reference->target.symbolic = NULL;
}
git__free(reference);
}
......@@ -123,14 +126,18 @@ static int reference_create(
static int reference_read(git_fbuffer *file_content, time_t *mtime, const char *repo_path, const char *ref_name, int *updated)
{
char path[GIT_PATH_MAX];
git_buf path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
assert(file_content && repo_path && ref_name);
/* Determine the full path of the file */
git_path_join(path, repo_path, ref_name);
if ((error = git_buf_joinpath(&path, repo_path, ref_name)) == GIT_SUCCESS)
error = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
git_buf_free(&path);
return git_futils_readbuffer_updated(file_content, path, mtime, updated);
return error;
}
static int loose_parse_symbolic(git_reference *ref, git_fbuffer *file_content)
......@@ -195,14 +202,14 @@ static int loose_parse_oid(git_oid *oid, git_fbuffer *file_content)
return GIT_SUCCESS;
}
static git_rtype loose_guess_rtype(const char *full_path)
static git_rtype loose_guess_rtype(const git_buf *full_path)
{
git_fbuffer ref_file = GIT_FBUFFER_INIT;
git_rtype type;
type = GIT_REF_INVALID;
if (git_futils_readbuffer(&ref_file, full_path) == GIT_SUCCESS) {
if (git_futils_readbuffer(&ref_file, full_path->ptr) == GIT_SUCCESS) {
if (git__prefixcmp((const char *)(ref_file.data), GIT_SYMREF) == 0)
type = GIT_REF_SYMBOLIC;
else
......@@ -287,14 +294,17 @@ cleanup:
static int loose_write(git_reference *ref)
{
git_filebuf file = GIT_FILEBUF_INIT;
char ref_path[GIT_PATH_MAX];
git_buf ref_path = GIT_BUF_INIT;
int error;
struct stat st;
git_path_join(ref_path, ref->owner->path_repository, ref->name);
error = git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name);
if (error < GIT_SUCCESS)
goto unlock;
if ((error = git_filebuf_open(&file, ref_path, GIT_FILEBUF_FORCE)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write loose reference");
error = git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE);
if (error < GIT_SUCCESS)
goto unlock;
if (ref->flags & GIT_REF_OID) {
char oid[GIT_OID_HEXSZ + 1];
......@@ -314,12 +324,15 @@ static int loose_write(git_reference *ref)
goto unlock;
}
if (p_stat(ref_path, &st) == GIT_SUCCESS)
if (p_stat(ref_path.ptr, &st) == GIT_SUCCESS)
ref->mtime = st.st_mtime;
git_buf_free(&ref_path);
return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
unlock:
git_buf_free(&ref_path);
git_filebuf_cleanup(&file);
return git__rethrow(error, "Failed to write loose reference");
}
......@@ -518,14 +531,13 @@ struct dirent_list_data {
void *callback_payload;
};
static int _dirent_loose_listall(void *_data, char *full_path)
static int _dirent_loose_listall(void *_data, git_buf *full_path)
{
struct dirent_list_data *data = (struct dirent_list_data *)_data;
char *file_path = full_path + data->repo_path_len;
const char *file_path = full_path->ptr + data->repo_path_len;
if (git_futils_isdir(full_path) == GIT_SUCCESS)
return git_futils_direach(full_path, GIT_PATH_MAX,
_dirent_loose_listall, _data);
if (git_futils_isdir(full_path->ptr) == GIT_SUCCESS)
return git_futils_direach(full_path, _dirent_loose_listall, _data);
/* do not add twice a reference that exists already in the packfile */
if ((data->list_flags & GIT_REF_PACKED) != 0 &&
......@@ -540,20 +552,18 @@ static int _dirent_loose_listall(void *_data, char *full_path)
return data->callback(file_path, data->callback_payload);
}
static int _dirent_loose_load(void *data, char *full_path)
static int _dirent_loose_load(void *data, git_buf *full_path)
{
git_repository *repository = (git_repository *)data;
void *old_ref = NULL;
struct packref *ref;
char *file_path;
const char *file_path;
int error;
if (git_futils_isdir(full_path) == GIT_SUCCESS)
return git_futils_direach(
full_path, GIT_PATH_MAX,
_dirent_loose_load, repository);
if (git_futils_isdir(full_path->ptr) == GIT_SUCCESS)
return git_futils_direach(full_path, _dirent_loose_load, repository);
file_path = full_path + strlen(repository->path_repository);
file_path = full_path->ptr + strlen(repository->path_repository);
error = loose_lookup_to_packfile(&ref, repository, file_path);
if (error == GIT_SUCCESS) {
......@@ -582,21 +592,24 @@ static int _dirent_loose_load(void *data, char *full_path)
*/
static int packed_loadloose(git_repository *repository)
{
char refs_path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
git_buf refs_path = GIT_BUF_INIT;
/* the packfile must have been previously loaded! */
assert(repository->references.packfile);
git_path_join(refs_path, repository->path_repository, GIT_REFS_DIR);
if ((error = git_buf_joinpath(&refs_path,
repository->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
return error;
/*
* Load all the loose files from disk into the Packfile table.
* This will overwrite any old packed entries with their
* updated loose versions
*/
return git_futils_direach(
refs_path, GIT_PATH_MAX,
_dirent_loose_load, repository);
error = git_futils_direach(&refs_path, _dirent_loose_load, repository);
git_buf_free(&refs_path);
return error;
}
/*
......@@ -704,20 +717,26 @@ static int packed_find_peel(git_repository *repo, struct packref *ref)
static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
{
unsigned int i;
char full_path[GIT_PATH_MAX];
git_buf full_path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
for (i = 0; i < packing_list->length; ++i) {
struct packref *ref = git_vector_get(packing_list, i);
int an_error;
if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
continue;
git_path_join(full_path, repo->path_repository, ref->name);
an_error = git_buf_joinpath(&full_path, repo->path_repository, ref->name);
if (an_error == GIT_SUCCESS &&
git_futils_exists(full_path.ptr) == GIT_SUCCESS &&
p_unlink(full_path.ptr) < GIT_SUCCESS)
an_error = GIT_EOSERR;
if (git_futils_exists(full_path) == GIT_SUCCESS &&
p_unlink(full_path) < GIT_SUCCESS)
error = GIT_EOSERR;
/* keep the error if we haven't seen one yet */
if (error > an_error)
error = an_error;
/*
* if we fail to remove a single file, this is *not* good,
......@@ -727,6 +746,8 @@ static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
*/
}
git_buf_free(&full_path);
return error == GIT_SUCCESS ?
GIT_SUCCESS :
git__rethrow(error, "Failed to remove loose packed reference");
......@@ -747,9 +768,9 @@ static int packed_write(git_repository *repo)
{
git_filebuf pack_file = GIT_FILEBUF_INIT;
int error;
const char *errmsg = "Failed to write packed references file";
unsigned int i;
char pack_file_path[GIT_PATH_MAX];
git_buf pack_file_path = GIT_BUF_INIT;
git_vector packing_list;
size_t total_refs;
......@@ -758,7 +779,7 @@ static int packed_write(git_repository *repo)
total_refs = repo->references.packfile->key_count;
if ((error =
git_vector_init(&packing_list, total_refs, packed_sort)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to init packed refernces list");
return git__rethrow(error, "Failed to init packed references list");
/* Load all the packfile into a vector */
{
......@@ -775,16 +796,23 @@ static int packed_write(git_repository *repo)
git_vector_sort(&packing_list);
/* Now we can open the file! */
git_path_join(pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
if ((error = git_filebuf_open(&pack_file, pack_file_path, 0)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write open packed references file");
error = git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE);
if (error < GIT_SUCCESS)
goto cleanup;
if ((error = git_filebuf_open(&pack_file, pack_file_path.ptr, 0)) < GIT_SUCCESS) {
errmsg = "Failed to open packed references file";
goto cleanup;
}
/* Packfiles have a header... apparently
* This is in fact not required, but we might as well print it
* just for kicks */
if ((error =
git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to write packed references file header");
git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < GIT_SUCCESS) {
errmsg = "Failed to write packed references file header";
goto cleanup;
}
for (i = 0; i < packing_list.length; ++i) {
struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
......@@ -812,17 +840,19 @@ cleanup:
error = packed_remove_loose(repo, &packing_list);
if (p_stat(pack_file_path, &st) == GIT_SUCCESS)
if (p_stat(pack_file_path.ptr, &st) == GIT_SUCCESS)
repo->references.packfile_time = st.st_mtime;
}
}
else git_filebuf_cleanup(&pack_file);
git_vector_free(&packing_list);
git_buf_free(&pack_file_path);
return error == GIT_SUCCESS ?
GIT_SUCCESS :
git__rethrow(error, "Failed to write packed references file");
if (error < GIT_SUCCESS)
git__rethrow(error, "%s", errmsg);
return error;
}
static int _reference_available_cb(const char *ref, void *data)
......@@ -873,21 +903,25 @@ static int reference_available(
static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
{
int error;
char ref_path[GIT_PATH_MAX];
git_buf ref_path = GIT_BUF_INIT;
error = packed_load(repo);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Cannot resolve if a reference exists");
git_path_join(ref_path, repo->path_repository, ref_name);
error = git_buf_joinpath(&ref_path, repo->path_repository, ref_name);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Cannot resolve if a reference exists");
if (git_futils_isfile(ref_path) == GIT_SUCCESS ||
git_hashtable_lookup(repo->references.packfile, ref_path) != NULL) {
if (git_futils_isfile(ref_path.ptr) == GIT_SUCCESS ||
git_hashtable_lookup(repo->references.packfile, ref_path.ptr) != NULL) {
*exists = 1;
} else {
*exists = 0;
}
git_buf_free(&ref_path);
return GIT_SUCCESS;
}
......@@ -972,12 +1006,15 @@ static int reference_delete(git_reference *ref)
/* If the reference is loose, we can just remove the reference
* from the filesystem */
} else {
char full_path[GIT_PATH_MAX];
git_reference *ref_in_pack;
git_buf full_path = GIT_BUF_INIT;
git_path_join(full_path, ref->owner->path_repository, ref->name);
error = git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name);
if (error < GIT_SUCCESS)
goto cleanup;
error = p_unlink(full_path);
error = p_unlink(full_path.ptr);
git_buf_free(&full_path); /* done with path at this point */
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -1261,8 +1298,7 @@ int git_reference_set_target(git_reference *ref, const char *target)
int git_reference_rename(git_reference *ref, const char *new_name, int force)
{
int error;
char aux_path[GIT_PATH_MAX];
git_buf aux_path = GIT_BUF_INIT;
char normalized[GIT_REFNAME_MAX];
const char *head_target = NULL;
......@@ -1309,6 +1345,13 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
return git__rethrow(error,
"Failed to rename reference. Reference already exists");
/* Initialize path now so we won't get an allocation failure once
* we actually start removing things.
*/
error = git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name);
if (error < GIT_SUCCESS)
goto cleanup;
/*
* Now delete the old ref and remove an possibly existing directory
* named `new_name`. Note that using the internal `reference_delete`
......@@ -1318,10 +1361,9 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
if ((error = reference_delete(ref)) < GIT_SUCCESS)
goto cleanup;
git_path_join(aux_path, ref->owner->path_repository, new_name);
if (git_futils_exists(aux_path) == GIT_SUCCESS) {
if (git_futils_isdir(aux_path) == GIT_SUCCESS) {
if ((error = git_futils_rmdir_r(aux_path, 0)) < GIT_SUCCESS)
if (git_futils_exists(aux_path.ptr) == GIT_SUCCESS) {
if (git_futils_isdir(aux_path.ptr) == GIT_SUCCESS) {
if ((error = git_futils_rmdir_r(aux_path.ptr, 0)) < GIT_SUCCESS)
goto rollback;
} else goto rollback;
}
......@@ -1360,9 +1402,12 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
/*
* Rename the reflog file.
*/
git_path_join_n(aux_path, 3, ref->owner->path_repository,
error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository,
GIT_REFLOG_DIR, ref->name);
if (git_futils_exists(aux_path) == GIT_SUCCESS)
if (error < GIT_SUCCESS)
goto cleanup;
if (git_futils_exists(aux_path.ptr) == GIT_SUCCESS)
error = git_reflog_rename(ref, new_name);
/*
......@@ -1377,6 +1422,7 @@ int git_reference_rename(git_reference *ref, const char *new_name, int force)
cleanup:
/* We no longer need the newly created reference nor the head */
git_reference_free(head);
git_buf_free(&aux_path);
return error == GIT_SUCCESS ?
GIT_SUCCESS :
git__rethrow(error, "Failed to rename reference");
......@@ -1395,6 +1441,8 @@ rollback:
/* The reference is no longer packed */
ref->flags &= ~GIT_REF_PACKED;
git_buf_free(&aux_path);
return error == GIT_SUCCESS ?
git__rethrow(GIT_ERROR, "Failed to rename reference. Did rollback") :
git__rethrow(error, "Failed to rename reference. Failed to rollback");
......@@ -1467,7 +1515,7 @@ int git_reference_foreach(
{
int error;
struct dirent_list_data data;
char refs_path[GIT_PATH_MAX];
git_buf refs_path = GIT_BUF_INIT;
/* list all the packed references first */
if (list_flags & GIT_REF_PACKED) {
......@@ -1493,8 +1541,15 @@ int git_reference_foreach(
data.callback = callback;
data.callback_payload = payload;
git_path_join(refs_path, repo->path_repository, GIT_REFS_DIR);
return git_futils_direach(refs_path, GIT_PATH_MAX, _dirent_loose_listall, &data);
if ((error = git_buf_joinpath(&refs_path,
repo->path_repository, GIT_REFS_DIR)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to alloc space for references");
error = git_futils_direach(&refs_path, _dirent_loose_listall, &data);
git_buf_free(&refs_path);
return error;
}
static int cb__reflist_add(const char *ref, void *data)
......
......@@ -93,3 +93,21 @@ int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, con
return GIT_SUCCESS;
}
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
{
if (git_buf_sets(out, spec->dst) < GIT_SUCCESS)
return git_buf_lasterror(out);
/*
* No '*' at the end means that it's mapped to one specific local
* branch, so no actual transformation is needed.
*/
if (out->size > 0 && out->ptr[out->size - 1] != '*')
return GIT_SUCCESS;
git_buf_truncate(out, out->size - 1); /* remove trailing '*' */
git_buf_puts(out, name);
return git_buf_lasterror(out);
}
......@@ -8,6 +8,7 @@
#define INCLUDE_refspec_h__
#include "git2/refspec.h"
#include "buffer.h"
struct git_refspec {
struct git_refspec *next;
......@@ -20,4 +21,15 @@ struct git_refspec {
int git_refspec_parse(struct git_refspec *refspec, const char *str);
/**
* Transform a reference to its target following the refspec's rules,
* and writes the results into a git_buf.
*
* @param out where to store the target name
* @param spec the refspec
* @param name the name of the reference to transform
* @return GIT_SUCCESS or error if buffer allocation fails
*/
int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name);
#endif
......@@ -263,7 +263,7 @@ int git_remote_update_tips(git_remote *remote)
{
int error = GIT_SUCCESS;
unsigned int i = 0;
char refname[GIT_PATH_MAX];
git_buf refname = GIT_BUF_INIT;
git_vector *refs = &remote->refs;
git_remote_head *head;
git_reference *ref;
......@@ -271,8 +271,6 @@ int git_remote_update_tips(git_remote *remote)
assert(remote);
memset(refname, 0x0, sizeof(refname));
if (refs->length == 0)
return GIT_SUCCESS;
......@@ -289,18 +287,20 @@ int git_remote_update_tips(git_remote *remote)
for (; i < refs->length; ++i) {
head = refs->contents[i];
error = git_refspec_transform(refname, sizeof(refname), spec, head->name);
error = git_refspec_transform_r(&refname, spec, head->name);
if (error < GIT_SUCCESS)
return error;
break;
error = git_reference_create_oid(&ref, remote->repo, refname, &head->oid, 1);
error = git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1);
if (error < GIT_SUCCESS)
return error;
break;
git_reference_free(ref);
}
return GIT_SUCCESS;
git_buf_free(&refname);
return error;
}
int git_remote_connected(git_remote *remote)
......
......@@ -75,21 +75,17 @@ void git_repository_free(git_repository *repo)
*
* Open a repository object from its path
*/
static int quickcheck_repository_dir(const char *repository_path)
static int quickcheck_repository_dir(git_buf *repository_path)
{
char path_aux[GIT_PATH_MAX];
/* Ensure HEAD file exists */
git_path_join(path_aux, repository_path, GIT_HEAD_FILE);
if (git_futils_isfile(path_aux) < 0)
/* Check OBJECTS_DIR first, since it will generate the longest path name */
if (git_futils_contains_dir(repository_path, GIT_OBJECTS_DIR, 0) < 0)
return GIT_ERROR;
git_path_join(path_aux, repository_path, GIT_OBJECTS_DIR);
if (git_futils_isdir(path_aux) < 0)
/* Ensure HEAD file exists */
if (git_futils_contains_file(repository_path, GIT_HEAD_FILE, 0) < 0)
return GIT_ERROR;
git_path_join(path_aux, repository_path, GIT_REFS_DIR);
if (git_futils_isdir(path_aux) < 0)
if (git_futils_contains_dir(repository_path, GIT_REFS_DIR, 0) < 0)
return GIT_ERROR;
return GIT_SUCCESS;
......@@ -135,74 +131,74 @@ static int load_config_data(git_repository *repo)
static int load_workdir(git_repository *repo)
{
if (!repo->is_bare) {
char workdir_buf[GIT_PATH_MAX];
int error;
git_buf workdir_buf = GIT_BUF_INIT;
if (git_path_dirname_r(workdir_buf, sizeof(workdir_buf), repo->path_repository) < 0)
return git__throw(GIT_EOSERR,
"Failed to resolved working directory");
if (repo->is_bare)
return GIT_SUCCESS;
git_path_join(workdir_buf, workdir_buf, "");
git_path_dirname_r(&workdir_buf, repo->path_repository);
git_path_to_dir(&workdir_buf);
repo->workdir = git__strdup(workdir_buf);
if (repo->workdir == NULL)
return GIT_ENOMEM;
}
if ((error = git_buf_lasterror(&workdir_buf)) == GIT_SUCCESS)
repo->workdir = git_buf_detach(&workdir_buf);
return GIT_SUCCESS;
git_buf_free(&workdir_buf);
return error;
}
int git_repository_open(git_repository **repo_out, const char *path)
{
int error = GIT_SUCCESS;
char path_buf[GIT_PATH_MAX];
size_t path_len;
git_buf path_buf = GIT_BUF_INIT;
git_repository *repo = NULL;
error = git_path_prettify_dir(path_buf, path, NULL);
error = git_path_prettify_dir(&path_buf, path, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to open repository");
path_len = strlen(path_buf);
goto cleanup;
/**
* Check if the path we've been given is actually the path
* of the working dir, by testing if it contains a `.git`
* folder inside of it.
*/
git_path_join(path_buf, path_buf, DOT_GIT);
if (git_futils_isdir(path_buf) < GIT_SUCCESS) {
path_buf[path_len] = 0;
}
git_futils_contains_dir(&path_buf, DOT_GIT, 1); /* append on success */
/* ignore error, since it just means `path/.git` doesn't exist */
if (quickcheck_repository_dir(path_buf) < GIT_SUCCESS)
return git__throw(GIT_ENOTAREPO,
if (quickcheck_repository_dir(&path_buf) < GIT_SUCCESS) {
error = git__throw(GIT_ENOTAREPO,
"The given path is not a valid Git repository");
goto cleanup;
}
repo = repository_alloc();
if (repo == NULL)
return GIT_ENOMEM;
if (repo == NULL) {
error = GIT_ENOMEM;
goto cleanup;
}
repo->path_repository = git__strdup(path_buf);
repo->path_repository = git_buf_detach(&path_buf);
if (repo->path_repository == NULL) {
git_repository_free(repo);
return GIT_ENOMEM;
error = GIT_ENOMEM;
goto cleanup;
}
error = load_config_data(repo);
if (error < GIT_SUCCESS) {
git_repository_free(repo);
return error;
}
if (error < GIT_SUCCESS)
goto cleanup;
error = load_workdir(repo);
if (error < GIT_SUCCESS) {
git_repository_free(repo);
return error;
}
if (error < GIT_SUCCESS)
goto cleanup;
*repo_out = repo;
return GIT_SUCCESS;
cleanup:
git_repository_free(repo);
git_buf_free(&path_buf);
return error;
}
static int load_config(
......@@ -211,7 +207,7 @@ static int load_config(
const char *global_config_path,
const char *system_config_path)
{
char config_path[GIT_PATH_MAX];
git_buf config_path = GIT_BUF_INIT;
int error;
git_config *cfg = NULL;
......@@ -221,8 +217,13 @@ static int load_config(
if (error < GIT_SUCCESS)
return error;
git_path_join(config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
error = git_config_add_file_ondisk(cfg, config_path, 3);
error = git_buf_joinpath(&config_path, repo->path_repository,
GIT_CONFIG_FILENAME_INREPO);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_config_add_file_ondisk(cfg, config_path.ptr, 3);
git_buf_free(&config_path); /* done with config_path now */
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -251,19 +252,22 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
if (repo->_config == NULL) {
int error;
char buf_global[GIT_PATH_MAX], buf_system[GIT_PATH_MAX];
git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
const char *global_config_path = NULL;
const char *system_config_path = NULL;
if (git_config_find_global(buf_global) == GIT_SUCCESS)
global_config_path = buf_global;
if (git_config_find_global_r(&global_buf) == GIT_SUCCESS)
global_config_path = global_buf.ptr;
if (git_config_find_system(buf_system) == GIT_SUCCESS)
system_config_path = buf_system;
if (git_config_find_system_r(&system_buf) == GIT_SUCCESS)
system_config_path = system_buf.ptr;
error = load_config(&repo->_config, repo, global_config_path, system_config_path);
git_buf_free(&global_buf);
git_buf_free(&system_buf);
if (error < GIT_SUCCESS)
return error;
......@@ -301,11 +305,14 @@ int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
if (repo->_odb == NULL) {
int error;
char odb_path[GIT_PATH_MAX];
git_buf odb_path = GIT_BUF_INIT;
git_path_join(odb_path, repo->path_repository, GIT_OBJECTS_DIR);
error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
if (error < GIT_SUCCESS)
return error;
error = git_odb_open(&repo->_odb, odb_path);
error = git_odb_open(&repo->_odb, odb_path.ptr);
git_buf_free(&odb_path); /* done with path */
if (error < GIT_SUCCESS)
return error;
......@@ -346,11 +353,14 @@ int git_repository_index__weakptr(git_index **out, git_repository *repo)
if (repo->_index == NULL) {
int error;
char index_path[GIT_PATH_MAX];
git_buf index_path = GIT_BUF_INIT;
git_path_join(index_path, repo->path_repository, GIT_INDEX_FILE);
error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
if (error < GIT_SUCCESS)
return error;
error = git_index_open(&repo->_index, index_path);
error = git_index_open(&repo->_index, index_path.ptr);
git_buf_free(&index_path); /* done with path */
if (error < GIT_SUCCESS)
return error;
......@@ -397,7 +407,17 @@ static int retrieve_device(dev_t *device_out, const char *path)
return GIT_SUCCESS;
}
static int retrieve_ceiling_directories_offset(const char *path, const char *ceiling_directories)
/*
* This function returns furthest offset into path where a ceiling dir
* is found, so we can stop processing the path at that point.
*
* Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
* the stack could remove directories name limits, but at the cost of doing
* repeated malloc/frees inside the loop below, so let's not do it now.
*/
static int retrieve_ceiling_directories_offset(
const char *path,
const char *ceiling_directories)
{
char buf[GIT_PATH_MAX + 1];
char buf2[GIT_PATH_MAX + 1];
......@@ -416,7 +436,7 @@ static int retrieve_ceiling_directories_offset(const char *path, const char *cei
for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
len = sep - ceil;
if (len == 0 || len > GIT_PATH_MAX || git_path_root(ceil) == -1)
if (len == 0 || len >= (int)sizeof(buf) || git_path_root(ceil) == -1)
continue;
strncpy(buf, ceil, len);
......@@ -440,45 +460,43 @@ static int retrieve_ceiling_directories_offset(const char *path, const char *cei
return max_len <= min_len ? min_len : max_len;
}
static int read_gitfile(char *path_out, const char *file_path, const char *base_path)
/*
* Read the contents of `file_path` and set `path_out` to the repo dir that
* it points to. Before calling, set `path_out` to the base directory that
* should be used if the contents of `file_path` are a relative path.
*/
static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path)
{
git_fbuffer file;
int error;
size_t end_offset;
char *data;
assert(path_out && file_path && base_path);
assert(path_out && file_path);
error = git_futils_readbuffer(&file, file_path);
if (error < GIT_SUCCESS)
return error;
data = (char*)(file.data);
if (git__prefixcmp(data, GIT_FILE_CONTENT_PREFIX)) {
if (git__prefixcmp((char *)file.data, GIT_FILE_CONTENT_PREFIX)) {
git_futils_freebuffer(&file);
return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path);
}
end_offset = strlen(data) - 1;
git_futils_fbuffer_rtrim(&file);
for (;data[end_offset] == '\r' || data[end_offset] == '\n'; --end_offset);
data[end_offset + 1] = '\0';
if (strlen(GIT_FILE_CONTENT_PREFIX) == end_offset + 1) {
if (strlen(GIT_FILE_CONTENT_PREFIX) == file.len) {
git_futils_freebuffer(&file);
return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path);
}
data = data + strlen(GIT_FILE_CONTENT_PREFIX);
error = git_path_prettify_dir(path_out, data, base_path);
error = git_path_prettify_dir(path_out,
((char *)file.data) + strlen(GIT_FILE_CONTENT_PREFIX), base_path);
git_futils_freebuffer(&file);
if (error == 0 && git_futils_exists(path_out) == 0)
if (error == GIT_SUCCESS && git_futils_exists(path_out->ptr) == 0)
return GIT_SUCCESS;
return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to an inexisting path");
return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to a nonexistent path");
}
int git_repository_discover(
......@@ -489,54 +507,62 @@ int git_repository_discover(
const char *ceiling_dirs)
{
int error, ceiling_offset;
char bare_path[GIT_PATH_MAX];
char normal_path[GIT_PATH_MAX];
char *found_path;
git_buf bare_path = GIT_BUF_INIT;
git_buf normal_path = GIT_BUF_INIT;
git_buf *found_path = NULL;
dev_t current_device = 0;
assert(start_path && repository_path);
error = git_path_prettify_dir(bare_path, start_path, NULL);
*repository_path = '\0';
error = git_path_prettify_dir(&bare_path, start_path, NULL);
if (error < GIT_SUCCESS)
return error;
goto cleanup;
if (!across_fs) {
error = retrieve_device(&current_device, bare_path);
error = retrieve_device(&current_device, bare_path.ptr);
if (error < GIT_SUCCESS)
return error;
goto cleanup;
}
ceiling_offset = retrieve_ceiling_directories_offset(bare_path, ceiling_dirs);
git_path_join(normal_path, bare_path, DOT_GIT);
ceiling_offset = retrieve_ceiling_directories_offset(bare_path.ptr, ceiling_dirs);
while(1) {
error = git_buf_joinpath(&normal_path, bare_path.ptr, DOT_GIT);
if (error < GIT_SUCCESS)
break;
/**
* If the `.git` file is regular instead of
* a directory, it should contain the path of the actual git repository
*/
if (git_futils_isfile(normal_path) == GIT_SUCCESS) {
error = read_gitfile(repository_path, normal_path, bare_path);
if (error < GIT_SUCCESS)
return git__rethrow(error,
"Unable to read git file `%s`", normal_path);
if (git_futils_isfile(normal_path.ptr) == GIT_SUCCESS) {
git_buf gitfile_path = GIT_BUF_INIT;
error = quickcheck_repository_dir(repository_path);
error = read_gitfile(&gitfile_path, normal_path.ptr, bare_path.ptr);
if (error < GIT_SUCCESS)
return git__throw(GIT_ENOTFOUND,
"The `.git` file found at '%s' points"
"to an inexisting Git folder", normal_path);
git__rethrow(error, "Unable to read git file `%s`", normal_path.ptr);
else if ((error = quickcheck_repository_dir(&gitfile_path)) < GIT_SUCCESS)
git__throw(GIT_ENOTFOUND,
"The `.git` file found at '%s' points "
"to a nonexistent git folder", normal_path.ptr);
else {
git_buf_swap(&normal_path, &gitfile_path);
found_path = &normal_path;
}
return GIT_SUCCESS;
git_buf_free(&gitfile_path);
break;
}
/**
* If the `.git` file is a folder, we check inside of it
*/
if (git_futils_isdir(normal_path) == GIT_SUCCESS) {
error = quickcheck_repository_dir(normal_path);
if (git_futils_isdir(normal_path.ptr) == GIT_SUCCESS) {
error = quickcheck_repository_dir(&normal_path);
if (error == GIT_SUCCESS) {
found_path = normal_path;
found_path = &normal_path;
break;
}
}
......@@ -545,44 +571,63 @@ int git_repository_discover(
* Otherwise, the repository may be bare, let's check
* the root anyway
*/
error = quickcheck_repository_dir(bare_path);
error = quickcheck_repository_dir(&bare_path);
if (error == GIT_SUCCESS) {
found_path = bare_path;
found_path = &bare_path;
break;
}
/**
* If we didn't find it, walk up the tree
*/
error = git_path_dirname_r(&normal_path, bare_path.ptr);
if (error < GIT_SUCCESS) {
git__rethrow(GIT_EOSERR, "Failed to dirname '%s'", bare_path.ptr);
break;
}
if (git_path_dirname_r(normal_path, sizeof(normal_path), bare_path) < GIT_SUCCESS)
return git__throw(GIT_EOSERR, "Failed to dirname '%s'", bare_path);
git_buf_swap(&bare_path, &normal_path);
if (!across_fs) {
dev_t new_device;
error = retrieve_device(&new_device, normal_path);
error = retrieve_device(&new_device, bare_path.ptr);
if (error < GIT_SUCCESS || current_device != new_device) {
return git__throw(GIT_ENOTAREPO,
error = git__throw(GIT_ENOTAREPO,
"Not a git repository (or any parent up to mount parent %s)\n"
"Stopping at filesystem boundary.", bare_path);
"Stopping at filesystem boundary.", normal_path.ptr);
break;
}
current_device = new_device;
}
strcpy(bare_path, normal_path);
git_path_join(normal_path, bare_path, DOT_GIT);
// nothing has been found, lets try the parent directory
if (bare_path[ceiling_offset] == '\0') {
return git__throw(GIT_ENOTAREPO,
/* nothing has been found, lets try the parent directory
* but stop if we hit one of the ceiling directories
*/
if (bare_path.ptr[ceiling_offset] == '\0') {
error = git__throw(GIT_ENOTAREPO,
"Not a git repository (or any of the parent directories): %s", start_path);
break;
}
}
if (size < strlen(found_path) + 2) {
return git__throw(GIT_ESHORTBUFFER,
"The repository buffer is not long enough to handle the repository path `%s`", found_path);
assert(found_path || error != GIT_SUCCESS);
if (found_path) {
if ((error = git_path_to_dir(found_path)) < GIT_SUCCESS)
git__rethrow(error, "Could not convert git repository to directory");
else if (size < (size_t)(found_path->size + 1))
error = git__throw(GIT_ESHORTBUFFER,
"The repository buffer is not long enough to "
"handle the repository path `%s`", found_path->ptr);
else
git_buf_copy_cstr(repository_path, size, found_path);
}
git_path_join(repository_path, found_path, "");
return GIT_SUCCESS;
cleanup:
git_buf_free(&bare_path);
git_buf_free(&normal_path);
return error;
}
static int repo_init_reinit(const char *repository_path, int is_bare)
......@@ -596,21 +641,23 @@ static int repo_init_reinit(const char *repository_path, int is_bare)
static int repo_init_createhead(const char *git_dir)
{
char ref_path[GIT_PATH_MAX];
int error;
git_buf ref_path = GIT_BUF_INIT;
git_filebuf ref = GIT_FILEBUF_INIT;
git_path_join(ref_path, git_dir, GIT_HEAD_FILE);
if (!(error = git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) &&
!(error = git_filebuf_open(&ref, ref_path.ptr, 0)) &&
!(error = git_filebuf_printf(&ref, "ref: refs/heads/master\n")))
error = git_filebuf_commit(&ref, GIT_REFS_FILE_MODE);
git_filebuf_open(&ref, ref_path, 0);
git_filebuf_printf(&ref, "ref: refs/heads/master\n");
return git_filebuf_commit(&ref, GIT_REFS_FILE_MODE);
git_buf_free(&ref_path);
return error;
}
static int repo_init_config(const char *git_dir, int is_bare)
{
char cfg_path[GIT_PATH_MAX];
git_config *config;
git_buf cfg_path = GIT_BUF_INIT;
git_config *config = NULL;
int error = GIT_SUCCESS;
#define SET_REPO_CONFIG(type, name, val) {\
......@@ -619,29 +666,40 @@ static int repo_init_config(const char *git_dir, int is_bare)
goto cleanup;\
}
git_path_join(cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO);
error = git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_config_open_ondisk(&config, cfg_path);
error = git_config_open_ondisk(&config, cfg_path.ptr);
if (error < GIT_SUCCESS)
return error;
goto cleanup;
SET_REPO_CONFIG(bool, "core.bare", is_bare);
SET_REPO_CONFIG(int32, "core.repositoryformatversion", 0);
/* TODO: what other defaults? */
cleanup:
git_buf_free(&cfg_path);
git_config_free(config);
return error;
}
static int repo_init_structure(const char *git_dir, int is_bare)
{
int error;
char temp_path[GIT_PATH_MAX];
if (git_futils_mkdir_r(git_dir, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE))
return git__throw(GIT_ERROR, "Failed to initialize repository structure. Could not mkdir");
int error, i;
struct { const char *dir; mode_t mode; } dirs[] = {
{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */
{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */
{ NULL, 0 }
};
/* Make the base directory */
error = git_futils_mkdir_r(git_dir, NULL, is_bare ?
GIT_BARE_DIR_MODE : GIT_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to initialize repository structure. Could not mkdir");
/* Hides the ".git" directory */
if (!is_bare) {
......@@ -652,67 +710,52 @@ static int repo_init_structure(const char *git_dir, int is_bare)
#endif
}
/* Creates the '/objects/info/' directory */
git_path_join(temp_path, git_dir, GIT_OBJECTS_INFO_DIR);
error = git_futils_mkdir_r(temp_path, GIT_OBJECT_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to initialize repository structure");
/* Creates the '/objects/pack/' directory */
git_path_join(temp_path, git_dir, GIT_OBJECTS_PACK_DIR);
error = p_mkdir(temp_path, GIT_OBJECT_DIR_MODE);
if (error < GIT_SUCCESS)
return git__throw(error, "Unable to create `%s` folder", temp_path);
/* Creates the '/refs/heads/' directory */
git_path_join(temp_path, git_dir, GIT_REFS_HEADS_DIR);
error = git_futils_mkdir_r(temp_path, GIT_REFS_DIR_MODE);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to initialize repository structure");
/* Creates the '/refs/tags/' directory */
git_path_join(temp_path, git_dir, GIT_REFS_TAGS_DIR);
error = p_mkdir(temp_path, GIT_REFS_DIR_MODE);
/* Make subdirectories as needed */
for (i = 0; dirs[i].dir != NULL; ++i) {
error = git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode);
if (error < GIT_SUCCESS)
return git__throw(error, "Unable to create `%s` folder", temp_path);
return git__rethrow(error,
"Failed to create repository folder `%s`", dirs[i].dir);
}
/* TODO: what's left? templates? */
return GIT_SUCCESS;
return error;
}
int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
{
int error = GIT_SUCCESS;
git_repository *repo = NULL;
char repository_path[GIT_PATH_MAX];
git_buf repository_path = GIT_BUF_INIT;
assert(repo_out && path);
git_path_join(repository_path, path, is_bare ? "" : GIT_DIR);
error = git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR);
if (error < GIT_SUCCESS)
return error;
if (git_futils_isdir(repository_path)) {
if (quickcheck_repository_dir(repository_path) == GIT_SUCCESS)
return repo_init_reinit(repository_path, is_bare);
if (git_futils_isdir(repository_path.ptr) == GIT_SUCCESS) {
if (quickcheck_repository_dir(&repository_path) == GIT_SUCCESS) {
error = repo_init_reinit(repository_path.ptr, is_bare);
git_buf_free(&repository_path);
return error;
}
}
error = repo_init_structure(repository_path, is_bare);
if (error < GIT_SUCCESS)
goto cleanup;
error = repo_init_config(repository_path, is_bare);
if (error < GIT_SUCCESS)
goto cleanup;
if (!(error = repo_init_structure(repository_path.ptr, is_bare)) &&
!(error = repo_init_config(repository_path.ptr, is_bare)) &&
!(error = repo_init_createhead(repository_path.ptr)))
error = git_repository_open(repo_out, repository_path.ptr);
else
git_repository_free(repo);
error = repo_init_createhead(repository_path);
if (error < GIT_SUCCESS)
goto cleanup;
git_buf_free(&repository_path);
return git_repository_open(repo_out, repository_path);
if (error != GIT_SUCCESS)
git__rethrow(error, "Failed to (re)init the repository `%s`", path);
cleanup:
git_repository_free(repo);
return git__rethrow(error, "Failed to (re)init the repository `%s`", path);
return error;
}
int git_repository_head_detached(git_repository *repo)
......
......@@ -16,7 +16,9 @@ void git_signature_free(git_signature *sig)
return;
git__free(sig->name);
sig->name = NULL;
git__free(sig->email);
sig->email = NULL;
git__free(sig);
}
......
......@@ -28,12 +28,10 @@ struct status_entry {
static struct status_entry *status_entry_new(git_vector *entries, const char *path)
{
struct status_entry *e = git__malloc(sizeof(*e) + strlen(path) + 1);
struct status_entry *e = git__calloc(sizeof(*e) + strlen(path) + 1, 1);
if (e == NULL)
return NULL;
memset(e, 0x0, sizeof(*e));
if (entries != NULL)
git_vector_insert(entries, e);
......@@ -73,7 +71,7 @@ static void status_entry_update_from_index(struct status_entry *e, git_index *in
status_entry_update_from_index_entry(e, index_entry);
}
static int status_entry_update_from_workdir(struct status_entry *e, char* full_path)
static int status_entry_update_from_workdir(struct status_entry *e, const char* full_path)
{
struct stat filest;
......@@ -125,7 +123,7 @@ struct status_st {
git_tree *tree;
int workdir_path_len;
char* head_tree_relative_path;
git_buf head_tree_relative_path;
int head_tree_relative_path_len;
unsigned int tree_position;
unsigned int index_position;
......@@ -155,6 +153,7 @@ static int retrieve_head_tree(git_tree **tree_out, git_repository *repo)
return git__rethrow(error, "The tip of HEAD can't be retrieved");
git_reference_free(resolved_head_ref);
if ((error = git_commit_tree(&tree, head_commit)) < GIT_SUCCESS) {
error = git__rethrow(error, "The tree of HEAD can't be retrieved");
goto exit;
......@@ -174,12 +173,15 @@ enum path_type {
GIT_STATUS_PATH_FOLDER,
};
static int dirent_cb(void *state, char *full_path);
static int dirent_cb(void *state, git_buf *full_path);
static int alphasorted_futils_direach(
char *path, size_t path_sz,
int (*fn)(void *, char *), void *arg);
git_buf *path, int (*fn)(void *, git_buf *), void *arg);
static int process_folder(struct status_st *st, const git_tree_entry *tree_entry, char *full_path, enum path_type path_type)
static int process_folder(
struct status_st *st,
const git_tree_entry *tree_entry,
git_buf *full_path,
enum path_type path_type)
{
git_object *subtree = NULL;
git_tree *pushed_tree = NULL;
......@@ -209,10 +211,9 @@ static int process_folder(struct status_st *st, const git_tree_entry *tree_entry
}
if (full_path != NULL && path_type == GIT_STATUS_PATH_FOLDER)
error = alphasorted_futils_direach(full_path, GIT_PATH_MAX, dirent_cb, st);
else {
error = alphasorted_futils_direach(full_path, dirent_cb, st);
else
error = dirent_cb(st, NULL);
}
if (tree_entry_type == GIT_OBJ_TREE) {
git_object_free(subtree);
......@@ -243,7 +244,7 @@ static int determine_status(struct status_st *st,
int in_head, int in_index, int in_workdir,
const git_tree_entry *tree_entry,
const git_index_entry *index_entry,
char *full_path,
git_buf *full_path,
const char *status_path,
enum path_type path_type)
{
......@@ -273,7 +274,8 @@ static int determine_status(struct status_st *st,
}
if (in_workdir)
if ((error = status_entry_update_from_workdir(e, full_path)) < GIT_SUCCESS)
if ((error = status_entry_update_from_workdir(e, full_path->ptr
)) < GIT_SUCCESS)
return error; /* The callee has already set the error message */
return store_if_changed(st, e);
......@@ -284,7 +286,7 @@ static int determine_status(struct status_st *st,
return process_folder(st, tree_entry, full_path, path_type);
}
static int path_type_from(char *full_path, int is_dir)
static int path_type_from(git_buf *full_path, int is_dir)
{
if (full_path == NULL)
return GIT_STATUS_PATH_NULL;
......@@ -292,7 +294,7 @@ static int path_type_from(char *full_path, int is_dir)
if (!is_dir)
return GIT_STATUS_PATH_FILE;
if (!git__suffixcmp(full_path, "/" DOT_GIT "/"))
if (!git__suffixcmp(full_path->ptr, "/" DOT_GIT "/"))
return GIT_STATUS_PATH_IGNORE;
return GIT_STATUS_PATH_FOLDER;
......@@ -330,7 +332,7 @@ static int compare(const char *left, const char *right)
/* Greatly inspired from JGit IndexTreeWalker */
/* https://github.com/spearce/jgit/blob/ed47e29c777accfa78c6f50685a5df2b8f5b8ff5/org.spearce.jgit/src/org/spearce/jgit/lib/IndexTreeWalker.java#L88 */
static int dirent_cb(void *state, char *a)
static int dirent_cb(void *state, git_buf *a)
{
const git_tree_entry *m;
const git_index_entry *entry;
......@@ -346,7 +348,7 @@ static int dirent_cb(void *state, char *a)
if (path_type == GIT_STATUS_PATH_IGNORE)
return GIT_SUCCESS; /* Let's skip the ".git" directory */
a_name = (path_type != GIT_STATUS_PATH_NULL) ? a + st->workdir_path_len : NULL;
a_name = (path_type != GIT_STATUS_PATH_NULL) ? a->ptr + st->workdir_path_len : NULL;
while (1) {
if (st->tree == NULL)
......@@ -360,15 +362,18 @@ static int dirent_cb(void *state, char *a)
return GIT_SUCCESS;
if (m != NULL) {
st->head_tree_relative_path[st->head_tree_relative_path_len] = '\0';
git_buf_truncate(&st->head_tree_relative_path,
st->head_tree_relative_path_len);
git_buf_joinpath(&st->head_tree_relative_path,
st->head_tree_relative_path.ptr, m->filename);
/* When the tree entry is a folder, append a forward slash to its name */
if (git_tree_entry_type(m) == GIT_OBJ_TREE)
git_path_join_n(st->head_tree_relative_path, 3, st->head_tree_relative_path, m->filename, "");
else
git_path_join(st->head_tree_relative_path, st->head_tree_relative_path, m->filename);
git_path_to_dir(&st->head_tree_relative_path);
m_name = st->head_tree_relative_path;
if (error < GIT_SUCCESS)
return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
m_name = st->head_tree_relative_path.ptr;
} else
m_name = NULL;
......@@ -383,7 +388,7 @@ static int dirent_cb(void *state, char *a)
pi = ((cmpmi >= 0) && (cmpai >= 0)) ? i_name : NULL;
if((error = determine_status(st, pm != NULL, pi != NULL, pa != NULL, m, entry, a, status_path(pm, pi, pa), path_type)) < GIT_SUCCESS)
return git__rethrow(error, "An error occured while determining the status of '%s'", a);
return git__rethrow(error, "An error occured while determining the status of '%s'", a->ptr);
if ((pa != NULL) || (path_type == GIT_STATUS_PATH_FOLDER))
return GIT_SUCCESS;
......@@ -404,9 +409,8 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
{
git_vector entries;
git_index *index = NULL;
char temp_path[GIT_PATH_MAX];
char tree_path[GIT_PATH_MAX] = "";
struct status_st dirent_st;
git_buf temp_path = GIT_BUF_INIT;
struct status_st dirent_st = {0};
int error = GIT_SUCCESS;
unsigned int i;
git_tree *tree;
......@@ -435,23 +439,21 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
dirent_st.tree = tree;
dirent_st.index = index;
dirent_st.vector = &entries;
dirent_st.head_tree_relative_path = tree_path;
git_buf_init(&dirent_st.head_tree_relative_path, 0);
dirent_st.head_tree_relative_path_len = 0;
dirent_st.is_dir = 1;
strcpy(temp_path, workdir);
if (git_futils_isdir(temp_path)) {
if (git_futils_isdir(workdir)) {
error = git__throw(GIT_EINVALIDPATH,
"Failed to determine status of file '%s'. "
"The given path doesn't lead to a folder", temp_path);
"The given path doesn't lead to a folder", workdir);
goto exit;
}
git_buf_sets(&temp_path, workdir);
error = alphasorted_futils_direach(
temp_path, sizeof(temp_path),
dirent_cb, &dirent_st
);
&temp_path, dirent_cb, &dirent_st);
if (error < GIT_SUCCESS)
error = git__rethrow(error,
......@@ -477,6 +479,8 @@ int git_status_foreach(git_repository *repo, int (*callback)(const char *, unsig
}
exit:
git_buf_free(&dirent_st.head_tree_relative_path);
git_buf_free(&temp_path);
git_vector_free(&entries);
git_tree_free(tree);
return error;
......@@ -521,7 +525,7 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char
{
struct status_entry *e;
git_index *index = NULL;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
int error = GIT_SUCCESS;
git_tree *tree = NULL;
const char *workdir;
......@@ -532,60 +536,72 @@ int git_status_file(unsigned int *status_flags, git_repository *repo, const char
return git__throw(GIT_ERROR,
"Cannot retrieve status on a bare repository");
git_path_join(temp_path, workdir, path);
if (git_futils_isdir(temp_path) == GIT_SUCCESS)
if ((error = git_buf_joinpath(&temp_path, workdir, path)) < GIT_SUCCESS)
return git__rethrow(error,
"Failed to determine status of file '%s'", path);
if (git_futils_isdir(temp_path.ptr) == GIT_SUCCESS) {
git_buf_free(&temp_path);
return git__throw(GIT_EINVALIDPATH,
"Failed to determine status of file '%s'. "
"Given path leads to a folder, not a file", path);
}
e = status_entry_new(NULL, path);
if (e == NULL)
if (e == NULL) {
git_buf_free(&temp_path);
return GIT_ENOMEM;
}
/* Find file in Workdir */
if (git_futils_exists(temp_path) == GIT_SUCCESS) {
if ((error = status_entry_update_from_workdir(e, temp_path)) < GIT_SUCCESS)
goto exit; /* The callee has already set the error message */
if (git_futils_exists(temp_path.ptr) == GIT_SUCCESS) {
if ((error = status_entry_update_from_workdir(e, temp_path.ptr)) < GIT_SUCCESS)
goto cleanup; /* The callee has already set the error message */
}
/* Find file in Index */
if ((error = git_repository_index__weakptr(&index, repo)) < GIT_SUCCESS) {
error = git__rethrow(error,
git__rethrow(error,
"Failed to determine status of file '%s'."
"Index can't be opened", path);
goto exit;
goto cleanup;
}
status_entry_update_from_index(e, index);
if ((error = retrieve_head_tree(&tree, repo)) < GIT_SUCCESS) {
error = git__rethrow(error,
git__rethrow(error,
"Failed to determine status of file '%s'", path);
goto exit;
goto cleanup;
}
/* If the repository is not empty, try and locate the file in HEAD */
if (tree != NULL) {
strcpy(temp_path, path);
if ((error = git_buf_sets(&temp_path, path)) < GIT_SUCCESS) {
git__rethrow(error,
"Failed to determine status of file '%s'", path);
goto cleanup;
}
error = recurse_tree_entry(tree, e, temp_path);
error = recurse_tree_entry(tree, e, temp_path.ptr);
if (error < GIT_SUCCESS) {
error = git__rethrow(error,
git__rethrow(error,
"Failed to determine status of file '%s'. "
"An error occured while processing the tree", path);
goto exit;
goto cleanup;
}
}
/* Determine status */
if ((error = status_entry_update_flags(e)) < GIT_SUCCESS) {
error = git__throw(error, "Nonexistent file");
goto exit;
git__throw(error, "Nonexistent file");
goto cleanup;
}
*status_flags = e->status_flags;
exit:
cleanup:
git_buf_free(&temp_path);
git_tree_free(tree);
git__free(e);
return error;
......@@ -600,25 +616,22 @@ exit:
struct alphasorted_dirent_info {
int is_dir;
char path[GIT_FLEX_ARRAY]; /* more */
};
static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const char *path)
static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const git_buf *path)
{
int is_dir, size;
struct alphasorted_dirent_info *di;
is_dir = git_futils_isdir(path) == GIT_SUCCESS ? 1 : 0;
size = sizeof(*di) + (is_dir ? GIT_PATH_MAX : strlen(path)) + 2;
is_dir = git_futils_isdir(path->ptr) == GIT_SUCCESS ? 1 : 0;
size = sizeof(*di) + path->size + is_dir + 1;
di = git__malloc(size);
di = git__calloc(size, 1);
if (di == NULL)
return NULL;
memset(di, 0x0, size);
strcpy(di->path, path);
git_buf_copy_cstr(di->path, path->size + 1, path);
if (is_dir) {
di->is_dir = 1;
......@@ -630,7 +643,7 @@ static struct alphasorted_dirent_info *alphasorted_dirent_info_new(const char *p
* The file "subdir" should appear before the file "subdir.txt"
* The folder "subdir" should appear after the file "subdir.txt"
*/
di->path[strlen(path)] = '/';
di->path[path->size] = '/';
}
return di;
......@@ -644,7 +657,7 @@ static int alphasorted_dirent_info_cmp(const void *a, const void *b)
return strcmp(stra->path, strb->path);
}
static int alphasorted_dirent_cb(void *state, char *full_path)
static int alphasorted_dirent_cb(void *state, git_buf *full_path)
{
struct alphasorted_dirent_info *entry;
git_vector *entry_names;
......@@ -664,34 +677,42 @@ static int alphasorted_dirent_cb(void *state, char *full_path)
}
static int alphasorted_futils_direach(
char *path,
size_t path_sz,
int (*fn)(void *, char *),
git_buf *path,
int (*fn)(void *, git_buf *),
void *arg)
{
struct alphasorted_dirent_info *entry;
git_vector entry_names;
unsigned int idx;
int error = GIT_SUCCESS;
git_buf entry_path = GIT_BUF_INIT;
if (git_vector_init(&entry_names, 16, alphasorted_dirent_info_cmp) < GIT_SUCCESS)
return GIT_ENOMEM;
error = git_futils_direach(path, path_sz, alphasorted_dirent_cb, &entry_names);
error = git_futils_direach(path, alphasorted_dirent_cb, &entry_names);
git_vector_sort(&entry_names);
for (idx = 0; idx < entry_names.length; ++idx) {
entry = (struct alphasorted_dirent_info *)git_vector_get(&entry_names, idx);
/* We have to walk the entire vector even if there was an error,
* in order to free up memory, but we stop making callbacks after
* an error.
*/
if (error == GIT_SUCCESS)
error = git_buf_sets(&entry_path, entry->path);
if (error == GIT_SUCCESS) {
((struct status_st *)arg)->is_dir = entry->is_dir;
error = fn(arg, entry->path);
error = fn(arg, &entry_path);
}
git__free(entry);
}
git_buf_free(&entry_path);
git_vector_free(&entry_names);
return error;
}
......@@ -148,15 +148,22 @@ static int parse_tag_buffer(git_tag *tag, const char *buffer, const char *buffer
return GIT_SUCCESS;
}
static int retrieve_tag_reference(git_reference **tag_reference_out, char *ref_name_out, git_repository *repo, const char *tag_name)
static int retrieve_tag_reference(
git_reference **tag_reference_out,
git_buf *ref_name_out,
git_repository *repo,
const char *tag_name)
{
git_reference *tag_ref;
int error;
*tag_reference_out = NULL;
git_path_join(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
error = git_reference_lookup(&tag_ref, repo, ref_name_out);
error = git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to retrieve tag reference");
error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to retrieve tag reference");
......@@ -184,9 +191,10 @@ static int write_tag_annotation(
git_buf_putc(&tag, '\n');
git_buf_puts(&tag, message);
if (git_buf_oom(&tag)) {
error = git_buf_lasterror(&tag);
if (error < GIT_SUCCESS) {
git_buf_free(&tag);
return git__throw(GIT_ENOMEM, "Not enough memory to build the tag data");
return git__rethrow(error, "Not enough memory to build the tag data");
}
error = git_repository_odb__weakptr(&odb, repo);
......@@ -215,56 +223,54 @@ static int git_tag_create__internal(
int create_tag_annotation)
{
git_reference *new_ref = NULL;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
int error, should_update_ref = 0;
const char *errmsg = "Failed to create tag";
assert(repo && tag_name && target);
assert(!create_tag_annotation || (tagger && message));
if (git_object_owner(target) != repo)
return git__throw(GIT_EINVALIDARGS, "The given target does not belong to this repository");
return git__throw(GIT_EINVALIDARGS,
"The given target does not belong to this repository");
error = retrieve_tag_reference(&new_ref, ref_name, repo, tag_name);
switch (error) {
case GIT_SUCCESS:
case GIT_ENOTFOUND:
break;
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
goto cleanup;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
error = GIT_EEXISTS;
errmsg = "Tag already exists";
goto cleanup;
} else {
should_update_ref = 1;
}
}
if (create_tag_annotation) {
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return error;
}
if ((error = write_tag_annotation(oid, repo, tag_name, target, tagger, message)) < GIT_SUCCESS)
goto cleanup;
} else
git_oid_cpy(oid, git_object_id(target));
if (!should_update_ref)
error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
else
error = git_reference_set_oid(new_ref, oid);
cleanup:
git_reference_free(new_ref);
git_buf_free(&ref_name);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
if (error < GIT_SUCCESS)
git__rethrow(error, "%s", errmsg);
return error;
}
int git_tag_create(
......@@ -293,12 +299,13 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
{
git_tag tag;
int error, should_update_ref = 0;
const char *errmsg = "Failed to create tag";
git_odb *odb;
git_odb_stream *stream;
git_odb_object *target_obj;
git_reference *new_ref = NULL;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
assert(oid && buffer);
......@@ -310,78 +317,77 @@ int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *bu
/* validate the buffer */
if ((error = parse_tag_buffer(&tag, buffer, buffer + strlen(buffer))) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create tag");
goto cleanup;
/* validate the target */
if ((error = git_odb_read(&target_obj, odb, &tag.target)) < GIT_SUCCESS)
return git__rethrow(error, "Failed to create tag");
goto cleanup;
if (tag.type != target_obj->raw.type)
return git__throw(error, "The type for the given target is invalid");
if (tag.type != target_obj->raw.type) {
error = GIT_EINVALIDTYPE;
errmsg = "The type for the given target is invalid";
goto cleanup;
}
git_odb_object_free(target_obj);
error = retrieve_tag_reference(&new_ref, ref_name, repo, tag.tag_name);
switch (error) {
case GIT_SUCCESS:
case GIT_ENOTFOUND:
break;
default:
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
error = retrieve_tag_reference(&new_ref, &ref_name, repo, tag.tag_name);
if (error < GIT_SUCCESS && error != GIT_ENOTFOUND)
goto cleanup;
/** Ensure the tag name doesn't conflict with an already existing
* reference unless overwriting has explictly been requested **/
if (new_ref != NULL) {
if (!allow_ref_overwrite) {
git_oid_cpy(oid, git_reference_oid(new_ref));
git_reference_free(new_ref);
return git__throw(GIT_EEXISTS, "Tag already exists");
error = GIT_EEXISTS;
errmsg = "Tag already exists";
goto cleanup;
} else {
should_update_ref = 1;
}
}
/* write the buffer */
if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
if ((error = git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG)) < GIT_SUCCESS)
goto cleanup;
stream->write(stream, buffer, strlen(buffer));
error = stream->finalize_write(oid, stream);
stream->free(stream);
if (error < GIT_SUCCESS) {
git_reference_free(new_ref);
return git__rethrow(error, "Failed to create tag");
}
if (error < GIT_SUCCESS)
goto cleanup;
if (!should_update_ref)
error = git_reference_create_oid(&new_ref, repo, ref_name, oid, 0);
error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, 0);
else
error = git_reference_set_oid(new_ref, oid);
cleanup:
git_reference_free(new_ref);
git_signature_free(tag.tagger);
git__free(tag.tag_name);
git__free(tag.message);
git_buf_free(&ref_name);
return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag");
if (error < GIT_SUCCESS)
git__rethrow(error, "%s", errmsg);
return error;
}
int git_tag_delete(git_repository *repo, const char *tag_name)
{
int error;
git_reference *tag_ref;
char ref_name[GIT_REFNAME_MAX];
git_buf ref_name = GIT_BUF_INIT;
error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
git_buf_free(&ref_name);
error = retrieve_tag_reference(&tag_ref, ref_name, repo, tag_name);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to delete tag");
......
......@@ -68,7 +68,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url)
git_buf_put(request, url, delim - url);
git_buf_putc(request, '\0');
return git_buf_oom(request);
return git_buf_lasterror(request);
}
static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
......
......@@ -77,10 +77,7 @@ static int gen_request(git_buf *buf, const char *url, const char *host, const ch
}
git_buf_puts(buf, "\r\n");
if (git_buf_oom(buf))
return GIT_ENOMEM;
return GIT_SUCCESS;
return git_buf_lasterror(buf);
}
static int do_connect(transport_http *t, const char *host, const char *port)
......@@ -608,8 +605,9 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
char buffer[1024];
gitno_buffer buf;
download_pack_cbdata data;
git_filebuf file;
char path[GIT_PATH_MAX], suff[] = "/objects/pack/pack-received\0";
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
char suff[] = "/objects/pack/pack-received\0";
/*
* This is part of the previous response, so we don't want to
......@@ -625,13 +623,15 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
git_path_join(path, repo->path_repository, suff);
if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
}
error = git_filebuf_open(&file, path, GIT_FILEBUF_TEMPORARY);
error = git_buf_joinpath(&path, repo->path_repository, suff);
if (error < GIT_SUCCESS)
goto cleanup;
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
if (error < GIT_SUCCESS)
goto cleanup;
......@@ -671,6 +671,7 @@ static int http_download_pack(char **out, git_transport *transport, git_reposito
cleanup:
if (error < GIT_SUCCESS)
git_filebuf_cleanup(&file);
git_buf_free(&path);
return error;
}
......
......@@ -577,9 +577,9 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
}
if (git_buf_oom(&tree)) {
if ((error = git_buf_lasterror(&tree)) < GIT_SUCCESS) {
git_buf_free(&tree);
return git__throw(GIT_ENOMEM, "Not enough memory to build the tree data");
return git__rethrow(error, "Not enough memory to build the tree data");
}
error = git_repository_odb__weakptr(&odb, repo);
......@@ -631,7 +631,7 @@ void git_treebuilder_free(git_treebuilder *bld)
static int tree_frompath(
git_tree **parent_out,
git_tree *root,
const char *treeentry_path,
git_buf *treeentry_path,
int offset)
{
char *slash_pos = NULL;
......@@ -639,11 +639,11 @@ static int tree_frompath(
int error = GIT_SUCCESS;
git_tree *subtree;
if (!*(treeentry_path + offset))
if (!*(treeentry_path->ptr + offset))
return git__rethrow(GIT_EINVALIDPATH,
"Invalid relative path to a tree entry '%s'.", treeentry_path);
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
slash_pos = (char *)strchr(treeentry_path + offset, '/');
slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/');
if (slash_pos == NULL)
return git_tree_lookup(
......@@ -652,13 +652,13 @@ static int tree_frompath(
git_object_id((const git_object *)root)
);
if (slash_pos == treeentry_path + offset)
if (slash_pos == treeentry_path->ptr + offset)
return git__rethrow(GIT_EINVALIDPATH,
"Invalid relative path to a tree entry '%s'.", treeentry_path);
"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
*slash_pos = '\0';
entry = git_tree_entry_byname(root, treeentry_path + offset);
entry = git_tree_entry_byname(root, treeentry_path->ptr + offset);
if (slash_pos != NULL)
*slash_pos = '/';
......@@ -666,7 +666,7 @@ static int tree_frompath(
if (entry == NULL)
return git__rethrow(GIT_ENOTFOUND,
"No tree entry can be found from "
"the given tree and relative path '%s'.", treeentry_path);
"the given tree and relative path '%s'.", treeentry_path->ptr);
error = git_tree_lookup(&subtree, root->object.repo, &entry->oid);
......@@ -677,7 +677,7 @@ static int tree_frompath(
parent_out,
subtree,
treeentry_path,
slash_pos - treeentry_path + 1
(slash_pos - treeentry_path->ptr) + 1
);
git_tree_free(subtree);
......@@ -689,70 +689,82 @@ int git_tree_get_subtree(
git_tree *root,
const char *subtree_path)
{
char buffer[GIT_PATH_MAX];
int error;
git_buf buffer = GIT_BUF_INIT;
assert(subtree && root && subtree_path);
strncpy(buffer, subtree_path, GIT_PATH_MAX);
return tree_frompath(subtree, root, buffer, 0);
if ((error = git_buf_sets(&buffer, subtree_path)) == GIT_SUCCESS)
error = tree_frompath(subtree, root, &buffer, 0);
git_buf_free(&buffer);
return error;
}
static int tree_walk_post(
git_tree *tree,
git_treewalk_cb callback,
char *root,
size_t root_len,
git_buf *path,
void *payload)
{
int error;
int error = GIT_SUCCESS;
unsigned int i;
for (i = 0; i < tree->entries.length; ++i) {
git_tree_entry *entry = tree->entries.contents[i];
root[root_len] = '\0';
if (callback(root, entry, payload) < 0)
if (callback(path->ptr, entry, payload) < 0)
continue;
if (ENTRY_IS_TREE(entry)) {
git_tree *subtree;
size_t path_len = path->size;
if ((error = git_tree_lookup(
&subtree, tree->object.repo, &entry->oid)) < 0)
return error;
break;
strcpy(root + root_len, entry->filename);
root[root_len + entry->filename_len] = '/';
/* append the next entry to the path */
git_buf_puts(path, entry->filename);
git_buf_putc(path, '/');
if ((error = git_buf_lasterror(path)) < GIT_SUCCESS)
break;
tree_walk_post(subtree,
callback, root,
root_len + entry->filename_len + 1,
payload
);
error = tree_walk_post(subtree, callback, path, payload);
if (error < GIT_SUCCESS)
break;
git_buf_truncate(path, path_len);
git_tree_free(subtree);
}
}
return GIT_SUCCESS;
return error;
}
int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
{
char root_path[GIT_PATH_MAX];
int error = GIT_SUCCESS;
git_buf root_path = GIT_BUF_INIT;
root_path[0] = '\0';
switch (mode) {
case GIT_TREEWALK_POST:
return tree_walk_post(tree, callback, root_path, 0, payload);
error = tree_walk_post(tree, callback, &root_path, payload);
break;
case GIT_TREEWALK_PRE:
return git__throw(GIT_ENOTIMPLEMENTED,
error = git__throw(GIT_ENOTIMPLEMENTED,
"Preorder tree walking is still not implemented");
break;
default:
return git__throw(GIT_EINVALIDARGS,
error = git__throw(GIT_EINVALIDARGS,
"Invalid walking mode for tree walk");
break;
}
git_buf_free(&root_path);
return error;
}
......@@ -304,31 +304,36 @@ int p_hide_directory__w32(const char *path)
char *p_realpath(const char *orig_path, char *buffer)
{
int ret, alloc = 0;
int ret;
wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
if (buffer == NULL) {
buffer = (char *)git__malloc(GIT_PATH_MAX);
alloc = 1;
}
ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
git__free(orig_path_w);
if (!ret || ret > GIT_PATH_MAX) {
git__free(buffer_w);
if (alloc) git__free(buffer);
return NULL;
buffer = NULL;
goto done;
}
if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) {
git__free(buffer_w);
if (alloc) git__free(buffer);
if (buffer == NULL) {
int buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
if (!buffer_sz ||
!(buffer = (char *)git__malloc(buffer_sz)) ||
!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))
{
git__free(buffer);
buffer = NULL;
}
} else {
if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL))
buffer = NULL;
}
done:
git__free(buffer_w);
if (buffer)
git_path_mkposix(buffer);
return buffer;
}
......
......@@ -111,6 +111,7 @@ extern void test_core_path__1(void);
extern void test_core_path__2(void);
extern void test_core_path__5(void);
extern void test_core_path__6(void);
extern void test_core_path__7(void);
extern void test_core_rmdir__delete_recursive(void);
extern void test_core_rmdir__fail_to_delete_non_empty_dir(void);
extern void test_core_rmdir__initialize(void);
......
......@@ -42,4 +42,13 @@ GIT_INLINE(void) cl_assert_strequal_internal(
}
}
/*
* Some utility macros for building long strings
*/
#define REP4(STR) STR STR STR STR
#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP256(STR) REP16(REP16(STR))
#define REP1024(STR) REP4(REP256(STR))
#endif
......@@ -171,7 +171,8 @@ static const struct clay_func _clay_cb_core_path[] = {
{"1", &test_core_path__1},
{"2", &test_core_path__2},
{"5", &test_core_path__5},
{"6", &test_core_path__6}
{"6", &test_core_path__6},
{"7", &test_core_path__7}
};
static const struct clay_func _clay_cb_core_rmdir[] = {
{"delete_recursive", &test_core_rmdir__delete_recursive},
......@@ -351,7 +352,7 @@ static const struct clay_suite _clay_suites[] = {
"core::path",
{NULL, NULL},
{NULL, NULL},
_clay_cb_core_path, 5
_clay_cb_core_path, 6
},
{
"core::rmdir",
......@@ -494,7 +495,7 @@ static const struct clay_suite _clay_suites[] = {
};
static size_t _clay_suite_count = 34;
static size_t _clay_callback_count = 113;
static size_t _clay_callback_count = 114;
/* Core test functions */
static void
......
......@@ -5,9 +5,6 @@
const char *test_string = TESTSTR;
const char *test_string_x2 = TESTSTR TESTSTR;
#define REP4(STR) STR STR STR STR
#define REP16(STR) REP4(REP4(STR))
#define REP1024(STR) REP16(REP16(REP4(STR)))
#define TESTSTR_4096 REP1024("1234")
#define TESTSTR_8192 REP1024("12341234")
const char *test_4096 = TESTSTR_4096;
......@@ -52,7 +49,7 @@ void test_core_buffer__2(void)
{
git_buf buf = GIT_BUF_INIT;
int i;
char data[100];
char data[128];
cl_assert(buf.size == 0);
......@@ -135,22 +132,28 @@ void test_core_buffer__2(void)
git_buf_puts(&buf, REP4("0123456789"));
cl_assert(git_buf_oom(&buf) == 0);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal(REP4("0123456789"), data);
git_buf_copy_cstr(data, 11, &buf);
cl_assert_strequal(data, "0123456789");
cl_assert_strequal("0123456789", data);
git_buf_copy_cstr(data, 3, &buf);
cl_assert_strequal(data, "01");
cl_assert_strequal("01", data);
git_buf_copy_cstr(data, 1, &buf);
cl_assert_strequal(data, "");
cl_assert_strequal("", data);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal(REP4("0123456789"), data);
git_buf_sets(&buf, REP256("x"));
git_buf_copy_cstr(data, sizeof(data), &buf);
/* since sizeof(data) == 128, only 127 bytes should be copied */
cl_assert_strequal(REP4(REP16("x")) REP16("x") REP16("x")
REP16("x") "xxxxxxxxxxxxxxx", data);
git_buf_free(&buf);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, "");
git_buf_copy_cstr(data, sizeof(data), &buf);
cl_assert_strequal("", data);
}
/* let's do some tests with larger buffers to push our limits */
......@@ -340,9 +343,10 @@ void test_core_buffer__6(void)
}
/* test take cstr data */
/* test detach/attach data */
void test_core_buffer__7(void)
{
const char *fun = "This is fun";
git_buf a = GIT_BUF_INIT;
char *b = NULL;
......@@ -350,18 +354,36 @@ void test_core_buffer__7(void)
cl_assert(git_buf_oom(&a) == 0);
cl_assert_strequal("foo", git_buf_cstr(&a));
b = git_buf_take_cstr(&a);
b = git_buf_detach(&a);
cl_assert_strequal("foo", b);
cl_assert_strequal("", a.ptr);
git__free(b);
b = git_buf_take_cstr(&a);
b = git_buf_detach(&a);
cl_assert_strequal(NULL, b);
cl_assert_strequal("", a.ptr);
git_buf_free(&a);
b = git__strdup(fun);
git_buf_attach(&a, b, 0);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
git_buf_free(&a);
b = git__strdup(fun);
git_buf_attach(&a, b, strlen(fun) + 1);
cl_assert_strequal(fun, a.ptr);
cl_assert(a.size == (ssize_t)strlen(fun));
cl_assert(a.asize == (ssize_t)strlen(fun) + 1);
git_buf_free(&a);
}
......
......@@ -9,10 +9,10 @@ typedef struct name_data {
typedef struct walk_data {
char *sub; /* sub-directory name */
name_data *names; /* name state data */
git_buf path;
} walk_data;
static char path_buffer[GIT_PATH_MAX];
static char *top_dir = "dir-walk";
static walk_data *state_loc;
......@@ -27,7 +27,8 @@ static void setup(walk_data *d)
if (strcmp(d->sub, ".") != 0)
cl_must_pass(p_mkdir(d->sub, 0777));
strcpy(path_buffer, d->sub);
cl_git_pass(git_buf_sets(&d->path, d->sub));
state_loc = d;
for (n = d->names; n->name; n++) {
......@@ -53,6 +54,8 @@ static void dirent_cleanup__cb(void *_d)
cl_must_pass(p_chdir(".."));
cl_must_pass(p_rmdir(top_dir));
git_buf_free(&d->path);
}
static void check_counts(walk_data *d)
......@@ -64,7 +67,7 @@ static void check_counts(walk_data *d)
}
}
static int one_entry(void *state, char *path)
static int one_entry(void *state, git_buf *path)
{
walk_data *d = (walk_data *) state;
name_data *n;
......@@ -72,11 +75,11 @@ static int one_entry(void *state, char *path)
if (state != state_loc)
return GIT_ERROR;
if (path != path_buffer)
if (path != &d->path)
return GIT_ERROR;
for (n = d->names; n->name; n++) {
if (!strcmp(n->name, path)) {
if (!strcmp(n->name, path->ptr)) {
n->count++;
return 0;
}
......@@ -85,7 +88,7 @@ static int one_entry(void *state, char *path)
return GIT_ERROR;
}
static int dont_call_me(void *GIT_UNUSED(state), char *GIT_UNUSED(path))
static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
{
GIT_UNUSED_ARG(state)
GIT_UNUSED_ARG(path)
......@@ -102,7 +105,8 @@ static name_data dot_names[] = {
};
static walk_data dot = {
".",
dot_names
dot_names,
GIT_BUF_INIT
};
/* make sure that the '.' folder is not traversed */
......@@ -111,8 +115,7 @@ void test_core_dirent__dont_traverse_dot(void)
cl_set_cleanup(&dirent_cleanup__cb, &dot);
setup(&dot);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&dot.path,
one_entry,
&dot));
......@@ -128,7 +131,8 @@ static name_data sub_names[] = {
};
static walk_data sub = {
"sub",
sub_names
sub_names,
GIT_BUF_INIT
};
/* traverse a subfolder */
......@@ -137,8 +141,7 @@ void test_core_dirent__traverse_subfolder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub);
setup(&sub);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&sub.path,
one_entry,
&sub));
......@@ -148,7 +151,8 @@ void test_core_dirent__traverse_subfolder(void)
static walk_data sub_slash = {
"sub/",
sub_names
sub_names,
GIT_BUF_INIT
};
/* traverse a slash-terminated subfolder */
......@@ -157,8 +161,7 @@ void test_core_dirent__traverse_slash_terminated_folder(void)
cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
setup(&sub_slash);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&sub_slash.path,
one_entry,
&sub_slash));
......@@ -171,7 +174,8 @@ static name_data empty_names[] = {
};
static walk_data empty = {
"empty",
empty_names
empty_names,
GIT_BUF_INIT
};
/* make sure that empty folders are not traversed */
......@@ -180,16 +184,14 @@ void test_core_dirent__dont_traverse_empty_folders(void)
cl_set_cleanup(&dirent_cleanup__cb, &empty);
setup(&empty);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&empty.path,
one_entry,
&empty));
check_counts(&empty);
/* make sure callback not called */
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&empty.path,
dont_call_me,
&empty));
}
......@@ -204,7 +206,8 @@ static name_data odd_names[] = {
};
static walk_data odd = {
"odd",
odd_names
odd_names,
GIT_BUF_INIT
};
/* make sure that strange looking filenames ('..c') are traversed */
......@@ -213,8 +216,7 @@ void test_core_dirent__traverse_weird_filenames(void)
cl_set_cleanup(&dirent_cleanup__cb, &odd);
setup(&odd);
cl_git_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
cl_git_pass(git_futils_direach(&odd.path,
one_entry,
&odd));
......
......@@ -4,26 +4,30 @@
static void
check_dirname(const char *A, const char *B)
{
char dir[64], *dir2;
git_buf dir = GIT_BUF_INIT;
char *dir2;
cl_assert(git_path_dirname_r(dir, sizeof(dir), A) >= 0);
cl_assert(strcmp(dir, B) == 0);
cl_assert((dir2 = git_path_dirname(A)) != NULL);
cl_assert(strcmp(dir2, B) == 0);
cl_assert(git_path_dirname_r(&dir, A) >= 0);
cl_assert_strequal(B, dir.ptr);
git_buf_free(&dir);
cl_assert((dir2 = git_path_dirname(A)) != NULL);
cl_assert_strequal(B, dir2);
git__free(dir2);
}
static void
check_basename(const char *A, const char *B)
{
char base[64], *base2;
git_buf base = GIT_BUF_INIT;
char *base2;
cl_assert(git_path_basename_r(base, sizeof(base), A) >= 0);
cl_assert(strcmp(base, B) == 0);
cl_assert((base2 = git_path_basename(A)) != NULL);
cl_assert(strcmp(base2, B) == 0);
cl_assert(git_path_basename_r(&base, A) >= 0);
cl_assert_strequal(B, base.ptr);
git_buf_free(&base);
cl_assert((base2 = git_path_basename(A)) != NULL);
cl_assert_strequal(B, base2);
git__free(base2);
}
......@@ -33,16 +37,18 @@ check_topdir(const char *A, const char *B)
const char *dir;
cl_assert((dir = git_path_topdir(A)) != NULL);
cl_assert(strcmp(dir, B) == 0);
cl_assert_strequal(B, dir);
}
static void
check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_buf joined_path = GIT_BUF_INIT;
git_path_join(joined_path, path_a, path_b);
cl_assert(strcmp(joined_path, expected_path) == 0);
cl_git_pass(git_buf_joinpath(&joined_path, path_a, path_b));
cl_assert_strequal(expected_path, joined_path.ptr);
git_buf_free(&joined_path);
}
static void
......@@ -53,17 +59,19 @@ check_joinpath_n(
const char *path_d,
const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_buf joined_path = GIT_BUF_INIT;
cl_git_pass(git_buf_join_n(&joined_path, '/', 4,
path_a, path_b, path_c, path_d));
cl_assert_strequal(expected_path, joined_path.ptr);
git_path_join_n(joined_path, 4, path_a, path_b, path_c, path_d);
cl_assert(strcmp(joined_path, expected_path) == 0);
git_buf_free(&joined_path);
}
/* get the dirname of a path */
void test_core_path__0(void)
{
check_dirname(NULL, ".");
check_dirname("", ".");
check_dirname("a", ".");
......@@ -77,6 +85,8 @@ void test_core_path__0(void)
check_dirname("usr/lib/", "usr");
check_dirname("usr/lib//", "usr");
check_dirname(".git/", ".");
check_dirname(REP16("/abc"), REP15("/abc"));
}
/* get the base name of a path */
......@@ -91,6 +101,9 @@ void test_core_path__1(void)
check_basename("/usr/lib", "lib");
check_basename("/usr/lib//", "lib");
check_basename("usr/lib", "lib");
check_basename(REP16("/abc"), "abc");
check_basename(REP1024("/abc"), "abc");
}
/* get the latest component in a path */
......@@ -125,6 +138,20 @@ void test_core_path__5(void)
check_joinpath("/a", "/b/", "/a/b/");
check_joinpath("/a/", "b/", "/a/b/");
check_joinpath("/a/", "/b/", "/a/b/");
check_joinpath("/abcd", "/defg", "/abcd/defg");
check_joinpath("/abcd", "/defg/", "/abcd/defg/");
check_joinpath("/abcd/", "defg/", "/abcd/defg/");
check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
REP1024("aaaa") "/" REP1024("bbbb"));
check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
REP1024("/aaaa") REP1024("/bbbb"));
}
/* properly join path components for more than one path */
......@@ -136,4 +163,74 @@ void test_core_path__6(void)
check_joinpath_n("", "", "", "a", "a");
check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
REP1024("a") "/" REP1024("b") "/"
REP1024("c") "/" REP1024("d"));
check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
REP1024("/a") REP1024("/b")
REP1024("/c") REP1024("/d"));
}
static void
check_path_to_dir(
const char* path,
const char* expected)
{
git_buf tgt = GIT_BUF_INIT;
git_buf_sets(&tgt, path);
cl_git_pass(git_path_to_dir(&tgt));
cl_assert_strequal(expected, tgt.ptr);
git_buf_free(&tgt);
}
static void
check_string_to_dir(
const char* path,
int maxlen,
const char* expected)
{
int len = strlen(path);
char *buf = git__malloc(len + 2);
strncpy(buf, path, len + 2);
git_path_string_to_dir(buf, maxlen);
cl_assert_strequal(expected, buf);
git__free(buf);
}
/* convert paths to dirs */
void test_core_path__7(void)
{
check_path_to_dir("", "");
check_path_to_dir(".", "./");
check_path_to_dir("./", "./");
check_path_to_dir("a/", "a/");
check_path_to_dir("ab", "ab/");
/* make sure we try just under and just over an expansion that will
* require a realloc
*/
check_path_to_dir("abcdef", "abcdef/");
check_path_to_dir("abcdefg", "abcdefg/");
check_path_to_dir("abcdefgh", "abcdefgh/");
check_path_to_dir("abcdefghi", "abcdefghi/");
check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
check_string_to_dir("", 1, "");
check_string_to_dir(".", 1, ".");
check_string_to_dir(".", 2, "./");
check_string_to_dir(".", 3, "./");
check_string_to_dir("abcd", 3, "abcd");
check_string_to_dir("abcd", 4, "abcd");
check_string_to_dir("abcd", 5, "abcd/");
check_string_to_dir("abcd", 6, "abcd/");
}
......@@ -5,24 +5,26 @@ static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
void test_core_rmdir__initialize(void)
{
char path[GIT_PATH_MAX];
git_buf path = GIT_BUF_INIT;
cl_must_pass(p_mkdir(empty_tmp_dir, 0777));
git_path_join(path, empty_tmp_dir, "/one");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_one");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_two");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/one/two_two/three");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_path_join(path, empty_tmp_dir, "/two");
cl_must_pass(p_mkdir(path, 0777));
cl_git_pass(git_buf_joinpath(&path, empty_tmp_dir, "/two"));
cl_must_pass(p_mkdir(path.ptr, 0777));
git_buf_free(&path);
}
/* make sure empty dir can be deleted recusively */
......@@ -34,17 +36,19 @@ void test_core_rmdir__delete_recursive(void)
/* make sure non-empty dir cannot be deleted recusively */
void test_core_rmdir__fail_to_delete_non_empty_dir(void)
{
char file[GIT_PATH_MAX];
git_buf file = GIT_BUF_INIT;
int fd;
git_path_join(file, empty_tmp_dir, "/two/file.txt");
cl_git_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
fd = p_creat(file, 0666);
fd = p_creat(file.ptr, 0666);
cl_assert(fd >= 0);
cl_must_pass(p_close(fd));
cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
cl_must_pass(p_unlink(file));
cl_must_pass(p_unlink(file.ptr));
cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
git_buf_free(&file);
}
......@@ -77,17 +77,19 @@ void test_repo_init__bare_repo_noslash(void)
#if 0
BEGIN_TEST(init2, "Initialize and open a bare repo with a relative path escaping out of the current working directory")
char path_repository[GIT_PATH_MAX];
git_buf path_repository = GIT_BUF_INIT;
char current_workdir[GIT_PATH_MAX];
const mode_t mode = 0777;
git_repository* repo;
must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
git_path_join(path_repository, TEMP_REPO_FOLDER, "a/b/c/");
must_pass(git_futils_mkdir_r(path_repository, mode));
must_pass(git_buf_joinpath(&path_repository, TEMP_REPO_FOLDER, "a/b/c/"));
must_pass(git_futils_mkdir_r(path_repository.ptr, mode));
must_pass(chdir(path_repository));
must_pass(chdir(path_repository.ptr));
git_buf_free(&path_repository);
must_pass(git_repository_init(&repo, "../d/e.git", 1));
must_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
......
......@@ -26,23 +26,24 @@ void test_repo_open__standard_empty_repo(void)
/* TODO TODO */
#if 0
BEGIN_TEST(open2, "Open a bare repository with a relative path escaping out of the current working directory")
char new_current_workdir[GIT_PATH_MAX];
char current_workdir[GIT_PATH_MAX];
char path_repository[GIT_PATH_MAX];
git_buf new_current_workdir = GIT_BUF_INIT;
git_buf path_repository = GIT_BUF_INIT;
const mode_t mode = 0777;
git_repository* repo;
/* Setup the repository to open */
must_pass(p_getcwd(current_workdir, sizeof(current_workdir)));
strcpy(path_repository, current_workdir);
git_path_join_n(path_repository, 3, path_repository, TEMP_REPO_FOLDER, "a/d/e.git");
must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository));
must_pass(git_buf_join_n(&path_repository, 3, current_workdir, TEMP_REPO_FOLDER, "a/d/e.git"));
must_pass(copydir_recurs(REPOSITORY_FOLDER, path_repository.ptr));
git_buf_free(&path_repository);
/* Change the current working directory */
git_path_join(new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/");
must_pass(git_futils_mkdir_r(new_current_workdir, mode));
must_pass(chdir(new_current_workdir));
must_pass(git_buf_joinpath(&new_current_workdir, TEMP_REPO_FOLDER, "a/b/c/"));
must_pass(git_futils_mkdir_r(new_current_workdir.ptr, mode));
must_pass(chdir(new_current_workdir.ptr));
git_buf_free(&new_current_workdir);
must_pass(git_repository_open(&repo, "../../d/e.git"));
......
......@@ -105,11 +105,12 @@ END_TEST
BEGIN_TEST(path0, "get the dirname of a path")
char dir[64], *dir2;
git_buf dir = GIT_BUF_INIT;
char *dir2;
#define DIRNAME_TEST(A, B) { \
must_be_true(git_path_dirname_r(dir, sizeof(dir), A) >= 0); \
must_be_true(strcmp(dir, B) == 0); \
must_be_true(git_path_dirname_r(&dir, A) >= 0); \
must_be_true(strcmp(B, dir.ptr) == 0); \
must_be_true((dir2 = git_path_dirname(A)) != NULL); \
must_be_true(strcmp(dir2, B) == 0); \
git__free(dir2); \
......@@ -131,14 +132,16 @@ BEGIN_TEST(path0, "get the dirname of a path")
#undef DIRNAME_TEST
git_buf_free(&dir);
END_TEST
BEGIN_TEST(path1, "get the base name of a path")
char base[64], *base2;
git_buf base = GIT_BUF_INIT;
char *base2;
#define BASENAME_TEST(A, B) { \
must_be_true(git_path_basename_r(base, sizeof(base), A) >= 0); \
must_be_true(strcmp(base, B) == 0); \
must_be_true(git_path_basename_r(&base, A) >= 0); \
must_be_true(strcmp(B, base.ptr) == 0); \
must_be_true((base2 = git_path_basename(A)) != NULL); \
must_be_true(strcmp(base2, B) == 0); \
git__free(base2); \
......@@ -156,6 +159,7 @@ BEGIN_TEST(path1, "get the base name of a path")
#undef BASENAME_TEST
git_buf_free(&base);
END_TEST
BEGIN_TEST(path2, "get the latest component in a path")
......@@ -184,9 +188,13 @@ END_TEST
static int ensure_joinpath(const char *path_a, const char *path_b, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_path_join(joined_path, path_a, path_b);
return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR;
int error = GIT_SUCCESS;
git_buf joined_path = GIT_BUF_INIT;
if (!(error = git_buf_joinpath(&joined_path, path_a, path_b)))
error = strcmp(joined_path.ptr, expected_path) == 0 ?
GIT_SUCCESS : GIT_ERROR;
git_buf_free(&joined_path);
return error;
}
BEGIN_TEST(path5, "properly join path components")
......@@ -206,9 +214,14 @@ END_TEST
static int ensure_joinpath_n(const char *path_a, const char *path_b, const char *path_c, const char *path_d, const char *expected_path)
{
char joined_path[GIT_PATH_MAX];
git_path_join_n(joined_path, 4, path_a, path_b, path_c, path_d);
return strcmp(joined_path, expected_path) == 0 ? GIT_SUCCESS : GIT_ERROR;
int error = GIT_SUCCESS;
git_buf joined_path = GIT_BUF_INIT;
if (!(error = git_buf_join_n(&joined_path, '/', 4,
path_a, path_b, path_c, path_d)))
error = strcmp(joined_path.ptr, expected_path) == 0 ?
GIT_SUCCESS : GIT_ERROR;
git_buf_free(&joined_path);
return error;
}
BEGIN_TEST(path6, "properly join path components for more than one path")
......@@ -228,10 +241,10 @@ typedef struct name_data {
typedef struct walk_data {
char *sub; /* sub-directory name */
name_data *names; /* name state data */
git_buf path; /* buffer to store path */
} walk_data;
static char path_buffer[GIT_PATH_MAX];
static char *top_dir = "dir-walk";
static walk_data *state_loc;
......@@ -260,7 +273,9 @@ static int setup(walk_data *d)
if (p_mkdir(d->sub, 0777) < 0)
return error("can't mkdir(\"%s\")", d->sub);
strcpy(path_buffer, d->sub);
if (git_buf_sets(&d->path, d->sub) < 0)
return error("can't allocate space for \"%s\"", d->sub);
state_loc = d;
for (n = d->names; n->name; n++) {
......@@ -278,6 +293,8 @@ static int knockdown(walk_data *d)
{
name_data *n;
git_buf_free(&d->path);
for (n = d->names; n->name; n++) {
if (p_unlink(n->name) < 0)
return error("can't unlink(\"%s\")", n->name);
......@@ -308,7 +325,7 @@ static int check_counts(walk_data *d)
return ret;
}
static int one_entry(void *state, char *path)
static int one_entry(void *state, git_buf *path)
{
walk_data *d = (walk_data *) state;
name_data *n;
......@@ -316,11 +333,11 @@ static int one_entry(void *state, char *path)
if (state != state_loc)
return GIT_ERROR;
if (path != path_buffer)
if (path != &d->path)
return GIT_ERROR;
for (n = d->names; n->name; n++) {
if (!strcmp(n->name, path)) {
if (!strcmp(n->name, path->ptr)) {
n->count++;
return 0;
}
......@@ -338,15 +355,14 @@ static name_data dot_names[] = {
};
static walk_data dot = {
".",
dot_names
dot_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent0, "make sure that the '.' folder is not traversed")
must_pass(setup(&dot));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&dot.path,
one_entry,
&dot));
......@@ -363,15 +379,15 @@ static name_data sub_names[] = {
};
static walk_data sub = {
"sub",
sub_names
sub_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent1, "traverse a subfolder")
must_pass(setup(&sub));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&sub.path,
one_entry,
&sub));
......@@ -382,15 +398,15 @@ END_TEST
static walk_data sub_slash = {
"sub/",
sub_names
sub_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent2, "traverse a slash-terminated subfolder")
must_pass(setup(&sub_slash));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&sub_slash.path,
one_entry,
&sub_slash));
......@@ -404,10 +420,11 @@ static name_data empty_names[] = {
};
static walk_data empty = {
"empty",
empty_names
empty_names,
GIT_BUF_INIT
};
static int dont_call_me(void *GIT_UNUSED(state), char *GIT_UNUSED(path))
static int dont_call_me(void *GIT_UNUSED(state), git_buf *GIT_UNUSED(path))
{
GIT_UNUSED_ARG(state)
GIT_UNUSED_ARG(path)
......@@ -418,16 +435,14 @@ BEGIN_TEST(dirent3, "make sure that empty folders are not traversed")
must_pass(setup(&empty));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&empty.path,
one_entry,
&empty));
must_pass(check_counts(&empty));
/* make sure callback not called */
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&empty.path,
dont_call_me,
&empty));
......@@ -444,15 +459,15 @@ static name_data odd_names[] = {
};
static walk_data odd = {
"odd",
odd_names
odd_names,
GIT_BUF_INIT
};
BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traversed")
must_pass(setup(&odd));
must_pass(git_futils_direach(path_buffer,
sizeof(path_buffer),
must_pass(git_futils_direach(&odd.path,
one_entry,
&odd));
......@@ -508,32 +523,24 @@ static char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
static int setup_empty_tmp_dir(void)
{
char path[GIT_PATH_MAX];
if (p_mkdir(empty_tmp_dir, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_one");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_two");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/one/two_two/three");
if (p_mkdir(path, 0777))
return -1;
git_path_join(path, empty_tmp_dir, "/two");
if (p_mkdir(path, 0777))
return -1;
return 0;
git_buf path = GIT_BUF_INIT;
int error =
p_mkdir(empty_tmp_dir, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_one") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/one/two_two/three") ||
p_mkdir(path.ptr, 0777) ||
git_buf_joinpath(&path, empty_tmp_dir, "/two") ||
p_mkdir(path.ptr, 0777);
git_buf_free(&path);
return error ? -1 : 0;
}
BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively")
......@@ -542,17 +549,18 @@ BEGIN_TEST(rmdir0, "make sure empty dir can be deleted recusively")
END_TEST
BEGIN_TEST(rmdir1, "make sure non-empty dir cannot be deleted recusively")
char file[GIT_PATH_MAX];
git_buf file = GIT_BUF_INIT;
int fd;
must_pass(setup_empty_tmp_dir());
git_path_join(file, empty_tmp_dir, "/two/file.txt");
fd = p_creat(file, 0777);
must_pass(git_buf_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
fd = p_creat(file.ptr, 0777);
must_pass(fd);
must_pass(p_close(fd));
must_fail(git_futils_rmdir_r(empty_tmp_dir, 0));
must_pass(p_unlink(file));
must_pass(p_unlink(file.ptr));
must_pass(git_futils_rmdir_r(empty_tmp_dir, 0));
git_buf_free(&file);
END_TEST
BEGIN_TEST(strtol0, "parsing out 32 integers from a string")
......
......@@ -37,7 +37,7 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference")
git_repository *repo;
git_reference *reference;
git_object *object;
char ref_name_from_tag_name[GIT_REFNAME_MAX];
git_buf ref_name_from_tag_name = GIT_BUF_INIT;
must_pass(git_repository_open(&repo, REPOSITORY_FOLDER));
......@@ -51,8 +51,9 @@ BEGIN_TEST(readtag0, "lookup a loose tag reference")
must_be_true(git_object_type(object) == GIT_OBJ_TAG);
/* Ensure the name of the tag matches the name of the reference */
git_path_join(ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object));
must_be_true(strcmp(ref_name_from_tag_name, loose_tag_ref_name) == 0);
must_pass(git_buf_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)));
must_be_true(strcmp(ref_name_from_tag_name.ptr, loose_tag_ref_name) == 0);
git_buf_free(&ref_name_from_tag_name);
git_object_free(object);
git_repository_free(repo);
......@@ -227,7 +228,7 @@ BEGIN_TEST(create0, "create a new symbolic reference")
git_reference *new_reference, *looked_up_ref, *resolved_ref;
git_repository *repo, *repo2;
git_oid id;
char ref_path[GIT_PATH_MAX];
git_buf ref_path = GIT_BUF_INIT;
const char *new_head_tracker = "another-head-tracker";
......@@ -236,7 +237,8 @@ BEGIN_TEST(create0, "create a new symbolic reference")
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Retrieve the physical path to the symbolic ref for further cleaning */
git_path_join(ref_path, repo->path_repository, new_head_tracker);
must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head_tracker));
git_buf_free(&ref_path);
/* Create and write the new symbolic reference */
must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0));
......@@ -276,7 +278,7 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
git_reference *new_reference, *looked_up_ref, *resolved_ref;
git_repository *repo;
git_oid id;
char ref_path[GIT_PATH_MAX];
git_buf ref_path = GIT_BUF_INIT;
const char *new_head_tracker = "deep/rooted/tracker";
......@@ -284,7 +286,7 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
git_path_join(ref_path, repo->path_repository, new_head_tracker);
must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head_tracker));
must_pass(git_reference_create_symbolic(&new_reference, repo, new_head_tracker, current_head_target, 0));
must_pass(git_reference_lookup(&looked_up_ref, repo, new_head_tracker));
must_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
......@@ -295,13 +297,14 @@ BEGIN_TEST(create1, "create a deep symbolic reference")
git_reference_free(new_reference);
git_reference_free(looked_up_ref);
git_reference_free(resolved_ref);
git_buf_free(&ref_path);
END_TEST
BEGIN_TEST(create2, "create a new OID reference")
git_reference *new_reference, *looked_up_ref;
git_repository *repo, *repo2;
git_oid id;
char ref_path[GIT_PATH_MAX];
git_buf ref_path = GIT_BUF_INIT;
const char *new_head = "refs/heads/new-head";
......@@ -310,7 +313,7 @@ BEGIN_TEST(create2, "create a new OID reference")
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Retrieve the physical path to the symbolic ref for further cleaning */
git_path_join(ref_path, repo->path_repository, new_head);
must_pass(git_buf_joinpath(&ref_path, repo->path_repository, new_head));
/* Create and write the new object id reference */
must_pass(git_reference_create_oid(&new_reference, repo, new_head, &id, 0));
......@@ -337,6 +340,7 @@ BEGIN_TEST(create2, "create a new OID reference")
git_reference_free(new_reference);
git_reference_free(looked_up_ref);
git_buf_free(&ref_path);
END_TEST
BEGIN_TEST(create3, "Can not create a new OID reference which targets at an unknown id")
......@@ -491,12 +495,13 @@ END_TEST
BEGIN_TEST(pack0, "create a packfile for an empty folder")
git_repository *repo;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
git_path_join_n(temp_path, 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir");
must_pass(git_futils_mkdir_r(temp_path, GIT_REFS_DIR_MODE));
must_pass(git_buf_join_n(&temp_path, '/', 3, repo->path_repository, GIT_REFS_HEADS_DIR, "empty_dir"));
must_pass(git_futils_mkdir_r(temp_path.ptr, NULL, GIT_REFS_DIR_MODE));
git_buf_free(&temp_path);
must_pass(git_reference_packall(repo));
......@@ -506,7 +511,7 @@ END_TEST
BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
git_repository *repo;
git_reference *reference;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
......@@ -524,8 +529,8 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
must_pass(git_reference_packall(repo));
/* Ensure the packed-refs file exists */
git_path_join(temp_path, repo->path_repository, GIT_PACKEDREFS_FILE);
must_pass(git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, GIT_PACKEDREFS_FILE));
must_pass(git_futils_exists(temp_path.ptr));
/* Ensure the known ref can still be looked up but is now packed */
must_pass(git_reference_lookup(&reference, repo, loose_tag_ref_name));
......@@ -533,25 +538,26 @@ BEGIN_TEST(pack1, "create a packfile from all the loose rn a repo")
must_be_true(strcmp(reference->name, loose_tag_ref_name) == 0);
/* Ensure the known ref has been removed from the loose folder structure */
git_path_join(temp_path, repo->path_repository, loose_tag_ref_name);
must_pass(!git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, loose_tag_ref_name));
must_pass(!git_futils_exists(temp_path.ptr));
close_temp_repo(repo);
git_reference_free(reference);
git_buf_free(&temp_path);
END_TEST
BEGIN_TEST(rename0, "rename a loose reference")
git_reference *looked_up_ref, *another_looked_up_ref;
git_repository *repo;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Ensure the ref doesn't exist on the file system */
git_path_join(temp_path, repo->path_repository, new_name);
must_pass(!git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, new_name));
must_pass(!git_futils_exists(temp_path.ptr));
/* Retrieval of the reference to rename */
must_pass(git_reference_lookup(&looked_up_ref, repo, loose_tag_ref_name));
......@@ -575,26 +581,27 @@ BEGIN_TEST(rename0, "rename a loose reference")
must_be_true(git_reference_is_packed(looked_up_ref) == 0);
/* ...and the ref can be found in the file system */
git_path_join(temp_path, repo->path_repository, new_name);
must_pass(git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, new_name));
must_pass(git_futils_exists(temp_path.ptr));
close_temp_repo(repo);
git_reference_free(looked_up_ref);
git_reference_free(another_looked_up_ref);
git_buf_free(&temp_path);
END_TEST
BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
git_reference *looked_up_ref, *another_looked_up_ref;
git_repository *repo;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
const char *brand_new_name = "refs/heads/brand_new_name";
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Ensure the ref doesn't exist on the file system */
git_path_join(temp_path, repo->path_repository, packed_head_name);
must_pass(!git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_head_name));
must_pass(!git_futils_exists(temp_path.ptr));
/* The reference can however be looked-up... */
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_head_name));
......@@ -618,26 +625,27 @@ BEGIN_TEST(rename1, "rename a packed reference (should make it loose)")
must_be_true(git_reference_is_packed(looked_up_ref) == 0);
/* ...and the ref now happily lives in the file system */
git_path_join(temp_path, repo->path_repository, brand_new_name);
must_pass(git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, brand_new_name));
must_pass(git_futils_exists(temp_path.ptr));
close_temp_repo(repo);
git_reference_free(looked_up_ref);
git_reference_free(another_looked_up_ref);
git_buf_free(&temp_path);
END_TEST
BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference which happens to be in both loose and pack state")
git_reference *looked_up_ref, *another_looked_up_ref;
git_repository *repo;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
const char *brand_new_name = "refs/heads/brand_new_name";
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Ensure the other reference exists on the file system */
git_path_join(temp_path, repo->path_repository, packed_test_head_name);
must_pass(git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_test_head_name));
must_pass(git_futils_exists(temp_path.ptr));
/* Lookup the other reference */
must_pass(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
......@@ -662,12 +670,13 @@ BEGIN_TEST(rename2, "renaming a packed reference does not pack another reference
must_be_true(git_reference_is_packed(another_looked_up_ref) == 0);
/* Ensure the other ref still exists on the file system */
must_pass(git_futils_exists(temp_path));
must_pass(git_futils_exists(temp_path.ptr));
close_temp_repo(repo);
git_reference_free(looked_up_ref);
git_reference_free(another_looked_up_ref);
git_buf_free(&temp_path);
END_TEST
BEGIN_TEST(rename3, "can not rename a reference with the name of an existing reference")
......@@ -884,13 +893,13 @@ END_TEST
BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove both tracks in the filesystem")
git_reference *looked_up_ref, *another_looked_up_ref;
git_repository *repo;
char temp_path[GIT_PATH_MAX];
git_buf temp_path = GIT_BUF_INIT;
must_pass(open_temp_repo(&repo, REPOSITORY_FOLDER));
/* Ensure the loose reference exists on the file system */
git_path_join(temp_path, repo->path_repository, packed_test_head_name);
must_pass(git_futils_exists(temp_path));
must_pass(git_buf_joinpath(&temp_path, repo->path_repository, packed_test_head_name));
must_pass(git_futils_exists(temp_path.ptr));
/* Lookup the reference */
must_pass(git_reference_lookup(&looked_up_ref, repo, packed_test_head_name));
......@@ -905,11 +914,12 @@ BEGIN_TEST(delete0, "deleting a ref which is both packed and loose should remove
must_fail(git_reference_lookup(&another_looked_up_ref, repo, packed_test_head_name));
/* Ensure the loose reference doesn't exist any longer on the file system */
must_pass(!git_futils_exists(temp_path));
must_pass(!git_futils_exists(temp_path.ptr));
close_temp_repo(repo);
git_reference_free(another_looked_up_ref);
git_buf_free(&temp_path);
END_TEST
BEGIN_TEST(delete1, "can delete a just packed reference")
......
......@@ -87,31 +87,37 @@ static int write_file(const char *path, const char *content)
}
//no check is performed on ceiling_dirs length, so be sure it's long enough
static int append_ceiling_dir(char *ceiling_dirs, const char *path)
static int append_ceiling_dir(git_buf *ceiling_dirs, const char *path)
{
int len = strlen(ceiling_dirs);
git_buf pretty_path = GIT_BUF_INIT;
int error;
char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' };
error = git_path_prettify_dir(ceiling_dirs + len + (len ? 1 : 0), path, NULL);
error = git_path_prettify_dir(&pretty_path, path, NULL);
if (error < GIT_SUCCESS)
return git__rethrow(error, "Failed to append ceiling directory.");
if (len)
ceiling_dirs[len] = GIT_PATH_LIST_SEPARATOR;
if (ceiling_dirs->size > 0)
git_buf_puts(ceiling_dirs, ceiling_separator);
git_buf_puts(ceiling_dirs, pretty_path.ptr);
return GIT_SUCCESS;
git_buf_free(&pretty_path);
return git_buf_lasterror(ceiling_dirs);
}
BEGIN_TEST(discover0, "test discover")
git_repository *repo;
char ceiling_dirs[GIT_PATH_MAX * 2] = "";
git_buf ceiling_dirs_buf = GIT_BUF_INIT;
const char *ceiling_dirs;
char repository_path[GIT_PATH_MAX];
char sub_repository_path[GIT_PATH_MAX];
char found_path[GIT_PATH_MAX];
const mode_t mode = 0777;
git_futils_mkdir_r(DISCOVER_FOLDER, mode);
must_pass(append_ceiling_dir(ceiling_dirs, TEMP_REPO_FOLDER));
git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode);
must_pass(append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER));
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
must_be_true(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs) == GIT_ENOTAREPO);
......@@ -120,15 +126,15 @@ BEGIN_TEST(discover0, "test discover")
git_repository_free(repo);
must_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
must_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT));
must_pass(write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"));
......@@ -137,20 +143,22 @@ BEGIN_TEST(discover0, "test discover")
must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path));
must_pass(ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:"));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, NULL, mode));
must_pass(write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"));
must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
must_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode));
must_pass(write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs));
must_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs));
must_pass(append_ceiling_dir(ceiling_dirs, SUB_REPOSITORY_FOLDER));
must_pass(append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER));
ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);
//this must pass as ceiling_directories cannot predent the current
//working directory to be checked
must_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs));
......@@ -166,6 +174,7 @@ BEGIN_TEST(discover0, "test discover")
must_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, 1));
git_repository_free(repo);
git_buf_free(&ceiling_dirs_buf);
END_TEST
BEGIN_SUITE(repository)
......
......@@ -392,15 +392,15 @@ END_TEST
BEGIN_TEST(singlestatus3, "test retrieving status for a new file in an empty repository")
git_repository *repo;
unsigned int status_flags;
char file_path[GIT_PATH_MAX];
git_buf file_path = GIT_BUF_INIT;
char filename[] = "new_file";
int fd;
must_pass(copydir_recurs(EMPTY_REPOSITORY_FOLDER, TEST_STD_REPO_FOLDER));
must_pass(remove_placeholders(TEST_STD_REPO_FOLDER, "dummy-marker.txt"));
git_path_join(file_path, TEMP_REPO_FOLDER, filename);
fd = p_creat(file_path, 0666);
must_pass(git_buf_joinpath(&file_path, TEMP_REPO_FOLDER, filename));
fd = p_creat(file_path.ptr, 0666);
must_pass(fd);
must_pass(p_write(fd, "new_file\n", 9));
must_pass(p_close(fd));
......@@ -411,6 +411,7 @@ BEGIN_TEST(singlestatus3, "test retrieving status for a new file in an empty rep
must_be_true(status_flags == GIT_STATUS_WT_NEW);
git_repository_free(repo);
git_buf_free(&file_path);
git_futils_rmdir_r(TEMP_REPO_FOLDER, 1);
END_TEST
......
......@@ -225,42 +225,49 @@ int cmp_files(const char *a, const char *b)
}
typedef struct {
size_t src_len, dst_len;
char *dst;
git_buf src;
size_t src_baselen;
git_buf dst;
size_t dst_baselen;
} copydir_data;
static int copy_filesystem_element_recurs(void *_data, char *source)
static int copy_filesystem_element_recurs(void *_data, git_buf *source)
{
copydir_data *data = (copydir_data *)_data;
data->dst[data->dst_len] = 0;
git_path_join(data->dst, data->dst, source + data->src_len);
git_buf_truncate(&data->dst, data->dst_baselen);
git_buf_joinpath(&data->dst, data->dst.ptr, source->ptr + data->src_baselen);
if (git_futils_isdir(source) == GIT_SUCCESS)
return git_futils_direach(source, GIT_PATH_MAX, copy_filesystem_element_recurs, _data);
return copy_file(source, data->dst);
if (git_futils_isdir(source->ptr) == GIT_SUCCESS)
return git_futils_direach(source, copy_filesystem_element_recurs, _data);
else
return copy_file(source->ptr, data->dst.ptr);
}
int copydir_recurs(const char *source_directory_path, const char *destination_directory_path)
int copydir_recurs(
const char *source_directory_path,
const char *destination_directory_path)
{
char source_buffer[GIT_PATH_MAX];
char dest_buffer[GIT_PATH_MAX];
copydir_data data;
int error;
copydir_data data = { GIT_BUF_INIT, 0, GIT_BUF_INIT, 0 };
/* Source has to exist, Destination hast to _not_ exist */
if (git_futils_isdir(source_directory_path) != GIT_SUCCESS ||
git_futils_isdir(destination_directory_path) == GIT_SUCCESS)
return GIT_EINVALIDPATH;
git_path_join(source_buffer, source_directory_path, "");
data.src_len = strlen(source_buffer);
git_buf_joinpath(&data.src, source_directory_path, "");
data.src_baselen = data.src.size;
git_buf_joinpath(&data.dst, destination_directory_path, "");
data.dst_baselen = data.dst.size;
git_path_join(dest_buffer, destination_directory_path, "");
data.dst = dest_buffer;
data.dst_len = strlen(dest_buffer);
error = copy_filesystem_element_recurs(&data, &data.src);
return copy_filesystem_element_recurs(&data, source_buffer);
git_buf_free(&data.src);
git_buf_free(&data.dst);
return error;
}
int open_temp_repo(git_repository **repo, const char *path)
......@@ -281,30 +288,51 @@ void close_temp_repo(git_repository *repo)
}
}
static int remove_placeholders_recurs(void *filename, char *path)
typedef struct {
const char *filename;
size_t filename_len;
} remove_data;
static int remove_placeholders_recurs(void *_data, git_buf *path)
{
char passed_filename[GIT_PATH_MAX];
char *data = (char *)filename;
remove_data *data = (remove_data *)_data;
size_t pathlen;
if (!git_futils_isdir(path))
return git_futils_direach(path, GIT_PATH_MAX, remove_placeholders_recurs, data);
if (!git_futils_isdir(path->ptr))
return git_futils_direach(path, remove_placeholders_recurs, data);
if (git_path_basename_r(passed_filename, sizeof(passed_filename), path) < GIT_SUCCESS)
return GIT_EINVALIDPATH;
pathlen = path->size;
if (!strcmp(data, passed_filename))
return p_unlink(path);
if (pathlen < data->filename_len)
return GIT_SUCCESS;
/* if path ends in '/'+filename (or equals filename) */
if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
(pathlen == data->filename_len ||
path->ptr[pathlen - data->filename_len - 1] == '/'))
return p_unlink(path->ptr);
return GIT_SUCCESS;
}
int remove_placeholders(char *directory_path, char *filename)
int remove_placeholders(const char *directory_path, const char *filename)
{
char buffer[GIT_PATH_MAX];
int error;
remove_data data;
git_buf buffer = GIT_BUF_INIT;
if (git_futils_isdir(directory_path))
return GIT_EINVALIDPATH;
strcpy(buffer, directory_path);
return remove_placeholders_recurs(filename, buffer);
if ((error = git_buf_sets(&buffer, directory_path)) < GIT_SUCCESS)
return error;
data.filename = filename;
data.filename_len = strlen(filename);
error = remove_placeholders_recurs(&data, &buffer);
git_buf_free(&buffer);
return error;
}
......@@ -74,7 +74,7 @@ extern int cmp_files(const char *a, const char *b);
extern int copy_file(const char *source, const char *dest);
extern int rmdir_recurs(const char *directory_path);
extern int copydir_recurs(const char *source_directory_path, const char *destination_directory_path);
extern int remove_placeholders(char *directory_path, char *filename);
extern int remove_placeholders(const char *directory_path, const char *filename);
extern int open_temp_repo(git_repository **repo, const char *path);
extern void close_temp_repo(git_repository *repo);
......
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