diff options
| author | Michael Fraenkel <michael.fraenkel@gmail.com> | 2014-07-17 17:02:46 +1000 |
|---|---|---|
| committer | Alex Brainman <alex.brainman@gmail.com> | 2014-07-17 17:02:46 +1000 |
| commit | cf521ce64f50c4f300294d6b649f34eca87bb8a3 (patch) | |
| tree | a7f4a5612448eae5076254bd2c2193dc87727b34 /src/pkg/path/filepath | |
| parent | 47fd6bd9b62c8c028f03fc9407e3ce261fb8ed6d (diff) | |
| download | go-cf521ce64f50c4f300294d6b649f34eca87bb8a3.tar.xz | |
os: Implement symlink support for Windows
Fixes #5750.
https://code.google.com/p/go/issues/detail?id=5750
os: Separate windows from posix. Implement windows support.
path/filepath: Use the same implementation as other platforms
syscall: Add/rework new APIs for Windows
LGTM=alex.brainman
R=golang-codereviews, alex.brainman, gobot, rsc, minux
CC=golang-codereviews
https://golang.org/cl/86160044
Diffstat (limited to 'src/pkg/path/filepath')
| -rw-r--r-- | src/pkg/path/filepath/match_test.go | 7 | ||||
| -rw-r--r-- | src/pkg/path/filepath/path_test.go | 12 | ||||
| -rw-r--r-- | src/pkg/path/filepath/path_windows_test.go | 20 | ||||
| -rw-r--r-- | src/pkg/path/filepath/symlink.go | 19 | ||||
| -rw-r--r-- | src/pkg/path/filepath/symlink_unix.go | 7 | ||||
| -rw-r--r-- | src/pkg/path/filepath/symlink_windows.go | 5 |
6 files changed, 57 insertions, 13 deletions
diff --git a/src/pkg/path/filepath/match_test.go b/src/pkg/path/filepath/match_test.go index 382692eaa4..20ec5aa2a1 100644 --- a/src/pkg/path/filepath/match_test.go +++ b/src/pkg/path/filepath/match_test.go @@ -167,8 +167,13 @@ var globSymlinkTests = []struct { func TestGlobSymlink(t *testing.T) { switch runtime.GOOS { - case "nacl", "plan9", "windows": + case "nacl", "plan9": t.Skipf("skipping on %s", runtime.GOOS) + case "windows": + if !supportsSymlinks { + t.Skipf("skipping on %s", runtime.GOOS) + } + } tmpDir, err := ioutil.TempDir("", "globsymlink") diff --git a/src/pkg/path/filepath/path_test.go b/src/pkg/path/filepath/path_test.go index 819bd217cc..17b53bdf92 100644 --- a/src/pkg/path/filepath/path_test.go +++ b/src/pkg/path/filepath/path_test.go @@ -15,6 +15,8 @@ import ( "testing" ) +var supportsSymlinks = true + type PathTest struct { path, result string } @@ -716,7 +718,7 @@ func TestEvalSymlinks(t *testing.T) { if d.dest == "" { err = os.Mkdir(path, 0755) } else { - if runtime.GOOS != "windows" { + if supportsSymlinks { err = os.Symlink(d.dest, path) } } @@ -726,7 +728,9 @@ func TestEvalSymlinks(t *testing.T) { } var tests []EvalSymlinksTest - if runtime.GOOS == "windows" { + if supportsSymlinks { + tests = EvalSymlinksTests + } else { for _, d := range EvalSymlinksTests { if d.path == d.dest { // will test only real files and directories @@ -739,15 +743,13 @@ func TestEvalSymlinks(t *testing.T) { tests = append(tests, d2) } } - } else { - tests = EvalSymlinksTests } // Evaluate the symlink farm. for _, d := range tests { path := simpleJoin(tmpDir, d.path) dest := simpleJoin(tmpDir, d.dest) - if filepath.IsAbs(d.dest) { + if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) { dest = d.dest } if p, err := filepath.EvalSymlinks(path); err != nil { diff --git a/src/pkg/path/filepath/path_windows_test.go b/src/pkg/path/filepath/path_windows_test.go index 8a9be8e896..100cf30a45 100644 --- a/src/pkg/path/filepath/path_windows_test.go +++ b/src/pkg/path/filepath/path_windows_test.go @@ -10,9 +10,29 @@ import ( "os/exec" "path/filepath" "reflect" + "syscall" "testing" ) +func init() { + tmpdir, err := ioutil.TempDir("", "symtest") + if err != nil { + panic("failed to create temp directory: " + err.Error()) + } + defer os.RemoveAll(tmpdir) + + err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) + if err == nil { + return + } + + err = err.(*os.LinkError).Err + switch err { + case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: + supportsSymlinks = false + } +} + func TestWinSplitListTestsAreValid(t *testing.T) { comspec := os.Getenv("ComSpec") if comspec == "" { diff --git a/src/pkg/path/filepath/symlink.go b/src/pkg/path/filepath/symlink.go index 307dd0f8fe..df0a9e0c2b 100644 --- a/src/pkg/path/filepath/symlink.go +++ b/src/pkg/path/filepath/symlink.go @@ -2,18 +2,17 @@ // 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 utf8RuneSelf = 0x80 + +func walkSymlinks(path string) (string, error) { const maxIter = 255 originalPath := path // consume path by taking each frontmost path element, @@ -25,7 +24,13 @@ func evalSymlinks(path string) (string, error) { } // find next path component, p - i := strings.IndexRune(path, Separator) + var i = -1 + for j, c := range path { + if c < utf8RuneSelf && os.IsPathSeparator(uint8(c)) { + i = j + break + } + } var p string if i == -1 { p, path = path, "" @@ -47,7 +52,7 @@ func evalSymlinks(path string) (string, error) { } if fi.Mode()&os.ModeSymlink == 0 { b.WriteString(p) - if path != "" { + if path != "" || (b.Len() == 2 && len(p) == 2 && p[1] == ':') { b.WriteRune(Separator) } continue @@ -58,7 +63,7 @@ func evalSymlinks(path string) (string, error) { if err != nil { return "", err } - if IsAbs(dest) { + if IsAbs(dest) || os.IsPathSeparator(dest[0]) { b.Reset() } path = dest + string(Separator) + path diff --git a/src/pkg/path/filepath/symlink_unix.go b/src/pkg/path/filepath/symlink_unix.go new file mode 100644 index 0000000000..d20e63a987 --- /dev/null +++ b/src/pkg/path/filepath/symlink_unix.go @@ -0,0 +1,7 @@ +// +build !windows + +package filepath + +func evalSymlinks(path string) (string, error) { + return walkSymlinks(path) +} diff --git a/src/pkg/path/filepath/symlink_windows.go b/src/pkg/path/filepath/symlink_windows.go index 9adc8a48af..327c2c89a3 100644 --- a/src/pkg/path/filepath/symlink_windows.go +++ b/src/pkg/path/filepath/symlink_windows.go @@ -50,6 +50,11 @@ func toLong(path string) (string, error) { } func evalSymlinks(path string) (string, error) { + path, err := walkSymlinks(path) + if err != nil { + return "", err + } + p, err := toShort(path) if err != nil { return "", err |
