mirror of
https://github.com/rclone/rclone.git
synced 2024-11-28 11:16:45 +08:00
123 lines
6.5 KiB
Go
123 lines
6.5 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"
|
||
|
)
|
||
|
|
||
|
func checkMkdirAll(t *testing.T, path string, valid bool, errormsgs ...string) {
|
||
|
if valid {
|
||
|
assert.NoError(t, os.MkdirAll(path, 0777))
|
||
|
} else {
|
||
|
err := os.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, false, fmt.Sprintf(`mkdir \\?\%s: Access is denied.`, drive)) // This isn't actually a valid Windows path, it worked under go1.21.3 but fails under go1.21.4 and newer
|
||
|
checkMkdirAll(t, `\\?\`+drive+`\`, true, "")
|
||
|
checkMkdirAllSubdirs(t, path, true, "")
|
||
|
checkMkdirAllSubdirs(t, `\\?\`+path, true, "")
|
||
|
}
|
||
|
|
||
|
// Testing paths on unused drive
|
||
|
// This covers the cases that we wanted to improve with our own custom version
|
||
|
// of golang's os.MkdirAll, introduced in PR #5401. Before go1.22 the original
|
||
|
// os.MkdirAll 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 stopped the recursion at drive's root directory, and reported,
|
||
|
// before go1.21.4:
|
||
|
// "mkdir \\?\A:\: The system cannot find the path specified."
|
||
|
// or, starting with go1.21.4:
|
||
|
// "mkdir \\?\A:: The system cannot find the path specified."
|
||
|
// See https://github.com/rclone/rclone/pull/5401.
|
||
|
// Starting with go1.22 golang's os.MkdirAll have similar improvements that made
|
||
|
// our custom version no longer necessary.
|
||
|
func TestMkdirAllOnUnusedDrive(t *testing.T) {
|
||
|
letter := FindUnusedDriveLetter()
|
||
|
require.NotEqual(t, letter, 0)
|
||
|
drive := string(letter) + ":"
|
||
|
checkMkdirAll(t, drive, false, fmt.Sprintf(`mkdir %s: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\`, false, fmt.Sprintf(`mkdir %s\: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\`, false, fmt.Sprintf(`mkdir %s\parent\: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\child`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\child\`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
|
||
|
drive = `\\?\` + drive
|
||
|
checkMkdirAll(t, drive, false, fmt.Sprintf(`mkdir %s: The system cannot find the file specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\`, false, fmt.Sprintf(`mkdir %s\: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\`, false, fmt.Sprintf(`mkdir %s\parent\: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\child`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
checkMkdirAll(t, drive+`\parent\child\`, false, fmt.Sprintf(`mkdir %s\parent: The system cannot find the path specified.`, drive))
|
||
|
}
|
||
|
|
||
|
// Testing paths on unknown network host
|
||
|
// This covers more cases that we wanted to improve in our custom version of
|
||
|
// golang's os.MkdirAll, extending that explained in TestMkdirAllOnUnusedDrive.
|
||
|
// 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 noninformative error:
|
||
|
// "mkdir \\?\UNC\\: The filename, directory name, or volume label syntax is incorrect."
|
||
|
// Our version stopped the recursion at level before this, and reports:
|
||
|
// "mkdir \\?\UNC\0.0.0.0: The specified path is invalid."
|
||
|
// See https://github.com/rclone/rclone/pull/6420.
|
||
|
// Starting with go1.22 golang's os.MkdirAll have similar improvements that made
|
||
|
// our custom version no longer necessary.
|
||
|
func TestMkdirAllOnUnusedNetworkHost(t *testing.T) {
|
||
|
sharePath := `\\0.0.0.0\share`
|
||
|
checkMkdirAll(t, sharePath, false, fmt.Sprintf(`mkdir %s: The format of the specified network name is invalid.`, sharePath))
|
||
|
checkMkdirAll(t, sharePath+`\`, false, fmt.Sprintf(`mkdir %s\: The format of the specified network name is invalid.`, sharePath))
|
||
|
checkMkdirAll(t, sharePath+`\parent`, false, fmt.Sprintf(`mkdir %s\parent: The format of the specified network name is invalid.`, sharePath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\`, false, fmt.Sprintf(`mkdir %s\parent\: The format of the specified network name is invalid.`, sharePath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\child`, false, fmt.Sprintf(`mkdir %s\parent: The format of the specified network name is invalid.`, sharePath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\child\`, false, fmt.Sprintf(`mkdir %s\parent: The format of the specified network name is invalid.`, sharePath))
|
||
|
|
||
|
serverPath := `\\?\UNC\0.0.0.0`
|
||
|
sharePath = serverPath + `\share`
|
||
|
checkMkdirAll(t, sharePath, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
checkMkdirAll(t, sharePath+`\`, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
checkMkdirAll(t, sharePath+`\parent`, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\`, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\child`, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
checkMkdirAll(t, sharePath+`\parent\child\`, false, fmt.Sprintf(`mkdir %s: The specified path is invalid.`, serverPath))
|
||
|
}
|