From 865baaf928bd29274c753d85db59a324d4bd8b82 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 5 Feb 2015 11:06:01 -0500 Subject: [PATCH 1/4] repo: ensure we can create repo at filesystem root Test to ensure that we can create a repository at the filesystem root. Introduces a new test environment variable, `GITTEST_INVASIVE_FILESYSTEM` for tests that do terrible things like escaping the clar sandbox and writing to the root directory. It is expected that the CI builds will enable this but that normal people would not want this. --- tests/repo/init.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/repo/init.c b/tests/repo/init.c index ed86f6e4f..91747c9f5 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -714,3 +714,29 @@ void test_repo_init__init_with_initial_commit(void) git_index_free(index); } + +void test_repo_init__at_filesystem_root(void) +{ + git_repository *repo; + const char *sandbox = clar_sandbox_path(); + git_buf root = GIT_BUF_INIT; + int root_len; + + if (!cl_getenv("GITTEST_INVASIVE_FILESYSTEM")) + cl_skip(); + + root_len = git_path_root(sandbox); + cl_assert(root_len >= 0); + + git_buf_put(&root, sandbox, root_len+1); + git_buf_joinpath(&root, root.ptr, "libgit2_test_dir"); + + cl_assert(!git_path_exists(root.ptr)); + + cl_git_pass(git_repository_init(&repo, root.ptr, 0)); + cl_assert(git_path_isdir(root.ptr)); + cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + + git_buf_free(&root); + git_repository_free(repo); +} From 2f4ee00039ebe63e2d954ede118593c279486893 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Thu, 5 Feb 2015 11:55:00 -0500 Subject: [PATCH 2/4] appveyor: include the invasive filesystem tests --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index aa3a70eb5..de633bbee 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,8 @@ branches: only: - master environment: + GITTEST_INVASIVE_FILESYSTEM: 1 + matrix: - GENERATOR: "Visual Studio 11" ARCH: 32 From 3c68bfcd088dd5a1ef06b98ea7fd203bb048fa42 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 4 Feb 2015 18:24:31 -0500 Subject: [PATCH 3/4] stat: don't remove trailing '/' from root on win32 `p_stat` calls `git_win32_path_from_utf8`, which canonicalizes the path. Do not further try to modify the path, else we trim the trailing slash from a root directory and try to access `C:` instead of `C:/`. --- src/win32/posix_w32.c | 8 ++------ tests/core/stat.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index e446ccab0..346f537e4 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -448,12 +448,8 @@ int p_stat(const char* path, struct stat* buf) git_win32_path path_w; int len; - if ((len = git_win32_path_from_utf8(path_w, path)) < 0) - return -1; - - git_win32__path_trim_end(path_w, len); - - if (lstat_w(path_w, buf, false) < 0) + if ((len = git_win32_path_from_utf8(path_w, path)) < 0 || + lstat_w(path_w, buf, false) < 0) return -1; /* The item is a symbolic link or mount point. No need to iterate diff --git a/tests/core/stat.c b/tests/core/stat.c index 2e4abfb79..bd9b990e3 100644 --- a/tests/core/stat.c +++ b/tests/core/stat.c @@ -95,3 +95,20 @@ void test_core_stat__0(void) cl_assert_error(ENOTDIR); } +void test_core_stat__root(void) +{ + const char *sandbox = clar_sandbox_path(); + git_buf root = GIT_BUF_INIT; + int root_len; + struct stat st; + + root_len = git_path_root(sandbox); + cl_assert(root_len >= 0); + + git_buf_set(&root, sandbox, root_len+1); + + cl_must_pass(p_stat(root.ptr, &st)); + cl_assert(S_ISDIR(st.st_mode)); + + git_buf_free(&root); +} From 9cb5b0f73c610692cc62f6d98f527bf1ab326924 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 4 Feb 2015 23:45:22 -0500 Subject: [PATCH 4/4] mkdir: respect the root path Don't try to strip trailing paths from the root directory on Windows (trying to create `C:` will fail). --- src/fileops.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/fileops.c b/src/fileops.c index 2ee9535be..4a62d210d 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -330,7 +330,7 @@ int git_futils_mkdir_withperf( { int error = -1; git_buf make_path = GIT_BUF_INIT; - ssize_t root = 0, min_root_len; + ssize_t root = 0, min_root_len, root_len; char lastch = '/', *tail; struct stat st; @@ -343,22 +343,29 @@ int git_futils_mkdir_withperf( goto done; } - /* remove trailing slashes on path */ - while (make_path.ptr[make_path.size - 1] == '/') { - make_path.size--; - make_path.ptr[make_path.size] = '\0'; - } + /* Trim trailing slashes (except the root) */ + if ((root_len = git_path_root(make_path.ptr)) < 0) + root_len = 0; + else + root_len++; + + while (make_path.size > (size_t)root_len && + make_path.ptr[make_path.size - 1] == '/') + make_path.ptr[--make_path.size] = '\0'; /* if we are not supposed to made the last element, truncate it */ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { - git_buf_rtruncate_at_char(&make_path, '/'); + git_path_dirname_r(&make_path, make_path.ptr); flags |= GIT_MKDIR_SKIP_LAST; } - if ((flags & GIT_MKDIR_SKIP_LAST) != 0) - git_buf_rtruncate_at_char(&make_path, '/'); + if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { + git_path_dirname_r(&make_path, make_path.ptr); + } - /* if nothing left after truncation, then we're done! */ - if (!make_path.size) { + /* We were either given the root path (or trimmed it to + * the root), we don't have anything to do. + */ + if (make_path.size <= (size_t)root_len) { error = 0; goto done; }