summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2020-01-10 12:44:51 +0100
committerPatrick Steinhardt <ps@pks.im>2020-01-10 12:48:32 +0100
commit7d55bee6d19a20b73c5f6cbeb7f4debc45bd76e9 (patch)
tree8d73a785bce52c88916cc75284d352919f7579f5
parent9181e4b586e2b27846554c12c06002438b159086 (diff)
downloadlibgit2-7d55bee6d19a20b73c5f6cbeb7f4debc45bd76e9.tar.gz
win32: fix relative symlinks pointing into dirs
On Windows platforms, we need some logic to emulate symlink(3P) defined by POSIX. As unprivileged symlinks on Windows are a rather new feature, our current implementation is comparatively new and still has some rough edges in special cases. One such case is relative symlinks. While relative symlinks to files in the same directory work as expected, libgit2 currently fails to create reltaive symlinks pointing into other directories. This is due to the fact that we forgot to translate the Unix-style target path to Windows-style. Most importantly, we are currently not converting directory separators from "/" to "\". Fix the issue by calling `git_win32_path_canonicalize` on the target. Add a test that verifies our ability to create such relative links across directories.
-rw-r--r--src/win32/posix_w32.c10
-rw-r--r--tests/core/posix.c27
2 files changed, 36 insertions, 1 deletions
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 2bc93a3c7..29641bdaf 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -439,8 +439,16 @@ int p_symlink(const char *target, const char *path)
git_win32_path target_w, path_w;
DWORD dwFlags;
+ /*
+ * Convert both target and path to Windows-style paths. Note that we do
+ * not want to use `git_win32_path_from_utf8` for converting the target,
+ * as that function will automatically pre-pend the current working
+ * directory in case the path is not absolute. As Git will instead use
+ * relative symlinks, this is not someting we want.
+ */
if (git_win32_path_from_utf8(path_w, path) < 0 ||
- git__utf8_to_16(target_w, MAX_PATH, target) < 0)
+ git__utf8_to_16(target_w, MAX_PATH, target) < 0 ||
+ git_win32_path_canonicalize(target_w) < 0)
return -1;
dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
diff --git a/tests/core/posix.c b/tests/core/posix.c
index 77ac65ad6..764ca1942 100644
--- a/tests/core/posix.c
+++ b/tests/core/posix.c
@@ -189,3 +189,30 @@ void test_core_posix__symlink_resolves_to_correct_type(void)
git_buf_dispose(&contents);
}
+
+void test_core_posix__symlink_to_file_across_dirs(void)
+{
+ git_buf contents = GIT_BUF_INIT;
+
+ if (!git_path_supports_symlinks(clar_sandbox_path()))
+ clar__skip();
+
+ /*
+ * Create a relative symlink that points into another
+ * directory. This used to not work on Win32, where we
+ * forgot to convert directory separators to
+ * Windows-style ones.
+ */
+ cl_must_pass(git_futils_mkdir("dir", 0777, 0));
+ cl_git_mkfile("dir/target", "symlink target");
+ cl_git_pass(p_symlink("dir/target", "link"));
+
+ cl_git_pass(git_futils_readbuffer(&contents, "dir/target"));
+ cl_assert_equal_s(contents.ptr, "symlink target");
+
+ cl_must_pass(p_unlink("dir/target"));
+ cl_must_pass(p_unlink("link"));
+ cl_must_pass(p_rmdir("dir"));
+
+ git_buf_dispose(&contents);
+}