Description
What version of Go are you using (go version
)?
$ go version go version go1.15.3 windows/amd64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env set GO111MODULE= set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\kevpar\AppData\Local\go-build set GOENV=C:\Users\kevpar\AppData\Roaming\go\env set GOEXE=.exe set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Users\kevpar\go\pkg\mod set GONOPROXY= set GONOSUMDB= set GOOS=windows set GOPATH=C:\Users\kevpar\go set GOPRIVATE= set GOPROXY=https://proxy.golang.org,direct set GOROOT=c:\go set GOSUMDB=sum.golang.org set GOTMPDIR= set GOTOOLDIR=c:\go\pkg\tool\windows_amd64 set GCCGO=gccgo set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=C:\Users\kevpar\Source\repos\test\go.mod set CGO_CFLAGS=-g -O2 set CGO_CPPFLAGS= set CGO_CXXFLAGS=-g -O2 set CGO_FFLAGS=-g -O2 set CGO_LDFLAGS=-g -O2 set PKG_CONFIG=pkg-config set GOGCCFLAGS=-m64 -mthreads -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\Users\kevpar\AppData\Local\Temp\go-build377379998=/tmp/go-build -gno-record-gcc-switches GOROOT/bin/go version: go version go1.15.3 windows/amd64 GOROOT/bin/go tool compile -V: compile version go1.15.3
What did you do?
main.go
package main
import "path/filepath"
func main() {
p, err := filepath.EvalSymlinks(`\\localhost\c$`)
if err != nil {
panic(err)
}
println(p)
}
Repro:
> go run .
What did you expect to see?
\\localhost\c$
What did you see instead?
panic: The system cannot find the file specified.
goroutine 1 [running]:
main.main()
C:/Users/kevpar/Source/repos/test/main.go:8 +0xad
exit status 2
Details
This is due to EvalSymlinks
using the Windows API FindFirstFile to attempt to resolve each path component after the volume name. Since \\localhost\c$
is parsed as the volume name with an empty path following it, this results in a call to FindFirstFile
for the path of \\localhost\c$
, which fails with The system cannot find the file specified.
.
The failure from FindFirstFile
is expected. Per the documentation for that function:
On network shares, you can use an lpFileName in the form of the following: "\\Server\Share*". However, you cannot use an lpFileName that points to the share itself; for example, "\\Server\Share" is not valid.
One important occurrence of this bug is with Kubernetes. The containerd/containerd runtime uses EvalSymlinks
to resolve each container mount path on the host. When a mount path is the root of a UNC share (or a symlink to the share root), then EvalSymlinks
fails, preventing the container from starting. More discussion on this in kubernetes/kubernetes#94213.
As a workaround, if the path to EvalSymlinks
has a \
appended, the function works properly, as it hits a conditional that causes an early return, before calling FindFirstFile
. However, it would be good to fix the source of this issue as well.
Note: This repros for all UNC share roots passed to EvalSymlinks
. I used the admin share (\\localhost\c$
) in the example, as that allows a repro without needing access to an external network share, which makes testing easier.