parent
1054ce9354
commit
eb4fe808c5
@ -211,6 +211,63 @@ func isMaybeWin83(absPath string) bool {
|
|||||||
return strings.Contains(strings.TrimPrefix(filepath.Base(absPath), WindowsTempPrefix), "~")
|
return strings.Contains(strings.TrimPrefix(filepath.Base(absPath), WindowsTempPrefix), "~")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFinalPathName(in string) (string, error) {
|
||||||
|
// Return the normalized path
|
||||||
|
// Wrap the call to GetFinalPathNameByHandleW
|
||||||
|
// The string returned by this function uses the \?\ syntax
|
||||||
|
// Implies GetFullPathName + GetLongPathName
|
||||||
|
kernel32, err := syscall.LoadDLL("kernel32.dll")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
GetFinalPathNameByHandleW, err := kernel32.FindProc("GetFinalPathNameByHandleW")
|
||||||
|
// https://github.com/golang/go/blob/ff048033e4304898245d843e79ed1a0897006c6d/src/internal/syscall/windows/syscall_windows.go#L303
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
inPath, err := syscall.UTF16PtrFromString(in)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
// Get a file handler
|
||||||
|
h, err := syscall.CreateFile(inPath,
|
||||||
|
syscall.GENERIC_READ,
|
||||||
|
syscall.FILE_SHARE_READ,
|
||||||
|
nil,
|
||||||
|
syscall.OPEN_EXISTING,
|
||||||
|
uint32(syscall.FILE_FLAG_BACKUP_SEMANTICS),
|
||||||
|
0)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer syscall.CloseHandle(h)
|
||||||
|
// Call GetFinalPathNameByHandleW
|
||||||
|
var VOLUME_NAME_DOS uint32 = 0x0 // not yet defined in syscall
|
||||||
|
var bufSize uint32 = syscall.MAX_PATH // 260
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
buf := make([]uint16, bufSize)
|
||||||
|
var ret uintptr
|
||||||
|
ret, _, err = GetFinalPathNameByHandleW.Call(
|
||||||
|
uintptr(h), // HANDLE hFile
|
||||||
|
uintptr(unsafe.Pointer(&buf[0])), // LPWSTR lpszFilePath
|
||||||
|
uintptr(bufSize), // DWORD cchFilePath
|
||||||
|
uintptr(VOLUME_NAME_DOS), // DWORD dwFlags
|
||||||
|
)
|
||||||
|
// The returned value is the actual length of the norm path
|
||||||
|
// After Win 10 build 1607, MAX_PATH limitations have been removed
|
||||||
|
// so it is necessary to check newBufSize
|
||||||
|
newBufSize := uint32(ret) + 1
|
||||||
|
if ret == 0 || newBufSize > bufSize*100 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if newBufSize <= bufSize {
|
||||||
|
return syscall.UTF16ToString(buf), nil
|
||||||
|
}
|
||||||
|
bufSize = newBufSize
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
func evalSymlinks(in string) (string, error) {
|
func evalSymlinks(in string) (string, error) {
|
||||||
out, err := filepath.EvalSymlinks(in)
|
out, err := filepath.EvalSymlinks(in)
|
||||||
if err != nil && strings.HasPrefix(in, `\\?\`) {
|
if err != nil && strings.HasPrefix(in, `\\?\`) {
|
||||||
@ -218,7 +275,19 @@ func evalSymlinks(in string) (string, error) {
|
|||||||
out, err = filepath.EvalSymlinks(in[4:])
|
out, err = filepath.EvalSymlinks(in[4:])
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
// Try to get a normalized path from Win-API
|
||||||
|
var err1 error
|
||||||
|
out, err1 = getFinalPathName(in)
|
||||||
|
if err1 != nil {
|
||||||
|
return "", err // return the prior error
|
||||||
|
}
|
||||||
|
// Trim UNC prefix, equivalent to
|
||||||
|
// https://github.com/golang/go/blob/2396101e0590cb7d77556924249c26af0ccd9eff/src/os/file_windows.go#L470
|
||||||
|
if strings.HasPrefix(out, `\\?\UNC\`) {
|
||||||
|
out = `\` + out[7:] // path like \\server\share\...
|
||||||
|
} else {
|
||||||
|
out = strings.TrimPrefix(out, `\\?\`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return longFilenameSupport(out), nil
|
return longFilenameSupport(out), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -139,3 +139,42 @@ func TestRelUnrootedCheckedWindows(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetFinalPath(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
input string
|
||||||
|
expectedPath string
|
||||||
|
eqToEvalSyml bool
|
||||||
|
ignoreMissing bool
|
||||||
|
}{
|
||||||
|
{`c:\`, `C:\`, true, false},
|
||||||
|
{`\\?\c:\`, `C:\`, false, false},
|
||||||
|
{`c:\wInDows\sYstEm32`, `C:\Windows\System32`, true, false},
|
||||||
|
{`c:\parent\child`, `C:\parent\child`, false, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
out, err := getFinalPathName(testCase.input)
|
||||||
|
if err != nil {
|
||||||
|
if testCase.ignoreMissing && os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t.Errorf("getFinalPathName failed at %q with error %s", testCase.input, err)
|
||||||
|
}
|
||||||
|
// Trim UNC prefix
|
||||||
|
if strings.HasPrefix(out, `\\?\UNC\`) {
|
||||||
|
out = `\` + out[7:]
|
||||||
|
} else {
|
||||||
|
out = strings.TrimPrefix(out, `\\?\`)
|
||||||
|
}
|
||||||
|
if out != testCase.expectedPath {
|
||||||
|
t.Errorf("getFinalPathName got wrong path: %q (expected %q)", out, testCase.expectedPath)
|
||||||
|
}
|
||||||
|
if testCase.eqToEvalSyml {
|
||||||
|
evlPath, err1 := filepath.EvalSymlinks(testCase.input)
|
||||||
|
if err1 != nil || out != evlPath {
|
||||||
|
t.Errorf("EvalSymlinks got different results %q %s", evlPath, err1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user