Commit 247b3f4e by Vicent Marti

Merge remote-tracking branch 'origin/master_patch'

parents 4eb97ef3 dce7b1a4
...@@ -136,3 +136,7 @@ v0.21 + 1 ...@@ -136,3 +136,7 @@ v0.21 + 1
* git_libgit2_init() and git_libgit2_shutdown() now return the number of * git_libgit2_init() and git_libgit2_shutdown() now return the number of
initializations of the library, so consumers may schedule work on the initializations of the library, so consumers may schedule work on the
first initialization. first initialization.
* git_treebuilder_create now takes a repository so that it can query
repository configuration. Subsequently, git_treebuilder_write no
longer takes a repository.
...@@ -247,11 +247,12 @@ GIT_EXTERN(int) git_tree_entry_to_object( ...@@ -247,11 +247,12 @@ GIT_EXTERN(int) git_tree_entry_to_object(
* entries and will have to be filled manually. * entries and will have to be filled manually.
* *
* @param out Pointer where to store the tree builder * @param out Pointer where to store the tree builder
* @param repo Repository in which to store the object
* @param source Source tree to initialize the builder (optional) * @param source Source tree to initialize the builder (optional)
* @return 0 on success; error code otherwise * @return 0 on success; error code otherwise
*/ */
GIT_EXTERN(int) git_treebuilder_create( GIT_EXTERN(int) git_treebuilder_create(
git_treebuilder **out, const git_tree *source); git_treebuilder **out, git_repository *repo, const git_tree *source);
/** /**
* Clear all the entires in the builder * Clear all the entires in the builder
...@@ -368,12 +369,11 @@ GIT_EXTERN(void) git_treebuilder_filter( ...@@ -368,12 +369,11 @@ GIT_EXTERN(void) git_treebuilder_filter(
* identifying SHA1 hash will be stored in the `id` pointer. * identifying SHA1 hash will be stored in the `id` pointer.
* *
* @param id Pointer to store the OID of the newly written tree * @param id Pointer to store the OID of the newly written tree
* @param repo Repository in which to store the object
* @param bld Tree builder to write * @param bld Tree builder to write
* @return 0 or an error code * @return 0 or an error code
*/ */
GIT_EXTERN(int) git_treebuilder_write( GIT_EXTERN(int) git_treebuilder_write(
git_oid *id, git_repository *repo, git_treebuilder *bld); git_oid *id, git_treebuilder *bld);
/** Callback for the tree traversal method */ /** Callback for the tree traversal method */
......
...@@ -1172,6 +1172,30 @@ static int checkout_get_remove_conflicts( ...@@ -1172,6 +1172,30 @@ static int checkout_get_remove_conflicts(
return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data); return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data);
} }
static int checkout_verify_paths(
git_repository *repo,
int action,
git_diff_delta *delta)
{
unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT;
if (action & CHECKOUT_ACTION__REMOVE) {
if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path);
return -1;
}
}
if (action & ~CHECKOUT_ACTION__REMOVE) {
if (!git_path_isvalid(repo, delta->new_file.path, flags)) {
giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->old_file.path);
return -1;
}
}
return 0;
}
static int checkout_get_actions( static int checkout_get_actions(
uint32_t **actions_ptr, uint32_t **actions_ptr,
size_t **counts_ptr, size_t **counts_ptr,
...@@ -1205,7 +1229,9 @@ static int checkout_get_actions( ...@@ -1205,7 +1229,9 @@ static int checkout_get_actions(
} }
git_vector_foreach(deltas, i, delta) { git_vector_foreach(deltas, i, delta) {
error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec); if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0)
error = checkout_verify_paths(data->repo, act, delta);
if (error != 0) if (error != 0)
goto fail; goto fail;
......
...@@ -76,6 +76,8 @@ static struct map_data _cvar_maps[] = { ...@@ -76,6 +76,8 @@ static struct map_data _cvar_maps[] = {
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT }, {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
{"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT}, {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT},
{"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT }, {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT },
{"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
{"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
}; };
int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar) int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar)
......
...@@ -762,23 +762,35 @@ void git_index_entry__init_from_stat( ...@@ -762,23 +762,35 @@ void git_index_entry__init_from_stat(
entry->file_size = st->st_size; entry->file_size = st->st_size;
} }
static git_index_entry *index_entry_alloc(const char *path) static int index_entry_create(
git_index_entry **out,
git_repository *repo,
const char *path)
{ {
size_t pathlen = strlen(path); size_t pathlen = strlen(path);
struct entry_internal *entry = struct entry_internal *entry;
git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
if (!entry) if (!git_path_isvalid(repo, path,
return NULL; GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) {
giterr_set(GITERR_INDEX, "Invalid path: '%s'", path);
return -1;
}
entry = git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1);
GITERR_CHECK_ALLOC(entry);
entry->pathlen = pathlen; entry->pathlen = pathlen;
memcpy(entry->path, path, pathlen); memcpy(entry->path, path, pathlen);
entry->entry.path = entry->path; entry->entry.path = entry->path;
return (git_index_entry *)entry; *out = (git_index_entry *)entry;
return 0;
} }
static int index_entry_init( static int index_entry_init(
git_index_entry **entry_out, git_index *index, const char *rel_path) git_index_entry **entry_out,
git_index *index,
const char *rel_path)
{ {
int error = 0; int error = 0;
git_index_entry *entry = NULL; git_index_entry *entry = NULL;
...@@ -790,14 +802,17 @@ static int index_entry_init( ...@@ -790,14 +802,17 @@ static int index_entry_init(
"Could not initialize index entry. " "Could not initialize index entry. "
"Index is not backed up by an existing repository."); "Index is not backed up by an existing repository.");
if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0)
return -1;
/* write the blob to disk and get the oid and stat info */ /* write the blob to disk and get the oid and stat info */
error = git_blob__create_from_paths( error = git_blob__create_from_paths(
&oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true); &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
if (error < 0)
return error;
entry = index_entry_alloc(rel_path); if (error < 0) {
GITERR_CHECK_ALLOC(entry); index_entry_free(entry);
return error;
}
entry->id = oid; entry->id = oid;
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
...@@ -853,7 +868,10 @@ static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src) ...@@ -853,7 +868,10 @@ static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src)
tgt->path = tgt_path; /* reset to existing path data */ tgt->path = tgt_path; /* reset to existing path data */
} }
static int index_entry_dup(git_index_entry **out, const git_index_entry *src) static int index_entry_dup(
git_index_entry **out,
git_repository *repo,
const git_index_entry *src)
{ {
git_index_entry *entry; git_index_entry *entry;
...@@ -862,11 +880,11 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src) ...@@ -862,11 +880,11 @@ static int index_entry_dup(git_index_entry **out, const git_index_entry *src)
return 0; return 0;
} }
*out = entry = index_entry_alloc(src->path); if (index_entry_create(&entry, repo, src->path) < 0)
GITERR_CHECK_ALLOC(entry); return -1;
index_entry_cpy(entry, src); index_entry_cpy(entry, src);
*out = entry;
return 0; return 0;
} }
...@@ -1131,7 +1149,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry) ...@@ -1131,7 +1149,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
return -1; return -1;
} }
if ((ret = index_entry_dup(&entry, source_entry)) < 0 || if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 ||
(ret = index_insert(index, &entry, 1)) < 0) (ret = index_insert(index, &entry, 1)) < 0)
return ret; return ret;
...@@ -1251,9 +1269,9 @@ int git_index_conflict_add(git_index *index, ...@@ -1251,9 +1269,9 @@ int git_index_conflict_add(git_index *index,
assert (index); assert (index);
if ((ret = index_entry_dup(&entries[0], ancestor_entry)) < 0 || if ((ret = index_entry_dup(&entries[0], INDEX_OWNER(index), ancestor_entry)) < 0 ||
(ret = index_entry_dup(&entries[1], our_entry)) < 0 || (ret = index_entry_dup(&entries[1], INDEX_OWNER(index), our_entry)) < 0 ||
(ret = index_entry_dup(&entries[2], their_entry)) < 0) (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0)
goto on_error; goto on_error;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
...@@ -1770,7 +1788,10 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size ...@@ -1770,7 +1788,10 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
} }
static size_t read_entry( static size_t read_entry(
git_index_entry **out, const void *buffer, size_t buffer_size) git_index_entry **out,
git_index *index,
const void *buffer,
size_t buffer_size)
{ {
size_t path_length, entry_size; size_t path_length, entry_size;
const char *path_ptr; const char *path_ptr;
...@@ -1834,7 +1855,7 @@ static size_t read_entry( ...@@ -1834,7 +1855,7 @@ static size_t read_entry(
entry.path = (char *)path_ptr; entry.path = (char *)path_ptr;
if (index_entry_dup(out, &entry) < 0) if (index_entry_dup(out, INDEX_OWNER(index), &entry) < 0)
return 0; return 0;
return entry_size; return entry_size;
...@@ -1935,7 +1956,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) ...@@ -1935,7 +1956,7 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
/* Parse all the entries */ /* Parse all the entries */
for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
git_index_entry *entry; git_index_entry *entry;
size_t entry_size = read_entry(&entry, buffer, buffer_size); size_t entry_size = read_entry(&entry, index, buffer, buffer_size);
/* 0 bytes read means an object corruption */ /* 0 bytes read means an object corruption */
if (entry_size == 0) { if (entry_size == 0) {
...@@ -2296,6 +2317,7 @@ int git_index_entry_stage(const git_index_entry *entry) ...@@ -2296,6 +2317,7 @@ int git_index_entry_stage(const git_index_entry *entry)
} }
typedef struct read_tree_data { typedef struct read_tree_data {
git_index *index;
git_vector *old_entries; git_vector *old_entries;
git_vector *new_entries; git_vector *new_entries;
git_vector_cmp entry_cmp; git_vector_cmp entry_cmp;
...@@ -2316,8 +2338,8 @@ static int read_tree_cb( ...@@ -2316,8 +2338,8 @@ static int read_tree_cb(
if (git_buf_joinpath(&path, root, tentry->filename) < 0) if (git_buf_joinpath(&path, root, tentry->filename) < 0)
return -1; return -1;
entry = index_entry_alloc(path.ptr); if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0)
GITERR_CHECK_ALLOC(entry); return -1;
entry->mode = tentry->attr; entry->mode = tentry->attr;
entry->id = tentry->oid; entry->id = tentry->oid;
...@@ -2357,6 +2379,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree) ...@@ -2357,6 +2379,7 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */ git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
data.index = index;
data.old_entries = &index->entries; data.old_entries = &index->entries;
data.new_entries = &entries; data.new_entries = &entries;
data.entry_cmp = index->entries_search; data.entry_cmp = index->entries_search;
...@@ -2476,7 +2499,7 @@ int git_index_add_all( ...@@ -2476,7 +2499,7 @@ int git_index_add_all(
break; break;
/* make the new entry to insert */ /* make the new entry to insert */
if ((error = index_entry_dup(&entry, wd)) < 0) if ((error = index_entry_dup(&entry, INDEX_OWNER(index), wd)) < 0)
break; break;
entry->id = blobid; entry->id = blobid;
......
...@@ -107,7 +107,7 @@ static int tree_write( ...@@ -107,7 +107,7 @@ static int tree_write(
const git_tree_entry *entry; const git_tree_entry *entry;
git_oid tree_oid; git_oid tree_oid;
if ((error = git_treebuilder_create(&tb, source_tree)) < 0) if ((error = git_treebuilder_create(&tb, repo, source_tree)) < 0)
goto cleanup; goto cleanup;
if (object_oid) { if (object_oid) {
...@@ -119,7 +119,7 @@ static int tree_write( ...@@ -119,7 +119,7 @@ static int tree_write(
goto cleanup; goto cleanup;
} }
if ((error = git_treebuilder_write(&tree_oid, repo, tb)) < 0) if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
goto cleanup; goto cleanup;
error = git_tree_lookup(out, repo, &tree_oid); error = git_tree_lookup(out, repo, &tree_oid);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "common.h" #include "common.h"
#include "path.h" #include "path.h"
#include "posix.h" #include "posix.h"
#include "repository.h"
#ifdef GIT_WIN32 #ifdef GIT_WIN32
#include "win32/posix.h" #include "win32/posix.h"
#include "win32/w32_util.h" #include "win32/w32_util.h"
...@@ -1238,3 +1239,258 @@ int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path) ...@@ -1238,3 +1239,258 @@ int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path)
else else
return git_buf_sets(local_path_out, url_or_path); return git_buf_sets(local_path_out, url_or_path);
} }
/* Reject paths like AUX or COM1, or those versions that end in a dot or
* colon. ("AUX." or "AUX:")
*/
GIT_INLINE(bool) verify_dospath(
const char *component,
size_t len,
const char dospath[3],
bool trailing_num)
{
size_t last = trailing_num ? 4 : 3;
if (len < last || git__strncasecmp(component, dospath, 3) != 0)
return true;
if (trailing_num && !git__isdigit(component[3]))
return true;
return (len > last &&
component[last] != '.' &&
component[last] != ':');
}
static int32_t next_hfs_char(const char **in, size_t *len)
{
while (*len) {
int32_t codepoint;
int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint);
if (cp_len < 0)
return -1;
(*in) += cp_len;
(*len) -= cp_len;
/* these code points are ignored completely */
switch (codepoint) {
case 0x200c: /* ZERO WIDTH NON-JOINER */
case 0x200d: /* ZERO WIDTH JOINER */
case 0x200e: /* LEFT-TO-RIGHT MARK */
case 0x200f: /* RIGHT-TO-LEFT MARK */
case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
case 0x202c: /* POP DIRECTIONAL FORMATTING */
case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
case 0x206e: /* NATIONAL DIGIT SHAPES */
case 0x206f: /* NOMINAL DIGIT SHAPES */
case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
continue;
}
/* fold into lowercase -- this will only fold characters in
* the ASCII range, which is perfectly fine, because the
* git folder name can only be composed of ascii characters
*/
return tolower(codepoint);
}
return 0; /* NULL byte -- end of string */
}
static bool verify_dotgit_hfs(const char *path, size_t len)
{
if (next_hfs_char(&path, &len) != '.' ||
next_hfs_char(&path, &len) != 'g' ||
next_hfs_char(&path, &len) != 'i' ||
next_hfs_char(&path, &len) != 't' ||
next_hfs_char(&path, &len) != 0)
return true;
return false;
}
GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len)
{
const char *shortname = NULL;
size_t i, start, shortname_len = 0;
/* See if the repo has a custom shortname (not "GIT~1") */
if (repo &&
(shortname = git_repository__8dot3_name(repo)) &&
shortname != git_repository__8dot3_default)
shortname_len = strlen(shortname);
if (len >= 4 && strncasecmp(path, ".git", 4) == 0)
start = 4;
else if (len >= git_repository__8dot3_default_len &&
strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0)
start = git_repository__8dot3_default_len;
else if (shortname_len && len >= shortname_len &&
strncasecmp(path, shortname, shortname_len) == 0)
start = shortname_len;
else
return true;
/* Reject paths beginning with ".git\" */
if (path[start] == '\\')
return false;
for (i = start; i < len; i++) {
if (path[i] != ' ' && path[i] != '.')
return true;
}
return false;
}
GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
{
if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
return false;
if ((flags & GIT_PATH_REJECT_SLASH) && c == '/')
return false;
if (flags & GIT_PATH_REJECT_NT_CHARS) {
if (c < 32)
return false;
switch (c) {
case '<':
case '>':
case ':':
case '"':
case '|':
case '?':
case '*':
return false;
}
}
return true;
}
/*
* We fundamentally don't like some paths when dealing with user-inputted
* strings (in checkout or ref names): we don't want dot or dot-dot
* anywhere, we want to avoid writing weird paths on Windows that can't
* be handled by tools that use the non-\\?\ APIs, we don't want slashes
* or double slashes at the end of paths that can make them ambiguous.
*
* For checkout, we don't want to recurse into ".git" either.
*/
static bool verify_component(
git_repository *repo,
const char *component,
size_t len,
unsigned int flags)
{
if (len == 0)
return false;
if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
len == 1 && component[0] == '.')
return false;
if ((flags & GIT_PATH_REJECT_TRAVERSAL) &&
len == 2 && component[0] == '.' && component[1] == '.')
return false;
if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.')
return false;
if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ')
return false;
if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':')
return false;
if (flags & GIT_PATH_REJECT_DOS_PATHS) {
if (!verify_dospath(component, len, "CON", false) ||
!verify_dospath(component, len, "PRN", false) ||
!verify_dospath(component, len, "AUX", false) ||
!verify_dospath(component, len, "NUL", false) ||
!verify_dospath(component, len, "COM", true) ||
!verify_dospath(component, len, "LPT", true))
return false;
}
if (flags & GIT_PATH_REJECT_DOT_GIT_HFS &&
!verify_dotgit_hfs(component, len))
return false;
if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS &&
!verify_dotgit_ntfs(repo, component, len))
return false;
if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
(flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
(flags & GIT_PATH_REJECT_DOT_GIT) &&
len == 4 &&
component[0] == '.' &&
(component[1] == 'g' || component[1] == 'G') &&
(component[2] == 'i' || component[2] == 'I') &&
(component[3] == 't' || component[3] == 'T'))
return false;
return true;
}
GIT_INLINE(unsigned int) dotgit_flags(
git_repository *repo,
unsigned int flags)
{
int protectHFS = 0, protectNTFS = 0;
#ifdef __APPLE__
protectHFS = 1;
#endif
#ifdef GIT_WIN32
protectNTFS = 1;
#endif
if (repo && !protectHFS)
git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
if (protectHFS)
flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
if (repo && !protectNTFS)
git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
if (protectNTFS)
flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
return flags;
}
bool git_path_isvalid(
git_repository *repo,
const char *path,
unsigned int flags)
{
const char *start, *c;
/* Upgrade the ".git" checks based on platform */
if ((flags & GIT_PATH_REJECT_DOT_GIT))
flags = dotgit_flags(repo, flags);
for (start = c = path; *c; c++) {
if (!verify_char(*c, flags))
return false;
if (*c == '/') {
if (!verify_component(repo, start, (c - start), flags))
return false;
start = c+1;
}
}
return verify_component(repo, start, (c - start), flags);
}
...@@ -462,4 +462,47 @@ extern bool git_path_does_fs_decompose_unicode(const char *root); ...@@ -462,4 +462,47 @@ extern bool git_path_does_fs_decompose_unicode(const char *root);
extern bool git_path_is_local_file_url(const char *file_url); extern bool git_path_is_local_file_url(const char *file_url);
extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path); extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path);
/* Flags to determine path validity in `git_path_isvalid` */
#define GIT_PATH_REJECT_TRAVERSAL (1 << 0)
#define GIT_PATH_REJECT_DOT_GIT (1 << 1)
#define GIT_PATH_REJECT_SLASH (1 << 2)
#define GIT_PATH_REJECT_BACKSLASH (1 << 3)
#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4)
#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5)
#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
/* Default path safety for writing files to disk: since we use the
* Win32 "File Namespace" APIs ("\\?\") we need to protect from
* paths that the normal Win32 APIs would not write.
*/
#ifdef GIT_WIN32
# define GIT_PATH_REJECT_DEFAULTS \
GIT_PATH_REJECT_TRAVERSAL | \
GIT_PATH_REJECT_BACKSLASH | \
GIT_PATH_REJECT_TRAILING_DOT | \
GIT_PATH_REJECT_TRAILING_SPACE | \
GIT_PATH_REJECT_TRAILING_COLON | \
GIT_PATH_REJECT_DOS_PATHS | \
GIT_PATH_REJECT_NT_CHARS
#else
# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
#endif
/*
* Determine whether a path is a valid git path or not - this must not contain
* a '.' or '..' component, or a component that is ".git" (in any case).
*
* `repo` is optional. If specified, it will be used to determine the short
* path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified),
* in addition to the default of "git~1".
*/
extern bool git_path_isvalid(
git_repository *repo,
const char *path,
unsigned int flags);
#endif #endif
...@@ -712,6 +712,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char * ...@@ -712,6 +712,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
assert(file && backend && name); assert(file && backend && name);
if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) {
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
return GIT_EINVALIDSPEC;
}
/* Remove a possibly existing empty directory hierarchy /* Remove a possibly existing empty directory hierarchy
* which name would collide with the reference name * which name would collide with the reference name
*/ */
...@@ -1653,6 +1658,11 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char ...@@ -1653,6 +1658,11 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char
repo = backend->repo; repo = backend->repo;
if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) {
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
return GIT_EINVALIDSPEC;
}
if (retrieve_reflog_path(&log_path, repo, refname) < 0) if (retrieve_reflog_path(&log_path, repo, refname) < 0)
return -1; return -1;
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
#define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION 0
const char *git_repository__8dot3_default = "GIT~1";
size_t git_repository__8dot3_default_len = 5;
static void set_odb(git_repository *repo, git_odb *odb) static void set_odb(git_repository *repo, git_odb *odb)
{ {
if (odb) { if (odb) {
...@@ -120,6 +123,7 @@ void git_repository_free(git_repository *repo) ...@@ -120,6 +123,7 @@ void git_repository_free(git_repository *repo)
git__free(repo->path_repository); git__free(repo->path_repository);
git__free(repo->workdir); git__free(repo->workdir);
git__free(repo->namespace); git__free(repo->namespace);
git__free(repo->name_8dot3);
git__memzero(repo, sizeof(*repo)); git__memzero(repo, sizeof(*repo));
git__free(repo); git__free(repo);
...@@ -791,6 +795,27 @@ const char *git_repository_get_namespace(git_repository *repo) ...@@ -791,6 +795,27 @@ const char *git_repository_get_namespace(git_repository *repo)
return repo->namespace; return repo->namespace;
} }
const char *git_repository__8dot3_name(git_repository *repo)
{
if (!repo->has_8dot3) {
repo->has_8dot3 = 1;
#ifdef GIT_WIN32
if (!repo->is_bare) {
repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository);
/* We anticipate the 8.3 name is "GIT~1", so use a static for
* easy testing in the common case */
if (strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0)
repo->has_8dot3_default = 1;
}
#endif
}
return repo->has_8dot3_default ?
git_repository__8dot3_default : repo->name_8dot3;
}
static int check_repositoryformatversion(git_config *config) static int check_repositoryformatversion(git_config *config)
{ {
int version; int version;
......
...@@ -40,6 +40,8 @@ typedef enum { ...@@ -40,6 +40,8 @@ typedef enum {
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */ GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
GIT_CVAR_SAFE_CRLF, /* core.safecrlf */ GIT_CVAR_SAFE_CRLF, /* core.safecrlf */
GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */ GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */
GIT_CVAR_PROTECTHFS, /* core.protectHFS */
GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */
GIT_CVAR_CACHE_MAX GIT_CVAR_CACHE_MAX
} git_cvar_cached; } git_cvar_cached;
...@@ -96,6 +98,10 @@ typedef enum { ...@@ -96,6 +98,10 @@ typedef enum {
/* core.logallrefupdates */ /* core.logallrefupdates */
GIT_LOGALLREFUPDATES_UNSET = 2, GIT_LOGALLREFUPDATES_UNSET = 2,
GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET, GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
/* core.protectHFS */
GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
/* core.protectNTFS */
GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value; } git_cvar_value;
/* internal repository init flags */ /* internal repository init flags */
...@@ -120,8 +126,11 @@ struct git_repository { ...@@ -120,8 +126,11 @@ struct git_repository {
char *path_repository; char *path_repository;
char *workdir; char *workdir;
char *namespace; char *namespace;
char *name_8dot3;
unsigned is_bare:1; unsigned is_bare:1,
has_8dot3:1,
has_8dot3_default:1;
unsigned int lru_counter; unsigned int lru_counter;
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX]; git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
...@@ -174,4 +183,19 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head ...@@ -174,4 +183,19 @@ int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head
int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len); int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
/*
* Gets the DOS-compatible 8.3 "short name". This will return only the
* short name for the repository directory (ie, "git~1" for ".git"). This
* will always return a pointer to `git_repository__8dot3_default` when
* "GIT~1" is the short name. This will return NULL for bare repositories,
* and systems that do not have a short name.
*/
const char *git_repository__8dot3_name(git_repository *repo);
/* The default DOS-compatible 8.3 "short name" for a git repository,
* "GIT~1".
*/
extern const char *git_repository__8dot3_default;
extern size_t git_repository__8dot3_default_len;
#endif #endif
...@@ -50,14 +50,11 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode) ...@@ -50,14 +50,11 @@ GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
return GIT_FILEMODE_BLOB; return GIT_FILEMODE_BLOB;
} }
static int valid_entry_name(const char *filename) static int valid_entry_name(git_repository *repo, const char *filename)
{ {
return *filename != '\0' && return *filename != '\0' &&
strchr(filename, '/') == NULL && git_path_isvalid(repo, filename,
(*filename != '.' || GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH);
(strcmp(filename, ".") != 0 &&
strcmp(filename, "..") != 0 &&
strcmp(filename, DOT_GIT) != 0));
} }
static int entry_sort_cmp(const void *a, const void *b) static int entry_sort_cmp(const void *a, const void *b)
...@@ -455,7 +452,7 @@ static int append_entry( ...@@ -455,7 +452,7 @@ static int append_entry(
git_tree_entry *entry; git_tree_entry *entry;
int error = 0; int error = 0;
if (!valid_entry_name(filename)) if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
entry = alloc_entry(filename); entry = alloc_entry(filename);
...@@ -493,7 +490,7 @@ static int write_tree( ...@@ -493,7 +490,7 @@ static int write_tree(
return (int)find_next_dir(dirname, index, start); return (int)find_next_dir(dirname, index, start);
} }
if ((error = git_treebuilder_create(&bld, NULL)) < 0 || bld == NULL) if ((error = git_treebuilder_create(&bld, repo, NULL)) < 0 || bld == NULL)
return -1; return -1;
/* /*
...@@ -564,7 +561,7 @@ static int write_tree( ...@@ -564,7 +561,7 @@ static int write_tree(
} }
} }
if (git_treebuilder_write(oid, repo, bld) < 0) if (git_treebuilder_write(oid, bld) < 0)
goto on_error; goto on_error;
git_treebuilder_free(bld); git_treebuilder_free(bld);
...@@ -627,16 +624,21 @@ int git_tree__write_index( ...@@ -627,16 +624,21 @@ int git_tree__write_index(
return ret; return ret;
} }
int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) int git_treebuilder_create(
git_treebuilder **builder_p,
git_repository *repo,
const git_tree *source)
{ {
git_treebuilder *bld; git_treebuilder *bld;
size_t i; size_t i;
assert(builder_p); assert(builder_p && repo);
bld = git__calloc(1, sizeof(git_treebuilder)); bld = git__calloc(1, sizeof(git_treebuilder));
GITERR_CHECK_ALLOC(bld); GITERR_CHECK_ALLOC(bld);
bld->repo = repo;
if (git_strmap_alloc(&bld->map) < 0) { if (git_strmap_alloc(&bld->map) < 0) {
git__free(bld); git__free(bld);
return -1; return -1;
...@@ -678,7 +680,7 @@ int git_treebuilder_insert( ...@@ -678,7 +680,7 @@ int git_treebuilder_insert(
if (!valid_filemode(filemode)) if (!valid_filemode(filemode))
return tree_error("Failed to insert entry. Invalid filemode for file", filename); return tree_error("Failed to insert entry. Invalid filemode for file", filename);
if (!valid_entry_name(filename)) if (!valid_entry_name(bld->repo, filename))
return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
pos = git_strmap_lookup_index(bld->map, filename); pos = git_strmap_lookup_index(bld->map, filename);
...@@ -738,7 +740,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) ...@@ -738,7 +740,7 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
return 0; return 0;
} }
int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{ {
int error = 0; int error = 0;
size_t i, entrycount; size_t i, entrycount;
...@@ -777,7 +779,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b ...@@ -777,7 +779,7 @@ int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *b
git_vector_free(&entries); git_vector_free(&entries);
if (!error && if (!error &&
!(error = git_repository_odb__weakptr(&odb, repo))) !(error = git_repository_odb__weakptr(&odb, bld->repo)))
error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
git_buf_free(&tree); git_buf_free(&tree);
......
...@@ -26,6 +26,7 @@ struct git_tree { ...@@ -26,6 +26,7 @@ struct git_tree {
}; };
struct git_treebuilder { struct git_treebuilder {
git_repository *repo;
git_strmap *map; git_strmap *map;
}; };
......
...@@ -250,6 +250,21 @@ int git__prefixcmp_icase(const char *str, const char *prefix) ...@@ -250,6 +250,21 @@ int git__prefixcmp_icase(const char *str, const char *prefix)
return strncasecmp(str, prefix, strlen(prefix)); return strncasecmp(str, prefix, strlen(prefix));
} }
int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
{
int s, p;
while(str_n--) {
s = (unsigned char)tolower(*str++);
p = (unsigned char)tolower(*prefix++);
if (s != p)
return s - p;
}
return (0 - *prefix);
}
int git__suffixcmp(const char *str, const char *suffix) int git__suffixcmp(const char *str, const char *suffix)
{ {
size_t a = strlen(str); size_t a = strlen(str);
...@@ -649,3 +664,79 @@ void git__insertsort_r( ...@@ -649,3 +664,79 @@ void git__insertsort_r(
if (freeswap) if (freeswap)
git__free(swapel); git__free(swapel);
} }
static const int8_t utf8proc_utf8class[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
};
int git__utf8_charlen(const uint8_t *str, int str_len)
{
int length, i;
length = utf8proc_utf8class[str[0]];
if (!length)
return -1;
if (str_len >= 0 && length > str_len)
return -str_len;
for (i = 1; i < length; i++) {
if ((str[i] & 0xC0) != 0x80)
return -i;
}
return length;
}
int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst)
{
int length;
int32_t uc = -1;
*dst = -1;
length = git__utf8_charlen(str, str_len);
if (length < 0)
return -1;
switch (length) {
case 1:
uc = str[0];
break;
case 2:
uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
if (uc < 0x80) uc = -1;
break;
case 3:
uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
+ (str[2] & 0x3F);
if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
(uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
break;
case 4:
uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
+ ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
if (uc < 0x10000 || uc >= 0x110000) uc = -1;
break;
}
if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE))
return -1;
*dst = uc;
return length;
}
...@@ -106,6 +106,7 @@ GIT_INLINE(void) git__free(void *ptr) ...@@ -106,6 +106,7 @@ GIT_INLINE(void) git__free(void *ptr)
extern int git__prefixcmp(const char *str, const char *prefix); extern int git__prefixcmp(const char *str, const char *prefix);
extern int git__prefixcmp_icase(const char *str, const char *prefix); extern int git__prefixcmp_icase(const char *str, const char *prefix);
extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix); extern int git__suffixcmp(const char *str, const char *suffix);
GIT_INLINE(int) git__signum(int val) GIT_INLINE(int) git__signum(int val)
...@@ -367,6 +368,17 @@ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date); ...@@ -367,6 +368,17 @@ extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date);
extern size_t git__unescape(char *str); extern size_t git__unescape(char *str);
/* /*
* Iterate through an UTF-8 string, yielding one
* codepoint at a time.
*
* @param str current position in the string
* @param str_len size left in the string; -1 if the string is NULL-terminated
* @param dst pointer where to store the current codepoint
* @return length in bytes of the read codepoint; -1 if the codepoint was invalid
*/
extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst);
/*
* Safely zero-out memory, making sure that the compiler * Safely zero-out memory, making sure that the compiler
* doesn't optimize away the operation. * doesn't optimize away the operation.
*/ */
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "path_w32.h"
#include "utf-conv.h" #include "utf-conv.h"
#include "path.h" #include "path.h"
#include "findfile.h" #include "findfile.h"
......
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include "common.h"
#include "path.h"
#include "path_w32.h"
#include "utf-conv.h"
#define PATH__NT_NAMESPACE L"\\\\?\\"
#define PATH__NT_NAMESPACE_LEN 4
#define PATH__ABSOLUTE_LEN 3
#define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
#define path__is_absolute(p) \
(git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
#define path__is_nt_namespace(p) \
(((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
#define path__is_unc(p) \
(((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
GIT_INLINE(int) path__cwd(wchar_t *path, int size)
{
int len;
if ((len = GetCurrentDirectoryW(size, path)) == 0) {
errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT;
return -1;
} else if (len > size) {
errno = ENAMETOOLONG;
return -1;
}
/* The Win32 APIs may return "\\?\" once you've used it first.
* But it may not. What a gloriously predictible API!
*/
if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN))
return len;
len -= PATH__NT_NAMESPACE_LEN;
memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len);
return len;
}
static wchar_t *path__skip_server(wchar_t *path)
{
wchar_t *c;
for (c = path; *c; c++) {
if (path__is_dirsep(*c))
return c + 1;
}
return c;
}
static wchar_t *path__skip_prefix(wchar_t *path)
{
if (path__is_nt_namespace(path)) {
path += PATH__NT_NAMESPACE_LEN;
if (wcsncmp(path, L"UNC\\", 4) == 0)
path = path__skip_server(path + 4);
else if (path__is_absolute(path))
path += PATH__ABSOLUTE_LEN;
} else if (path__is_absolute(path)) {
path += PATH__ABSOLUTE_LEN;
} else if (path__is_unc(path)) {
path = path__skip_server(path + 2);
}
return path;
}
int git_win32_path_canonicalize(git_win32_path path)
{
wchar_t *base, *from, *to, *next;
size_t len;
base = to = path__skip_prefix(path);
/* Unposixify if the prefix */
for (from = path; from < to; from++) {
if (*from == L'/')
*from = L'\\';
}
while (*from) {
for (next = from; *next; ++next) {
if (*next == L'/') {
*next = L'\\';
break;
}
if (*next == L'\\')
break;
}
len = next - from;
if (len == 1 && from[0] == L'.')
/* do nothing with singleton dot */;
else if (len == 2 && from[0] == L'.' && from[1] == L'.') {
if (to == base) {
/* no more path segments to strip, eat the "../" */
if (*next == L'\\')
len++;
base = to;
} else {
/* back up a path segment */
while (to > base && to[-1] == L'\\') to--;
while (to > base && to[-1] != L'\\') to--;
}
} else {
if (*next == L'\\' && *from != L'\\')
len++;
if (to != from)
memmove(to, from, sizeof(wchar_t) * len);
to += len;
}
from += len;
while (*from == L'\\') from++;
}
/* Strip trailing backslashes */
while (to > base && to[-1] == L'\\') to--;
*to = L'\0';
return (to - path);
}
int git_win32_path__cwd(wchar_t *out, size_t len)
{
int cwd_len;
if ((cwd_len = path__cwd(out, len)) < 0)
return -1;
/* UNC paths */
if (wcsncmp(L"\\\\", out, 2) == 0) {
/* Our buffer must be at least 5 characters larger than the
* current working directory: we swallow one of the leading
* '\'s, but we we add a 'UNC' specifier to the path, plus
* a trailing directory separator, plus a NUL.
*/
if (cwd_len > MAX_PATH - 4) {
errno = ENAMETOOLONG;
return -1;
}
memmove(out+2, out, sizeof(wchar_t) * cwd_len);
out[0] = L'U';
out[1] = L'N';
out[2] = L'C';
cwd_len += 2;
}
/* Our buffer must be at least 2 characters larger than the current
* working directory. (One character for the directory separator,
* one for the null.
*/
else if (cwd_len > MAX_PATH - 2) {
errno = ENAMETOOLONG;
return -1;
}
return cwd_len;
}
int git_win32_path_from_utf8(git_win32_path out, const char *src)
{
wchar_t *dest = out;
/* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
dest += PATH__NT_NAMESPACE_LEN;
/* See if this is an absolute path (beginning with a drive letter) */
if (path__is_absolute(src)) {
if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
return -1;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
return -1;
}
/* UNC paths */
else if (path__is_unc(src)) {
memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4);
dest += 4;
/* Skip the leading "\\" */
if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
return -1;
}
/* Absolute paths omitting the drive letter */
else if (src[0] == '\\' || src[0] == '/') {
if (path__cwd(dest, MAX_PATH) < 0)
return -1;
if (!path__is_absolute(dest)) {
errno = ENOENT;
return -1;
}
/* Skip the drive letter specification ("C:") */
if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
return -1;
}
/* Relative paths */
else {
int cwd_len;
if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
return -1;
dest[cwd_len++] = L'\\';
if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
return -1;
}
return git_win32_path_canonicalize(out);
}
int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
{
char *out = dest;
int len;
/* Strip NT namespacing "\\?\" */
if (path__is_nt_namespace(src)) {
src += 4;
/* "\\?\UNC\server\share" -> "\\server\share" */
if (wcsncmp(src, L"UNC\\", 4) == 0) {
src += 4;
memcpy(dest, "\\\\", 2);
out = dest + 2;
}
}
if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0)
return len;
git_path_mkposix(dest);
return len;
}
char *git_win32_path_8dot3_name(const char *path)
{
git_win32_path longpath, shortpath;
wchar_t *start;
char *shortname;
int len, namelen = 1;
if (git_win32_path_from_utf8(longpath, path) < 0)
return NULL;
len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
while (len && shortpath[len-1] == L'\\')
shortpath[--len] = L'\0';
if (len == 0 || len >= GIT_WIN_PATH_UTF16)
return NULL;
for (start = shortpath + (len - 1);
start > shortpath && *(start-1) != '/' && *(start-1) != '\\';
start--)
namelen++;
/* We may not have actually been given a short name. But if we have,
* it will be in the ASCII byte range, so we don't need to worry about
* multi-byte sequences and can allocate naively.
*/
if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
return NULL;
if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0)
return NULL;
return shortname;
}
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_path_w32_h__
#define INCLUDE_git_path_w32_h__
/*
* Provides a large enough buffer to support Windows paths: MAX_PATH is
* 260, corresponding to a maximum path length of 259 characters plus a
* NULL terminator. Prefixing with "\\?\" adds 4 characters, but if the
* original was a UNC path, then we turn "\\server\share" into
* "\\?\UNC\server\share". So we replace the first two characters with
* 8 characters, a net gain of 6, so the maximum length is MAX_PATH+6.
*/
#define GIT_WIN_PATH_UTF16 MAX_PATH+6
/* Maximum size of a UTF-8 Win32 path. We remove the "\\?\" or "\\?\UNC\"
* prefixes for presentation, bringing us back to 259 (non-NULL)
* characters. UTF-8 does have 4-byte sequences, but they are encoded in
* UTF-16 using surrogate pairs, which takes up the space of two characters.
* Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8
* (6 bytes) than one surrogate pair (4 bytes).
*/
#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
/*
* The length of a Windows "shortname", for 8.3 compatibility.
*/
#define GIT_WIN_PATH_SHORTNAME 13
/* Win32 path types */
typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
/**
* Create a Win32 path (in UCS-2 format) from a UTF-8 string.
*
* @param dest The buffer to receive the wide string.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
*/
extern int git_win32_path_from_utf8(git_win32_path dest, const char *src);
/**
* Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the
* Win32 APIs: remove multiple directory separators, squashing to a single one,
* strip trailing directory separators, ensure directory separators are all
* canonical (always backslashes, never forward slashes) and process any
* directory entries of '.' or '..'.
*
* This processes the buffer in place.
*
* @param path The buffer to process
* @return The new length of the buffer, in wchar_t's (not counting the NULL terminator)
*/
extern int git_win32_path_canonicalize(git_win32_path path);
/**
* Create an internal format (posix-style) UTF-8 path from a Win32 UCS-2 path.
*
* @param dest The buffer to receive the UTF-8 string.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
*/
extern int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src);
/**
* Get the short name for the terminal path component in the given path.
* For example, given "C:\Foo\Bar\Asdf.txt", this will return the short name
* for the file "Asdf.txt".
*
* @param path The given path in UTF-8
* @return The name of the shortname for the given path
*/
extern char *git_win32_path_8dot3_name(const char *path);
#endif
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "common.h" #include "common.h"
#include "../posix.h" #include "../posix.h"
#include "path_w32.h"
#include "utf-conv.h" #include "utf-conv.h"
#include "dir.h" #include "dir.h"
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "../posix.h" #include "../posix.h"
#include "../fileops.h" #include "../fileops.h"
#include "path.h" #include "path.h"
#include "path_w32.h"
#include "utf-conv.h" #include "utf-conv.h"
#include "repository.h" #include "repository.h"
#include "reparse.h" #include "reparse.h"
...@@ -35,22 +36,6 @@ ...@@ -35,22 +36,6 @@
/* GetFinalPathNameByHandleW signature */ /* GetFinalPathNameByHandleW signature */
typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD);
/* Helper function which converts UTF-8 paths to UTF-16.
* On failure, errno is set. */
static int utf8_to_16_with_errno(git_win32_path dest, const char *src)
{
int len = git_win32_path_from_utf8(dest, src);
if (len < 0) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
errno = ENAMETOOLONG;
else
errno = EINVAL; /* Bad code point, presumably */
}
return len;
}
int p_ftruncate(int fd, long size) int p_ftruncate(int fd, long size)
{ {
#if defined(_MSC_VER) && _MSC_VER >= 1500 #if defined(_MSC_VER) && _MSC_VER >= 1500
...@@ -66,7 +51,7 @@ int p_mkdir(const char *path, mode_t mode) ...@@ -66,7 +51,7 @@ int p_mkdir(const char *path, mode_t mode)
GIT_UNUSED(mode); GIT_UNUSED(mode);
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
return _wmkdir(buf); return _wmkdir(buf);
...@@ -85,7 +70,7 @@ int p_unlink(const char *path) ...@@ -85,7 +70,7 @@ int p_unlink(const char *path)
git_win32_path buf; git_win32_path buf;
int error; int error;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
error = _wunlink(buf); error = _wunlink(buf);
...@@ -292,7 +277,7 @@ static int do_lstat(const char *path, struct stat *buf, bool posixly_correct) ...@@ -292,7 +277,7 @@ static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
git_win32_path path_w; git_win32_path path_w;
int len; int len;
if ((len = utf8_to_16_with_errno(path_w, path)) < 0) if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
return -1; return -1;
git_win32__path_trim_end(path_w, len); git_win32__path_trim_end(path_w, len);
...@@ -323,7 +308,7 @@ int p_readlink(const char *path, char *buf, size_t bufsiz) ...@@ -323,7 +308,7 @@ int p_readlink(const char *path, char *buf, size_t bufsiz)
* could occur in the middle of the encoding of a code point, * could occur in the middle of the encoding of a code point,
* we need to buffer the result on the stack. */ * we need to buffer the result on the stack. */
if (utf8_to_16_with_errno(path_w, path) < 0 || if (git_win32_path_from_utf8(path_w, path) < 0 ||
readlink_w(target_w, path_w) < 0 || readlink_w(target_w, path_w) < 0 ||
(len = git_win32_path_to_utf8(target, target_w)) < 0) (len = git_win32_path_to_utf8(target, target_w)) < 0)
return -1; return -1;
...@@ -347,7 +332,7 @@ int p_open(const char *path, int flags, ...) ...@@ -347,7 +332,7 @@ int p_open(const char *path, int flags, ...)
git_win32_path buf; git_win32_path buf;
mode_t mode = 0; mode_t mode = 0;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
if (flags & O_CREAT) { if (flags & O_CREAT) {
...@@ -365,7 +350,7 @@ int p_creat(const char *path, mode_t mode) ...@@ -365,7 +350,7 @@ int p_creat(const char *path, mode_t mode)
{ {
git_win32_path buf; git_win32_path buf;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode); return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode);
...@@ -463,7 +448,7 @@ int p_stat(const char* path, struct stat* buf) ...@@ -463,7 +448,7 @@ int p_stat(const char* path, struct stat* buf)
git_win32_path path_w; git_win32_path path_w;
int len; int len;
if ((len = utf8_to_16_with_errno(path_w, path)) < 0) if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
return -1; return -1;
git_win32__path_trim_end(path_w, len); git_win32__path_trim_end(path_w, len);
...@@ -483,7 +468,7 @@ int p_chdir(const char* path) ...@@ -483,7 +468,7 @@ int p_chdir(const char* path)
{ {
git_win32_path buf; git_win32_path buf;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
return _wchdir(buf); return _wchdir(buf);
...@@ -493,7 +478,7 @@ int p_chmod(const char* path, mode_t mode) ...@@ -493,7 +478,7 @@ int p_chmod(const char* path, mode_t mode)
{ {
git_win32_path buf; git_win32_path buf;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
return _wchmod(buf, mode); return _wchmod(buf, mode);
...@@ -504,7 +489,7 @@ int p_rmdir(const char* path) ...@@ -504,7 +489,7 @@ int p_rmdir(const char* path)
git_win32_path buf; git_win32_path buf;
int error; int error;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
error = _wrmdir(buf); error = _wrmdir(buf);
...@@ -533,7 +518,7 @@ char *p_realpath(const char *orig_path, char *buffer) ...@@ -533,7 +518,7 @@ char *p_realpath(const char *orig_path, char *buffer)
{ {
git_win32_path orig_path_w, buffer_w; git_win32_path orig_path_w, buffer_w;
if (utf8_to_16_with_errno(orig_path_w, orig_path) < 0) if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
return NULL; return NULL;
/* Note that if the path provided is a relative path, then the current directory /* Note that if the path provided is a relative path, then the current directory
...@@ -554,20 +539,17 @@ char *p_realpath(const char *orig_path, char *buffer) ...@@ -554,20 +539,17 @@ char *p_realpath(const char *orig_path, char *buffer)
return NULL; return NULL;
} }
/* Convert the path to UTF-8. */ if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
if (buffer) { errno = ENOMEM;
/* If the caller provided a buffer, then it is assumed to be GIT_WIN_PATH_UTF8
* characters in size. If it isn't, then we may overflow. */
if (git__utf16_to_8(buffer, GIT_WIN_PATH_UTF8, buffer_w) < 0)
return NULL;
} else {
/* If the caller did not provide a buffer, then we allocate one for the caller
* from the heap. */
if (git__utf16_to_8_alloc(&buffer, buffer_w) < 0)
return NULL; return NULL;
} }
/* Convert backslashes to forward slashes */ /* Convert the path to UTF-8. If the caller provided a buffer, then it
* is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
* then we may overflow. */
if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
return NULL;
git_path_mkposix(buffer); git_path_mkposix(buffer);
return buffer; return buffer;
...@@ -608,6 +590,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...) ...@@ -608,6 +590,7 @@ int p_snprintf(char *buffer, size_t count, const char *format, ...)
return r; return r;
} }
/* TODO: wut? */
int p_mkstemp(char *tmp_path) int p_mkstemp(char *tmp_path)
{ {
#if defined(_MSC_VER) && _MSC_VER >= 1500 #if defined(_MSC_VER) && _MSC_VER >= 1500
...@@ -625,7 +608,7 @@ int p_access(const char* path, mode_t mode) ...@@ -625,7 +608,7 @@ int p_access(const char* path, mode_t mode)
{ {
git_win32_path buf; git_win32_path buf;
if (utf8_to_16_with_errno(buf, path) < 0) if (git_win32_path_from_utf8(buf, path) < 0)
return -1; return -1;
return _waccess(buf, mode); return _waccess(buf, mode);
...@@ -664,8 +647,8 @@ int p_rename(const char *from, const char *to) ...@@ -664,8 +647,8 @@ int p_rename(const char *from, const char *to)
int rename_succeeded; int rename_succeeded;
int error; int error;
if (utf8_to_16_with_errno(wfrom, from) < 0 || if (git_win32_path_from_utf8(wfrom, from) < 0 ||
utf8_to_16_with_errno(wto, to) < 0) git_win32_path_from_utf8(wto, to) < 0)
return -1; return -1;
/* wait up to 50ms if file is locked by another thread or process */ /* wait up to 50ms if file is locked by another thread or process */
......
...@@ -26,6 +26,14 @@ GIT_INLINE(DWORD) get_wc_flags(void) ...@@ -26,6 +26,14 @@ GIT_INLINE(DWORD) get_wc_flags(void)
return flags; return flags;
} }
GIT_INLINE(void) git__set_errno(void)
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
errno = ENAMETOOLONG;
else
errno = EINVAL;
}
/** /**
* Converts a UTF-8 string to wide characters. * Converts a UTF-8 string to wide characters.
* *
...@@ -36,10 +44,15 @@ GIT_INLINE(DWORD) get_wc_flags(void) ...@@ -36,10 +44,15 @@ GIT_INLINE(DWORD) get_wc_flags(void)
*/ */
int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src) int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
{ {
int len;
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */ * length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1; if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0)
git__set_errno();
return len;
} }
/** /**
...@@ -52,10 +65,15 @@ int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src) ...@@ -52,10 +65,15 @@ int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
*/ */
int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src) int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
{ {
int len;
/* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to
* turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's
* length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */ * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */
return WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1; if ((len = WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0)
git__set_errno();
return len;
} }
/** /**
...@@ -76,17 +94,23 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src) ...@@ -76,17 +94,23 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src)
/* Length of -1 indicates NULL termination of the input string */ /* Length of -1 indicates NULL termination of the input string */
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0); utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
if (!utf16_size) if (!utf16_size) {
git__set_errno();
return -1; return -1;
}
*dest = git__malloc(utf16_size * sizeof(wchar_t)); *dest = git__malloc(utf16_size * sizeof(wchar_t));
if (!*dest) if (!*dest) {
errno = ENOMEM;
return -1; return -1;
}
utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size); utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size);
if (!utf16_size) { if (!utf16_size) {
git__set_errno();
git__free(*dest); git__free(*dest);
*dest = NULL; *dest = NULL;
} }
...@@ -116,17 +140,23 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src) ...@@ -116,17 +140,23 @@ int git__utf16_to_8_alloc(char **dest, const wchar_t *src)
/* Length of -1 indicates NULL termination of the input string */ /* Length of -1 indicates NULL termination of the input string */
utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL); utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL);
if (!utf8_size) if (!utf8_size) {
git__set_errno();
return -1; return -1;
}
*dest = git__malloc(utf8_size); *dest = git__malloc(utf8_size);
if (!*dest) if (!*dest) {
errno = ENOMEM;
return -1; return -1;
}
utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL); utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL);
if (!utf8_size) { if (!utf8_size) {
git__set_errno();
git__free(*dest); git__free(*dest);
*dest = NULL; *dest = NULL;
} }
......
...@@ -10,21 +10,6 @@ ...@@ -10,21 +10,6 @@
#include <wchar.h> #include <wchar.h>
#include "common.h" #include "common.h"
/* Equal to the Win32 MAX_PATH constant. The maximum path length is 259
* characters plus a NULL terminator. */
#define GIT_WIN_PATH_UTF16 260
/* Maximum size of a UTF-8 Win32 path. UTF-8 does have 4-byte sequences,
* but they are encoded in UTF-16 using surrogate pairs, which takes up
* the space of two characters. Two characters in the range U+0800 ->
* U+FFFF take up more space in UTF-8 (6 bytes) than one surrogate pair
* (4 bytes). */
#define GIT_WIN_PATH_UTF8 (259 * 3 + 1)
/* Win32 path types */
typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
/** /**
* Converts a UTF-8 string to wide characters. * Converts a UTF-8 string to wide characters.
* *
...@@ -67,28 +52,4 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src); ...@@ -67,28 +52,4 @@ int git__utf8_to_16_alloc(wchar_t **dest, const char *src);
*/ */
int git__utf16_to_8_alloc(char **dest, const wchar_t *src); int git__utf16_to_8_alloc(char **dest, const wchar_t *src);
/**
* Converts a UTF-8 Win32 path to wide characters.
*
* @param dest The buffer to receive the wide string.
* @param src The UTF-8 string to convert.
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
*/
GIT_INLINE(int) git_win32_path_from_utf8(git_win32_path dest, const char *src)
{
return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src);
}
/**
* Converts a wide Win32 path to UTF-8.
*
* @param dest The buffer to receive the UTF-8 string.
* @param src The wide string to convert.
* @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
*/
GIT_INLINE(int) git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
{
return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src);
}
#endif #endif
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#define INCLUDE_w32_util_h__ #define INCLUDE_w32_util_h__
#include "utf-conv.h" #include "utf-conv.h"
#include "path_w32.h"
GIT_INLINE(bool) git_win32__isalpha(wchar_t c) GIT_INLINE(bool) git_win32__isalpha(wchar_t c)
{ {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <stdarg.h> #include <stdarg.h>
#include <wchar.h>
/* required for sandboxing */ /* required for sandboxing */
#include <sys/types.h> #include <sys/types.h>
...@@ -525,6 +526,41 @@ void clar__assert_equal( ...@@ -525,6 +526,41 @@ void clar__assert_equal(
} }
} }
} }
else if (!strcmp("%ls", fmt)) {
const wchar_t *wcs1 = va_arg(args, const wchar_t *);
const wchar_t *wcs2 = va_arg(args, const wchar_t *);
is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2);
if (!is_equal) {
if (wcs1 && wcs2) {
int pos;
for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
wcs1, wcs2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
}
}
}
else if(!strcmp("%.*ls", fmt)) {
const wchar_t *wcs1 = va_arg(args, const wchar_t *);
const wchar_t *wcs2 = va_arg(args, const wchar_t *);
int len = va_arg(args, int);
is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len);
if (!is_equal) {
if (wcs1 && wcs2) {
int pos;
for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
/* find differing byte offset */;
p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
len, wcs1, len, wcs2, pos);
} else {
p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
}
}
}
else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) { else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t); size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
is_equal = (sz1 == sz2); is_equal = (sz1 == sz2);
......
...@@ -74,9 +74,15 @@ void cl_fixture_cleanup(const char *fixture_name); ...@@ -74,9 +74,15 @@ void cl_fixture_cleanup(const char *fixture_name);
#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) #define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) #define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) #define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) #define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) #define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) #define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) #define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
......
...@@ -83,8 +83,8 @@ void test_config_include__depth(void) ...@@ -83,8 +83,8 @@ void test_config_include__depth(void)
cl_git_fail(git_config_open_ondisk(&cfg, "a")); cl_git_fail(git_config_open_ondisk(&cfg, "a"));
unlink("a"); p_unlink("a");
unlink("b"); p_unlink("b");
} }
void test_config_include__missing(void) void test_config_include__missing(void)
......
...@@ -197,19 +197,6 @@ static void do_custom_reparse(const char *path) ...@@ -197,19 +197,6 @@ static void do_custom_reparse(const char *path)
#endif #endif
git_buf *unslashify(git_buf *buf)
{
#ifdef GIT_WIN32
size_t i;
for (i = 0; i < buf->size; i++)
if (buf->ptr[i] == '/')
buf->ptr[i] = '\\';
#endif
return buf;
}
void test_core_link__stat_regular_file(void) void test_core_link__stat_regular_file(void)
{ {
struct stat st; struct stat st;
...@@ -584,7 +571,7 @@ void test_core_link__readlink_symlink(void) ...@@ -584,7 +571,7 @@ void test_core_link__readlink_symlink(void)
buf[len] = 0; buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf); cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_free(&target_path); git_buf_free(&target_path);
} }
...@@ -607,7 +594,7 @@ void test_core_link__readlink_dangling(void) ...@@ -607,7 +594,7 @@ void test_core_link__readlink_dangling(void)
buf[len] = 0; buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(unslashify(&target_path)), buf); cl_assert_equal_s(git_buf_cstr(&target_path), buf);
git_buf_free(&target_path); git_buf_free(&target_path);
} }
...@@ -636,7 +623,7 @@ void test_core_link__readlink_multiple(void) ...@@ -636,7 +623,7 @@ void test_core_link__readlink_multiple(void)
buf[len] = 0; buf[len] = 0;
cl_assert_equal_s(git_buf_cstr(unslashify(&path2)), buf); cl_assert_equal_s(git_buf_cstr(&path2), buf);
git_buf_free(&path1); git_buf_free(&path1);
git_buf_free(&path2); git_buf_free(&path2);
......
...@@ -309,31 +309,137 @@ void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void) ...@@ -309,31 +309,137 @@ void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
git_repository_free(bare_repo); git_repository_free(bare_repo);
} }
static void add_invalid_filename(git_repository *repo, const char *fn)
{
git_index *index;
git_buf path = GIT_BUF_INIT;
cl_git_pass(git_repository_index(&index, repo));
cl_assert(git_index_entrycount(index) == 0);
git_buf_joinpath(&path, "./invalid", fn);
cl_git_mkfile(path.ptr, NULL);
cl_git_fail(git_index_add_bypath(index, fn));
cl_must_pass(p_unlink(path.ptr));
cl_assert(git_index_entrycount(index) == 0);
git_index_free(index);
}
/* Test that writing an invalid filename fails */ /* Test that writing an invalid filename fails */
void test_index_tests__write_invalid_filename(void) void test_index_tests__add_invalid_filename(void)
{ {
git_repository *repo; git_repository *repo;
p_mkdir("invalid", 0700);
cl_git_pass(git_repository_init(&repo, "./invalid", 0));
cl_must_pass(p_mkdir("./invalid/subdir", 0777));
add_invalid_filename(repo, ".git/hello");
add_invalid_filename(repo, ".GIT/hello");
add_invalid_filename(repo, ".GiT/hello");
add_invalid_filename(repo, "./.git/hello");
add_invalid_filename(repo, "./foo");
add_invalid_filename(repo, "./bar");
add_invalid_filename(repo, "subdir/../bar");
git_repository_free(repo);
cl_fixture_cleanup("invalid");
}
static void replace_char(char *str, char in, char out)
{
char *c = str;
while (*c++)
if (*c == in)
*c = out;
}
static void write_invalid_filename(git_repository *repo, const char *fn_orig)
{
git_index *index; git_index *index;
git_oid expected; git_oid expected;
const git_index_entry *entry;
git_buf path = GIT_BUF_INIT;
char *fn;
p_mkdir("read_tree", 0700);
cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_repository_index(&index, repo));
cl_assert(git_index_entrycount(index) == 0); cl_assert(git_index_entrycount(index) == 0);
cl_git_mkfile("./read_tree/.git/hello", NULL); /*
* Sneak a valid path into the index, we'll update it
* to an invalid path when we try to write the index.
*/
fn = git__strdup(fn_orig);
replace_char(fn, '/', '_');
git_buf_joinpath(&path, "./invalid", fn);
cl_git_pass(git_index_add_bypath(index, ".git/hello")); cl_git_mkfile(path.ptr, NULL);
cl_git_pass(git_index_add_bypath(index, fn));
cl_assert(entry = git_index_get_bypath(index, fn, 0));
/* kids, don't try this at home */
replace_char((char *)entry->path, '_', '/');
/* write-tree */ /* write-tree */
cl_git_fail(git_index_write_tree(&expected, index)); cl_git_fail(git_index_write_tree(&expected, index));
p_unlink(path.ptr);
cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
git_index_free(index); git_index_free(index);
git__free(fn);
}
/* Test that writing an invalid filename fails */
void test_index_tests__write_invalid_filename(void)
{
git_repository *repo;
p_mkdir("invalid", 0700);
cl_git_pass(git_repository_init(&repo, "./invalid", 0));
write_invalid_filename(repo, ".git/hello");
write_invalid_filename(repo, ".GIT/hello");
write_invalid_filename(repo, ".GiT/hello");
write_invalid_filename(repo, "./.git/hello");
write_invalid_filename(repo, "./foo");
write_invalid_filename(repo, "./bar");
write_invalid_filename(repo, "foo/../bar");
git_repository_free(repo);
cl_fixture_cleanup("invalid");
}
void test_index_tests__honors_protect_filesystems(void)
{
git_repository *repo;
p_mkdir("invalid", 0700);
cl_git_pass(git_repository_init(&repo, "./invalid", 0));
cl_repo_set_bool(repo, "core.protectHFS", true);
cl_repo_set_bool(repo, "core.protectNTFS", true);
write_invalid_filename(repo, ".git./hello");
write_invalid_filename(repo, ".git\xe2\x80\xad/hello");
write_invalid_filename(repo, "git~1/hello");
write_invalid_filename(repo, ".git\xe2\x81\xaf/hello");
git_repository_free(repo); git_repository_free(repo);
cl_fixture_cleanup("read_tree"); cl_fixture_cleanup("invalid");
} }
void test_index_tests__remove_entry(void) void test_index_tests__remove_entry(void)
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "tree.h" #include "tree.h"
static git_repository *repo;
static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021"; static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e"; static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
void test_object_tree_attributes__initialize(void)
{
repo = cl_git_sandbox_init("deprecated-mode.git");
}
void test_object_tree_attributes__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void) void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
{ {
git_treebuilder *builder; git_treebuilder *builder;
...@@ -11,7 +23,7 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion( ...@@ -11,7 +23,7 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
cl_git_pass(git_oid_fromstr(&oid, blob_oid)); cl_git_pass(git_oid_fromstr(&oid, blob_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777)); cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666)); cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
...@@ -22,7 +34,6 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion( ...@@ -22,7 +34,6 @@ void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(
void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void) void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
{ {
git_repository *repo;
git_oid tid; git_oid tid;
git_tree *tree; git_tree *tree;
const git_tree_entry *entry; const git_tree_entry *entry;
...@@ -38,7 +49,6 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_an ...@@ -38,7 +49,6 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_an
git_tree_entry_filemode(entry)); git_tree_entry_filemode(entry));
git_tree_free(tree); git_tree_free(tree);
git_repository_free(repo);
} }
void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void) void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
...@@ -48,7 +58,7 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void) ...@@ -48,7 +58,7 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
const git_tree_entry *entry; const git_tree_entry *entry;
cl_git_pass(git_oid_fromstr(&bid, blob_oid)); cl_git_pass(git_oid_fromstr(&bid, blob_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, repo, NULL));
cl_git_fail(git_treebuilder_insert( cl_git_fail(git_treebuilder_insert(
&entry, &entry,
...@@ -62,25 +72,22 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void) ...@@ -62,25 +72,22 @@ void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void) void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
{ {
git_repository *repo;
git_treebuilder *builder; git_treebuilder *builder;
git_oid tid, tid2; git_oid tid, tid2;
git_tree *tree; git_tree *tree;
const git_tree_entry *entry; const git_tree_entry *entry;
repo = cl_git_sandbox_init("deprecated-mode.git");
cl_git_pass(git_oid_fromstr(&tid, tree_oid)); cl_git_pass(git_oid_fromstr(&tid, tree_oid));
cl_git_pass(git_tree_lookup(&tree, repo, &tid)); cl_git_pass(git_tree_lookup(&tree, repo, &tid));
cl_git_pass(git_treebuilder_create(&builder, tree)); cl_git_pass(git_treebuilder_create(&builder, repo, tree));
entry = git_treebuilder_get(builder, "old_mode.txt"); entry = git_treebuilder_get(builder, "old_mode.txt");
cl_assert_equal_i( cl_assert_equal_i(
GIT_FILEMODE_BLOB, GIT_FILEMODE_BLOB,
git_tree_entry_filemode(entry)); git_tree_entry_filemode(entry));
cl_git_pass(git_treebuilder_write(&tid2, repo, builder)); cl_git_pass(git_treebuilder_write(&tid2, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
git_tree_free(tree); git_tree_free(tree);
...@@ -91,18 +98,14 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from ...@@ -91,18 +98,14 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from
git_tree_entry_filemode(entry)); git_tree_entry_filemode(entry));
git_tree_free(tree); git_tree_free(tree);
cl_git_sandbox_cleanup();
} }
void test_object_tree_attributes__normalize_600(void) void test_object_tree_attributes__normalize_600(void)
{ {
git_oid id; git_oid id;
git_tree *tree; git_tree *tree;
git_repository *repo;
const git_tree_entry *entry; const git_tree_entry *entry;
repo = cl_git_sandbox_init("deprecated-mode.git");
git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7"); git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7");
cl_git_pass(git_tree_lookup(&tree, repo, &id)); cl_git_pass(git_tree_lookup(&tree, repo, &id));
...@@ -111,5 +114,4 @@ void test_object_tree_attributes__normalize_600(void) ...@@ -111,5 +114,4 @@ void test_object_tree_attributes__normalize_600(void)
cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600); cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
git_tree_free(tree); git_tree_free(tree);
cl_git_sandbox_cleanup();
} }
...@@ -57,11 +57,11 @@ static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *)) ...@@ -57,11 +57,11 @@ static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
{ {
git_treebuilder *builder; git_treebuilder *builder;
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, _repo, NULL));
fn(builder); fn(builder);
cl_git_pass(git_treebuilder_write(out, _repo, builder)); cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
} }
......
...@@ -35,7 +35,7 @@ void test_object_tree_write__from_memory(void) ...@@ -35,7 +35,7 @@ void test_object_tree_write__from_memory(void)
* on REPOSITORY_FOLDER. * on REPOSITORY_FOLDER.
*/ */
cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
cl_git_pass(git_treebuilder_create(&builder, tree)); cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_fail(git_treebuilder_insert(NULL, builder, "", cl_git_fail(git_treebuilder_insert(NULL, builder, "",
&bid, GIT_FILEMODE_BLOB)); &bid, GIT_FILEMODE_BLOB));
...@@ -53,7 +53,7 @@ void test_object_tree_write__from_memory(void) ...@@ -53,7 +53,7 @@ void test_object_tree_write__from_memory(void)
cl_git_pass(git_treebuilder_insert( cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_write(&rid, g_repo, builder)); cl_git_pass(git_treebuilder_write(&rid, builder));
cl_assert(git_oid_cmp(&rid, &id2) == 0); cl_assert(git_oid_cmp(&rid, &id2) == 0);
...@@ -75,18 +75,18 @@ void test_object_tree_write__subtree(void) ...@@ -75,18 +75,18 @@ void test_object_tree_write__subtree(void)
git_oid_fromstr(&bid, blob_oid); git_oid_fromstr(&bid, blob_oid);
/* create subtree */ /* create subtree */
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_git_pass(git_treebuilder_insert( cl_git_pass(git_treebuilder_insert(
NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */
cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder)); cl_git_pass(git_treebuilder_write(&subtree_id, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
/* create parent tree */ /* create parent tree */
cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
cl_git_pass(git_treebuilder_create(&builder, tree)); cl_git_pass(git_treebuilder_create(&builder, g_repo, tree));
cl_git_pass(git_treebuilder_insert( cl_git_pass(git_treebuilder_insert(
NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */ NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */
cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder)); cl_git_pass(git_treebuilder_write(&id_hiearar, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
git_tree_free(tree); git_tree_free(tree);
...@@ -135,14 +135,14 @@ void test_object_tree_write__sorted_subtrees(void) ...@@ -135,14 +135,14 @@ void test_object_tree_write__sorted_subtrees(void)
memset(&blank_oid, 0x0, sizeof(blank_oid)); memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; i < ARRAY_SIZE(entries); ++i) { for (i = 0; i < ARRAY_SIZE(entries); ++i) {
cl_git_pass(git_treebuilder_insert(NULL, cl_git_pass(git_treebuilder_insert(NULL,
builder, entries[i].filename, &blank_oid, entries[i].attr)); builder, entries[i].filename, &blank_oid, entries[i].attr));
} }
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); cl_git_pass(git_treebuilder_write(&tree_oid, builder));
cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
for (i = 0; i < git_tree_entrycount(tree); i++) { for (i = 0; i < git_tree_entrycount(tree); i++) {
...@@ -192,7 +192,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) ...@@ -192,7 +192,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
memset(&blank_oid, 0x0, sizeof(blank_oid)); memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder)); cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
...@@ -229,7 +229,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) ...@@ -229,7 +229,7 @@ void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB)); NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB));
cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder)); cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
...@@ -283,7 +283,7 @@ void test_object_tree_write__filtering(void) ...@@ -283,7 +283,7 @@ void test_object_tree_write__filtering(void)
memset(&blank_oid, 0x0, sizeof(blank_oid)); memset(&blank_oid, 0x0, sizeof(blank_oid));
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (i = 0; _entries[i].filename; ++i) for (i = 0; _entries[i].filename; ++i)
cl_git_pass(git_treebuilder_insert(NULL, cl_git_pass(git_treebuilder_insert(NULL,
...@@ -310,7 +310,7 @@ void test_object_tree_write__filtering(void) ...@@ -310,7 +310,7 @@ void test_object_tree_write__filtering(void)
cl_assert(git_treebuilder_get(builder, "aardvark") == NULL); cl_assert(git_treebuilder_get(builder, "aardvark") == NULL);
cl_assert(git_treebuilder_get(builder, "last") != NULL); cl_assert(git_treebuilder_get(builder, "last") != NULL);
cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); cl_git_pass(git_treebuilder_write(&tree_oid, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
...@@ -346,13 +346,13 @@ void test_object_tree_write__cruel_paths(void) ...@@ -346,13 +346,13 @@ void test_object_tree_write__cruel_paths(void)
git_oid_fromstr(&bid, blob_oid); git_oid_fromstr(&bid, blob_oid);
/* create tree */ /* create tree */
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) { for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert( cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB)); NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB));
count++; count++;
} }
cl_git_pass(git_treebuilder_write(&id, g_repo, builder)); cl_git_pass(git_treebuilder_write(&id, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
/* check data is correct */ /* check data is correct */
...@@ -374,12 +374,12 @@ void test_object_tree_write__cruel_paths(void) ...@@ -374,12 +374,12 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree); git_tree_free(tree);
/* let's try longer paths */ /* let's try longer paths */
cl_git_pass(git_treebuilder_create(&builder, NULL)); cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
for (scan = the_paths; *scan; ++scan) { for (scan = the_paths; *scan; ++scan) {
cl_git_pass(git_treebuilder_insert( cl_git_pass(git_treebuilder_insert(
NULL, builder, *scan, &id, GIT_FILEMODE_TREE)); NULL, builder, *scan, &id, GIT_FILEMODE_TREE));
} }
cl_git_pass(git_treebuilder_write(&subid, g_repo, builder)); cl_git_pass(git_treebuilder_write(&subid, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
/* check data is correct */ /* check data is correct */
...@@ -400,3 +400,43 @@ void test_object_tree_write__cruel_paths(void) ...@@ -400,3 +400,43 @@ void test_object_tree_write__cruel_paths(void)
git_tree_free(tree); git_tree_free(tree);
} }
void test_object_tree_write__protect_filesystems(void)
{
git_treebuilder *builder;
git_oid bid;
/* Ensure that (by default) we can write objects with funny names on
* platforms that are not affected.
*/
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
#ifndef GIT_WIN32
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
#endif
#ifndef __APPLE__
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
#endif
git_treebuilder_free(builder);
/* Now turn on core.protectHFS and core.protectNTFS and validate that these
* paths are rejected.
*/
cl_repo_set_bool(g_repo, "core.protectHFS", true);
cl_repo_set_bool(g_repo, "core.protectNTFS", true);
cl_git_pass(git_treebuilder_create(&builder, g_repo, NULL));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
git_treebuilder_free(builder);
}
#include "clar_libgit2.h"
#include "path.h"
#ifdef GIT_WIN32
#include "win32/path_w32.h"
#endif
void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected)
{
#ifdef GIT_WIN32
git_win32_path path_utf16;
int path_utf16len;
cl_assert((path_utf16len = git_win32_path_from_utf8(path_utf16, utf8_in)) >= 0);
cl_assert_equal_wcs(utf16_expected, path_utf16);
cl_assert_equal_i(wcslen(utf16_expected), path_utf16len);
#else
GIT_UNUSED(utf8_in);
GIT_UNUSED(utf16_expected);
#endif
}
void test_path_win32__utf8_to_utf16(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("C:\\", L"\\\\?\\C:\\");
test_utf8_to_utf16("c:\\", L"\\\\?\\c:\\");
test_utf8_to_utf16("C:/", L"\\\\?\\C:\\");
test_utf8_to_utf16("c:/", L"\\\\?\\c:\\");
#endif
}
void test_path_win32__removes_trailing_slash(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("C:\\Foo\\", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:/Foo/", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:/Foo///", L"\\\\?\\C:\\Foo");
#endif
}
void test_path_win32__squashes_multiple_slashes(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("C:\\\\Foo\\Bar\\\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
test_utf8_to_utf16("C://Foo/Bar///Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
#endif
}
void test_path_win32__unc(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path");
test_utf8_to_utf16("//server/git/style/unc/path", L"\\\\?\\UNC\\server\\git\\style\\unc\\path");
#endif
}
void test_path_win32__honors_max_path(void)
{
#ifdef GIT_WIN32
git_win32_path path_utf16;
test_utf8_to_utf16("C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
L"\\\\?\\C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
test_utf8_to_utf16("\\\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij",
L"\\\\?\\UNC\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij");
cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 260 chars and is sadly too long for windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
cl_check_fail(git_win32_path_from_utf8(path_utf16, "\\\\unc\\paths are also bound by 260 character restrictions\\including the server name portion\\bcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"));
#endif
}
void test_path_win32__dot_and_dotdot(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("C:\\Foo\\..\\Foobar", L"\\\\?\\C:\\Foobar");
test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar", L"\\\\?\\C:\\Foo\\Foobar");
test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar\\..", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:\\Foobar\\..", L"\\\\?\\C:\\");
test_utf8_to_utf16("C:/Foo/Bar/../Foobar", L"\\\\?\\C:\\Foo\\Foobar");
test_utf8_to_utf16("C:/Foo/Bar/../Foobar/../Asdf/", L"\\\\?\\C:\\Foo\\Asdf");
test_utf8_to_utf16("C:/Foo/Bar/../Foobar/..", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("C:/Foo/..", L"\\\\?\\C:\\");
test_utf8_to_utf16("C:\\Foo\\Bar\\.\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
test_utf8_to_utf16("C:\\.\\Foo\\.\\Bar\\.\\Foobar\\.\\", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
test_utf8_to_utf16("C:/Foo/Bar/./Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
test_utf8_to_utf16("C:/Foo/../Bar/./Foobar/../", L"\\\\?\\C:\\Bar");
test_utf8_to_utf16("C:\\Foo\\..\\..\\Bar", L"\\\\?\\C:\\Bar");
#endif
}
void test_path_win32__absolute_from_no_drive_letter(void)
{
#ifdef GIT_WIN32
test_utf8_to_utf16("\\Foo", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
test_utf8_to_utf16("/Foo/Bar", L"\\\\?\\C:\\Foo\\Bar");
#endif
}
void test_path_win32__absolute_from_relative(void)
{
#ifdef GIT_WIN32
char cwd_backup[MAX_PATH];
cl_must_pass(p_getcwd(cwd_backup, MAX_PATH));
cl_must_pass(p_chdir("C:/"));
test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("..\\..\\Foo", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("Foo\\..", L"\\\\?\\C:\\");
test_utf8_to_utf16("Foo\\..\\..", L"\\\\?\\C:\\");
test_utf8_to_utf16("", L"\\\\?\\C:\\");
cl_must_pass(p_chdir("C:/Windows"));
test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Windows\\Foo");
test_utf8_to_utf16("Foo\\Bar", L"\\\\?\\C:\\Windows\\Foo\\Bar");
test_utf8_to_utf16("..\\Foo", L"\\\\?\\C:\\Foo");
test_utf8_to_utf16("Foo\\..\\Bar", L"\\\\?\\C:\\Windows\\Bar");
test_utf8_to_utf16("", L"\\\\?\\C:\\Windows");
cl_must_pass(p_chdir(cwd_backup));
#endif
}
void test_canonicalize(const wchar_t *in, const wchar_t *expected)
{
#ifdef GIT_WIN32
git_win32_path canonical;
cl_assert(wcslen(in) < MAX_PATH);
wcscpy(canonical, in);
cl_must_pass(git_win32_path_canonicalize(canonical));
cl_assert_equal_wcs(expected, canonical);
#else
GIT_UNUSED(in);
GIT_UNUSED(expected);
#endif
}
void test_path_win32__canonicalize(void)
{
#ifdef GIT_WIN32
test_canonicalize(L"C:\\Foo\\Bar", L"C:\\Foo\\Bar");
test_canonicalize(L"C:\\Foo\\", L"C:\\Foo");
test_canonicalize(L"C:\\Foo\\\\", L"C:\\Foo");
test_canonicalize(L"C:\\Foo\\..\\Bar", L"C:\\Bar");
test_canonicalize(L"C:\\Foo\\..\\..\\Bar", L"C:\\Bar");
test_canonicalize(L"C:\\Foo\\..\\..\\..\\..\\", L"C:\\");
test_canonicalize(L"C:/Foo/Bar", L"C:\\Foo\\Bar");
test_canonicalize(L"C:/", L"C:\\");
test_canonicalize(L"Foo\\\\Bar\\\\Asdf\\\\", L"Foo\\Bar\\Asdf");
test_canonicalize(L"Foo\\\\Bar\\\\..\\\\Asdf\\", L"Foo\\Asdf");
test_canonicalize(L"Foo\\\\Bar\\\\.\\\\Asdf\\", L"Foo\\Bar\\Asdf");
test_canonicalize(L"Foo\\\\..\\Bar\\\\.\\\\Asdf\\", L"Bar\\Asdf");
test_canonicalize(L"\\", L"");
test_canonicalize(L"", L"");
test_canonicalize(L"Foo\\..\\..\\..\\..", L"");
test_canonicalize(L"..\\..\\..\\..", L"");
test_canonicalize(L"\\..\\..\\..\\..", L"");
test_canonicalize(L"\\\\?\\C:\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
test_canonicalize(L"\\\\?\\C:\\Foo\\Bar\\", L"\\\\?\\C:\\Foo\\Bar");
test_canonicalize(L"\\\\?\\C:\\\\Foo\\.\\Bar\\\\..\\", L"\\\\?\\C:\\Foo");
test_canonicalize(L"\\\\?\\C:\\\\", L"\\\\?\\C:\\");
test_canonicalize(L"//?/C:/", L"\\\\?\\C:\\");
test_canonicalize(L"//?/C:/../../Foo/", L"\\\\?\\C:\\Foo");
test_canonicalize(L"//?/C:/Foo/../../", L"\\\\?\\C:\\");
test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\?\\UNC\\server\\C$\\folder");
test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\..\\..\\..\\..\\share\\", L"\\\\?\\UNC\\server\\share");
test_canonicalize(L"\\\\server\\share", L"\\\\server\\share");
test_canonicalize(L"\\\\server\\share\\", L"\\\\server\\share");
test_canonicalize(L"\\\\server\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
test_canonicalize(L"\\\\server\\\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
test_canonicalize(L"\\\\server\\share\\..\\foo", L"\\\\server\\foo");
test_canonicalize(L"\\\\server\\..\\..\\share\\.\\foo", L"\\\\server\\share\\foo");
#endif
}
void test_path_win32__8dot3_name(void)
{
#ifdef GIT_WIN32
char *shortname;
/* Some guaranteed short names */
cl_assert_equal_s("PROGRA~1", (shortname = git_win32_path_8dot3_name("C:\\Program Files")));
git__free(shortname);
cl_assert_equal_s("WINDOWS", (shortname = git_win32_path_8dot3_name("C:\\WINDOWS")));
git__free(shortname);
/* Create some predictible short names */
cl_must_pass(p_mkdir(".foo", 0777));
cl_assert_equal_s("FOO~1", (shortname = git_win32_path_8dot3_name(".foo")));
git__free(shortname);
cl_git_write2file("bar~1", "foobar\n", 7, O_RDWR|O_CREAT, 0666);
cl_must_pass(p_mkdir(".bar", 0777));
cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar")));
git__free(shortname);
#endif
}
...@@ -151,13 +151,11 @@ void test_refs_create__propagate_eexists(void) ...@@ -151,13 +151,11 @@ void test_refs_create__propagate_eexists(void)
cl_assert(error == GIT_EEXISTS); cl_assert(error == GIT_EEXISTS);
} }
void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void) static void test_invalid_name(const char *name)
{ {
git_reference *new_reference; git_reference *new_reference;
git_oid id; git_oid id;
const char *name = "refs/heads/inv@{id";
git_oid_fromstr(&id, current_master_tip); git_oid_fromstr(&id, current_master_tip);
cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create( cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create(
...@@ -166,3 +164,47 @@ void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALI ...@@ -166,3 +164,47 @@ void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALI
cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create( cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(
&new_reference, g_repo, name, current_head_target, 0, NULL, NULL)); &new_reference, g_repo, name, current_head_target, 0, NULL, NULL));
} }
void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
{
test_invalid_name("refs/heads/inv@{id");
test_invalid_name("refs/heads/back\\slash");
test_invalid_name("refs/heads/foo ");
test_invalid_name("refs/heads/foo /bar");
test_invalid_name("refs/heads/com1:bar/foo");
test_invalid_name("refs/heads/e:");
test_invalid_name("refs/heads/c:/foo");
test_invalid_name("refs/heads/foo.");
}
static void test_win32_name(const char *name)
{
git_reference *new_reference = NULL;
git_oid id;
int ret;
git_oid_fromstr(&id, current_master_tip);
ret = git_reference_create(&new_reference, g_repo, name, &id, 0, NULL, NULL);
#ifdef GIT_WIN32
cl_assert_equal_i(GIT_EINVALIDSPEC, ret);
#else
cl_git_pass(ret);
#endif
git_reference_free(new_reference);
}
void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void)
{
test_win32_name("refs/heads/foo./bar");
test_win32_name("refs/heads/aux");
test_win32_name("refs/heads/aux.foo/bar");
test_win32_name("refs/heads/com1");
}
...@@ -427,7 +427,7 @@ static void build_test_tree( ...@@ -427,7 +427,7 @@ static void build_test_tree(
git_buf name = GIT_BUF_INIT; git_buf name = GIT_BUF_INIT;
va_list arglist; va_list arglist;
cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */ cl_git_pass(git_treebuilder_create(&builder, repo, NULL)); /* start builder */
va_start(arglist, fmt); va_start(arglist, fmt);
while (*scan) { while (*scan) {
...@@ -451,7 +451,7 @@ static void build_test_tree( ...@@ -451,7 +451,7 @@ static void build_test_tree(
} }
va_end(arglist); va_end(arglist);
cl_git_pass(git_treebuilder_write(out, repo, builder)); cl_git_pass(git_treebuilder_write(out, builder));
git_treebuilder_free(builder); git_treebuilder_free(builder);
git_buf_free(&name); git_buf_free(&name);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment