os: remap / to \ in Windows os.Root.Symlink targets Matches os.Symlink behavior. Windows accepts forward and backward slash as interchangeable in most contexts, but not in symlink targets. Fixes #80073 Change-Id: I62fdf301c7a2e713faaa5fbe4654d06e6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/go/+/793020 Reviewed-by: Quim Muntal <quimmuntal@gmail.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/src/os/root_windows.go b/src/os/root_windows.go index ca3bea8..362963d 100644 --- a/src/os/root_windows.go +++ b/src/os/root_windows.go
@@ -242,6 +242,10 @@ return syscall.EINVAL } + // Windows treats / and \ as equivalent almost everywhere, but not in symlink targets. + // Match os.Symlink behavior and convert / to \. + oldname = filepathlite.FromSlash(oldname) + // CreateSymbolicLinkW converts volume-relative paths into absolute ones. // Do the same. if filepathlite.VolumeNameLen(oldname) > 0 && !filepathlite.IsAbs(oldname) {
diff --git a/src/os/root_windows_test.go b/src/os/root_windows_test.go index f393ad4..ea604d1 100644 --- a/src/os/root_windows_test.go +++ b/src/os/root_windows_test.go
@@ -235,6 +235,31 @@ } } +func TestRootSymlinkNormalization(t *testing.T) { + if !testenv.HasSymlink() { + t.Skip("skipping test; no symlink support") + } + const content = "dir/target" // same as file name + dir := makefs(t, []string{ + "dir/target", + }) + root, err := os.OpenRoot(dir) + if err != nil { + t.Fatal(err) + } + defer root.Close() + if err := root.Symlink("dir/target", "link"); err != nil { + t.Fatal(err) + } + got, err := os.ReadFile(dir + "/link") + if err != nil { + t.Fatal(err) + } + if string(got) != content { + t.Fatalf("read link contents %q, want %q", got, content) + } +} + func TestRootOpenFileTruncateNamedPipe(t *testing.T) { t.Parallel() name := pipeName()