101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
package osvfs
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"syscall"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// This implementation is based on what Node's fs.realpath.native does, via libuv: https://github.com/libuv/libuv/blob/ec5a4b54f7da7eeb01679005c615fee9633cdb3b/src/win/fs.c#L2937
|
|
|
|
func realpath(path string) (string, error) {
|
|
var h windows.Handle
|
|
if len(path) < 248 {
|
|
var err error
|
|
h, err = openMetadata(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer windows.CloseHandle(h) //nolint:errcheck
|
|
} else {
|
|
// For long paths, defer to os.Open to run the path through fixLongPath.
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer f.Close()
|
|
|
|
// Works on directories too since https://go.dev/cl/405275.
|
|
h = windows.Handle(f.Fd())
|
|
}
|
|
|
|
// based on https://github.com/golang/go/blob/f4e3ec3dbe3b8e04a058d266adf8e048bab563f2/src/os/file_windows.go#L389
|
|
|
|
const _VOLUME_NAME_DOS = 0
|
|
|
|
buf := make([]uint16, 310) // https://github.com/microsoft/go-winio/blob/3c9576c9346a1892dee136329e7e15309e82fb4f/internal/stringbuffer/wstring.go#L13
|
|
for {
|
|
n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), _VOLUME_NAME_DOS)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if n < uint32(len(buf)) {
|
|
break
|
|
}
|
|
buf = make([]uint16, n)
|
|
}
|
|
|
|
s := syscall.UTF16ToString(buf)
|
|
if len(s) > 4 && s[:4] == `\\?\` {
|
|
s = s[4:]
|
|
if len(s) > 3 && s[:3] == `UNC` {
|
|
// return path like \\server\share\...
|
|
return `\` + s[3:], nil
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s)
|
|
}
|
|
|
|
func openMetadata(path string) (windows.Handle, error) {
|
|
// based on https://github.com/microsoft/go-winio/blob/3c9576c9346a1892dee136329e7e15309e82fb4f/pkg/fs/resolve.go#L113
|
|
|
|
pathUTF16, err := windows.UTF16PtrFromString(path)
|
|
if err != nil {
|
|
return windows.InvalidHandle, err
|
|
}
|
|
|
|
const (
|
|
_FILE_ANY_ACCESS = 0
|
|
|
|
_FILE_SHARE_READ = 0x01
|
|
_FILE_SHARE_WRITE = 0x02
|
|
_FILE_SHARE_DELETE = 0x04
|
|
|
|
_OPEN_EXISTING = 0x03
|
|
|
|
_FILE_FLAG_BACKUP_SEMANTICS = 0x0200_0000
|
|
)
|
|
|
|
h, err := windows.CreateFile(
|
|
pathUTF16,
|
|
_FILE_ANY_ACCESS,
|
|
_FILE_SHARE_READ|_FILE_SHARE_WRITE|_FILE_SHARE_DELETE,
|
|
nil,
|
|
_OPEN_EXISTING,
|
|
_FILE_FLAG_BACKUP_SEMANTICS,
|
|
0,
|
|
)
|
|
if err != nil {
|
|
return 0, &os.PathError{
|
|
Op: "CreateFile",
|
|
Path: path,
|
|
Err: err,
|
|
}
|
|
}
|
|
return h, nil
|
|
}
|