mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-09 14:50:56 +00:00
parent
1054ce9354
commit
eb4fe808c5
@ -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
|
||||
}
|
||||
|
@ -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…
Reference in New Issue
Block a user