返回 DeepSeek-Reasonix
key_windows.go
根目录 / internal / pathidentity / key_windows.go
1 //go:build windows
2
3 package pathidentity
4
5 import (
6 "errors"
7 "path/filepath"
8 "strings"
9 "unsafe"
10
11 "golang.org/x/sys/windows"
12 )
13
14 func platformIdentityKey(path string) (string, error) {
15 return windowsIdentityKeyBy(path, directoryCaseInsensitive)
16 }
17
18 func windowsIdentityKeyBy(path string, caseForDirectory func(string) (bool, bool, error)) (string, error) {
19 path = stripExtendedPrefix(path)
20 volume := filepath.VolumeName(path)
21 if volume == "" {
22 return "", errors.New("windows path has no volume")
23 }
24 current := volume + string(filepath.Separator)
25 identity := strings.ToLower(current)
26 caseInsensitive := true
27 rest := strings.TrimLeft(path[len(volume):], `\/`)
28 for _, component := range strings.FieldsFunc(rest, func(r rune) bool { return r == '\\' || r == '/' }) {
29 insensitive, exists, err := caseForDirectory(current)
30 if err != nil {
31 return "", err
32 }
33 if exists {
34 caseInsensitive = insensitive
35 }
36 identityComponent := component
37 if caseInsensitive {
38 identityComponent = strings.ToLower(component)
39 }
40 identity = filepath.Join(identity, identityComponent)
41 current = filepath.Join(current, component)
42 }
43 return filepath.Clean(identity), nil
44 }
45
46 func directoryCaseInsensitive(path string) (insensitive, exists bool, err error) {
47 name, err := windows.UTF16PtrFromString(path)
48 if err != nil {
49 return false, false, err
50 }
51 handle, err := windows.CreateFile(name, windows.FILE_READ_ATTRIBUTES,
52 windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
53 nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0)
54 if err != nil {
55 if errors.Is(err, windows.ERROR_FILE_NOT_FOUND) || errors.Is(err, windows.ERROR_PATH_NOT_FOUND) {
56 return false, false, nil
57 }
58 return false, false, err
59 }
60 defer windows.CloseHandle(handle)
61 var iosb windows.IO_STATUS_BLOCK
62 var flags uint32
63 err = windows.NtQueryInformationFile(
64 handle,
65 &iosb,
66 (*byte)(unsafe.Pointer(&flags)),
67 uint32(unsafe.Sizeof(flags)),
68 windows.FileCaseSensitiveInformation,
69 )
70 if caseSensitivityQueryUnsupported(err) {
71 return true, true, nil
72 }
73 if err != nil {
74 return false, false, err
75 }
76 return flags&windows.FILE_CS_FLAG_CASE_SENSITIVE_DIR == 0, true, nil
77 }
78
79 func caseSensitivityQueryUnsupported(err error) bool {
80 var status windows.NTStatus
81 return errors.As(err, &status) && (status == windows.STATUS_INVALID_INFO_CLASS ||
82 status == windows.STATUS_INVALID_PARAMETER ||
83 status == windows.STATUS_NOT_SUPPORTED)
84 }
85
86 func stripExtendedPrefix(path string) string {
87 if strings.HasPrefix(strings.ToUpper(path), `\\?\UNC\`) {
88 return `\\` + path[len(`\\?\UNC\`):]
89 }
90 return strings.TrimPrefix(path, `\\?\`)
91 }
92
92 lines GO