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:
parent
ebf827ded7
commit
e65bce7144
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user