Merge pull request #5355 from pks-t/pks/win32-relative-symlink-across-dirs win32: fix relative symlinks pointing into dirs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 2bc93a3..29641bd 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 77ac65a..764ca19 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);
+}