Commit 8ab11dd53da0bba5152f1d755d92b9c436c71ff0

Gabriel DeBacker 2018-09-30T16:40:22

Fix issue with path canonicalization for Win32 paths

diff --git a/src/win32/w32_util.c b/src/win32/w32_util.c
index b7b1ffa..5bacd6e 100644
--- a/src/win32/w32_util.c
+++ b/src/win32/w32_util.c
@@ -132,6 +132,8 @@ size_t git_win32__canonicalize_path(wchar_t *str, size_t len)
 	static const wchar_t dosdevices_prefix[] = L"\\\?\?\\";
 	static const wchar_t nt_prefix[] = L"\\\\?\\";
 	static const wchar_t unc_prefix[] = L"UNC\\";
+	static const wchar_t unc_canonicalized_prefix[] = L"\\\\";
+
 	size_t to_advance = 0;
 
 	/* "\??\" -- DOS Devices prefix */
@@ -150,8 +152,18 @@ size_t git_win32__canonicalize_path(wchar_t *str, size_t len)
 	/* "\??\UNC\", "\\?\UNC\" -- UNC prefix */
 	if (to_advance && len >= CONST_STRLEN(unc_prefix) &&
 		!wcsncmp(str + to_advance, unc_prefix, CONST_STRLEN(unc_prefix))) {
-		to_advance += CONST_STRLEN(unc_prefix);
-		len -= CONST_STRLEN(unc_prefix);
+		/** 
+		 * The proper Win32 path for a UNC share has "\\" at beginning of it
+		 * and looks like "\\server\share\<folderStructure>".
+		 * So, remove th UNC prefix, but leave room for a "\\"
+		 */ 
+		to_advance += (CONST_STRLEN(unc_prefix) - CONST_STRLEN(unc_canonicalized_prefix));
+		len -= (CONST_STRLEN(unc_prefix) - CONST_STRLEN(unc_canonicalized_prefix));
+		
+		/**
+		 * Place a "\\" in the string so the result is "\\server\\share\<folderStructure>"
+		 */
+		memmove(str + to_advance, unc_canonicalized_prefix, CONST_STRLEN(unc_canonicalized_prefix) * sizeof(wchar_t));
 	}
 
 	if (to_advance) {
diff --git a/tests/path/win32.c b/tests/path/win32.c
index 4ff0397..6065002 100644
--- a/tests/path/win32.c
+++ b/tests/path/win32.c
@@ -145,6 +145,22 @@ void test_canonicalize(const wchar_t *in, const wchar_t *expected)
 #endif
 }
 
+void test_path_git_win32__canonicalize_path(const wchar_t *in, const wchar_t *expected)
+{
+#ifdef GIT_WIN32
+	git_win32_path canonical;
+
+	cl_assert(wcslen(in) < MAX_PATH);
+	wcscpy(canonical, in);
+
+	cl_must_pass(git_win32__canonicalize_path(canonical, wcslen(in)));
+	cl_assert_equal_wcs(expected, canonical);
+#else
+	GIT_UNUSED(in);
+	GIT_UNUSED(expected);
+#endif
+}
+
 void test_path_win32__canonicalize(void)
 {
 #ifdef GIT_WIN32
@@ -186,6 +202,8 @@ void test_path_win32__canonicalize(void)
 	test_canonicalize(L"\\\\server\\\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
 	test_canonicalize(L"\\\\server\\share\\..\\foo", L"\\\\server\\foo");
 	test_canonicalize(L"\\\\server\\..\\..\\share\\.\\foo", L"\\\\server\\share\\foo");
+
+	test_path_git_win32__canonicalize_path(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\server\\C$\\folder");
 #endif
 }