From: mattn Date: Tue, 6 Jan 2015 00:47:37 +0000 (+0900) Subject: syscall: Readlink doesn't handle junction on windows X-Git-Tag: go1.5beta1~2042 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e77fbd598faeffd9231c8ecd6e2c2f42312c717e;p=gostls13.git syscall: Readlink doesn't handle junction on windows Fixes #9190 Change-Id: I22177687ed834feed165454019d28c11fcbf0fa2 Reviewed-on: https://go-review.googlesource.com/2307 Reviewed-by: Alex Brainman --- diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index fd96713eac..3ea0fc7f4f 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -3,11 +3,15 @@ package os_test import ( "io/ioutil" "os" + osexec "os/exec" "path/filepath" + "strings" "syscall" "testing" ) +var supportJunctionLinks = true + func init() { tmpdir, err := ioutil.TempDir("", "symtest") if err != nil { @@ -16,14 +20,18 @@ func init() { defer os.RemoveAll(tmpdir) err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) - if err == nil { - return + if err != nil { + err = err.(*os.LinkError).Err + switch err { + case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: + supportsSymlinks = false + } } + defer os.Remove("target") - err = err.(*os.LinkError).Err - switch err { - case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: - supportsSymlinks = false + b, _ := osexec.Command("cmd", "/c", "mklink", "/?").Output() + if !strings.Contains(string(b), " /J ") { + supportJunctionLinks = false } } @@ -79,3 +87,33 @@ func TestSameWindowsFile(t *testing.T) { t.Errorf("files should be same") } } + +func TestStatJunctionLink(t *testing.T) { + if !supportJunctionLinks { + t.Skip("skipping because junction links are not supported") + } + + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + link := filepath.Join(filepath.Dir(dir), filepath.Base(dir)+"-link") + + output, err := osexec.Command("cmd", "/c", "mklink", "/J", link, dir).CombinedOutput() + if err != nil { + t.Fatalf("failed to run mklink %v %v: %v %q", link, dir, err, output) + } + defer os.Remove(link) + + fi, err := os.Stat(link) + if err != nil { + t.Fatalf("failed to stat link %v: %v", link, err) + } + expected := filepath.Base(dir) + got := fi.Name() + if !fi.IsDir() || expected != got { + t.Fatalf("link should point to %v but points to %v instead", expected, got) + } +} diff --git a/src/syscall/syscall_windows.go b/src/syscall/syscall_windows.go index 8ac498df78..feb329f530 100644 --- a/src/syscall/syscall_windows.go +++ b/src/syscall/syscall_windows.go @@ -1003,13 +1003,22 @@ func Readlink(path string, buf []byte) (n int, err error) { } rdb := (*reparseDataBuffer)(unsafe.Pointer(&rdbbuf[0])) - if uintptr(bytesReturned) < unsafe.Sizeof(*rdb) || - rdb.ReparseTag != IO_REPARSE_TAG_SYMLINK { - // the path is not a symlink but another type of reparse point + var s string + switch rdb.ReparseTag { + 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]) + 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]) + default: + // the path is not a symlink or junction but another type of reparse + // point return -1, ENOENT } - - s := UTF16ToString((*[0xffff]uint16)(unsafe.Pointer(&rdb.PathBuffer[0]))[:rdb.PrintNameLength/2]) n = copy(buf, []byte(s)) + return n, nil } diff --git a/src/syscall/ztypes_windows.go b/src/syscall/ztypes_windows.go index 4c8a99ab94..e5c732576b 100644 --- a/src/syscall/ztypes_windows.go +++ b/src/syscall/ztypes_windows.go @@ -1083,12 +1083,7 @@ type TCPKeepalive struct { Interval uint32 } -type reparseDataBuffer struct { - ReparseTag uint32 - ReparseDataLength uint16 - Reserved uint16 - - // SymbolicLinkReparseBuffer +type symbolicLinkReparseBuffer struct { SubstituteNameOffset uint16 SubstituteNameLength uint16 PrintNameOffset uint16 @@ -1097,9 +1092,27 @@ type reparseDataBuffer struct { PathBuffer [1]uint16 } +type mountPointReparseBuffer struct { + SubstituteNameOffset uint16 + SubstituteNameLength uint16 + PrintNameOffset uint16 + PrintNameLength uint16 + PathBuffer [1]uint16 +} + +type reparseDataBuffer struct { + ReparseTag uint32 + ReparseDataLength uint16 + Reserved uint16 + + // GenericReparseBuffer + reparseBuffer byte +} + const ( FSCTL_GET_REPARSE_POINT = 0x900A8 MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024 + _IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 IO_REPARSE_TAG_SYMLINK = 0xA000000C SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1 )