Commit e77fbd59 authored by mattn's avatar mattn Committed by Alex Brainman

syscall: Readlink doesn't handle junction on windows

Fixes #9190

Change-Id: I22177687ed834feed165454019d28c11fcbf0fa2
Reviewed-on: https://go-review.googlesource.com/2307Reviewed-by: 's avatarAlex Brainman <alex.brainman@gmail.com>
parent f58a5cb9
......@@ -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)
}
}
......@@ -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
}
......@@ -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
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment