返回 DeepSeek-Reasonix
physical_windows.go
根目录 / internal / pathidentity / physical_windows.go
1 //go:build windows
2
3 package pathidentity
4
5 import (
6 "errors"
7 "fmt"
8 "path/filepath"
9 "strings"
10 "unsafe"
11
12 "golang.org/x/sys/windows"
13 )
14
15 // The kernel reports exhausted reparse traversal as CANT_RESOLVE_FILENAME,
16 // rather than the synthetic syscall.ELOOP used by Go's userspace walker.
17 func platformLinkLoop(err error) bool {
18 return errors.Is(err, windows.ERROR_CANT_RESOLVE_FILENAME)
19 }
20
21 // resolveExistingPath asks the kernel to follow all reparse points, including
22 // junctions. EvalSymlinks does not follow mount points reported as ModeIrregular:
23 // it can return the alias unchanged for a leaf and ENOTDIR for its descendants.
24 // This must be the primary resolver, not merely a fallback after an error.
25 func resolveExistingPath(path string) (string, error) {
26 handle, err := openPhysicalPath(path)
27 if err != nil {
28 return "", fmt.Errorf("open physical path: %w", err)
29 }
30 defer windows.CloseHandle(handle)
31 for size := uint32(256); size <= 65536; {
32 buf := make([]uint16, size)
33 n, err := windows.GetFinalPathNameByHandle(handle, &buf[0], size, 0)
34 if err != nil {
35 return "", fmt.Errorf("get final physical path: %w", err)
36 }
37 if n < size {
38 return stripExtendedPrefix(windows.UTF16ToString(buf[:n])), nil
39 }
40 if n >= 65536 {
41 break
42 }
43 size = n + 1
44 }
45 return "", fmt.Errorf("final physical path exceeds 65536 UTF-16 units")
46 }
47
48 // NtCreateFile avoids CreateFile's implicit SYNCHRONIZE requirement. Owners
49 // denied file attributes can still query security metadata via READ_CONTROL.
50 func openPhysicalPath(path string) (windows.Handle, error) {
51 name, err := windows.NewNTUnicodeString(ntPhysicalPath(path))
52 if err != nil {
53 return 0, err
54 }
55 oa := windows.OBJECT_ATTRIBUTES{ObjectName: name, Attributes: windows.OBJ_CASE_INSENSITIVE}
56 oa.Length = uint32(unsafe.Sizeof(oa))
57 var handle windows.Handle
58 var iosb windows.IO_STATUS_BLOCK
59 open := func(access uint32) error {
60 return windows.NtCreateFile(&handle, access, &oa, &iosb, nil, 0,
61 windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE,
62 windows.FILE_OPEN, windows.FILE_OPEN_FOR_BACKUP_INTENT, 0, 0)
63 }
64 err = open(windows.FILE_READ_ATTRIBUTES)
65 if errors.Is(err, windows.STATUS_ACCESS_DENIED) {
66 err = open(windows.READ_CONTROL)
67 }
68 var status windows.NTStatus
69 if errors.As(err, &status) {
70 err = status.Errno()
71 }
72 return handle, err
73 }
74
75 func ntPhysicalPath(path string) string {
76 extended := extendedWindowsPath(path)
77 return `\??\` + extended[4:]
78 }
79
80 // Native Windows calls do not apply the long-path handling performed by os.
81 // Inputs here are already absolute and clean; preserve existing device paths.
82 func extendedWindowsPath(path string) string {
83 if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) {
84 return path
85 }
86 if strings.HasPrefix(path, `\\`) {
87 return `\\?\UNC\` + path[2:]
88 }
89 if filepath.IsAbs(path) {
90 return `\\?\` + path
91 }
92 return path
93 }
94
94 lines GO