mirror of
https://github.com/libgit2/libgit2.git
synced 2026-06-22 06:26:26 +00:00
Merge pull request #6739 from libgit2/ethomson/safedirectory
More `safe.directory` improvements
This commit is contained in:
@@ -558,37 +558,39 @@ typedef struct {
|
||||
static int validate_ownership_cb(const git_config_entry *entry, void *payload)
|
||||
{
|
||||
validate_ownership_data *data = payload;
|
||||
const char *test_path;
|
||||
|
||||
if (strcmp(entry->value, "") == 0) {
|
||||
*data->is_safe = false;
|
||||
} else if (strcmp(entry->value, "*") == 0) {
|
||||
*data->is_safe = true;
|
||||
} else {
|
||||
const char *test_path = entry->value;
|
||||
|
||||
if (git_str_sets(&data->tmp, test_path) < 0 ||
|
||||
git_fs_path_to_dir(&data->tmp) < 0)
|
||||
if (git_str_sets(&data->tmp, entry->value) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Ensure that `git_fs_path_to_dir` mutated the
|
||||
* input path by adding a trailing backslash.
|
||||
* A trailing backslash on the input is not allowed.
|
||||
*/
|
||||
if (strcmp(data->tmp.ptr, test_path) == 0)
|
||||
return 0;
|
||||
if (!git_fs_path_is_root(data->tmp.ptr)) {
|
||||
/* Input must not have trailing backslash. */
|
||||
if (!data->tmp.size ||
|
||||
data->tmp.ptr[data->tmp.size - 1] == '/')
|
||||
return 0;
|
||||
|
||||
if (git_fs_path_to_dir(&data->tmp) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
test_path = data->tmp.ptr;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
/*
|
||||
* Git for Windows does some truly bizarre things with
|
||||
* paths that start with a forward slash; and expects you
|
||||
* to escape that with `%(prefix)`. This syntax generally
|
||||
* means to add the prefix that Git was installed to -- eg
|
||||
* `/usr/local` -- unless it's an absolute path, in which
|
||||
* case the leading `%(prefix)/` is just removed. And Git
|
||||
* for Windows expects you to use this syntax for absolute
|
||||
* Unix-style paths (in "Git Bash" or Windows Subsystem for
|
||||
* Linux).
|
||||
* Git - and especially, Git for Windows - does some
|
||||
* truly bizarre things with paths that start with a
|
||||
* forward slash; and expects you to escape that with
|
||||
* `%(prefix)`. This syntax generally means to add the
|
||||
* prefix that Git was installed to (eg `/usr/local`)
|
||||
* unless it's an absolute path, in which case the
|
||||
* leading `%(prefix)/` is just removed. And Git for
|
||||
* Windows expects you to use this syntax for absolute
|
||||
* Unix-style paths (in "Git Bash" or Windows Subsystem
|
||||
* for Linux).
|
||||
*
|
||||
* Worse, the behavior used to be that a leading `/` was
|
||||
* not absolute. It would indicate that Git for Windows
|
||||
@@ -603,12 +605,8 @@ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
|
||||
*/
|
||||
if (strncmp(test_path, "%(prefix)//", strlen("%(prefix)//")) == 0)
|
||||
test_path += strlen("%(prefix)/");
|
||||
else if (strncmp(test_path, "//", 2) == 0 &&
|
||||
strncmp(test_path, "//wsl.localhost/", strlen("//wsl.localhost/")) != 0)
|
||||
test_path++;
|
||||
#endif
|
||||
|
||||
if (strcmp(data->tmp.ptr, data->repo_path) == 0)
|
||||
if (strcmp(test_path, data->repo_path) == 0)
|
||||
*data->is_safe = true;
|
||||
}
|
||||
|
||||
@@ -705,9 +703,12 @@ static int validate_ownership(git_repository *repo)
|
||||
goto done;
|
||||
|
||||
if (!is_safe) {
|
||||
size_t path_len = git_fs_path_is_root(path) ?
|
||||
strlen(path) : git_fs_path_dirlen(path);
|
||||
|
||||
git_error_set(GIT_ERROR_CONFIG,
|
||||
"repository path '%s' is not owned by current user",
|
||||
path);
|
||||
"repository path '%.*s' is not owned by current user",
|
||||
(int)min(path_len, INT_MAX), path);
|
||||
error = GIT_EOWNER;
|
||||
}
|
||||
|
||||
|
||||
@@ -419,6 +419,16 @@ int git_fs_path_to_dir(git_str *path)
|
||||
return git_str_oom(path) ? -1 : 0;
|
||||
}
|
||||
|
||||
size_t git_fs_path_dirlen(const char *path)
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
|
||||
while (len > 1 && path[len - 1] == '/')
|
||||
len--;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void git_fs_path_string_to_dir(char *path, size_t size)
|
||||
{
|
||||
size_t end = strlen(path);
|
||||
|
||||
@@ -86,6 +86,29 @@ extern int git_fs_path_to_dir(git_str *path);
|
||||
*/
|
||||
extern void git_fs_path_string_to_dir(char *path, size_t size);
|
||||
|
||||
/**
|
||||
* Provides the length of the given path string with no trailing
|
||||
* slashes.
|
||||
*/
|
||||
size_t git_fs_path_dirlen(const char *path);
|
||||
|
||||
/**
|
||||
* Returns nonzero if the given path is a filesystem root; on Windows, this
|
||||
* means a drive letter (eg `A:/`, `C:\`). On POSIX this is `/`.
|
||||
*/
|
||||
GIT_INLINE(int) git_fs_path_is_root(const char *name)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
if (((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z')) &&
|
||||
name[1] == ':' &&
|
||||
(name[2] == '/' || name[2] == '\\') &&
|
||||
name[3] == '\0')
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
return (name[0] == '/' && name[1] == '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Taken from git.git; returns nonzero if the given path is "." or "..".
|
||||
*/
|
||||
|
||||
@@ -787,13 +787,19 @@ int p_rmdir(const char *path)
|
||||
char *p_realpath(const char *orig_path, char *buffer)
|
||||
{
|
||||
git_win32_path orig_path_w, buffer_w;
|
||||
DWORD long_len;
|
||||
|
||||
if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
|
||||
return NULL;
|
||||
|
||||
/* Note that if the path provided is a relative path, then the current directory
|
||||
/*
|
||||
* POSIX realpath performs two functions: first, it turns relative
|
||||
* paths into absolute paths. For this, we need GetFullPathName.
|
||||
*
|
||||
* Note that if the path provided is a relative path, then the current directory
|
||||
* is used to resolve the path -- which is a concurrency issue because the current
|
||||
* directory is a process-wide variable. */
|
||||
* directory is a process-wide variable.
|
||||
*/
|
||||
if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||
errno = ENAMETOOLONG;
|
||||
@@ -803,9 +809,26 @@ char *p_realpath(const char *orig_path, char *buffer)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The path must exist. */
|
||||
if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
|
||||
errno = ENOENT;
|
||||
/*
|
||||
* Then, the path is canonicalized. eg, on macOS,
|
||||
* "/TMP" -> "/private/tmp". For this, we need GetLongPathName.
|
||||
*/
|
||||
if ((long_len = GetLongPathNameW(buffer_w, buffer_w, GIT_WIN_PATH_UTF16)) == 0) {
|
||||
DWORD error = GetLastError();
|
||||
|
||||
if (error == ERROR_FILE_NOT_FOUND ||
|
||||
error == ERROR_PATH_NOT_FOUND)
|
||||
errno = ENOENT;
|
||||
else if (error == ERROR_ACCESS_DENIED)
|
||||
errno = EPERM;
|
||||
else
|
||||
errno = EINVAL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (long_len > GIT_WIN_PATH_UTF16) {
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -821,7 +844,6 @@ char *p_realpath(const char *orig_path, char *buffer)
|
||||
return NULL;
|
||||
|
||||
git_fs_path_mkposix(buffer);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
|
||||
static char _clar_path[4096 + 1];
|
||||
#define CLAR_PATH_MAX 4096
|
||||
static char _clar_path[CLAR_PATH_MAX];
|
||||
|
||||
static int
|
||||
is_valid_tmp_path(const char *path)
|
||||
@@ -35,10 +36,9 @@ find_tmp_path(char *buffer, size_t length)
|
||||
continue;
|
||||
|
||||
if (is_valid_tmp_path(env)) {
|
||||
#ifdef __APPLE__
|
||||
if (length >= PATH_MAX && realpath(env, buffer) != NULL)
|
||||
return 0;
|
||||
#endif
|
||||
if (strlen(env) + 1 > CLAR_PATH_MAX)
|
||||
return -1;
|
||||
|
||||
strncpy(buffer, env, length - 1);
|
||||
buffer[length - 1] = '\0';
|
||||
return 0;
|
||||
@@ -47,10 +47,6 @@ find_tmp_path(char *buffer, size_t length)
|
||||
|
||||
/* If the environment doesn't say anything, try to use /tmp */
|
||||
if (is_valid_tmp_path("/tmp")) {
|
||||
#ifdef __APPLE__
|
||||
if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
|
||||
return 0;
|
||||
#endif
|
||||
strncpy(buffer, "/tmp", length - 1);
|
||||
buffer[length - 1] = '\0';
|
||||
return 0;
|
||||
@@ -75,6 +71,34 @@ find_tmp_path(char *buffer, size_t length)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int canonicalize_tmp_path(char *buffer)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char tmp[CLAR_PATH_MAX];
|
||||
DWORD ret;
|
||||
|
||||
ret = GetFullPathName(buffer, CLAR_PATH_MAX, tmp, NULL);
|
||||
|
||||
if (ret == 0 || ret > CLAR_PATH_MAX)
|
||||
return -1;
|
||||
|
||||
ret = GetLongPathName(tmp, buffer, CLAR_PATH_MAX);
|
||||
|
||||
if (ret == 0 || ret > CLAR_PATH_MAX)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
char tmp[CLAR_PATH_MAX];
|
||||
|
||||
if (realpath(buffer, tmp) == NULL)
|
||||
return -1;
|
||||
|
||||
strcpy(buffer, tmp);
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void clar_unsandbox(void)
|
||||
{
|
||||
if (_clar_path[0] == '\0')
|
||||
@@ -95,7 +119,8 @@ static int build_sandbox_path(void)
|
||||
|
||||
size_t len;
|
||||
|
||||
if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
|
||||
if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0 ||
|
||||
canonicalize_tmp_path(_clar_path) < 0)
|
||||
return -1;
|
||||
|
||||
len = strlen(_clar_path);
|
||||
|
||||
@@ -533,15 +533,21 @@ void test_repo_open__validates_bare_repo_ownership(void)
|
||||
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
|
||||
}
|
||||
|
||||
void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
|
||||
static int test_safe_path(const char *path)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_str config_path = GIT_STR_INIT,
|
||||
config_filename = GIT_STR_INIT,
|
||||
config_data = GIT_STR_INIT;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
|
||||
|
||||
/*
|
||||
* Sandbox the fixture, and ensure that when we fake an owner
|
||||
* of "other" that the repository cannot be opened (and fails
|
||||
* with `GIT_EOWNER`).
|
||||
*/
|
||||
cl_fixture_sandbox("empty_standard_repo");
|
||||
cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
|
||||
|
||||
@@ -555,93 +561,35 @@ void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
|
||||
|
||||
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
|
||||
|
||||
/* Test with incorrect exception (slash at the end) */
|
||||
git_str_printf(&config_data,
|
||||
"[foo]\n" \
|
||||
"\tbar = Foobar\n" \
|
||||
"\tbaz = Baz!\n" \
|
||||
"[safe]\n" \
|
||||
"\tdirectory = /non/existent/path\n" \
|
||||
"\tdirectory = /\n" \
|
||||
"\tdirectory = c:\\\\temp\n" \
|
||||
"\tdirectory = %s/%s/\n" \
|
||||
"\tdirectory = /tmp\n" \
|
||||
"[bar]\n" \
|
||||
"\tfoo = barfoo\n",
|
||||
clar_sandbox_path(), "empty_standard_repo");
|
||||
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
|
||||
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
|
||||
|
||||
/* Test with correct exception */
|
||||
git_str_clear(&config_data);
|
||||
git_str_printf(&config_data,
|
||||
"[foo]\n" \
|
||||
"\tbar = Foobar\n" \
|
||||
"\tbaz = Baz!\n" \
|
||||
"[safe]\n" \
|
||||
"\tdirectory = /non/existent/path\n" \
|
||||
"\tdirectory = /\n" \
|
||||
"\tdirectory = c:\\\\temp\n" \
|
||||
"\tdirectory = %s/%s\n" \
|
||||
"\tdirectory = /tmp\n" \
|
||||
"\tdirectory = %s\n" \
|
||||
"[bar]\n" \
|
||||
"\tfoo = barfoo\n",
|
||||
clar_sandbox_path(), "empty_standard_repo");
|
||||
path);
|
||||
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
|
||||
error = git_repository_open(&repo, "empty_standard_repo");
|
||||
git_repository_free(repo);
|
||||
|
||||
git_str_dispose(&config_path);
|
||||
git_str_dispose(&config_filename);
|
||||
git_str_dispose(&config_data);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
|
||||
|
||||
cl_fixture_sandbox("empty_standard_repo");
|
||||
cl_git_pass(cl_rename(
|
||||
"empty_standard_repo/.gitted", "empty_standard_repo/.git"));
|
||||
|
||||
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
|
||||
cl_git_fail_with(
|
||||
GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
|
||||
|
||||
/* Add safe.directory options to the global configuration */
|
||||
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
|
||||
cl_must_pass(p_mkdir(config_path.ptr, 0777));
|
||||
git_libgit2_opts(
|
||||
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
|
||||
config_path.ptr);
|
||||
|
||||
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
|
||||
|
||||
cl_git_rewritefile(config_filename.ptr, "[foo]\n"
|
||||
"\tbar = Foobar\n"
|
||||
"\tbaz = Baz!\n"
|
||||
"[safe]\n"
|
||||
"\tdirectory = *\n"
|
||||
"[bar]\n"
|
||||
"\tfoo = barfoo\n");
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
|
||||
git_repository_free(repo);
|
||||
|
||||
git_str_dispose(&config_path);
|
||||
git_str_dispose(&config_filename);
|
||||
}
|
||||
|
||||
void test_repo_open__can_allowlist_bare_gitdir(void)
|
||||
static int test_bare_safe_path(const char *path)
|
||||
{
|
||||
git_repository *repo;
|
||||
git_str config_path = GIT_STR_INIT,
|
||||
config_filename = GIT_STR_INIT,
|
||||
config_data = GIT_STR_INIT;
|
||||
int error;
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
|
||||
|
||||
@@ -665,56 +613,203 @@ void test_repo_open__can_allowlist_bare_gitdir(void)
|
||||
"\tdirectory = /non/existent/path\n" \
|
||||
"\tdirectory = /\n" \
|
||||
"\tdirectory = c:\\\\temp\n" \
|
||||
"\tdirectory = %s/%s\n" \
|
||||
"\tdirectory = %s\n" \
|
||||
"\tdirectory = /tmp\n" \
|
||||
"[bar]\n" \
|
||||
"\tfoo = barfoo\n",
|
||||
clar_sandbox_path(), "testrepo.git");
|
||||
path);
|
||||
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
||||
error = git_repository_open(&repo, "testrepo.git");
|
||||
git_repository_free(repo);
|
||||
|
||||
git_str_dispose(&config_path);
|
||||
git_str_dispose(&config_filename);
|
||||
git_str_dispose(&config_data);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_printf(&path, "%s/%s",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
cl_git_pass(test_safe_path(path.ptr));
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
void test_repo_open__safe_directory_fails_with_trailing_slash(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
|
||||
/*
|
||||
* "/tmp/foo/" is not permitted; safe path must be specified
|
||||
* as "/tmp/foo"
|
||||
*/
|
||||
cl_git_pass(git_str_printf(&path, "%s/%s/",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
|
||||
{
|
||||
cl_git_pass(test_safe_path("*"));
|
||||
}
|
||||
|
||||
void test_repo_open__can_allowlist_bare_gitdir(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_printf(&path, "%s/%s",
|
||||
clar_sandbox_path(), "testrepo.git"));
|
||||
cl_git_pass(test_bare_safe_path(path.ptr));
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
|
||||
{
|
||||
cl_git_pass(test_bare_safe_path("*"));
|
||||
}
|
||||
|
||||
void test_repo_open__can_handle_prefixed_safe_paths(void)
|
||||
{
|
||||
#ifndef GIT_WIN32
|
||||
git_str path = GIT_STR_INIT;
|
||||
|
||||
/*
|
||||
* Using "%(prefix)/" becomes "%(prefix)//tmp/foo" - so
|
||||
* "%(prefix)/" is stripped and means the literal path
|
||||
* follows.
|
||||
*/
|
||||
cl_git_pass(git_str_printf(&path, "%%(prefix)/%s/%s",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
cl_git_pass(test_safe_path(path.ptr));
|
||||
git_str_dispose(&path);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_repo_open__prefixed_safe_paths_must_have_two_slashes(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
|
||||
/*
|
||||
* Using "%(prefix)" becomes "%(prefix)/tmp/foo" - so it's
|
||||
* actually trying to look in the git prefix, for example,
|
||||
* "/usr/local/tmp/foo", which we don't actually support.
|
||||
*/
|
||||
cl_git_pass(git_str_printf(&path, "%%(prefix)%s/%s",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
cl_git_fail_with(GIT_EOWNER, test_safe_path(path.ptr));
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
void test_repo_open__can_handle_win32_prefixed_safe_paths(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_repository *repo;
|
||||
git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
|
||||
git_str unc_path = GIT_STR_INIT,
|
||||
config_path = GIT_STR_INIT,
|
||||
config_filename = GIT_STR_INIT,
|
||||
config_data = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
|
||||
|
||||
cl_fixture_sandbox("testrepo.git");
|
||||
cl_fixture_sandbox("empty_standard_repo");
|
||||
cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
|
||||
|
||||
/*
|
||||
* On Windows, we can generally map a local drive to a UNC path;
|
||||
* for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
|
||||
*/
|
||||
cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
|
||||
if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
|
||||
cl_skip();
|
||||
|
||||
unc_path.ptr[13] = '$';
|
||||
|
||||
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
|
||||
cl_git_fail_with(
|
||||
GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
|
||||
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
|
||||
|
||||
/* Add safe.directory options to the global configuration */
|
||||
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
|
||||
cl_must_pass(p_mkdir(config_path.ptr, 0777));
|
||||
git_libgit2_opts(
|
||||
GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
|
||||
config_path.ptr);
|
||||
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
|
||||
|
||||
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
|
||||
|
||||
cl_git_rewritefile(config_filename.ptr, "[foo]\n"
|
||||
"\tbar = Foobar\n"
|
||||
"\tbaz = Baz!\n"
|
||||
"[safe]\n"
|
||||
"\tdirectory = *\n"
|
||||
"[bar]\n"
|
||||
"\tfoo = barfoo\n");
|
||||
/* The blank resets our sandbox directory and opening fails */
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, "testrepo.git"));
|
||||
git_str_printf(&config_data,
|
||||
"[safe]\n\tdirectory = %%(prefix)/%s\n",
|
||||
unc_path.ptr);
|
||||
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, unc_path.ptr));
|
||||
git_repository_free(repo);
|
||||
|
||||
git_str_dispose(&config_path);
|
||||
git_str_dispose(&config_filename);
|
||||
git_str_dispose(&config_data);
|
||||
git_str_dispose(&unc_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_repo_open__can_handle_win32_unc_safe_paths(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_repository *repo;
|
||||
git_str unc_path = GIT_STR_INIT,
|
||||
config_path = GIT_STR_INIT,
|
||||
config_filename = GIT_STR_INIT,
|
||||
config_data = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
|
||||
|
||||
cl_fixture_sandbox("empty_standard_repo");
|
||||
cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
|
||||
|
||||
/*
|
||||
* On Windows, we can generally map a local drive to a UNC path;
|
||||
* for example C:\Foo\Bar becomes //localhost/C$/Foo/bar
|
||||
*/
|
||||
cl_git_pass(git_str_printf(&unc_path, "//localhost/%s/%s",
|
||||
clar_sandbox_path(), "empty_standard_repo"));
|
||||
|
||||
if (unc_path.ptr[13] != ':' || unc_path.ptr[14] != '/')
|
||||
cl_skip();
|
||||
|
||||
unc_path.ptr[13] = '$';
|
||||
|
||||
git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
|
||||
cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, unc_path.ptr));
|
||||
|
||||
/* Add safe.directory options to the global configuration */
|
||||
git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
|
||||
cl_must_pass(p_mkdir(config_path.ptr, 0777));
|
||||
git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
|
||||
|
||||
git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
|
||||
|
||||
/* The blank resets our sandbox directory and opening fails */
|
||||
|
||||
git_str_printf(&config_data,
|
||||
"[safe]\n\tdirectory = %s\n",
|
||||
unc_path.ptr);
|
||||
cl_git_rewritefile(config_filename.ptr, config_data.ptr);
|
||||
|
||||
cl_git_pass(git_repository_open(&repo, unc_path.ptr));
|
||||
git_repository_free(repo);
|
||||
|
||||
git_str_dispose(&config_path);
|
||||
git_str_dispose(&config_filename);
|
||||
git_str_dispose(&config_data);
|
||||
git_str_dispose(&unc_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_repo_open__can_reset_safe_directory_list(void)
|
||||
|
||||
@@ -1,768 +0,0 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "futils.h"
|
||||
#include "fs_path.h"
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
static char *path_save;
|
||||
|
||||
void test_path__initialize(void)
|
||||
{
|
||||
path_save = cl_getenv("PATH");
|
||||
}
|
||||
|
||||
void test_path__cleanup(void)
|
||||
{
|
||||
cl_setenv("PATH", path_save);
|
||||
git__free(path_save);
|
||||
path_save = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
check_dirname(const char *A, const char *B)
|
||||
{
|
||||
git_str dir = GIT_STR_INIT;
|
||||
char *dir2;
|
||||
|
||||
cl_assert(git_fs_path_dirname_r(&dir, A) >= 0);
|
||||
cl_assert_equal_s(B, dir.ptr);
|
||||
git_str_dispose(&dir);
|
||||
|
||||
cl_assert((dir2 = git_fs_path_dirname(A)) != NULL);
|
||||
cl_assert_equal_s(B, dir2);
|
||||
git__free(dir2);
|
||||
}
|
||||
|
||||
static void
|
||||
check_basename(const char *A, const char *B)
|
||||
{
|
||||
git_str base = GIT_STR_INIT;
|
||||
char *base2;
|
||||
|
||||
cl_assert(git_fs_path_basename_r(&base, A) >= 0);
|
||||
cl_assert_equal_s(B, base.ptr);
|
||||
git_str_dispose(&base);
|
||||
|
||||
cl_assert((base2 = git_fs_path_basename(A)) != NULL);
|
||||
cl_assert_equal_s(B, base2);
|
||||
git__free(base2);
|
||||
}
|
||||
|
||||
static void
|
||||
check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
|
||||
{
|
||||
git_str joined_path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b));
|
||||
cl_assert_equal_s(expected_path, joined_path.ptr);
|
||||
|
||||
git_str_dispose(&joined_path);
|
||||
}
|
||||
|
||||
static void
|
||||
check_joinpath_n(
|
||||
const char *path_a,
|
||||
const char *path_b,
|
||||
const char *path_c,
|
||||
const char *path_d,
|
||||
const char *expected_path)
|
||||
{
|
||||
git_str joined_path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_join_n(&joined_path, '/', 4,
|
||||
path_a, path_b, path_c, path_d));
|
||||
cl_assert_equal_s(expected_path, joined_path.ptr);
|
||||
|
||||
git_str_dispose(&joined_path);
|
||||
}
|
||||
|
||||
static void check_setenv(const char* name, const char* value)
|
||||
{
|
||||
char* check;
|
||||
|
||||
cl_git_pass(cl_setenv(name, value));
|
||||
check = cl_getenv(name);
|
||||
|
||||
if (value)
|
||||
cl_assert_equal_s(value, check);
|
||||
else
|
||||
cl_assert(check == NULL);
|
||||
|
||||
git__free(check);
|
||||
}
|
||||
|
||||
/* get the dirname of a path */
|
||||
void test_path__00_dirname(void)
|
||||
{
|
||||
check_dirname(NULL, ".");
|
||||
check_dirname("", ".");
|
||||
check_dirname("a", ".");
|
||||
check_dirname("/", "/");
|
||||
check_dirname("/usr", "/");
|
||||
check_dirname("/usr/", "/");
|
||||
check_dirname("/usr/lib", "/usr");
|
||||
check_dirname("/usr/lib/", "/usr");
|
||||
check_dirname("/usr/lib//", "/usr");
|
||||
check_dirname("usr/lib", "usr");
|
||||
check_dirname("usr/lib/", "usr");
|
||||
check_dirname("usr/lib//", "usr");
|
||||
check_dirname(".git/", ".");
|
||||
|
||||
check_dirname(REP16("/abc"), REP15("/abc"));
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
check_dirname("C:/", "C:/");
|
||||
check_dirname("C:", "C:/");
|
||||
check_dirname("C:/path/", "C:/");
|
||||
check_dirname("C:/path", "C:/");
|
||||
check_dirname("//computername/", "//computername/");
|
||||
check_dirname("//computername", "//computername/");
|
||||
check_dirname("//computername/path/", "//computername/");
|
||||
check_dirname("//computername/path", "//computername/");
|
||||
check_dirname("//computername/sub/path/", "//computername/sub");
|
||||
check_dirname("//computername/sub/path", "//computername/sub");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get the base name of a path */
|
||||
void test_path__01_basename(void)
|
||||
{
|
||||
check_basename(NULL, ".");
|
||||
check_basename("", ".");
|
||||
check_basename("a", "a");
|
||||
check_basename("/", "/");
|
||||
check_basename("/usr", "usr");
|
||||
check_basename("/usr/", "usr");
|
||||
check_basename("/usr/lib", "lib");
|
||||
check_basename("/usr/lib//", "lib");
|
||||
check_basename("usr/lib", "lib");
|
||||
|
||||
check_basename(REP16("/abc"), "abc");
|
||||
check_basename(REP1024("/abc"), "abc");
|
||||
}
|
||||
|
||||
/* properly join path components */
|
||||
void test_path__05_joins(void)
|
||||
{
|
||||
check_joinpath("", "", "");
|
||||
check_joinpath("", "a", "a");
|
||||
check_joinpath("", "/a", "/a");
|
||||
check_joinpath("a", "", "a/");
|
||||
check_joinpath("a", "/", "a/");
|
||||
check_joinpath("a", "b", "a/b");
|
||||
check_joinpath("/", "a", "/a");
|
||||
check_joinpath("/", "", "/");
|
||||
check_joinpath("/a", "/b", "/a/b");
|
||||
check_joinpath("/a", "/b/", "/a/b/");
|
||||
check_joinpath("/a/", "b/", "/a/b/");
|
||||
check_joinpath("/a/", "/b/", "/a/b/");
|
||||
|
||||
check_joinpath("/abcd", "/defg", "/abcd/defg");
|
||||
check_joinpath("/abcd", "/defg/", "/abcd/defg/");
|
||||
check_joinpath("/abcd/", "defg/", "/abcd/defg/");
|
||||
check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
|
||||
|
||||
check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
|
||||
check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
|
||||
check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
|
||||
|
||||
check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
|
||||
check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
|
||||
check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
|
||||
|
||||
check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
|
||||
REP1024("aaaa") "/" REP1024("bbbb"));
|
||||
check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
|
||||
REP1024("/aaaa") REP1024("/bbbb"));
|
||||
}
|
||||
|
||||
/* properly join path components for more than one path */
|
||||
void test_path__06_long_joins(void)
|
||||
{
|
||||
check_joinpath_n("", "", "", "", "");
|
||||
check_joinpath_n("", "a", "", "", "a/");
|
||||
check_joinpath_n("a", "", "", "", "a/");
|
||||
check_joinpath_n("", "", "", "a", "a");
|
||||
check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
|
||||
check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
|
||||
check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
|
||||
check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
|
||||
check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
|
||||
|
||||
check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
|
||||
REP1024("a") "/" REP1024("b") "/"
|
||||
REP1024("c") "/" REP1024("d"));
|
||||
check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
|
||||
REP1024("/a") REP1024("/b")
|
||||
REP1024("/c") REP1024("/d"));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_path_to_dir(
|
||||
const char* path,
|
||||
const char* expected)
|
||||
{
|
||||
git_str tgt = GIT_STR_INIT;
|
||||
|
||||
git_str_sets(&tgt, path);
|
||||
cl_git_pass(git_fs_path_to_dir(&tgt));
|
||||
cl_assert_equal_s(expected, tgt.ptr);
|
||||
|
||||
git_str_dispose(&tgt);
|
||||
}
|
||||
|
||||
static void
|
||||
check_string_to_dir(
|
||||
const char* path,
|
||||
size_t maxlen,
|
||||
const char* expected)
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
char *buf = git__malloc(len + 2);
|
||||
cl_assert(buf);
|
||||
|
||||
strncpy(buf, path, len + 2);
|
||||
|
||||
git_fs_path_string_to_dir(buf, maxlen);
|
||||
|
||||
cl_assert_equal_s(expected, buf);
|
||||
|
||||
git__free(buf);
|
||||
}
|
||||
|
||||
/* convert paths to dirs */
|
||||
void test_path__07_path_to_dir(void)
|
||||
{
|
||||
check_path_to_dir("", "");
|
||||
check_path_to_dir(".", "./");
|
||||
check_path_to_dir("./", "./");
|
||||
check_path_to_dir("a/", "a/");
|
||||
check_path_to_dir("ab", "ab/");
|
||||
/* make sure we try just under and just over an expansion that will
|
||||
* require a realloc
|
||||
*/
|
||||
check_path_to_dir("abcdef", "abcdef/");
|
||||
check_path_to_dir("abcdefg", "abcdefg/");
|
||||
check_path_to_dir("abcdefgh", "abcdefgh/");
|
||||
check_path_to_dir("abcdefghi", "abcdefghi/");
|
||||
check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
|
||||
check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
|
||||
|
||||
check_string_to_dir("", 1, "");
|
||||
check_string_to_dir(".", 1, ".");
|
||||
check_string_to_dir(".", 2, "./");
|
||||
check_string_to_dir(".", 3, "./");
|
||||
check_string_to_dir("abcd", 3, "abcd");
|
||||
check_string_to_dir("abcd", 4, "abcd");
|
||||
check_string_to_dir("abcd", 5, "abcd/");
|
||||
check_string_to_dir("abcd", 6, "abcd/");
|
||||
}
|
||||
|
||||
/* join path to itself */
|
||||
void test_path__08_self_join(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
size_t asize = 0;
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_sets(&path, "/foo"));
|
||||
cl_assert_equal_s(path.ptr, "/foo");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string"));
|
||||
cl_assert_equal_s(path.ptr, "/foo/this is a new string");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
|
||||
cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
git_str_dispose(&path);
|
||||
cl_git_pass(git_str_sets(&path, "/foo/bar"));
|
||||
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz"));
|
||||
cl_assert_equal_s(path.ptr, "/bar/baz");
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
|
||||
cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
static void check_percent_decoding(const char *expected_result, const char *input)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git__percent_decode(&buf, input));
|
||||
cl_assert_equal_s(expected_result, git_str_cstr(&buf));
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
void test_path__09_percent_decode(void)
|
||||
{
|
||||
check_percent_decoding("abcd", "abcd");
|
||||
check_percent_decoding("a2%", "a2%");
|
||||
check_percent_decoding("a2%3", "a2%3");
|
||||
check_percent_decoding("a2%%3", "a2%%3");
|
||||
check_percent_decoding("a2%3z", "a2%3z");
|
||||
check_percent_decoding("a,", "a%2c");
|
||||
check_percent_decoding("a21", "a2%31");
|
||||
check_percent_decoding("a2%1", "a2%%31");
|
||||
check_percent_decoding("a bc ", "a%20bc%20");
|
||||
check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
|
||||
}
|
||||
|
||||
static void check_fromurl(const char *expected_result, const char *input, int should_fail)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
assert(should_fail || expected_result);
|
||||
|
||||
if (!should_fail) {
|
||||
cl_git_pass(git_fs_path_fromurl(&buf, input));
|
||||
cl_assert_equal_s(expected_result, git_str_cstr(&buf));
|
||||
} else
|
||||
cl_git_fail(git_fs_path_fromurl(&buf, input));
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
#define ABS_PATH_MARKER ""
|
||||
#else
|
||||
#define ABS_PATH_MARKER "/"
|
||||
#endif
|
||||
|
||||
void test_path__10_fromurl(void)
|
||||
{
|
||||
/* Failing cases */
|
||||
check_fromurl(NULL, "a", 1);
|
||||
check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file:///", 1);
|
||||
check_fromurl(NULL, "file:////", 1);
|
||||
check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
|
||||
|
||||
/* Passing cases */
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int expect_idx;
|
||||
int cancel_after;
|
||||
char **expect;
|
||||
} check_walkup_info;
|
||||
|
||||
#define CANCEL_VALUE 1234
|
||||
|
||||
static int check_one_walkup_step(void *ref, const char *path)
|
||||
{
|
||||
check_walkup_info *info = (check_walkup_info *)ref;
|
||||
|
||||
if (!info->cancel_after) {
|
||||
cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]");
|
||||
return CANCEL_VALUE;
|
||||
}
|
||||
info->cancel_after--;
|
||||
|
||||
cl_assert(info->expect[info->expect_idx] != NULL);
|
||||
cl_assert_equal_s(info->expect[info->expect_idx], path);
|
||||
info->expect_idx++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_path__11_walkup(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
char *expect[] = {
|
||||
/* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
|
||||
/* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
|
||||
/* 7 */ "this_is_a_path", "", NULL,
|
||||
/* 8 */ "this_is_a_path/", "", NULL,
|
||||
/* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
|
||||
/* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
|
||||
/* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
|
||||
/* 12 */ "a/b/c/", "a/b/", "a/", NULL,
|
||||
/* 13 */ "", NULL,
|
||||
/* 14 */ "/", NULL,
|
||||
/* 15 */ NULL
|
||||
};
|
||||
|
||||
char *root[] = {
|
||||
/* 1 */ NULL,
|
||||
/* 2 */ NULL,
|
||||
/* 3 */ "/",
|
||||
/* 4 */ "",
|
||||
/* 5 */ "/a/b",
|
||||
/* 6 */ "/a/b/",
|
||||
/* 7 */ NULL,
|
||||
/* 8 */ NULL,
|
||||
/* 9 */ NULL,
|
||||
/* 10 */ NULL,
|
||||
/* 11 */ NULL,
|
||||
/* 12 */ "a/",
|
||||
/* 13 */ NULL,
|
||||
/* 14 */ NULL,
|
||||
};
|
||||
|
||||
int i, j;
|
||||
check_walkup_info info;
|
||||
|
||||
info.expect = expect;
|
||||
info.cancel_after = -1;
|
||||
|
||||
for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
|
||||
|
||||
git_str_sets(&p, expect[i]);
|
||||
|
||||
info.expect_idx = i;
|
||||
cl_git_pass(
|
||||
git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
|
||||
);
|
||||
|
||||
cl_assert_equal_s(p.ptr, expect[i]);
|
||||
cl_assert(expect[info.expect_idx] == NULL);
|
||||
i = info.expect_idx;
|
||||
}
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path__11a_walkup_cancel(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
int cancel[] = { 3, 2, 1, 0 };
|
||||
char *expect[] = {
|
||||
"/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL,
|
||||
"/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL,
|
||||
"/a/b/c/d/e", "[CANCEL]", NULL,
|
||||
"[CANCEL]", NULL,
|
||||
NULL
|
||||
};
|
||||
char *root[] = { NULL, NULL, "/", "", NULL };
|
||||
int i, j;
|
||||
check_walkup_info info;
|
||||
|
||||
info.expect = expect;
|
||||
|
||||
for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
|
||||
|
||||
git_str_sets(&p, expect[i]);
|
||||
|
||||
info.cancel_after = cancel[j];
|
||||
info.expect_idx = i;
|
||||
|
||||
cl_assert_equal_i(
|
||||
CANCEL_VALUE,
|
||||
git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
|
||||
);
|
||||
|
||||
/* skip to next run of expectations */
|
||||
while (expect[i] != NULL) i++;
|
||||
}
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path__12_offset_to_path_root(void)
|
||||
{
|
||||
cl_assert(git_fs_path_root("non/rooted/path") == -1);
|
||||
cl_assert(git_fs_path_root("/rooted/path") == 0);
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
/* Windows specific tests */
|
||||
cl_assert(git_fs_path_root("C:non/rooted/path") == -1);
|
||||
cl_assert(git_fs_path_root("C:/rooted/path") == 2);
|
||||
cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14);
|
||||
cl_assert(git_fs_path_root("//computername/sharefolder") == 14);
|
||||
cl_assert(git_fs_path_root("//computername") == -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
|
||||
|
||||
void test_path__13_cannot_prettify_a_non_existing_file(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false);
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path__14_apply_relative(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "/this/is/a/base"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../test"));
|
||||
cl_assert_equal_s("/this/is/a/test", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end"));
|
||||
cl_assert_equal_s("/this/is/the/end", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string"));
|
||||
cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../.."));
|
||||
cl_assert_equal_s("/this/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../"));
|
||||
cl_assert_equal_s("/", p.ptr);
|
||||
|
||||
cl_git_fail(git_fs_path_apply_relative(&p, "../../.."));
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "d:/another/test"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../.."));
|
||||
cl_assert_equal_s("d:/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/."));
|
||||
cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../another.git"));
|
||||
cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch"));
|
||||
cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, ".."));
|
||||
cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../"));
|
||||
cl_assert_equal_s("https://", p.ptr);
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "../../this/is/relative"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix"));
|
||||
cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that"));
|
||||
cl_assert_equal_s("../../that", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../there"));
|
||||
cl_assert_equal_s("../../there", p.ptr);
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
static void assert_resolve_relative(
|
||||
git_str *buf, const char *expected, const char *path)
|
||||
{
|
||||
cl_git_pass(git_str_sets(buf, path));
|
||||
cl_git_pass(git_fs_path_resolve_relative(buf, 0));
|
||||
cl_assert_equal_s(expected, buf->ptr);
|
||||
}
|
||||
|
||||
void test_path__15_resolve_relative(void)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
assert_resolve_relative(&buf, "", "");
|
||||
assert_resolve_relative(&buf, "", ".");
|
||||
assert_resolve_relative(&buf, "", "./");
|
||||
assert_resolve_relative(&buf, "..", "..");
|
||||
assert_resolve_relative(&buf, "../", "../");
|
||||
assert_resolve_relative(&buf, "..", "./..");
|
||||
assert_resolve_relative(&buf, "../", "./../");
|
||||
assert_resolve_relative(&buf, "../", "../.");
|
||||
assert_resolve_relative(&buf, "../", ".././");
|
||||
assert_resolve_relative(&buf, "../..", "../..");
|
||||
assert_resolve_relative(&buf, "../../", "../../");
|
||||
|
||||
assert_resolve_relative(&buf, "/", "/");
|
||||
assert_resolve_relative(&buf, "/", "/.");
|
||||
|
||||
assert_resolve_relative(&buf, "", "a/..");
|
||||
assert_resolve_relative(&buf, "", "a/../");
|
||||
assert_resolve_relative(&buf, "", "a/../.");
|
||||
|
||||
assert_resolve_relative(&buf, "/a", "/a");
|
||||
assert_resolve_relative(&buf, "/a/", "/a/.");
|
||||
assert_resolve_relative(&buf, "/", "/a/../");
|
||||
assert_resolve_relative(&buf, "/", "/a/../.");
|
||||
assert_resolve_relative(&buf, "/", "/a/.././");
|
||||
|
||||
assert_resolve_relative(&buf, "a", "a");
|
||||
assert_resolve_relative(&buf, "a/", "a/");
|
||||
assert_resolve_relative(&buf, "a/", "a/.");
|
||||
assert_resolve_relative(&buf, "a/", "a/./");
|
||||
|
||||
assert_resolve_relative(&buf, "a/b", "a//b");
|
||||
assert_resolve_relative(&buf, "a/b/c", "a/b/c");
|
||||
assert_resolve_relative(&buf, "b/c", "./b/c");
|
||||
assert_resolve_relative(&buf, "a/c", "a/./c");
|
||||
assert_resolve_relative(&buf, "a/b/", "a/b/.");
|
||||
|
||||
assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
|
||||
assert_resolve_relative(&buf, "/", "////");
|
||||
assert_resolve_relative(&buf, "/a", "///a");
|
||||
assert_resolve_relative(&buf, "/", "///.");
|
||||
assert_resolve_relative(&buf, "/", "///a/..");
|
||||
|
||||
assert_resolve_relative(&buf, "../../path", "../../test//../././path");
|
||||
assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/./.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/.//.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/../."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/../.././../a"));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "////.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
/* things that start with Windows network paths */
|
||||
#ifdef GIT_WIN32
|
||||
assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
|
||||
assert_resolve_relative(&buf, "//a/", "//a/b/..");
|
||||
assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "//a/b/../.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
#else
|
||||
assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
|
||||
assert_resolve_relative(&buf, "/a/", "//a/b/..");
|
||||
assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
|
||||
assert_resolve_relative(&buf, "/", "//a/b/../..");
|
||||
#endif
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
#define assert_common_dirlen(i, p, q) \
|
||||
cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q)));
|
||||
|
||||
void test_path__16_resolve_relative(void)
|
||||
{
|
||||
assert_common_dirlen(0, "", "");
|
||||
assert_common_dirlen(0, "", "bar.txt");
|
||||
assert_common_dirlen(0, "foo.txt", "bar.txt");
|
||||
assert_common_dirlen(0, "foo.txt", "");
|
||||
assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt");
|
||||
assert_common_dirlen(0, "foo/bar.txt", "../foo.txt");
|
||||
|
||||
assert_common_dirlen(1, "/one.txt", "/two.txt");
|
||||
assert_common_dirlen(4, "foo/one.txt", "foo/two.txt");
|
||||
assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt");
|
||||
|
||||
assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt");
|
||||
assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt");
|
||||
}
|
||||
|
||||
static void fix_path(git_str *s)
|
||||
{
|
||||
#ifndef GIT_WIN32
|
||||
GIT_UNUSED(s);
|
||||
#else
|
||||
char* c;
|
||||
|
||||
for (c = s->ptr; *c; c++) {
|
||||
if (*c == '/')
|
||||
*c = '\\';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_path__find_exe_in_path(void)
|
||||
{
|
||||
char *orig_path;
|
||||
git_str sandbox_path = GIT_STR_INIT;
|
||||
git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT,
|
||||
dummy_path = GIT_STR_INIT;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
static const char *bogus_path_1 = "c:\\does\\not\\exist\\";
|
||||
static const char *bogus_path_2 = "e:\\non\\existent";
|
||||
#else
|
||||
static const char *bogus_path_1 = "/this/path/does/not/exist/";
|
||||
static const char *bogus_path_2 = "/non/existent";
|
||||
#endif
|
||||
|
||||
orig_path = cl_getenv("PATH");
|
||||
|
||||
git_str_puts(&sandbox_path, clar_sandbox_path());
|
||||
git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file");
|
||||
cl_git_rewritefile(dummy_path.ptr, "this is a dummy file");
|
||||
|
||||
fix_path(&sandbox_path);
|
||||
fix_path(&dummy_path);
|
||||
|
||||
cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s",
|
||||
bogus_path_1, GIT_PATH_LIST_SEPARATOR,
|
||||
orig_path, GIT_PATH_LIST_SEPARATOR,
|
||||
sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR,
|
||||
bogus_path_2));
|
||||
|
||||
check_setenv("PATH", new_path.ptr);
|
||||
|
||||
cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist"));
|
||||
cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file"));
|
||||
|
||||
cl_assert_equal_s(full_path.ptr, dummy_path.ptr);
|
||||
|
||||
git_str_dispose(&full_path);
|
||||
git_str_dispose(&new_path);
|
||||
git_str_dispose(&dummy_path);
|
||||
git_str_dispose(&sandbox_path);
|
||||
git__free(orig_path);
|
||||
}
|
||||
|
||||
void test_path__validate_current_user_ownership(void)
|
||||
{
|
||||
bool is_cur;
|
||||
|
||||
cl_must_pass(p_mkdir("testdir", 0777));
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir"));
|
||||
cl_assert_equal_i(is_cur, 1);
|
||||
|
||||
cl_git_rewritefile("testfile", "This is a test file.");
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile"));
|
||||
cl_assert_equal_i(is_cur, 1);
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
|
||||
cl_assert_equal_i(is_cur, 0);
|
||||
|
||||
cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
|
||||
#else
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
|
||||
cl_assert_equal_i(is_cur, (geteuid() == 0));
|
||||
|
||||
cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
|
||||
#endif
|
||||
}
|
||||
@@ -1,6 +1,784 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "futils.h"
|
||||
#include "fs_path.h"
|
||||
|
||||
#ifndef GIT_WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
static char *path_save;
|
||||
|
||||
void test_path_core__initialize(void)
|
||||
{
|
||||
path_save = cl_getenv("PATH");
|
||||
}
|
||||
|
||||
void test_path_core__cleanup(void)
|
||||
{
|
||||
cl_setenv("PATH", path_save);
|
||||
git__free(path_save);
|
||||
path_save = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
check_dirname(const char *A, const char *B)
|
||||
{
|
||||
git_str dir = GIT_STR_INIT;
|
||||
char *dir2;
|
||||
|
||||
cl_assert(git_fs_path_dirname_r(&dir, A) >= 0);
|
||||
cl_assert_equal_s(B, dir.ptr);
|
||||
git_str_dispose(&dir);
|
||||
|
||||
cl_assert((dir2 = git_fs_path_dirname(A)) != NULL);
|
||||
cl_assert_equal_s(B, dir2);
|
||||
git__free(dir2);
|
||||
}
|
||||
|
||||
static void
|
||||
check_basename(const char *A, const char *B)
|
||||
{
|
||||
git_str base = GIT_STR_INIT;
|
||||
char *base2;
|
||||
|
||||
cl_assert(git_fs_path_basename_r(&base, A) >= 0);
|
||||
cl_assert_equal_s(B, base.ptr);
|
||||
git_str_dispose(&base);
|
||||
|
||||
cl_assert((base2 = git_fs_path_basename(A)) != NULL);
|
||||
cl_assert_equal_s(B, base2);
|
||||
git__free(base2);
|
||||
}
|
||||
|
||||
static void
|
||||
check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
|
||||
{
|
||||
git_str joined_path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b));
|
||||
cl_assert_equal_s(expected_path, joined_path.ptr);
|
||||
|
||||
git_str_dispose(&joined_path);
|
||||
}
|
||||
|
||||
static void
|
||||
check_joinpath_n(
|
||||
const char *path_a,
|
||||
const char *path_b,
|
||||
const char *path_c,
|
||||
const char *path_d,
|
||||
const char *expected_path)
|
||||
{
|
||||
git_str joined_path = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_join_n(&joined_path, '/', 4,
|
||||
path_a, path_b, path_c, path_d));
|
||||
cl_assert_equal_s(expected_path, joined_path.ptr);
|
||||
|
||||
git_str_dispose(&joined_path);
|
||||
}
|
||||
|
||||
static void check_setenv(const char* name, const char* value)
|
||||
{
|
||||
char* check;
|
||||
|
||||
cl_git_pass(cl_setenv(name, value));
|
||||
check = cl_getenv(name);
|
||||
|
||||
if (value)
|
||||
cl_assert_equal_s(value, check);
|
||||
else
|
||||
cl_assert(check == NULL);
|
||||
|
||||
git__free(check);
|
||||
}
|
||||
|
||||
/* get the dirname of a path */
|
||||
void test_path_core__00_dirname(void)
|
||||
{
|
||||
check_dirname(NULL, ".");
|
||||
check_dirname("", ".");
|
||||
check_dirname("a", ".");
|
||||
check_dirname("/", "/");
|
||||
check_dirname("/usr", "/");
|
||||
check_dirname("/usr/", "/");
|
||||
check_dirname("/usr/lib", "/usr");
|
||||
check_dirname("/usr/lib/", "/usr");
|
||||
check_dirname("/usr/lib//", "/usr");
|
||||
check_dirname("usr/lib", "usr");
|
||||
check_dirname("usr/lib/", "usr");
|
||||
check_dirname("usr/lib//", "usr");
|
||||
check_dirname(".git/", ".");
|
||||
|
||||
check_dirname(REP16("/abc"), REP15("/abc"));
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
check_dirname("C:/", "C:/");
|
||||
check_dirname("C:", "C:/");
|
||||
check_dirname("C:/path/", "C:/");
|
||||
check_dirname("C:/path", "C:/");
|
||||
check_dirname("//computername/", "//computername/");
|
||||
check_dirname("//computername", "//computername/");
|
||||
check_dirname("//computername/path/", "//computername/");
|
||||
check_dirname("//computername/path", "//computername/");
|
||||
check_dirname("//computername/sub/path/", "//computername/sub");
|
||||
check_dirname("//computername/sub/path", "//computername/sub");
|
||||
#endif
|
||||
}
|
||||
|
||||
/* get the base name of a path */
|
||||
void test_path_core__01_basename(void)
|
||||
{
|
||||
check_basename(NULL, ".");
|
||||
check_basename("", ".");
|
||||
check_basename("a", "a");
|
||||
check_basename("/", "/");
|
||||
check_basename("/usr", "usr");
|
||||
check_basename("/usr/", "usr");
|
||||
check_basename("/usr/lib", "lib");
|
||||
check_basename("/usr/lib//", "lib");
|
||||
check_basename("usr/lib", "lib");
|
||||
|
||||
check_basename(REP16("/abc"), "abc");
|
||||
check_basename(REP1024("/abc"), "abc");
|
||||
}
|
||||
|
||||
/* properly join path components */
|
||||
void test_path_core__05_joins(void)
|
||||
{
|
||||
check_joinpath("", "", "");
|
||||
check_joinpath("", "a", "a");
|
||||
check_joinpath("", "/a", "/a");
|
||||
check_joinpath("a", "", "a/");
|
||||
check_joinpath("a", "/", "a/");
|
||||
check_joinpath("a", "b", "a/b");
|
||||
check_joinpath("/", "a", "/a");
|
||||
check_joinpath("/", "", "/");
|
||||
check_joinpath("/a", "/b", "/a/b");
|
||||
check_joinpath("/a", "/b/", "/a/b/");
|
||||
check_joinpath("/a/", "b/", "/a/b/");
|
||||
check_joinpath("/a/", "/b/", "/a/b/");
|
||||
|
||||
check_joinpath("/abcd", "/defg", "/abcd/defg");
|
||||
check_joinpath("/abcd", "/defg/", "/abcd/defg/");
|
||||
check_joinpath("/abcd/", "defg/", "/abcd/defg/");
|
||||
check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
|
||||
|
||||
check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
|
||||
check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
|
||||
check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
|
||||
|
||||
check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
|
||||
check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
|
||||
check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
|
||||
|
||||
check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
|
||||
REP1024("aaaa") "/" REP1024("bbbb"));
|
||||
check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
|
||||
REP1024("/aaaa") REP1024("/bbbb"));
|
||||
}
|
||||
|
||||
/* properly join path components for more than one path */
|
||||
void test_path_core__06_long_joins(void)
|
||||
{
|
||||
check_joinpath_n("", "", "", "", "");
|
||||
check_joinpath_n("", "a", "", "", "a/");
|
||||
check_joinpath_n("a", "", "", "", "a/");
|
||||
check_joinpath_n("", "", "", "a", "a");
|
||||
check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
|
||||
check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
|
||||
check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
|
||||
check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
|
||||
check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
|
||||
|
||||
check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
|
||||
REP1024("a") "/" REP1024("b") "/"
|
||||
REP1024("c") "/" REP1024("d"));
|
||||
check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
|
||||
REP1024("/a") REP1024("/b")
|
||||
REP1024("/c") REP1024("/d"));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
check_path_to_dir(
|
||||
const char* path,
|
||||
const char* expected)
|
||||
{
|
||||
git_str tgt = GIT_STR_INIT;
|
||||
|
||||
git_str_sets(&tgt, path);
|
||||
cl_git_pass(git_fs_path_to_dir(&tgt));
|
||||
cl_assert_equal_s(expected, tgt.ptr);
|
||||
|
||||
git_str_dispose(&tgt);
|
||||
}
|
||||
|
||||
static void
|
||||
check_string_to_dir(
|
||||
const char* path,
|
||||
size_t maxlen,
|
||||
const char* expected)
|
||||
{
|
||||
size_t len = strlen(path);
|
||||
char *buf = git__malloc(len + 2);
|
||||
cl_assert(buf);
|
||||
|
||||
strncpy(buf, path, len + 2);
|
||||
|
||||
git_fs_path_string_to_dir(buf, maxlen);
|
||||
|
||||
cl_assert_equal_s(expected, buf);
|
||||
|
||||
git__free(buf);
|
||||
}
|
||||
|
||||
/* convert paths to dirs */
|
||||
void test_path_core__07_path_to_dir(void)
|
||||
{
|
||||
check_path_to_dir("", "");
|
||||
check_path_to_dir(".", "./");
|
||||
check_path_to_dir("./", "./");
|
||||
check_path_to_dir("a/", "a/");
|
||||
check_path_to_dir("ab", "ab/");
|
||||
/* make sure we try just under and just over an expansion that will
|
||||
* require a realloc
|
||||
*/
|
||||
check_path_to_dir("abcdef", "abcdef/");
|
||||
check_path_to_dir("abcdefg", "abcdefg/");
|
||||
check_path_to_dir("abcdefgh", "abcdefgh/");
|
||||
check_path_to_dir("abcdefghi", "abcdefghi/");
|
||||
check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
|
||||
check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
|
||||
|
||||
check_string_to_dir("", 1, "");
|
||||
check_string_to_dir(".", 1, ".");
|
||||
check_string_to_dir(".", 2, "./");
|
||||
check_string_to_dir(".", 3, "./");
|
||||
check_string_to_dir("abcd", 3, "abcd");
|
||||
check_string_to_dir("abcd", 4, "abcd");
|
||||
check_string_to_dir("abcd", 5, "abcd/");
|
||||
check_string_to_dir("abcd", 6, "abcd/");
|
||||
}
|
||||
|
||||
/* join path to itself */
|
||||
void test_path_core__08_self_join(void)
|
||||
{
|
||||
git_str path = GIT_STR_INIT;
|
||||
size_t asize = 0;
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_sets(&path, "/foo"));
|
||||
cl_assert_equal_s(path.ptr, "/foo");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string"));
|
||||
cl_assert_equal_s(path.ptr, "/foo/this is a new string");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
|
||||
cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
git_str_dispose(&path);
|
||||
cl_git_pass(git_str_sets(&path, "/foo/bar"));
|
||||
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz"));
|
||||
cl_assert_equal_s(path.ptr, "/bar/baz");
|
||||
|
||||
asize = path.asize;
|
||||
cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
|
||||
cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
|
||||
cl_assert(asize < path.asize);
|
||||
|
||||
git_str_dispose(&path);
|
||||
}
|
||||
|
||||
static void check_percent_decoding(const char *expected_result, const char *input)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git__percent_decode(&buf, input));
|
||||
cl_assert_equal_s(expected_result, git_str_cstr(&buf));
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
void test_path_core__09_percent_decode(void)
|
||||
{
|
||||
check_percent_decoding("abcd", "abcd");
|
||||
check_percent_decoding("a2%", "a2%");
|
||||
check_percent_decoding("a2%3", "a2%3");
|
||||
check_percent_decoding("a2%%3", "a2%%3");
|
||||
check_percent_decoding("a2%3z", "a2%3z");
|
||||
check_percent_decoding("a,", "a%2c");
|
||||
check_percent_decoding("a21", "a2%31");
|
||||
check_percent_decoding("a2%1", "a2%%31");
|
||||
check_percent_decoding("a bc ", "a%20bc%20");
|
||||
check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
|
||||
}
|
||||
|
||||
static void check_fromurl(const char *expected_result, const char *input, int should_fail)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
assert(should_fail || expected_result);
|
||||
|
||||
if (!should_fail) {
|
||||
cl_git_pass(git_fs_path_fromurl(&buf, input));
|
||||
cl_assert_equal_s(expected_result, git_str_cstr(&buf));
|
||||
} else
|
||||
cl_git_fail(git_fs_path_fromurl(&buf, input));
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
#define ABS_PATH_MARKER ""
|
||||
#else
|
||||
#define ABS_PATH_MARKER "/"
|
||||
#endif
|
||||
|
||||
void test_path_core__10_fromurl(void)
|
||||
{
|
||||
/* Failing cases */
|
||||
check_fromurl(NULL, "a", 1);
|
||||
check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
|
||||
check_fromurl(NULL, "file:///", 1);
|
||||
check_fromurl(NULL, "file:////", 1);
|
||||
check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
|
||||
|
||||
/* Passing cases */
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
|
||||
check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int expect_idx;
|
||||
int cancel_after;
|
||||
char **expect;
|
||||
} check_walkup_info;
|
||||
|
||||
#define CANCEL_VALUE 1234
|
||||
|
||||
static int check_one_walkup_step(void *ref, const char *path)
|
||||
{
|
||||
check_walkup_info *info = (check_walkup_info *)ref;
|
||||
|
||||
if (!info->cancel_after) {
|
||||
cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]");
|
||||
return CANCEL_VALUE;
|
||||
}
|
||||
info->cancel_after--;
|
||||
|
||||
cl_assert(info->expect[info->expect_idx] != NULL);
|
||||
cl_assert_equal_s(info->expect[info->expect_idx], path);
|
||||
info->expect_idx++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test_path_core__11_walkup(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
char *expect[] = {
|
||||
/* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
|
||||
/* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
|
||||
/* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
|
||||
/* 7 */ "this_is_a_path", "", NULL,
|
||||
/* 8 */ "this_is_a_path/", "", NULL,
|
||||
/* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
|
||||
/* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
|
||||
/* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
|
||||
/* 12 */ "a/b/c/", "a/b/", "a/", NULL,
|
||||
/* 13 */ "", NULL,
|
||||
/* 14 */ "/", NULL,
|
||||
/* 15 */ NULL
|
||||
};
|
||||
|
||||
char *root[] = {
|
||||
/* 1 */ NULL,
|
||||
/* 2 */ NULL,
|
||||
/* 3 */ "/",
|
||||
/* 4 */ "",
|
||||
/* 5 */ "/a/b",
|
||||
/* 6 */ "/a/b/",
|
||||
/* 7 */ NULL,
|
||||
/* 8 */ NULL,
|
||||
/* 9 */ NULL,
|
||||
/* 10 */ NULL,
|
||||
/* 11 */ NULL,
|
||||
/* 12 */ "a/",
|
||||
/* 13 */ NULL,
|
||||
/* 14 */ NULL,
|
||||
};
|
||||
|
||||
int i, j;
|
||||
check_walkup_info info;
|
||||
|
||||
info.expect = expect;
|
||||
info.cancel_after = -1;
|
||||
|
||||
for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
|
||||
|
||||
git_str_sets(&p, expect[i]);
|
||||
|
||||
info.expect_idx = i;
|
||||
cl_git_pass(
|
||||
git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
|
||||
);
|
||||
|
||||
cl_assert_equal_s(p.ptr, expect[i]);
|
||||
cl_assert(expect[info.expect_idx] == NULL);
|
||||
i = info.expect_idx;
|
||||
}
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path_core__11a_walkup_cancel(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
int cancel[] = { 3, 2, 1, 0 };
|
||||
char *expect[] = {
|
||||
"/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL,
|
||||
"/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL,
|
||||
"/a/b/c/d/e", "[CANCEL]", NULL,
|
||||
"[CANCEL]", NULL,
|
||||
NULL
|
||||
};
|
||||
char *root[] = { NULL, NULL, "/", "", NULL };
|
||||
int i, j;
|
||||
check_walkup_info info;
|
||||
|
||||
info.expect = expect;
|
||||
|
||||
for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
|
||||
|
||||
git_str_sets(&p, expect[i]);
|
||||
|
||||
info.cancel_after = cancel[j];
|
||||
info.expect_idx = i;
|
||||
|
||||
cl_assert_equal_i(
|
||||
CANCEL_VALUE,
|
||||
git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
|
||||
);
|
||||
|
||||
/* skip to next run of expectations */
|
||||
while (expect[i] != NULL) i++;
|
||||
}
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path_core__12_offset_to_path_root(void)
|
||||
{
|
||||
cl_assert(git_fs_path_root("non/rooted/path") == -1);
|
||||
cl_assert(git_fs_path_root("/rooted/path") == 0);
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
/* Windows specific tests */
|
||||
cl_assert(git_fs_path_root("C:non/rooted/path") == -1);
|
||||
cl_assert(git_fs_path_root("C:/rooted/path") == 2);
|
||||
cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14);
|
||||
cl_assert(git_fs_path_root("//computername/sharefolder") == 14);
|
||||
cl_assert(git_fs_path_root("//computername") == -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
|
||||
|
||||
void test_path_core__13_cannot_prettify_a_non_existing_file(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false);
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
|
||||
cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
|
||||
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
void test_path_core__14_apply_relative(void)
|
||||
{
|
||||
git_str p = GIT_STR_INIT;
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "/this/is/a/base"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../test"));
|
||||
cl_assert_equal_s("/this/is/a/test", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end"));
|
||||
cl_assert_equal_s("/this/is/the/end", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string"));
|
||||
cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../.."));
|
||||
cl_assert_equal_s("/this/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../"));
|
||||
cl_assert_equal_s("/", p.ptr);
|
||||
|
||||
cl_git_fail(git_fs_path_apply_relative(&p, "../../.."));
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "d:/another/test"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../.."));
|
||||
cl_assert_equal_s("d:/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/."));
|
||||
cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../another.git"));
|
||||
cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch"));
|
||||
cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, ".."));
|
||||
cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../"));
|
||||
cl_assert_equal_s("https://", p.ptr);
|
||||
|
||||
|
||||
cl_git_pass(git_str_sets(&p, "../../this/is/relative"));
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix"));
|
||||
cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that"));
|
||||
cl_assert_equal_s("../../that", p.ptr);
|
||||
|
||||
cl_git_pass(git_fs_path_apply_relative(&p, "../there"));
|
||||
cl_assert_equal_s("../../there", p.ptr);
|
||||
git_str_dispose(&p);
|
||||
}
|
||||
|
||||
static void assert_resolve_relative(
|
||||
git_str *buf, const char *expected, const char *path)
|
||||
{
|
||||
cl_git_pass(git_str_sets(buf, path));
|
||||
cl_git_pass(git_fs_path_resolve_relative(buf, 0));
|
||||
cl_assert_equal_s(expected, buf->ptr);
|
||||
}
|
||||
|
||||
void test_path_core__15_resolve_relative(void)
|
||||
{
|
||||
git_str buf = GIT_STR_INIT;
|
||||
|
||||
assert_resolve_relative(&buf, "", "");
|
||||
assert_resolve_relative(&buf, "", ".");
|
||||
assert_resolve_relative(&buf, "", "./");
|
||||
assert_resolve_relative(&buf, "..", "..");
|
||||
assert_resolve_relative(&buf, "../", "../");
|
||||
assert_resolve_relative(&buf, "..", "./..");
|
||||
assert_resolve_relative(&buf, "../", "./../");
|
||||
assert_resolve_relative(&buf, "../", "../.");
|
||||
assert_resolve_relative(&buf, "../", ".././");
|
||||
assert_resolve_relative(&buf, "../..", "../..");
|
||||
assert_resolve_relative(&buf, "../../", "../../");
|
||||
|
||||
assert_resolve_relative(&buf, "/", "/");
|
||||
assert_resolve_relative(&buf, "/", "/.");
|
||||
|
||||
assert_resolve_relative(&buf, "", "a/..");
|
||||
assert_resolve_relative(&buf, "", "a/../");
|
||||
assert_resolve_relative(&buf, "", "a/../.");
|
||||
|
||||
assert_resolve_relative(&buf, "/a", "/a");
|
||||
assert_resolve_relative(&buf, "/a/", "/a/.");
|
||||
assert_resolve_relative(&buf, "/", "/a/../");
|
||||
assert_resolve_relative(&buf, "/", "/a/../.");
|
||||
assert_resolve_relative(&buf, "/", "/a/.././");
|
||||
|
||||
assert_resolve_relative(&buf, "a", "a");
|
||||
assert_resolve_relative(&buf, "a/", "a/");
|
||||
assert_resolve_relative(&buf, "a/", "a/.");
|
||||
assert_resolve_relative(&buf, "a/", "a/./");
|
||||
|
||||
assert_resolve_relative(&buf, "a/b", "a//b");
|
||||
assert_resolve_relative(&buf, "a/b/c", "a/b/c");
|
||||
assert_resolve_relative(&buf, "b/c", "./b/c");
|
||||
assert_resolve_relative(&buf, "a/c", "a/./c");
|
||||
assert_resolve_relative(&buf, "a/b/", "a/b/.");
|
||||
|
||||
assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
|
||||
assert_resolve_relative(&buf, "/", "////");
|
||||
assert_resolve_relative(&buf, "/a", "///a");
|
||||
assert_resolve_relative(&buf, "/", "///.");
|
||||
assert_resolve_relative(&buf, "/", "///a/..");
|
||||
|
||||
assert_resolve_relative(&buf, "../../path", "../../test//../././path");
|
||||
assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/./.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/.//.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/../."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "/../.././../a"));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "////.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
|
||||
/* things that start with Windows network paths */
|
||||
#ifdef GIT_WIN32
|
||||
assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
|
||||
assert_resolve_relative(&buf, "//a/", "//a/b/..");
|
||||
assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
|
||||
|
||||
cl_git_pass(git_str_sets(&buf, "//a/b/../.."));
|
||||
cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
|
||||
#else
|
||||
assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
|
||||
assert_resolve_relative(&buf, "/a/", "//a/b/..");
|
||||
assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
|
||||
assert_resolve_relative(&buf, "/", "//a/b/../..");
|
||||
#endif
|
||||
|
||||
git_str_dispose(&buf);
|
||||
}
|
||||
|
||||
#define assert_common_dirlen(i, p, q) \
|
||||
cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q)));
|
||||
|
||||
void test_path_core__16_resolve_relative(void)
|
||||
{
|
||||
assert_common_dirlen(0, "", "");
|
||||
assert_common_dirlen(0, "", "bar.txt");
|
||||
assert_common_dirlen(0, "foo.txt", "bar.txt");
|
||||
assert_common_dirlen(0, "foo.txt", "");
|
||||
assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt");
|
||||
assert_common_dirlen(0, "foo/bar.txt", "../foo.txt");
|
||||
|
||||
assert_common_dirlen(1, "/one.txt", "/two.txt");
|
||||
assert_common_dirlen(4, "foo/one.txt", "foo/two.txt");
|
||||
assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt");
|
||||
|
||||
assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt");
|
||||
assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt");
|
||||
}
|
||||
|
||||
static void fix_path(git_str *s)
|
||||
{
|
||||
#ifndef GIT_WIN32
|
||||
GIT_UNUSED(s);
|
||||
#else
|
||||
char* c;
|
||||
|
||||
for (c = s->ptr; *c; c++) {
|
||||
if (*c == '/')
|
||||
*c = '\\';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_path_core__find_exe_in_path(void)
|
||||
{
|
||||
char *orig_path;
|
||||
git_str sandbox_path = GIT_STR_INIT;
|
||||
git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT,
|
||||
dummy_path = GIT_STR_INIT;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
static const char *bogus_path_1 = "c:\\does\\not\\exist\\";
|
||||
static const char *bogus_path_2 = "e:\\non\\existent";
|
||||
#else
|
||||
static const char *bogus_path_1 = "/this/path/does/not/exist/";
|
||||
static const char *bogus_path_2 = "/non/existent";
|
||||
#endif
|
||||
|
||||
orig_path = cl_getenv("PATH");
|
||||
|
||||
git_str_puts(&sandbox_path, clar_sandbox_path());
|
||||
git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file");
|
||||
cl_git_rewritefile(dummy_path.ptr, "this is a dummy file");
|
||||
|
||||
fix_path(&sandbox_path);
|
||||
fix_path(&dummy_path);
|
||||
|
||||
cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s",
|
||||
bogus_path_1, GIT_PATH_LIST_SEPARATOR,
|
||||
orig_path, GIT_PATH_LIST_SEPARATOR,
|
||||
sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR,
|
||||
bogus_path_2));
|
||||
|
||||
check_setenv("PATH", new_path.ptr);
|
||||
|
||||
cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist"));
|
||||
cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file"));
|
||||
|
||||
cl_assert_equal_s(full_path.ptr, dummy_path.ptr);
|
||||
|
||||
git_str_dispose(&full_path);
|
||||
git_str_dispose(&new_path);
|
||||
git_str_dispose(&dummy_path);
|
||||
git_str_dispose(&sandbox_path);
|
||||
git__free(orig_path);
|
||||
}
|
||||
|
||||
void test_path_core__validate_current_user_ownership(void)
|
||||
{
|
||||
bool is_cur;
|
||||
|
||||
cl_must_pass(p_mkdir("testdir", 0777));
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir"));
|
||||
cl_assert_equal_i(is_cur, 1);
|
||||
|
||||
cl_git_rewritefile("testfile", "This is a test file.");
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile"));
|
||||
cl_assert_equal_i(is_cur, 1);
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
|
||||
cl_assert_equal_i(is_cur, 0);
|
||||
|
||||
cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
|
||||
#else
|
||||
cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
|
||||
cl_assert_equal_i(is_cur, (geteuid() == 0));
|
||||
|
||||
cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_path_core__dirlen(void)
|
||||
{
|
||||
cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf"));
|
||||
cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf/"));
|
||||
cl_assert_equal_sz(13, git_fs_path_dirlen("/foo/bar/asdf//"));
|
||||
cl_assert_equal_sz(3, git_fs_path_dirlen("foo////"));
|
||||
cl_assert_equal_sz(3, git_fs_path_dirlen("foo"));
|
||||
cl_assert_equal_sz(1, git_fs_path_dirlen("/"));
|
||||
cl_assert_equal_sz(1, git_fs_path_dirlen("////"));
|
||||
cl_assert_equal_sz(0, git_fs_path_dirlen(""));
|
||||
}
|
||||
|
||||
static void test_make_relative(
|
||||
const char *expected_path,
|
||||
const char *path,
|
||||
@@ -341,3 +1119,29 @@ void test_path_core__join_unrooted_respects_funny_windows_roots(void)
|
||||
test_join_unrooted("💩:/foo/bar/foobar", 13, "💩:/foo/bar/foobar", "💩:/foo/bar");
|
||||
test_join_unrooted("💩:/foo/bar/foobar", 9, "💩:/foo/bar/foobar", "💩:/foo/");
|
||||
}
|
||||
|
||||
void test_path_core__is_root(void)
|
||||
{
|
||||
cl_assert_equal_b(true, git_fs_path_is_root("/"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("//"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("foo/"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("/foo/"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("/foo"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\"));
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
cl_assert_equal_b(true, git_fs_path_is_root("A:\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("B:\\foo\\"));
|
||||
cl_assert_equal_b(true, git_fs_path_is_root("C:\\"));
|
||||
cl_assert_equal_b(true, git_fs_path_is_root("c:\\"));
|
||||
cl_assert_equal_b(true, git_fs_path_is_root("z:\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("z:\\\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\localhost\\c$\\Foo\\"));
|
||||
cl_assert_equal_b(false, git_fs_path_is_root("\\\\Volume\\12345\\Foo\\Bar.txt"));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -278,5 +278,38 @@ void test_path_win32__8dot3_name(void)
|
||||
cl_must_pass(p_mkdir(".bar", 0777));
|
||||
cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar")));
|
||||
git__free(shortname);
|
||||
|
||||
p_rmdir(".foo");
|
||||
p_rmdir(".bar");
|
||||
p_unlink("bar~1");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_path_win32__realpath(void)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
git_str expected = GIT_STR_INIT;
|
||||
char result[GIT_PATH_MAX];
|
||||
|
||||
/* Ensure relative paths become absolute */
|
||||
cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "abcdef"));
|
||||
cl_must_pass(p_mkdir("abcdef", 0777));
|
||||
cl_assert(p_realpath("./abcdef", result) != NULL);
|
||||
cl_assert_equal_s(expected.ptr, result);
|
||||
|
||||
/* Ensure case is canonicalized */
|
||||
git_str_clear(&expected);
|
||||
cl_must_pass(git_str_joinpath(&expected, clar_sandbox_path(), "FOO"));
|
||||
cl_must_pass(p_mkdir("FOO", 0777));
|
||||
cl_assert(p_realpath("foo", result) != NULL);
|
||||
cl_assert_equal_s(expected.ptr, result);
|
||||
|
||||
cl_assert(p_realpath("nonexistent", result) == NULL);
|
||||
cl_assert_equal_i(ENOENT, errno);
|
||||
|
||||
git_str_dispose(&expected);
|
||||
|
||||
p_rmdir("abcdef");
|
||||
p_rmdir("FOO");
|
||||
#endif
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user