mirror of
https://github.com/golang/go
synced 2024-11-23 19:50:06 -07:00
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 <alex.brainman@gmail.com>
This commit is contained in:
parent
f58a5cb9e2
commit
e77fbd598f
@ -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
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user