|
@@ -211,6 +211,63 @@ func isMaybeWin83(absPath string) bool {
|
|
|
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) {
|
|
|
out, err := filepath.EvalSymlinks(in)
|
|
|
if err != nil && strings.HasPrefix(in, `\\?\`) {
|
|
@@ -218,7 +275,19 @@ func evalSymlinks(in string) (string, error) {
|
|
|
out, err = filepath.EvalSymlinks(in[4:])
|
|
|
}
|
|
|
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
|
|
|
}
|