Commit e65bce71 authored by Hiroshi Ioka's avatar Hiroshi Ioka Committed by Alex Brainman

os, syscall: fix incorrect offset calculation in Readlink on windows

Current implementation of syscall.Readlink mistakenly calculates
the end offset of the PrintName field.
Also, there are some cases that the PrintName field is empty.
Instead, the CL uses SubstituteName with correct calculation.

Fixes #15978
Fixes #16145

Change-Id: If3257137141129ac1c552d003726d5b9c08bb754
Reviewed-on: https://go-review.googlesource.com/31118Reviewed-by: default avatarAlex Brainman <alex.brainman@gmail.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent ebf827de
...@@ -148,3 +148,19 @@ const MB_ERR_INVALID_CHARS = 8 ...@@ -148,3 +148,19 @@ const MB_ERR_INVALID_CHARS = 8
//sys GetConsoleCP() (ccp uint32) = kernel32.GetConsoleCP //sys GetConsoleCP() (ccp uint32) = kernel32.GetConsoleCP
//sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar //sys MultiByteToWideChar(codePage uint32, dwFlags uint32, str *byte, nstr int32, wchar *uint16, nwchar int32) (nwrite int32, err error) = kernel32.MultiByteToWideChar
//sys GetCurrentThread() (pseudoHandle syscall.Handle, err error) = kernel32.GetCurrentThread //sys GetCurrentThread() (pseudoHandle syscall.Handle, err error) = kernel32.GetCurrentThread
const STYPE_DISKTREE = 0x00
type SHARE_INFO_2 struct {
Netname *uint16
Type uint32
Remark *uint16
Permissions uint32
MaxUses uint32
CurrentUses uint32
Path *uint16
Passwd *uint16
}
//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
...@@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error { ...@@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error {
var ( var (
modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll")) modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll")) modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll")) modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses") procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
...@@ -47,6 +48,8 @@ var ( ...@@ -47,6 +48,8 @@ var (
procGetConsoleCP = modkernel32.NewProc("GetConsoleCP") procGetConsoleCP = modkernel32.NewProc("GetConsoleCP")
procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar") procMultiByteToWideChar = modkernel32.NewProc("MultiByteToWideChar")
procGetCurrentThread = modkernel32.NewProc("GetCurrentThread") procGetCurrentThread = modkernel32.NewProc("GetCurrentThread")
procNetShareAdd = modnetapi32.NewProc("NetShareAdd")
procNetShareDel = modnetapi32.NewProc("NetShareDel")
procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf") procImpersonateSelf = modadvapi32.NewProc("ImpersonateSelf")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf") procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken") procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
...@@ -124,6 +127,22 @@ func GetCurrentThread() (pseudoHandle syscall.Handle, err error) { ...@@ -124,6 +127,22 @@ func GetCurrentThread() (pseudoHandle syscall.Handle, err error) {
return return
} }
func NetShareAdd(serverName *uint16, level uint32, buf *byte, parmErr *uint16) (neterr error) {
r0, _, _ := syscall.Syscall6(procNetShareAdd.Addr(), 4, uintptr(unsafe.Pointer(serverName)), uintptr(level), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(parmErr)), 0, 0)
if r0 != 0 {
neterr = syscall.Errno(r0)
}
return
}
func NetShareDel(serverName *uint16, netName *uint16, reserved uint32) (neterr error) {
r0, _, _ := syscall.Syscall(procNetShareDel.Addr(), 3, uintptr(unsafe.Pointer(serverName)), uintptr(unsafe.Pointer(netName)), uintptr(reserved))
if r0 != 0 {
neterr = syscall.Errno(r0)
}
return
}
func ImpersonateSelf(impersonationlevel uint32) (err error) { func ImpersonateSelf(impersonationlevel uint32) (err error) {
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0) r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0)
if r1 == 0 { if r1 == 0 {
......
...@@ -263,7 +263,6 @@ func TestDirectoryJunction(t *testing.T) { ...@@ -263,7 +263,6 @@ func TestDirectoryJunction(t *testing.T) {
t.addPrintName("") t.addPrintName("")
return createMountPoint(link, &t) return createMountPoint(link, &t)
}, },
issueNo: 16145,
}, },
} }
output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output() output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
...@@ -396,12 +395,106 @@ func TestDirectorySymbolicLink(t *testing.T) { ...@@ -396,12 +395,106 @@ func TestDirectorySymbolicLink(t *testing.T) {
t.addPrintNameNoNUL(filepath.Base(target)) t.addPrintNameNoNUL(filepath.Base(target))
return createSymbolicLink(link, &t, true) return createSymbolicLink(link, &t, true)
}, },
issueNo: 15978,
}, },
) )
testDirLinks(t, tests) testDirLinks(t, tests)
} }
func TestNetworkSymbolicLink(t *testing.T) {
testenv.MustHaveSymlink(t)
dir, err := ioutil.TempDir("", "TestNetworkSymbolicLink")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
oldwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
err = os.Chdir(dir)
if err != nil {
t.Fatal(err)
}
defer os.Chdir(oldwd)
shareName := "GoSymbolicLinkTestShare" // hope no conflictions
sharePath := filepath.Join(dir, shareName)
testDir := "TestDir"
err = os.MkdirAll(filepath.Join(sharePath, testDir), 0777)
if err != nil {
t.Fatal(err)
}
wShareName, err := syscall.UTF16PtrFromString(shareName)
if err != nil {
t.Fatal(err)
}
wSharePath, err := syscall.UTF16PtrFromString(sharePath)
if err != nil {
t.Fatal(err)
}
p := windows.SHARE_INFO_2{
Netname: wShareName,
Type: windows.STYPE_DISKTREE,
Remark: nil,
Permissions: 0,
MaxUses: 1,
CurrentUses: 0,
Path: wSharePath,
Passwd: nil,
}
err = windows.NetShareAdd(nil, 2, (*byte)(unsafe.Pointer(&p)), nil)
if err != nil {
if err == syscall.ERROR_ACCESS_DENIED {
t.Skip("you don't have enough privileges to add network share")
}
t.Fatal(err)
}
defer func() {
err := windows.NetShareDel(nil, wShareName, 0)
if err != nil {
t.Fatal(err)
}
}()
UNCPath := `\\localhost\` + shareName + `\`
fi1, err := os.Stat(sharePath)
if err != nil {
t.Fatal(err)
}
fi2, err := os.Stat(UNCPath)
if err != nil {
t.Fatal(err)
}
if !os.SameFile(fi1, fi2) {
t.Fatalf("%q and %q should be the same directory, but not", sharePath, UNCPath)
}
target := filepath.Join(UNCPath, testDir)
link := "link"
err = os.Symlink(target, link)
if err != nil {
t.Fatal(err)
}
defer os.Remove(link)
got, err := os.Readlink(link)
if err != nil {
t.Fatal(err)
}
if got != target {
t.Errorf(`os.Readlink("%s"): got %v, want %v`, link, got, target)
}
}
func TestStartProcessAttr(t *testing.T) { func TestStartProcessAttr(t *testing.T) {
p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr)) p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
if err != nil { if err != nil {
......
...@@ -1024,11 +1024,31 @@ func Readlink(path string, buf []byte) (n int, err error) { ...@@ -1024,11 +1024,31 @@ func Readlink(path string, buf []byte) (n int, err error) {
case IO_REPARSE_TAG_SYMLINK: case IO_REPARSE_TAG_SYMLINK:
data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2]) s = UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
if data.Flags&_SYMLINK_FLAG_RELATIVE == 0 {
if len(s) >= 4 && s[:4] == `\??\` {
s = s[4:]
switch {
case len(s) >= 2 && s[1] == ':': // \??\C:\foo\bar
// do nothing
case len(s) >= 4 && s[:4] == `UNC\`: // \??\UNC\foo\bar
s = `\\` + s[4:]
default:
// unexpected; do nothing
}
} else {
// unexpected; do nothing
}
}
case _IO_REPARSE_TAG_MOUNT_POINT: case _IO_REPARSE_TAG_MOUNT_POINT:
data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer)) data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0])) p := (*[0xffff]uint16)(unsafe.Pointer(&data.PathBuffer[0]))
s = UTF16ToString(p[data.PrintNameOffset/2 : (data.PrintNameLength-data.PrintNameOffset)/2]) s = UTF16ToString(p[data.SubstituteNameOffset/2 : (data.SubstituteNameOffset+data.SubstituteNameLength)/2])
if len(s) >= 4 && s[:4] == `\??\` { // \??\C:\foo\bar
s = s[4:]
} else {
// unexpected; do nothing
}
default: default:
// the path is not a symlink or junction but another type of reparse // the path is not a symlink or junction but another type of reparse
// point // point
......
...@@ -1116,4 +1116,5 @@ const ( ...@@ -1116,4 +1116,5 @@ const (
_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
IO_REPARSE_TAG_SYMLINK = 0xA000000C IO_REPARSE_TAG_SYMLINK = 0xA000000C
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
_SYMLINK_FLAG_RELATIVE = 1
) )
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment