"internal/poll": {"L0", "internal/race", "syscall", "time", "unicode/utf16", "unicode/utf8"},
"os": {"L1", "os", "syscall", "time", "internal/poll", "internal/syscall/windows"},
- "path/filepath": {"L2", "os", "syscall"},
+ "path/filepath": {"L2", "os", "syscall", "internal/syscall/windows"},
"io/ioutil": {"L2", "os", "path/filepath", "time"},
"os/exec": {"L2", "os", "context", "path/filepath", "syscall"},
"os/signal": {"L2", "os", "syscall"},
//sys NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) = netapi32.NetShareAdd
//sys NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) = netapi32.NetShareDel
+
+const (
+ FILE_NAME_NORMALIZED = 0x0
+ FILE_NAME_OPENED = 0x8
+
+ VOLUME_NAME_DOS = 0x0
+ VOLUME_NAME_GUID = 0x1
+ VOLUME_NAME_NONE = 0x4
+ VOLUME_NAME_NT = 0x2
+)
+
+//sys GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) = kernel32.GetFinalPathNameByHandleW
+
+func LoadGetFinalPathNameByHandle() error {
+ return procGetFinalPathNameByHandleW.Find()
+}
modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
- procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
- procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
- procMoveFileExW = modkernel32.NewProc("MoveFileExW")
- procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
- procGetACP = modkernel32.NewProc("GetACP")
- procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
- procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
- procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
- procNetShareAdd = modnetapi32.NewProc("NetShareAdd")
- procNetShareDel = modnetapi32.NewProc("NetShareDel")
- procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
- procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
- procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
- procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
- procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
+ procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
+ procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW")
+ procMoveFileExW = modkernel32.NewProc("MoveFileExW")
+ procGetModuleFileNameW = modkernel32.NewProc("GetModuleFileNameW")
+ procGetACP = modkernel32.NewProc("GetACP")
+ procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
+ procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
+ procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
+ procNetShareAdd = modnetapi32.NewProc("NetShareAdd")
+ procNetShareDel = modnetapi32.NewProc("NetShareDel")
+ procGetFinalPathNameByHandleW = modkernel32.NewProc("GetFinalPathNameByHandleW")
+ procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
+ procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
+ procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
+ procLookupPrivilegeValueW = modadvapi32.NewProc("LookupPrivilegeValueW")
+ procAdjustTokenPrivileges = modadvapi32.NewProc("AdjustTokenPrivileges")
)
func GetAdaptersAddresses(family uint32, flags uint32, reserved uintptr, adapterAddresses *IpAdapterAddresses, sizePointer *uint32) (errcode error) {
return
}
+func GetFinalPathNameByHandle(file syscall.Handle, filePath *uint16, filePathSize uint32, flags uint32) (n uint32, err error) {
+ r0, _, e1 := syscall.Syscall6(procGetFinalPathNameByHandleW.Addr(), 4, uintptr(file), uintptr(unsafe.Pointer(filePath)), uintptr(filePathSize), uintptr(flags), 0, 0)
+ n = uint32(r0)
+ if n == 0 {
+ if e1 != 0 {
+ err = errnoErr(e1)
+ } else {
+ err = syscall.EINVAL
+ }
+ }
+ return
+}
+
func ImpersonateSelf(impersonationlevel uint32) (err error) {
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0)
if r1 == 0 {
if err != nil {
t.Fatal(err)
}
-
if got != target {
t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
}
+
+ got, err = filepath.EvalSymlinks(link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got != target {
+ t.Errorf(`filepath.EvalSymlinks("%s"): got %v, want %v`, link, got, target)
+ }
}
func TestStartProcessAttr(t *testing.T) {
testenv.MustHaveSymlink(t)
testWalkMklink(t, "D")
}
+
+func TestNTNamespaceSymlink(t *testing.T) {
+ output, _ := exec.Command("cmd", "/c", "mklink", "/?").Output()
+ if !strings.Contains(string(output), " /J ") {
+ t.Skip("skipping test because mklink command does not support junctions")
+ }
+
+ tmpdir, err := ioutil.TempDir("", "TestNTNamespaceSymlink")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ vol := filepath.VolumeName(tmpdir)
+ output, err = exec.Command("cmd", "/c", "mountvol", vol, "/L").CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mountvol %v /L: %v %q", vol, err, output)
+ }
+ target := strings.Trim(string(output), " \n\r")
+
+ link := filepath.Join(tmpdir, "link")
+ output, err = exec.Command("cmd", "/c", "mklink", "/J", link, target).CombinedOutput()
+ if err != nil {
+ t.Fatalf("failed to run mklink %v %v: %v %q", link, target, err, output)
+ }
+
+ got, err := filepath.EvalSymlinks(link)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if want := vol + `\`; got != want {
+ t.Errorf(`EvalSymlinks(%q): got %q, want %q`, link, got, want)
+ }
+}
package filepath
import (
+ "errors"
+ "internal/syscall/windows"
+ "os"
"strings"
"syscall"
)
return volume + normPath, nil
}
+// evalSymlinksUsingGetFinalPathNameByHandle uses Windows
+// GetFinalPathNameByHandle API to retrieve the final
+// path for the specified file.
+func evalSymlinksUsingGetFinalPathNameByHandle(path string) (string, error) {
+ err := windows.LoadGetFinalPathNameByHandle()
+ if err != nil {
+ // we must be using old version of Windows
+ return "", err
+ }
+
+ if path == "" {
+ return path, nil
+ }
+
+ // Use Windows I/O manager to dereference the symbolic link, as per
+ // https://blogs.msdn.microsoft.com/oldnewthing/20100212-00/?p=14963/
+ p, err := syscall.UTF16PtrFromString(path)
+ if err != nil {
+ return "", err
+ }
+ h, err := syscall.CreateFile(p, 0, 0, nil,
+ syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
+ if err != nil {
+ return "", err
+ }
+ defer syscall.CloseHandle(h)
+
+ buf := make([]uint16, 100)
+ for {
+ n, err := windows.GetFinalPathNameByHandle(h, &buf[0], uint32(len(buf)), windows.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 samefile(path1, path2 string) bool {
+ fi1, err := os.Lstat(path1)
+ if err != nil {
+ return false
+ }
+ fi2, err := os.Lstat(path2)
+ if err != nil {
+ return false
+ }
+ return os.SameFile(fi1, fi2)
+}
+
func evalSymlinks(path string) (string, error) {
- path, err := walkSymlinks(path)
+ newpath, err := walkSymlinks(path)
+ if err != nil {
+ newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
+ if err2 == nil {
+ return toNorm(newpath2, normBase)
+ }
+ return "", err
+ }
+ newpath, err = toNorm(newpath, normBase)
if err != nil {
+ newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
+ if err2 == nil {
+ return toNorm(newpath2, normBase)
+ }
return "", err
}
- return toNorm(path, normBase)
+ if strings.ToUpper(newpath) == strings.ToUpper(path) {
+ // walkSymlinks did not actually walk any symlinks,
+ // so we don't need to try GetFinalPathNameByHandle.
+ return newpath, nil
+ }
+ newpath2, err2 := evalSymlinksUsingGetFinalPathNameByHandle(path)
+ if err2 != nil {
+ return newpath, nil
+ }
+ newpath2, err2 = toNorm(newpath2, normBase)
+ if err2 != nil {
+ return newpath, nil
+ }
+ if samefile(newpath, newpath2) {
+ return newpath, nil
+ }
+ return newpath2, nil
}