-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
os: MkdirAll fails when a parent folder contains trailing spaces #54040
Comments
cc @golang/windows |
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
|
how much normalization should we do vs having the user specify |
Normalizing this should be trivial, but I would argue against doing so. It would fix the 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 Additional data point: .Net does not support this either for the sake of interoperability. dotnet/runtime#23292 (comment). |
I am also against normalizing this. If Windows changes its behavior in the future, Go will have to follow the changes. |
Creating the folder with \?\ works with MkdirAll. Can we make this the default behavior on windows? Otherwise the programmer needs to prefix the MkdirAll call if needed. I wonder if anyone even knows about that special \?\ thing. |
From Windows docs:
|
I agree that we should not normalize paths here, but could we at least improve the failure mode here? (Perhaps something in the call stack could notice the leading or trailing space in the intermediate paths and fail with a clearer error message.) |
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, |
Change https://go.dev/cl/458136 mentions this issue: |
@vladtenlive Use |
Or I guess that we now recommend the equivalent |
@ianlancetaylor fixed, thank you |
I think that leaves us back where we started: if we can't distinguish between “a concurrent process deleted the directory“ and “Windows did not honor the folder path in a previous |
Note that many of the functions in the So under certain conditions today, In other conditions, 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
or: b. Change (and rename)
On balance, I would prefer to make (CC @neild @zx2c4 for Windows path normalization, the gift that keeps on giving) |
I would suggest the following (https://go.dev/play/p/T63O30FOM8Y?v=gotip) as a regression test. It passes today on Linux. // Regression test for https://go.dev/issue/54040:
// Mkdir for a path ending with a trailing ASCII space (or period)
// should either return a non-nil error or create a usable directory.
func TestMkdirTrailingSpace(t *testing.T) {
parent := t.TempDir()
dir := filepath.Join(parent, "ends in space ")
err := os.Mkdir(dir, 0777)
if err != nil {
if runtime.GOOS == "windows" && !strings.HasPrefix(dir, `\\?\`) {
t.Skipf("skipping: Windows does not support paths with trailing spaces")
}
t.Fatal(err)
}
t.Logf("Mkdir(%q, 0777)", dir)
t.Cleanup(func() {
err := os.Remove(dir)
if err == nil {
t.Logf("Remove(%q)", dir)
} else {
t.Error(err)
}
})
fi, err := os.Lstat(dir)
if err == nil {
t.Logf("Lstat(%q).Name() = %q", dir, fi.Name())
} else {
t.Error(err)
}
fi, err = os.Stat(dir)
if err == nil {
t.Logf("Stat(%q).Name() = %q", dir, fi.Name())
} else {
t.Error(err)
}
ents, err := os.ReadDir(dir)
if err == nil {
t.Logf("ReadDir(%q) = %v", dir, ents)
} else {
t.Error(err)
}
sub := filepath.Join(dir, "sub")
err = os.Mkdir(sub, 0777)
if err == nil {
t.Logf("Mkdir(%q, 0777)", sub)
err := os.Remove(sub)
if err == nil {
t.Logf("Remove(%q)", sub)
} else {
t.Error(err)
}
} else {
t.Error(err)
}
} |
This option sounds reasonable to me. (Although rather than "for non-UNC paths", say "for paths not starting with |
Thank you, @bcmills, for such a great explanation with the possible options.
|
Change https://go.dev/cl/618496 mentions this issue: |
Checks for a valid Windows path by ensuring the path doesn't trailing spaces or periods. Fixes golang#54040. Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64 Change-Id: I266f79963c821f8cc474097d3e57c5645ad996fc
The more I think about the "option a" detailed in #54040 (comment) (and implemented in CL 618496), the less I like it, as it is a breaking change. Until today, one could do something like the following (pseudo)code snippet and assume Windows compatibility layer to chime by creating and opening a file without the trailing space. const name = "test.txt "
os.Create(name)
os.Open(name) After CL 618496, Go will error out in IMO fixing the original issue ( Also, thanks to the work done in #67002, we will soon have some additional primitives ( Having said all this, I propose to revert |
I agree, I'll revert the CL now via CL 629595 |
This reverts commit CL 618496. Reason for revert: #54040 (comment) Change-Id: I3bf27f7fdd475a005cb6aa190994153504e96fb5 Reviewed-on: https://go-review.googlesource.com/c/go/+/629595 Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Quim Muntal <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Damien Neil <[email protected]>
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat 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.
The text was updated successfully, but these errors were encountered: