Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

path/filepath: Clean() incorrect trunc for volume-guid paths on Windows #30307

Closed
lowenna opened this issue Feb 19, 2019 · 13 comments
Closed

path/filepath: Clean() incorrect trunc for volume-guid paths on Windows #30307

lowenna opened this issue Feb 19, 2019 · 13 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. OS-Windows release-blocker
Milestone

Comments

@lowenna
Copy link

lowenna commented Feb 19, 2019

What version of Go are you using (go version)?

$ go version
go version go1.12rc1 windows/amd64

Does this issue reproduce with the latest release?

Yes, and is a change in behaviour from 1.11.x

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
PS E:\docker\build\golang1.12> go env
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\Administrator\AppData\Local\go-build
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=e:\go
set GOPROXY=
set GORACE=
set GOROOT=C:\Go
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
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\ADMINI~1\AppData\Local\Temp\1\go-build584421990=/tmp/go-build -gno-record-gcc-switches
PS E:\docker\build\golang1.12>

@alexbrainman

This is the root cause of failures on Windows for upgrading github.com/moby/moby to golang 1.12. (See moby/moby#38404).

The cause appears to be the fix for #27791: d1f7470

In the MSDN documentation for "volume-guid"-style naming, Windows should have a trailing backslash (https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-volume). The specific quote is

To solve this problem, the operating system uses volume GUID paths to identify volumes. These are strings of this form:
"\\?\Volume{GUID}\"
where GUID is a globally unique identifier (GUID) that identifies the volume.

To simplify demonstration of the issue, run this test code on golang 1.11.5 and 1.12RC1 (or 1.12Beta)

package main

import (
	"fmt"
	"path/filepath"
)

func main() {
	p := `\\?\Volume{7650f9bc-33f8-11e9-9bf2-e06fe09706b2}\`
	fmt.Println(filepath.Clean(p))
}

On go 1.11.5

E:\docker\build\golang1.12> .\golang1.12.exe
\\?\Volume{7650f9bc-33f8-11e9-9bf2-e06fe09706b2}\ <nil>
E:\docker\build\golang1.12>

On go 1.12RC1

PS E:\docker\build\golang1.12> .\golang1.12.exe
\\?\Volume{7650f9bc-33f8-11e9-9bf2-e06fe09706b2} <nil>
PS E:\docker\build\golang1.12>

Specifically, the trailing backslash is now omitted making an invalid filepath following the MSDN documentation.

@mikioh mikioh changed the title Windows: filepath.Clean() incorrect trunc for volume-guid paths path/filepath: Clean() incorrect trunc for volume-guid paths on Windows Feb 19, 2019
@thaJeztah
Copy link
Contributor

@bcmills @mikioh can this be added to the 1.12 milestone (as it caused a regression)?

@mikioh mikioh added this to the Go1.12 milestone Feb 20, 2019
@mikioh mikioh added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Feb 20, 2019
@mikioh
Copy link
Contributor

mikioh commented Feb 20, 2019

I added the requested label and NeedsInvestigation temporarily.
@ianlancetaylor, can you please triage this issue?

@ianlancetaylor
Copy link
Contributor

CC @QtRoS @alexbrainman

This does seem to have been introduced by https://golang.org/cl/137055 for #27991. I don't understand the fix. The issue says that Clean(\\somepath\dir\) should return \\somepath\dir, which seems reasonable although I don't know if it's actually correct. And I'm not sure what to make of the MSDN docs, which use a trailing slash for everything.

Given that this broke something, we should probably just roll it back and consider trying again for 1.13.

@gopherbot
Copy link

Change https://golang.org/cl/163077 mentions this issue: path/filepath: revert "fix Windows-specific Clean bug"

@ianlancetaylor
Copy link
Contributor

I sent https://golang.org/cl/163077 but I don't really know if it's right. It is, at least, likely to be safe.

@lowenna
Copy link
Author

lowenna commented Feb 20, 2019

Thanks @ianlancetaylor. I'm happy to try and figure out the right fix in conjunction with yourself and @alexbrainman for 1.13 if you would like. However, at RC1 of 1.12, I tend to agree that reverting the previous fix is probably the safest bet at this stage rather than trying to put a potentially faulty fix in.

I believe you are correct that a "traditional" UNC-style path Clean("\\server\share\") should return "\\server\share" based on the description of filepath.Clean(). Specifically Clean returns the shortest path name equivalent to path...

For volume-guid paths, the rule is, somewhat annoyingly, but hey - I didn't create this API!, slightly different. It stems from this sentence in https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-volume: All volume and mounted folder functions that take a volume GUID path as an input parameter require the trailing backslash. In other words, (excluding the CreateFile API exception), a volume-guid style path requires the trailing backslash when there's nothing following it.

So I believe (although haven't done full validation of this), the following should be the expected behaviour when calling Clean on volume-GUID style paths to get the shortest (valid) path name:

  • \\?\Volume{GUID}\ --> \\?\Volume{GUID}\ (backslash remains otherwise invalid)
  • \\?\Volume{GUID}\folder\ --> \\?\Volume{GUID}\folder (backslash stripped)*
  • \\?\Volume{GUID}\folder\..\folder2\file.txt --> \\?\Volume{GUID}\folder2\file.txt

The one marked * is where I'm not entirely sure as there's some ambiguity reading the documentation, but is certainly what golang 1.11 does, and does not appear to have any issues I am aware of. The way I read the docs is that it's referring to when it's "just" a volume-guid style path, without any further relative paths added - ie just the \\?\Volume{GUID}\, not \\?\Volume{GUID}\something\else\here.txt

@gopherbot
Copy link

Change https://golang.org/cl/163078 mentions this issue: [release-branch.go1.12] path/filepath: revert "fix Windows-specific Clean bug"

@QtRoS
Copy link
Contributor

QtRoS commented Feb 20, 2019

@ianlancetaylor sorry for incomplete fix. Maybe changing method's documentation will be helpful in addiction to revert? I believe that current description is not correct, because of it some kind of misunderstanding and wrong expectations is possible.

@ianlancetaylor
Copy link
Contributor

It is not obvious to me that the documentation is wrong. Perhaps \\server\share\ could be considered a kind of root directory. But I don't know much about Windows.

nebulabox pushed a commit to nebulabox/go that referenced this issue Feb 21, 2019
Revert CL 137055, which changed Clean("\\somepath\dir\") to return
"\\somepath\dir" on Windows. It's not entirely clear this is correct,
as this path is really "\\server\share\", and as such the trailing
slash may be the path on that share, much like "C:\". In any case, the
change broke existing code, so roll it back for now and rethink for 1.13.

Updates golang#27791
Fixes golang#30307

Change-Id: I69200b1efe38bdb6d452b744582a2bfbb3acbcec
Reviewed-on: https://go-review.googlesource.com/c/163077
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
@alexbrainman
Copy link
Member

I sent https://golang.org/cl/163077 but I don't really know if it's right. It is, at least, likely to be safe.

Thank you @ianlancetaylor for reverting promptly - sounds like the safest option. I will think about what to do here, and report back.

Alex

@lowenna
Copy link
Author

lowenna commented Feb 26, 2019

@alexbrainman Hey Alex - am I interpreting gerrit correctly and the reversion didn't make into go 1.12 released yesterday? https://go-review.googlesource.com/c/go/+/163078 still shows active. So I guess I have to wait for 1.12.1? Thanks.

gopherbot pushed a commit that referenced this issue Feb 26, 2019
…lean bug"

Revert CL 137055, which changed Clean("\\somepath\dir\") to return
"\\somepath\dir" on Windows. It's not entirely clear this is correct,
as this path is really "\\server\share\", and as such the trailing
slash may be the path on that share, much like "C:\". In any case, the
change broke existing code, so roll it back for now and rethink for 1.13.

Updates #27791
Updates #30307

Change-Id: I69200b1efe38bdb6d452b744582a2bfbb3acbcec
Reviewed-on: https://go-review.googlesource.com/c/163077
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
(cherry picked from commit 153c0da)
Reviewed-on: https://go-review.googlesource.com/c/163078
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
@alexbrainman
Copy link
Member

@alexbrainman Hey Alex - am I interpreting gerrit correctly and the reversion didn't make into go 1.12 released yesterday? https://go-review.googlesource.com/c/go/+/163078 still shows active. So I guess I have to wait for 1.12.1?

I think you are correct.

There were 2 CLs merged - none of them made to go1.12.

https://golang.org/cl/163077 is merged to master branch.

And https://go-review.googlesource.com/c/go/+/163078 is merged to release-branch.go1.12 branch. There are 3 commits (including CL 163078) on top of go1.12 tag on that branch at this moment. That branch will be used to make go1.12.1.

Alex

@alexbrainman
Copy link
Member

We still need to merge fix for #27791 (originally it was fixed by https://golang.org/cl/137055 but then reversed).

This time we need to make sure that fix for #27791 does not break paths that start with \\?\Volume{...}.

We can just adjust filepath.Clean carefully enough, so not to break volume-guid paths (we will still need new test for filepath.Clean(\\?\Volume{...}).

Alternatively, we could make sure that everything in path/filepath and os package handles volume-guid path first, and then fix #27791.

I was considering what is involved in doing the latter, and I think it is quite involved.

If we are to handle all non-DOS paths. we would have to handle (see https://docs.microsoft.com/en-us/windows/desktop/fileio/naming-a-file for details):

  • Win32 File Namespaces = file paths start with \\?\
  • Win32 Device Namespaces = file paths start with \\.\

Below are some random questions / issues I can think of in this area. There will be many more, like this.


What would VolumeName return for paths starting with \\?\ ?

\\?\C:\Users\Alex - ( VolumeName(C:\Users\Alex) returns C:)

\\?\UTC\server\share - ( VolumeName(\\server\share) returns \\server\share )

\\?\Volume{ABC}\

\\.\COM1

... probably many others, that I did not considered.


VolumeName was originally introduced to handle relative paths. But none of \\?\ or \\.\ can be relative. So VolumeName has no meaning for these.

And relative path can only start with "drive letter", like C: or D:, so VolumeName function name is wrong now - it should have being called DriveLetter or something.


There are many functions in path/filepath package that needs to be fixed, if we are to support paths starting with \\?\.

We also have many non-exported functions in os package (like isAbs, and many others), that needs to be fixed too.

A lot of work.

Maybe we could move all current implementation into internal package to avoid duplication.


I just submitted fix to make Readlink(\\?\Volume{ABC}) work (see CL 164201). My CL makes symlink targets, like \\?\Volume{ABC}, walk extra step past file that looks like \\?\Volume{ABC}, and into the file that looks like C:\....

But, if we are to support paths, like \\?\Volume{ABC}, maybe my CL should not do that extra step.

The CL will be released in go1.13. Maybe we should adjust my CL before go1.13 is released to prevent future compatibility problems.


Both / and \ in paths are OK to use in os and path/filepath at this moment.

But paths starting with \\?\ will not allow / in the paths.


Same.

Both . and .. in paths are OK to use in os and path/filepath.

Paths starting with \\?\ will not allow . or .. in the paths.


Path like \\?\Volume{ABC} is used for windows-arm - windows-arm temporary directory is stored inside of symlink to \\?\Volume{ABC} (see #29746 (comment) for details). Perhaps path like \\?\Volume{ABC} will become more common.


So should we just make small change in filepath.Clean, or should we make all code in path/filepath and os handle paths like \\?\ ?

/cc @bradfitz and @mattn for their opinion.

Alex

@golang golang locked and limited conversation to collaborators Mar 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. OS-Windows release-blocker
Projects
None yet
Development

No branches or pull requests

7 participants