From 0de49f05ec2b03819ae698c97e9445a6ab928d4c Mon Sep 17 00:00:00 2001
From: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Date: Fri, 22 Apr 2022 10:07:51 +0900
Subject: [PATCH 1/2] path/filepath: do not remove prefix "." when following
 path contains ":".

Fixes #52476

Change-Id: I9eb72ac7dbccd6322d060291f31831dc389eb9bb
Reviewed-on: https://go-review.googlesource.com/c/go/+/401595
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

Conflict: NA
Reference: https://go-review.googlesource.com/c/go/+/401595/

---
 src/path/filepath/path.go              | 14 +++++++++++++-
 src/path/filepath/path_test.go         |  3 +++
 src/path/filepath/path_windows_test.go | 26 ++++++++++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go
index 26f1833189..92dc090eea 100644
--- a/src/path/filepath/path.go
+++ b/src/path/filepath/path.go
@@ -116,9 +116,21 @@ func Clean(path string) string {
 		case os.IsPathSeparator(path[r]):
 			// empty path element
 			r++
-		case path[r] == '.' && (r+1 == n || os.IsPathSeparator(path[r+1])):
+		case path[r] == '.' && r+1 == n:
 			// . element
 			r++
+		case path[r] == '.' && os.IsPathSeparator(path[r+1]):
+			// ./ element
+			r++
+
+			for r < len(path) && os.IsPathSeparator(path[r]) {
+				r++
+			}
+			if out.w == 0 && volumeNameLen(path[r:]) > 0 {
+				// When joining prefix "." and an absolute path on Windows,
+				// the prefix should not be removed.
+				out.append('.')
+			}
 		case path[r] == '.' && path[r+1] == '.' && (r+2 == n || os.IsPathSeparator(path[r+2])):
 			// .. element: remove to last separator
 			r += 2
diff --git a/src/path/filepath/path_test.go b/src/path/filepath/path_test.go
index d6f680556c..531a66333b 100644
--- a/src/path/filepath/path_test.go
+++ b/src/path/filepath/path_test.go
@@ -93,6 +93,9 @@ var wincleantests = []PathTest{
 	{`//host/share/foo/../baz`, `\\host\share\baz`},
 	{`\\a\b\..\c`, `\\a\b\c`},
 	{`\\a\b`, `\\a\b`},
+	{`.\c:`, `.\c:`},
+	{`.\c:\foo`, `.\c:\foo`},
+	{`.\c:foo`, `.\c:foo`},
 }
 
 func TestClean(t *testing.T) {
diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go
index f7c454bf65..e3979fe1e7 100644
--- a/src/path/filepath/path_windows_test.go
+++ b/src/path/filepath/path_windows_test.go
@@ -581,3 +581,29 @@ func TestNTNamespaceSymlink(t *testing.T) {
 		t.Errorf(`EvalSymlinks(%q): got %q, want %q`, filelink, got, want)
 	}
 }
+
+func TestIssue52476(t *testing.T) {
+	tests := []struct {
+		lhs, rhs string
+		want     string
+	}{
+		{`..\.`, `C:`, `..\C:`},
+		{`..`, `C:`, `..\C:`},
+		{`.`, `:`, `:`},
+		{`.`, `C:`, `.\C:`},
+		{`.`, `C:/a/b/../c`, `.\C:\a\c`},
+		{`.`, `\C:`, `.\C:`},
+		{`C:\`, `.`, `C:\`},
+		{`C:\`, `C:\`, `C:\C:`},
+		{`C`, `:`, `C\:`},
+		{`\.`, `C:`, `\C:`},
+		{`\`, `C:`, `\C:`},
+	}
+
+	for _, test := range tests {
+		got := filepath.Join(test.lhs, test.rhs)
+		if got != test.want {
+			t.Errorf(`Join(%q, %q): got %q, want %q`, test.lhs, test.rhs, got, test.want)
+		}
+	}
+}
-- 
2.30.2

