mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
os: handle trailing slashes in os.RemoveDir on Windows
CL 661575 inadvertently caused os.RemoveDir on Windows to fail when given a path with a trailing / or \, due to the splitPath function not correctly stripping trailing separators. Fixes #73317 Change-Id: I21977b94bb08ff1e563de6f5f16a4bdf5024a15e Reviewed-on: https://go-review.googlesource.com/c/go/+/664715 Auto-Submit: Damien Neil <dneil@google.com> TryBot-Bypass: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Alan Donovan <adonovan@google.com>
This commit is contained in:
parent
396a48bea6
commit
79b809afb3
@ -11,4 +11,5 @@ var (
|
||||
NewConsoleFile = newConsoleFile
|
||||
CommandLineToArgv = commandLineToArgv
|
||||
AllowReadDirFileID = &allowReadDirFileID
|
||||
SplitPath = splitPath
|
||||
)
|
||||
|
@ -2074,3 +2074,39 @@ func TestFileAssociatedWithExternalIOCP(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitPath(t *testing.T) {
|
||||
t.Parallel()
|
||||
for _, tt := range []struct{ path, wantDir, wantBase string }{
|
||||
{`a`, `.`, `a`},
|
||||
{`a\`, `.`, `a`},
|
||||
{`a\\`, `.`, `a`},
|
||||
{`a\b`, `a`, `b`},
|
||||
{`a\\b`, `a`, `b`},
|
||||
{`a\b\`, `a`, `b`},
|
||||
{`a\b\c`, `a\b`, `c`},
|
||||
{`\a`, `\`, `a`},
|
||||
{`\a\`, `\`, `a`},
|
||||
{`\a\b`, `\a`, `b`},
|
||||
{`\a\b\`, `\a`, `b`},
|
||||
{`\a\b\c`, `\a\b`, `c`},
|
||||
{`\\a`, `\\a`, `.`},
|
||||
{`\\a\`, `\\a\`, `.`},
|
||||
{`\\\a`, `\\\a`, `.`},
|
||||
{`\\\a\`, `\\\a`, `.`},
|
||||
{`\\a\b\c`, `\\a\b`, `c`},
|
||||
{`c:`, `c:`, `.`},
|
||||
{`c:\`, `c:\`, `.`},
|
||||
{`c:\a`, `c:\`, `a`},
|
||||
{`c:a`, `c:`, `a`},
|
||||
{`c:a\b\`, `c:a`, `b`},
|
||||
{`c:base`, `c:`, `base`},
|
||||
{`a/b/c`, `a/b`, `c`},
|
||||
{`a/b/c/`, `a/b`, `c`},
|
||||
{`\\?\c:\a`, `\\?\c:\`, `a`},
|
||||
} {
|
||||
if dir, base := os.SplitPath(tt.path); dir != tt.wantDir || base != tt.wantBase {
|
||||
t.Errorf("splitPath(%q) = %q, %q, want %q, %q", tt.path, dir, base, tt.wantDir, tt.wantBase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,51 @@ func IsPathSeparator(c uint8) bool {
|
||||
|
||||
// splitPath returns the base name and parent directory.
|
||||
func splitPath(path string) (string, string) {
|
||||
dirname, basename := filepathlite.Split(path)
|
||||
volnamelen := filepathlite.VolumeNameLen(dirname)
|
||||
for len(dirname) > volnamelen && IsPathSeparator(dirname[len(dirname)-1]) {
|
||||
dirname = dirname[:len(dirname)-1]
|
||||
if path == "" {
|
||||
return ".", "."
|
||||
}
|
||||
|
||||
// The first prefixlen bytes are part of the parent directory.
|
||||
// The prefix consists of the volume name (if any) and the first \ (if significant).
|
||||
prefixlen := filepathlite.VolumeNameLen(path)
|
||||
if len(path) > prefixlen && IsPathSeparator(path[prefixlen]) {
|
||||
if prefixlen == 0 {
|
||||
// This is a path relative to the current volume, like \foo.
|
||||
// Include the initial \ in the prefix.
|
||||
prefixlen = 1
|
||||
} else if path[prefixlen-1] == ':' {
|
||||
// This is an absolute path on a named drive, like c:\foo.
|
||||
// Include the initial \ in the prefix.
|
||||
prefixlen++
|
||||
}
|
||||
}
|
||||
|
||||
i := len(path) - 1
|
||||
|
||||
// Remove trailing slashes.
|
||||
for i >= prefixlen && IsPathSeparator(path[i]) {
|
||||
i--
|
||||
}
|
||||
path = path[:i+1]
|
||||
|
||||
// Find the last path separator. The basename is what follows.
|
||||
for i >= prefixlen && !IsPathSeparator(path[i]) {
|
||||
i--
|
||||
}
|
||||
basename := path[i+1:]
|
||||
if basename == "" {
|
||||
basename = "."
|
||||
}
|
||||
|
||||
// Remove trailing slashes. The remainder is dirname.
|
||||
for i >= prefixlen && IsPathSeparator(path[i]) {
|
||||
i--
|
||||
}
|
||||
dirname := path[:i+1]
|
||||
if dirname == "" {
|
||||
dirname = "."
|
||||
}
|
||||
|
||||
return dirname, basename
|
||||
}
|
||||
|
||||
|
@ -471,6 +471,27 @@ func TestRemoveAllNoFcntl(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveAllTrailingSlash(t *testing.T) {
|
||||
slashes := []string{"/"}
|
||||
if runtime.GOOS == "windows" {
|
||||
slashes = append(slashes, `\`)
|
||||
}
|
||||
for _, slash := range slashes {
|
||||
dir := makefs(t, []string{
|
||||
"dir/a/file1",
|
||||
"dir/a/file2",
|
||||
"dir/file3",
|
||||
})
|
||||
path := dir + "/dir"
|
||||
if err := RemoveAll(path + slash); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := Stat(path); !IsNotExist(err) {
|
||||
t.Errorf("after RemoveAll(%q), directory still exists", path+slash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRemoveAll(b *testing.B) {
|
||||
tmpDir := filepath.Join(b.TempDir(), "target")
|
||||
b.ReportAllocs()
|
||||
|
Loading…
x
Reference in New Issue
Block a user