Commit 8d45b4691c81d2d1cde7a1b2f828dedd55315b3f

Edward Thomson 2014-10-11T14:34:24

p_lstat win32: don't canonicalize volume mounts A reparse point that is an IO_REPARSE_TAG_MOUNT_POINT could be a junction or an actual filesystem mount point. (Who knew?) If it's the latter, its reparse point will report the actual volume information \??\Volume{GUID}\ and we should not attempt to dereference that further, instead readlink should report EINVAL since it's not a symlink / junction and its original path was canonical. Yes, really.

diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 0023f95..7b45557 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -131,6 +131,11 @@ GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
 	return (time_t)winTime;
 }
 
+static bool path_is_volume(wchar_t *target, size_t target_len)
+{
+	return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0);
+}
+
 /* On success, returns the length, in characters, of the path stored in dest.
  * On failure, returns a negative value. */
 static int readlink_w(
@@ -177,7 +182,13 @@ static int readlink_w(
 		goto on_error;
 	}
 
-	if (target_len) {
+	if (path_is_volume(target, target_len)) {
+		/* This path is a reparse point that represents another volume mounted
+		 * at this location, it is not a symbolic link our input was canonical.
+		 */
+		errno = EINVAL;
+		error = -1;
+	} else if (target_len) {
 		/* The path may need to have a prefix removed. */
 		target_len = git_win32__canonicalize_path(target, target_len);