| 1 | //go:build windows |
| 2 | |
| 3 | package main |
| 4 | |
| 5 | import ( |
| 6 | "fmt" |
| 7 | "strings" |
| 8 | |
| 9 | "golang.org/x/sys/windows" |
| 10 | ) |
| 11 | |
| 12 | func nativePath(path string) outcome { |
| 13 | name, err := windows.UTF16PtrFromString(path) |
| 14 | if err != nil { |
| 15 | return failureOutcome(err) |
| 16 | } |
| 17 | handle, err := windows.CreateFile(name, windows.FILE_READ_ATTRIBUTES, |
| 18 | windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, |
| 19 | nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) |
| 20 | if err != nil { |
| 21 | return failureOutcome(fmt.Errorf("CreateFileW OPEN_EXISTING: %w", err)) |
| 22 | } |
| 23 | defer windows.CloseHandle(handle) |
| 24 | result := outcome{OK: true} |
| 25 | var info windows.ByHandleFileInformation |
| 26 | if err := windows.GetFileInformationByHandle(handle, &info); err != nil { |
| 27 | result.Errors = failureOutcome(fmt.Errorf("GetFileInformationByHandle: %w", err)).Errors |
| 28 | } else { |
| 29 | result.Volume = fmt.Sprintf("%08x", info.VolumeSerialNumber) |
| 30 | result.FileID = fmt.Sprintf("%08x%08x", info.FileIndexHigh, info.FileIndexLow) |
| 31 | } |
| 32 | for size := uint32(256); size <= 65536; { |
| 33 | buf := make([]uint16, size) |
| 34 | n, err := windows.GetFinalPathNameByHandle(handle, &buf[0], size, 0) |
| 35 | if err != nil { |
| 36 | return failureOutcome(fmt.Errorf("GetFinalPathNameByHandleW: %w", err)) |
| 37 | } |
| 38 | if n < size { |
| 39 | result.Path = normalizeNativePath(windows.UTF16ToString(buf[:n])) |
| 40 | return result |
| 41 | } |
| 42 | if n >= 65536 { |
| 43 | break |
| 44 | } |
| 45 | size = n + 1 |
| 46 | } |
| 47 | return failureOutcome(fmt.Errorf("GetFinalPathNameByHandleW exceeds 65536 UTF-16 units")) |
| 48 | } |
| 49 | |
| 50 | func normalizeNativePath(path string) string { |
| 51 | if strings.HasPrefix(strings.ToUpper(path), `\\?\UNC\`) { |
| 52 | return `\\` + path[8:] |
| 53 | } |
| 54 | if len(path) >= 7 && strings.HasPrefix(path, `\\?\`) && path[5] == ':' && path[6] == '\\' { |
| 55 | return path[4:] |
| 56 | } |
| 57 | return path |
| 58 | } |
| 59 |