]> Cypherpunks repositories - gostls13.git/commitdiff
os, syscall: fix incorrect offset calculation in Readlink on windows
authorHiroshi Ioka <hirochachacha@gmail.com>
Sun, 16 Oct 2016 04:25:53 +0000 (13:25 +0900)
committerAlex Brainman <alex.brainman@gmail.com>
Wed, 19 Oct 2016 01:25:18 +0000 (01:25 +0000)
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>

src/internal/syscall/windows/syscall_windows.go
src/internal/syscall/windows/zsyscall_windows.go
src/os/os_windows_test.go
src/syscall/syscall_windows.go
src/syscall/ztypes_windows.go

index 4a30afbbfcfefe95771ac626f56035dfd59eb478..dd2df92ff6106335f43f9a3630fbbf0899f408a6 100644 (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
index f6a89540725a2625f1fad103d1eff8b6f514613b..55af05d3e9a0d94ea29a0a44fcc31a3a3748e6db 100644 (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 {
index 1a7946ae9fa9abbbb9b0fe02d829185e51f09064..72af075e5ba76a4a096f4e0c935ef4a0a7ab1183 100644 (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 {
index e13d6e2dd5a531d45ab946e83537a038b5b8de9b..f4f8f3ad094cf0c5bdd80fac5f0b53b522f768e4 100644 (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
index 8c2e19653aadbc317b09e1009cc3946c8ebd4243..1fb6f5c29f8b0587aa74db3997010838acece6ed 100644 (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
 )