Skip to content

Commit 7d55bee

Browse files
committed
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.
1 parent 9181e4b commit 7d55bee

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

src/win32/posix_w32.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,8 +439,16 @@ int p_symlink(const char *target, const char *path)
439439
git_win32_path target_w, path_w;
440440
DWORD dwFlags;
441441

442+
/*
443+
* Convert both target and path to Windows-style paths. Note that we do
444+
* not want to use `git_win32_path_from_utf8` for converting the target,
445+
* as that function will automatically pre-pend the current working
446+
* directory in case the path is not absolute. As Git will instead use
447+
* relative symlinks, this is not someting we want.
448+
*/
442449
if (git_win32_path_from_utf8(path_w, path) < 0 ||
443-
git__utf8_to_16(target_w, MAX_PATH, target) < 0)
450+
git__utf8_to_16(target_w, MAX_PATH, target) < 0 ||
451+
git_win32_path_canonicalize(target_w) < 0)
444452
return -1;
445453

446454
dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;

tests/core/posix.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,30 @@ void test_core_posix__symlink_resolves_to_correct_type(void)
189189

190190
git_buf_dispose(&contents);
191191
}
192+
193+
void test_core_posix__symlink_to_file_across_dirs(void)
194+
{
195+
git_buf contents = GIT_BUF_INIT;
196+
197+
if (!git_path_supports_symlinks(clar_sandbox_path()))
198+
clar__skip();
199+
200+
/*
201+
* Create a relative symlink that points into another
202+
* directory. This used to not work on Win32, where we
203+
* forgot to convert directory separators to
204+
* Windows-style ones.
205+
*/
206+
cl_must_pass(git_futils_mkdir("dir", 0777, 0));
207+
cl_git_mkfile("dir/target", "symlink target");
208+
cl_git_pass(p_symlink("dir/target", "link"));
209+
210+
cl_git_pass(git_futils_readbuffer(&contents, "dir/target"));
211+
cl_assert_equal_s(contents.ptr, "symlink target");
212+
213+
cl_must_pass(p_unlink("dir/target"));
214+
cl_must_pass(p_unlink("link"));
215+
cl_must_pass(p_rmdir("dir"));
216+
217+
git_buf_dispose(&contents);
218+
}

0 commit comments

Comments
 (0)