go: os: MkdirAll fails when a parent folder contains trailing spaces
What version of Go are you using (go version)?
$ go version go version go1.18.4 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=on set GOARCH=amd64 set GOBIN= set GOCACHE=C:\Users\TEST\AppData\Local\go-build set GOENV=C:\Users\TEST\AppData\Roaming\go\env set GOEXE=.exe set GOEXPERIMENT= set GOFLAGS= set GOHOSTARCH=amd64 set GOHOSTOS=windows set GOINSECURE= set GOMODCACHE=C:\Users\TEST\go\pkg\mod set GONOPROXY= set GONOSUMDB= set GOOS=windows set GOPATH=C:\Users\TEST\go;C:\Daten\go set GOPRIVATE= set GOPROXY=https://proxy.golang.org,direct set GOROOT=C:\Program Files\Go set GOSUMDB=sum.golang.org set GOTMPDIR= set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64 set GOVCS= set GOVERSION=go1.18.4 set GCCGO=gccgo set GOAMD64=v1 set AR=ar set CC=gcc set CXX=g++ set CGO_ENABLED=1 set GOMOD=C:\Daten\go\src\megabildgo\go.mod set GOWORK= 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 -fmessage-length=0 -fdebug-prefix-map=C:\Users\TEST\AppData\Local\Temp\go-build1829370843=/tmp/go-build -gno-record-gcc-switches
What did you do?
err := os.MkdirAll(“C:\\temp\\folder \\this one fails”, 0644)
if err != nil {
fmt.Println(err)
}
What did you expect to see?
All folders being created
What did you see instead?
There is a folder “folder” without trailing space. The folder “this one fails” is not created. an os.PathError is thrown: mkdir C:\temp\folder \this one fails: Das System kann den angegebenen Pfad nicht finden.
About this issue
- Original URL
- State: open
- Created 2 years ago
- Comments: 19 (14 by maintainers)
Note that many of the functions in the
ospackage already call thefixLongPathhelper function, which converts paths of length ≥ 248 to the equivalent\\?\form provided thatos.canUseLongPathsis also false (see #3358, #41734).So under certain conditions today,
Mkdir(andMkdirAll) will actually succeed in creating the space-containing paths as requested.In other conditions,
Mkdirsilently creates a path that differs by a trailing character, andMkdirAllcreates a path that differs from some parent directory by a trailing character, then fails. Neither of those behaviors seems like what the user intends, and neither is particularly useful.I think that, rather than letting the OS do as it likes and possibly fail in ways that are subtle and difficult to predict, we should explicitly choose to either always succeed or always fail for these paths. That is, we should choose either:
a. Change
os.Mkdir(and probably alsoos.OpenFilewithO_CREATEas well?) to check path components for non-UNC paths for trailing ASCII spaces and periods, and explicitly reject paths that end in an ASCII space or period.os.MkdirAllto refuse to create such paths.\\?\form to manipulate them.os.ReadDirat HEAD seems to fail for such a directory.)or:
b. Change (and rename)
fixLongPathto check for such paths and escape them to their corresponding\\?\forms.os.ReadDirthat fail for such paths today, so the improved consistency would be limited at best. (Unless we fully open the can of worms and try to fix everything to work with these paths…)On balance, I would prefer to make
MkdirandOpenFilereject these paths explicitly.(CC @neild @zx2c4 for Windows path normalization, the gift that keeps on giving)
Normalizing this should be trivial, but I would argue against doing so. It would fix the
MkdirAll, yet the underlying problem will still be there: the white-spaced path won’t exist even thoughMkdirAllsucceed, so any other operation on that path is going to fail.We either always normalize all paths or we don’t, else we will just be moving the failure to a later point, making it worst.
As @seankhliao mentioned, Windows already supports trailing whitespaces when the path is prefixed with
\\?\, so the workaround is there.Additional data point: .Net does not support this either for the sake of interoperability. https://github.com/dotnet/runtime/issues/23292#issuecomment-363237559.
Thank you, @bcmills, for such a great explanation with the possible options. I will start working on approach a., check it with the regression test you provided, and share the results here for further discussion.
Improving the error message seems like a good idea, but I would try to avoid adding logic to detect leading/trailing whitespaces or periods and instead rely on giving general tips based on windows error codes.
For example,
MkdirAlllogic ensures that intermediate directories exist by calling CreateDirectory on every non-existing intermediate directory. So, ifsyscall.CreateDirectoryreturnsERROR_PATH_NOT_FOUND(see error description in docs), it means that either a concurrent process deleted the directory or that Windows did not honor the folder path in a previousCreateDirectorycall.Windows docs have a special entry for dealing with whitespaces in files and folder names: https://docs.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/file-folder-name-whitespace-characters.
The relevant part is this:
Looking at how
MkdirAllis implemented on windows, it seems that we are not removing leading and trailing whitespaces from non-leaf folders. This leads to failure mode: