]> Cypherpunks repositories - gostls13.git/commitdiff
syscall: Readlink doesn't handle junction on windows
authormattn <mattn.jp@gmail.com>
Tue, 6 Jan 2015 00:47:37 +0000 (09:47 +0900)
committerAlex Brainman <alex.brainman@gmail.com>
Thu, 12 Feb 2015 02:03:25 +0000 (02:03 +0000)
Fixes #9190

Change-Id: I22177687ed834feed165454019d28c11fcbf0fa2
Reviewed-on: https://go-review.googlesource.com/2307
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
src/os/os_windows_test.go
src/syscall/syscall_windows.go
src/syscall/ztypes_windows.go

index fd96713eacbd4304dfb4f94a1431b1b4dfe742fa..3ea0fc7f4f29f3f7728f1db2b7d0850e362b638c 100644 (file)
@@ -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)
+       }
+}
index 8ac498df786a8c309bc860c879e7865d4d3eaa77..feb329f530061493ae5f74ef444ec75381f15ac9 100644 (file)
@@ -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
 }
index 4c8a99ab9453214c558929ba11271325d097eb91..e5c732576b0f5c5dfc75e1cb776c53a3b5af55e6 100644 (file)
@@ -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
 )