Skip to content

Commit

Permalink
os, syscall: fix incorrect offset calculation in Readlink on windows
Browse files Browse the repository at this point in the history
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 <[email protected]>
Run-TryBot: Alex Brainman <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
hirochachacha authored and alexbrainman committed Oct 19, 2016
1 parent ebf827d commit e65bce7
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 4 deletions.
16 changes: 16 additions & 0 deletions src/internal/syscall/windows/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
19 changes: 19 additions & 0 deletions src/internal/syscall/windows/zsyscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down Expand Up @@ -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 {
Expand Down
97 changes: 95 additions & 2 deletions src/os/os_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,6 @@ func TestDirectoryJunction(t *testing.T) {
t.addPrintName("")
return createMountPoint(link, &t)
},
issueNo: 16145,
},
}
output, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output()
Expand Down Expand Up @@ -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 {
Expand Down
24 changes: 22 additions & 2 deletions src/syscall/syscall_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/syscall/ztypes_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

0 comments on commit e65bce7

Please sign in to comment.