mirror of
https://github.com/rclone/rclone.git
synced 2024-12-19 09:13:43 +08:00
711478554e
Starting with go1.22 the standard os.MkdirAll has improved its handling of volume names, and as part of that it now stops recursing into parent directory if it is a volume name (see:cd589c8a73
). This is similar to what was our main change and reason for creating a custom version. When building with go1.22 or newer we can therefore stop using our custom version, with the advantage that we automatically get current and future relevant improvements from golang. To support building with go1.21 the existing custom version is still kept, and therefore also our wrapper function file.MkdirAll - but it now just calls os.MkdirAll with go1.22 or newer on Windows. See #5401, #6420 andacf1e2df84
for details about the creation of our custom version of MkdirAll.
162 lines
5.2 KiB
Go
162 lines
5.2 KiB
Go
//go:build windows && !go1.22
|
|
|
|
package file
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Basic test from golang's os/path_test.go
|
|
func TestMkdirAll(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
|
|
path := tmpDir + "/dir/./dir2"
|
|
err := MkdirAll(path, 0777)
|
|
if err != nil {
|
|
t.Fatalf("MkdirAll %q: %s", path, err)
|
|
}
|
|
|
|
// Already exists, should succeed.
|
|
err = MkdirAll(path, 0777)
|
|
if err != nil {
|
|
t.Fatalf("MkdirAll %q (second time): %s", path, err)
|
|
}
|
|
|
|
// Make file.
|
|
fpath := path + "/file"
|
|
f, err := Create(fpath)
|
|
if err != nil {
|
|
t.Fatalf("create %q: %s", fpath, err)
|
|
}
|
|
defer func() {
|
|
if err := f.Close(); err != nil {
|
|
t.Fatalf("Close %q: %s", fpath, err)
|
|
}
|
|
}()
|
|
|
|
// Can't make directory named after file.
|
|
err = MkdirAll(fpath, 0777)
|
|
if err == nil {
|
|
t.Fatalf("MkdirAll %q: no error", fpath)
|
|
}
|
|
perr, ok := err.(*os.PathError)
|
|
if !ok {
|
|
t.Fatalf("MkdirAll %q returned %T, not *PathError", fpath, err)
|
|
}
|
|
if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
|
|
t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", fpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
|
|
}
|
|
|
|
// Can't make subdirectory of file.
|
|
ffpath := fpath + "/subdir"
|
|
err = MkdirAll(ffpath, 0777)
|
|
if err == nil {
|
|
t.Fatalf("MkdirAll %q: no error", ffpath)
|
|
}
|
|
perr, ok = err.(*os.PathError)
|
|
if !ok {
|
|
t.Fatalf("MkdirAll %q returned %T, not *PathError", ffpath, err)
|
|
}
|
|
if filepath.Clean(perr.Path) != filepath.Clean(fpath) {
|
|
t.Fatalf("MkdirAll %q returned wrong error path: %q not %q", ffpath, filepath.Clean(perr.Path), filepath.Clean(fpath))
|
|
}
|
|
|
|
path = tmpDir + `\dir\.\dir2\`
|
|
err = MkdirAll(path, 0777)
|
|
if err != nil {
|
|
t.Fatalf("MkdirAll %q: %s", path, err)
|
|
}
|
|
}
|
|
|
|
func unusedDrive(t *testing.T) string {
|
|
letter := FindUnusedDriveLetter()
|
|
require.NotEqual(t, letter, 0)
|
|
return string(letter) + ":"
|
|
}
|
|
|
|
func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs ...string) {
|
|
if valid {
|
|
assert.NoError(t, MkdirAll(path, 0777))
|
|
} else {
|
|
err := MkdirAll(path, 0777)
|
|
assert.Error(t, err)
|
|
ok := false
|
|
for _, msg := range errormsgs {
|
|
if err.Error() == msg {
|
|
ok = true
|
|
}
|
|
}
|
|
assert.True(t, ok, fmt.Sprintf("Error message '%v' didn't match any of %v", err, errormsgs))
|
|
}
|
|
}
|
|
|
|
func checkMkdirAllSubdirs(t *testing.T, path string, valid bool, errormsgs ...string) {
|
|
checkMkdirAll(t, path, valid, errormsgs...)
|
|
checkMkdirAll(t, path+`\`, valid, errormsgs...)
|
|
checkMkdirAll(t, path+`\parent`, valid, errormsgs...)
|
|
checkMkdirAll(t, path+`\parent\`, valid, errormsgs...)
|
|
checkMkdirAll(t, path+`\parent\child`, valid, errormsgs...)
|
|
checkMkdirAll(t, path+`\parent\child\`, valid, errormsgs...)
|
|
}
|
|
|
|
// Testing paths on existing drive
|
|
func TestMkdirAllOnDrive(t *testing.T) {
|
|
path := t.TempDir()
|
|
|
|
dir, err := os.Stat(path)
|
|
require.NoError(t, err)
|
|
require.True(t, dir.IsDir())
|
|
|
|
drive := filepath.VolumeName(path)
|
|
|
|
checkMkdirAll(t, drive, true, "")
|
|
checkMkdirAll(t, drive+`\`, true, "")
|
|
// checkMkdirAll(t, `\\?\`+drive, true, "") - this isn't actually a Valid Windows path - this test used to work under go1.21.3 but fails under go1.21.4
|
|
checkMkdirAll(t, `\\?\`+drive+`\`, true, "")
|
|
checkMkdirAllSubdirs(t, path, true, "")
|
|
checkMkdirAllSubdirs(t, `\\?\`+path, true, "")
|
|
}
|
|
|
|
// Testing paths on unused drive
|
|
// This is where there is a difference from golang's os.MkdirAll. It would
|
|
// recurse extended-length paths down to the "\\?" prefix and return the
|
|
// noninformative error:
|
|
// "mkdir \\?: The filename, directory name, or volume label syntax is incorrect."
|
|
// Our version stops the recursion at drive's root directory, and reports:
|
|
// "mkdir \\?\A:\: The system cannot find the path specified."
|
|
func TestMkdirAllOnUnusedDrive(t *testing.T) {
|
|
path := unusedDrive(t)
|
|
errormsg := fmt.Sprintf(`mkdir %s\: The system cannot find the path specified.`, path)
|
|
checkMkdirAllSubdirs(t, path, false, errormsg)
|
|
errormsg1 := fmt.Sprintf(`mkdir \\?\%s\: The system cannot find the path specified.`, path) // pre go1.21.4
|
|
errormsg2 := fmt.Sprintf(`mkdir \\?\%s: The system cannot find the file specified.`, path) // go1.21.4 and after
|
|
checkMkdirAllSubdirs(t, `\\?\`+path, false, errormsg1, errormsg2)
|
|
}
|
|
|
|
// Testing paths on unknown network host
|
|
// This is an additional difference from golang's os.MkdirAll. With our
|
|
// first fix, stopping it from recursing extended-length paths down to
|
|
// the "\\?" prefix, it would now stop at `\\?\UNC`, because that is what
|
|
// filepath.VolumeName returns (which is wrong, that is not a volume name!),
|
|
// and still return a nonifnromative error:
|
|
// "mkdir \\?\UNC\\: The filename, directory name, or volume label syntax is incorrect."
|
|
// Our version stops the recursion at level before this, and reports:
|
|
// "mkdir \\?\UNC\0.0.0.0: The specified path is invalid."
|
|
func TestMkdirAllOnUnusedNetworkHost(t *testing.T) {
|
|
path := `\\0.0.0.0\share`
|
|
errormsg := fmt.Sprintf("mkdir %s\\: The format of the specified network name is invalid.", path)
|
|
checkMkdirAllSubdirs(t, path, false, errormsg)
|
|
path = `\\?\UNC\0.0.0.0\share`
|
|
checkMkdirAllSubdirs(t, path, false,
|
|
`mkdir \\?\UNC\0.0.0.0: The specified path is invalid.`, // pre go1.20
|
|
`mkdir \\?\UNC\0.0.0.0\share\: The format of the specified network name is invalid.`,
|
|
)
|
|
|
|
}
|