diff options
author | Russell Belfer <arrbee@arrbee.com> | 2011-11-30 23:27:15 +0400 |
---|---|---|
committer | Russell Belfer <arrbee@arrbee.com> | 2011-12-08 11:08:15 +0400 |
commit | 97769280ba9938ae27f6e06cbd0d5e8a768a86b9 (patch) | |
tree | 4fe43e99acb55f904f6b586bd7c5158610f9512f /src | |
parent | a22b14d32dd8d5f06f121aa154d45bac3b10a305 (diff) |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/blob.c | 51 | ||||
-rw-r--r-- | src/buffer.c | 96 | ||||
-rw-r--r-- | src/buffer.h | 79 | ||||
-rw-r--r-- | src/commit.c | 3 | ||||
-rw-r--r-- | src/config.c | 64 | ||||
-rw-r--r-- | src/config.h | 3 | ||||
-rw-r--r-- | src/fetch.c | 22 | ||||
-rw-r--r-- | src/filebuf.c | 7 | ||||
-rw-r--r-- | src/fileops.c | 181 | ||||
-rw-r--r-- | src/fileops.h | 31 | ||||
-rw-r--r-- | src/index.c | 22 | ||||
-rw-r--r-- | src/indexer.c | 50 | ||||
-rw-r--r-- | src/odb.c | 27 | ||||
-rw-r--r-- | src/odb_loose.c | 283 | ||||
-rw-r--r-- | src/odb_pack.c | 52 | ||||
-rw-r--r-- | src/path.c | 167 | ||||
-rw-r--r-- | src/path.h | 37 | ||||
-rw-r--r-- | src/pkt.c | 13 | ||||
-rw-r--r-- | src/posix.c | 3 | ||||
-rw-r--r-- | src/reflog.c | 119 | ||||
-rw-r--r-- | src/refs.c | 183 | ||||
-rw-r--r-- | src/refspec.c | 18 | ||||
-rw-r--r-- | src/refspec.h | 12 | ||||
-rw-r--r-- | src/remote.c | 16 | ||||
-rw-r--r-- | src/repository.c | 405 | ||||
-rw-r--r-- | src/signature.c | 2 | ||||
-rw-r--r-- | src/status.c | 167 | ||||
-rw-r--r-- | src/tag.c | 120 | ||||
-rw-r--r-- | src/transports/git.c | 2 | ||||
-rw-r--r-- | src/transports/http.c | 19 | ||||
-rw-r--r-- | src/tree.c | 80 | ||||
-rw-r--r-- | src/win32/posix_w32.c | 35 |
32 files changed, 1414 insertions, 955 deletions
diff --git a/src/blob.c b/src/blob.c index 87f5686af..7497ba7bf 100644 --- a/src/blob.c +++ b/src/blob.c @@ -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,18 +97,18 @@ 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; @@ -112,13 +116,11 @@ int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *pat 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); - stream->free(stream); - if (!islnk) + +cleanup: + if (stream) + stream->free(stream); + 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"); } diff --git a/src/buffer.c b/src/buffer.c index b90dd29c5..295b87e1a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -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; } diff --git a/src/buffer.h b/src/buffer.h index fa0c7f0b8..30658f3c2 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -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) diff --git a/src/commit.c b/src/commit.c index bf6ca7855..5d077d54e 100644 --- a/src/commit.c +++ b/src/commit.c @@ -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; } diff --git a/src/config.c b/src/config.c index a8e15405b..2e341d256 100644 --- a/src/config.c +++ b/src/config.c @@ -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; diff --git a/src/config.h b/src/config.h index 7f3494edc..fc639c6d4 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/fetch.c b/src/fetch.c index f447248c5..f9e15b232 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -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; - } diff --git a/src/filebuf.c b/src/filebuf.c index 6600bfa4b..aa47d5eb0 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -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; diff --git a/src/fileops.c b/src/fileops.c index 955bb1bf6..fb2f954d7 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -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); +} + diff --git a/src/fileops.h b/src/fileops.h index e1a59f633..df135d0db 100644 --- a/src/fileops.h +++ b/src/fileops.h @@ -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, diff --git a/src/index.c b/src/index.c index 9f336ba0a..9baab16a9 100644 --- a/src/index.c +++ b/src/index.c @@ -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; diff --git a/src/indexer.c b/src/indexer.c index a69ab850c..8fdf89d9d 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -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"); diff --git a/src/odb_loose.c b/src/odb_loose.c index f1789e071..f177af86c 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -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 (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; + } - if ((error = read_header_loose(&raw, object_path)) < GIT_SUCCESS) - return error; + git_buf_free(&object_path); - *len_p = raw.len; - *type_p = raw.type; - return GIT_SUCCESS; + 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"); + } - *buffer_p = raw.data; - *len_p = raw.len; - *type_p = raw.type; + git_buf_free(&object_path); - return GIT_SUCCESS; + 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 = 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; } - if ((error = read_loose(&raw, object_path)) < GIT_SUCCESS) - return git__rethrow(error, "Failed to read loose backend"); - - *buffer_p = raw.data; - *len_p = raw.len; - *type_p = raw.type; + git_buf_free(&object_path); } - return GIT_SUCCESS; + 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_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: - git_filebuf_cleanup(&fbuf); + if (error < GIT_SUCCESS) + git_filebuf_cleanup(&fbuf); + git_buf_free(&final_path); return error; } diff --git a/src/odb_pack.c b/src/odb_pack.c index 800e7b0da..757d6277e 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -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; } diff --git a/src/path.c b/src/path.c index a8851dfdc..e4b49f35d 100644 --- a/src/path.c +++ b/src/path.c @@ -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; + git_buf buf = GIT_BUF_INIT; + char *dirname; - len = (path ? strlen(path) : 0) + 2; - dname = (char *)git__malloc(len); - if (dname == NULL) - return NULL; + git_path_dirname_r(&buf, path); + dirname = git_buf_detach(&buf); + git_buf_free(&buf); /* avoid memleak if error occurs */ - if (git_path_dirname_r(dname, len, path) < GIT_SUCCESS) { - git__free(dname); - return NULL; - } - - return dname; + return dirname; } char *git_path_basename(const char *path) { - char *bname = NULL; - int len; - - len = (path ? strlen(path) : 0) + 2; - bname = (char *)git__malloc(len); - if (bname == NULL) - return NULL; + git_buf buf = GIT_BUF_INIT; + char *basename; - if (git_path_basename_r(bname, len, path) < GIT_SUCCESS) { - git__free(bname); - return NULL; - } + git_path_basename_r(&buf, path); + basename = git_buf_detach(&buf); + git_buf_free(&buf); /* avoid memleak if error occurs */ - 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; + } + + /* allow realpath to allocate the buffer */ + if (path != NULL) + result = p_realpath(path, NULL); - if (base == NULL || git_path_root(path) >= 0) { - result = p_realpath(path, path_out); + 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_SUCCESS; + return git_buf_lasterror(path); } + +void git_path_string_to_dir(char* path, size_t size) +{ + size_t end = strlen(path); + + if (end && path[end - 1] != '/' && end < size) { + path[end] = '/'; + path[end + 1] = '\0'; + } +} + diff --git a/src/path.h b/src/path.h index 51bedeed7..0c8cc349c 100644 --- a/src/path.h +++ b/src/path.h @@ -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) diff --git a/src/posix.c b/src/posix.c index 8c19588ee..916aad726 100644 --- a/src/posix.c +++ b/src/posix.c @@ -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; } diff --git a/src/reflog.c b/src/reflog.c index fbaaaea67..84ce52d91 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -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) diff --git a/src/refs.c b/src/refs.c index 8931d5bac..8c3f700ad 100644 --- a/src/refs.c +++ b/src/refs.c @@ -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, - GIT_REFLOG_DIR, ref->name); - if (git_futils_exists(aux_path) == GIT_SUCCESS) + error = git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, + GIT_REFLOG_DIR, ref->name); + 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) diff --git a/src/refspec.c b/src/refspec.c index 62683e7b7..7ce32ba14 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -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); +} diff --git a/src/refspec.h b/src/refspec.h index 7c389719b..cd9f894bf 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -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 diff --git a/src/remote.c b/src/remote.c index eca3f7748..ef42c6e2a 100644 --- a/src/remote.c +++ b/src/remote.c @@ -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) diff --git a/src/repository.c b/src/repository.c index 0cd4a8123..67afa2ee2 100644 --- a/src/repository.c +++ b/src/repository.c @@ -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(¤t_device, bare_path); + error = retrieve_device(¤t_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 (git_path_dirname_r(normal_path, sizeof(normal_path), bare_path) < GIT_SUCCESS) - return git__throw(GIT_EOSERR, "Failed to dirname '%s'", bare_path); + /** + * 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; + } + + 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); - - git_filebuf_open(&ref, ref_path, 0); - git_filebuf_printf(&ref, "ref: refs/heads/master\n"); + 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); - 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); - if (error < GIT_SUCCESS) - return git__throw(error, "Unable to create `%s` folder", temp_path); + /* 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__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) diff --git a/src/signature.c b/src/signature.c index 832d6439c..fb2bb3cce 100644 --- a/src/signature.c +++ b/src/signature.c @@ -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); } diff --git a/src/status.c b/src/status.c index 26dd11ea8..05e07be0a 100644 --- a/src/status.c +++ b/src/status.c @@ -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); @@ -229,7 +230,7 @@ static int store_if_changed(struct status_st *st, struct status_entry *e) { int error; if ((error = status_entry_update_flags(e)) < GIT_SUCCESS) - return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path); + return git__throw(error, "Failed to process the file '%s'. It doesn't exist in the workdir, in the HEAD nor in the index", e->path); if (e->status_flags == GIT_STATUS_CURRENT) { git__free(e); @@ -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); - - m_name = st->head_tree_relative_path; + git_path_to_dir(&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,37 +616,34 @@ 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; - /* - * Append a forward slash to the name to force folders + /* + * Append a forward slash to the name to force folders * to be ordered in a similar way than in a tree * * 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"); - - error = retrieve_tag_reference(&new_ref, ref_name, repo, tag_name); - - switch (error) { - case GIT_SUCCESS: - case GIT_ENOTFOUND: - break; + return git__throw(GIT_EINVALIDARGS, + "The given target does not belong to this repository"); - 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); + + if (error < GIT_SUCCESS) + git__rethrow(error, "%s", errmsg); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); + 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); + + if (error < GIT_SUCCESS) + git__rethrow(error, "%s", errmsg); - return error == GIT_SUCCESS ? GIT_SUCCESS : git__rethrow(error, "Failed to create tag"); + 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"); diff --git a/src/transports/git.c b/src/transports/git.c index bdb94d090..ece4d40a8 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -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) diff --git a/src/transports/http.c b/src/transports/http.c index e463a0f59..48ea78dce 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -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; } diff --git a/src/tree.c b/src/tree.c index 702095d14..23efb28e4 100644 --- a/src/tree.c +++ b/src/tree.c @@ -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; } diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 6f722581e..e406a8f6c 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -304,32 +304,37 @@ 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); - git_path_mkposix(buffer); + if (buffer) + git_path_mkposix(buffer); return buffer; } |