1
0
mirror of https://github.com/golang/go synced 2024-11-18 08:14:41 -07:00

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/31118
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
Run-TryBot: Alex Brainman <alex.brainman@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Hiroshi Ioka 2016-10-16 13:25:53 +09:00 committed by Alex Brainman
parent ebf827ded7
commit e65bce7144
5 changed files with 153 additions and 4 deletions

View File

@ -148,3 +148,19 @@ const MB_ERR_INVALID_CHARS = 8
//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 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

View File

@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error {
var (
modiphlpapi = syscall.NewLazyDLL(sysdll.Add("iphlpapi.dll"))
modkernel32 = syscall.NewLazyDLL(sysdll.Add("kernel32.dll"))
modnetapi32 = syscall.NewLazyDLL(sysdll.Add("netapi32.dll"))
modadvapi32 = syscall.NewLazyDLL(sysdll.Add("advapi32.dll"))
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
@ -47,6 +48,8 @@ var (
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")
@ -124,6 +127,22 @@ func GetCurrentThread() (pseudoHandle syscall.Handle, err error) {
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) {
r1, _, e1 := syscall.Syscall(procImpersonateSelf.Addr(), 1, uintptr(impersonationlevel), 0, 0)
if r1 == 0 {

View File

@ -263,7 +263,6 @@ func TestDirectoryJunction(t *testing.T) {
t.addPrintName("")
return createMountPoint(link, &t)
},
issueNo: 16145,
},
}
output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
@ -396,12 +395,106 @@ func TestDirectorySymbolicLink(t *testing.T) {
t.addPrintNameNoNUL(filepath.Base(target))
return createSymbolicLink(link, &t, true)
},
issueNo: 15978,
},
)
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) {
p, err := os.StartProcess(os.Getenv("COMSPEC"), []string{"/c", "cd"}, new(os.ProcAttr))
if err != nil {

View File

@ -1024,11 +1024,31 @@ func Readlink(path string, buf []byte) (n int, err error) {
case IO_REPARSE_TAG_SYMLINK:
data := (*symbolicLinkReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
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:
data := (*mountPointReparseBuffer)(unsafe.Pointer(&rdb.reparseBuffer))
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:
// the path is not a symlink or junction but another type of reparse
// point

View File

@ -1116,4 +1116,5 @@ const (
_IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003
IO_REPARSE_TAG_SYMLINK = 0xA000000C
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
_SYMLINK_FLAG_RELATIVE = 1
)