From: Alex Brainman Date: Wed, 7 Mar 2012 23:00:25 +0000 (+1100) Subject: path/filepath: retrieve real file name in windows EvalSymlinks X-Git-Tag: weekly.2012-03-13~114 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=0029b0d20744392260997441c1158dcb1b23a734;p=gostls13.git path/filepath: retrieve real file name in windows EvalSymlinks R=golang-dev, rsc CC=golang-dev https://golang.org/cl/5756049 --- diff --git a/src/pkg/go/build/deps_test.go b/src/pkg/go/build/deps_test.go index 13b9dc97bb..4e9f32a036 100644 --- a/src/pkg/go/build/deps_test.go +++ b/src/pkg/go/build/deps_test.go @@ -120,7 +120,7 @@ var pkgDeps = map[string][]string{ "syscall": {"L0", "unicode/utf16"}, "time": {"L0", "syscall"}, "os": {"L1", "os", "syscall", "time"}, - "path/filepath": {"L2", "os"}, + "path/filepath": {"L2", "os", "syscall"}, "io/ioutil": {"L2", "os", "path/filepath", "time"}, "os/exec": {"L2", "os", "syscall"}, "os/signal": {"L2", "os", "syscall"}, diff --git a/src/pkg/path/filepath/path.go b/src/pkg/path/filepath/path.go index cfe46981f1..1e74872636 100644 --- a/src/pkg/path/filepath/path.go +++ b/src/pkg/path/filepath/path.go @@ -7,10 +7,8 @@ package filepath import ( - "bytes" "errors" "os" - "runtime" "sort" "strings" ) @@ -191,64 +189,7 @@ func Ext(path string) string { // If path is relative the result will be relative to the current directory, // unless one of the components is an absolute symbolic link. func EvalSymlinks(path string) (string, error) { - if runtime.GOOS == "windows" { - // Symlinks are not supported under windows. - _, err := os.Lstat(path) - if err != nil { - return "", err - } - return Clean(path), nil - } - const maxIter = 255 - originalPath := path - // consume path by taking each frontmost path element, - // expanding it if it's a symlink, and appending it to b - var b bytes.Buffer - for n := 0; path != ""; n++ { - if n > maxIter { - return "", errors.New("EvalSymlinks: too many links in " + originalPath) - } - - // find next path component, p - i := strings.IndexRune(path, Separator) - var p string - if i == -1 { - p, path = path, "" - } else { - p, path = path[:i], path[i+1:] - } - - if p == "" { - if b.Len() == 0 { - // must be absolute path - b.WriteRune(Separator) - } - continue - } - - fi, err := os.Lstat(b.String() + p) - if err != nil { - return "", err - } - if fi.Mode()&os.ModeSymlink == 0 { - b.WriteString(p) - if path != "" { - b.WriteRune(Separator) - } - continue - } - - // it's a symlink, put it at the front of path - dest, err := os.Readlink(b.String() + p) - if err != nil { - return "", err - } - if IsAbs(dest) { - b.Reset() - } - path = dest + string(Separator) + path - } - return Clean(b.String()), nil + return evalSymlinks(path) } // Abs returns an absolute representation of path. diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index ad053177f5..71969cdf95 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -621,6 +621,12 @@ func TestEvalSymlinks(t *testing.T) { if d.path == d.dest { // will test only real files and directories tests = append(tests, d) + // test "canonical" names + d2 := EvalSymlinksTest{ + path: strings.ToUpper(d.path), + dest: d.dest, + } + tests = append(tests, d2) } } } else { diff --git a/src/pkg/path/filepath/symlink.go b/src/pkg/path/filepath/symlink.go new file mode 100644 index 0000000000..307dd0f8fe --- /dev/null +++ b/src/pkg/path/filepath/symlink.go @@ -0,0 +1,67 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package filepath + +import ( + "bytes" + "errors" + "os" + "strings" +) + +func evalSymlinks(path string) (string, error) { + const maxIter = 255 + originalPath := path + // consume path by taking each frontmost path element, + // expanding it if it's a symlink, and appending it to b + var b bytes.Buffer + for n := 0; path != ""; n++ { + if n > maxIter { + return "", errors.New("EvalSymlinks: too many links in " + originalPath) + } + + // find next path component, p + i := strings.IndexRune(path, Separator) + var p string + if i == -1 { + p, path = path, "" + } else { + p, path = path[:i], path[i+1:] + } + + if p == "" { + if b.Len() == 0 { + // must be absolute path + b.WriteRune(Separator) + } + continue + } + + fi, err := os.Lstat(b.String() + p) + if err != nil { + return "", err + } + if fi.Mode()&os.ModeSymlink == 0 { + b.WriteString(p) + if path != "" { + b.WriteRune(Separator) + } + continue + } + + // it's a symlink, put it at the front of path + dest, err := os.Readlink(b.String() + p) + if err != nil { + return "", err + } + if IsAbs(dest) { + b.Reset() + } + path = dest + string(Separator) + path + } + return Clean(b.String()), nil +} diff --git a/src/pkg/path/filepath/symlink_windows.go b/src/pkg/path/filepath/symlink_windows.go new file mode 100644 index 0000000000..afa88bfe87 --- /dev/null +++ b/src/pkg/path/filepath/symlink_windows.go @@ -0,0 +1,27 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package filepath + +import ( + "syscall" +) + +func evalSymlinks(path string) (string, error) { + p := syscall.StringToUTF16(path) + b := p // GetLongPathName says we can reuse buffer + n, err := syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + if n > uint32(len(b)) { + b = make([]uint16, n) + n, err = syscall.GetLongPathName(&p[0], &b[0], uint32(len(b))) + if err != nil { + return "", err + } + } + b = b[:n] + return Clean(syscall.UTF16ToString(b)), nil +} diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index 6ac98ceacb..b0c24cca69 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -174,6 +174,7 @@ func NewCallback(fn interface{}) uintptr //sys SetHandleInformation(handle Handle, mask uint32, flags uint32) (err error) //sys FlushFileBuffers(handle Handle) (err error) //sys GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) (n uint32, err error) = kernel32.GetFullPathNameW +//sys GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err error) = kernel32.GetLongPathNameW //sys CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, err error) = kernel32.CreateFileMappingW //sys MapViewOfFile(handle Handle, access uint32, offsetHigh uint32, offsetLow uint32, length uintptr) (addr uintptr, err error) //sys UnmapViewOfFile(addr uintptr) (err error) diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index ac3da3f61e..093c4768f2 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -78,6 +78,7 @@ var ( procSetHandleInformation = modkernel32.NewProc("SetHandleInformation") procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") + procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW") procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW") procMapViewOfFile = modkernel32.NewProc("MapViewOfFile") procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile") @@ -890,6 +891,19 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) ( return } +func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err error) { + r0, _, e1 := Syscall(procGetLongPathNameW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(buf)), uintptr(buflen)) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, err error) { r0, _, e1 := Syscall6(procCreateFileMappingW.Addr(), 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) handle = Handle(r0) diff --git a/src/pkg/syscall/zsyscall_windows_amd64.go b/src/pkg/syscall/zsyscall_windows_amd64.go index 1ffa1c7ff9..d298b7451f 100644 --- a/src/pkg/syscall/zsyscall_windows_amd64.go +++ b/src/pkg/syscall/zsyscall_windows_amd64.go @@ -78,6 +78,7 @@ var ( procSetHandleInformation = modkernel32.NewProc("SetHandleInformation") procFlushFileBuffers = modkernel32.NewProc("FlushFileBuffers") procGetFullPathNameW = modkernel32.NewProc("GetFullPathNameW") + procGetLongPathNameW = modkernel32.NewProc("GetLongPathNameW") procCreateFileMappingW = modkernel32.NewProc("CreateFileMappingW") procMapViewOfFile = modkernel32.NewProc("MapViewOfFile") procUnmapViewOfFile = modkernel32.NewProc("UnmapViewOfFile") @@ -890,6 +891,19 @@ func GetFullPathName(path *uint16, buflen uint32, buf *uint16, fname **uint16) ( return } +func GetLongPathName(path *uint16, buf *uint16, buflen uint32) (n uint32, err error) { + r0, _, e1 := Syscall(procGetLongPathNameW.Addr(), 3, uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(buf)), uintptr(buflen)) + n = uint32(r0) + if n == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = EINVAL + } + } + return +} + func CreateFileMapping(fhandle Handle, sa *SecurityAttributes, prot uint32, maxSizeHigh uint32, maxSizeLow uint32, name *uint16) (handle Handle, err error) { r0, _, e1 := Syscall6(procCreateFileMappingW.Addr(), 6, uintptr(fhandle), uintptr(unsafe.Pointer(sa)), uintptr(prot), uintptr(maxSizeHigh), uintptr(maxSizeLow), uintptr(unsafe.Pointer(name))) handle = Handle(r0)