Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

Commit

Permalink
internal/fs: clone symlinks on Windows and fall back to file copying
Browse files Browse the repository at this point in the history
Signed-off-by: Ibrahim AshShohail <ibra.sho@gmail.com>
  • Loading branch information
ibrasho committed Jul 23, 2017
1 parent 55095d2 commit 545881f
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 35 deletions.
40 changes: 19 additions & 21 deletions internal/fs/fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"strings"
"unicode"

Expand Down Expand Up @@ -270,9 +271,19 @@ func CopyDir(src, dst string) error {
// the copied data is synced/flushed to stable storage.
func copyFile(src, dst string) (err error) {
if sym, err := IsSymlink(src); err != nil {
return err
return errors.Wrap(err, "symlink check failed")
} else if sym {
return cloneSymlink(src, dst)
if err := cloneSymlink(src, dst); err != nil {
if runtime.GOOS == "windows" && strings.HasSuffix(err.Error(), "A required privilege is not held by the client.") {
// If cloning the symlink fails on Windows because the user
// does not have the required privileges, ignore the error and
// fall back to copying the file contents instead of cloning.
} else {
return err
}
} else {
return nil
}
}

in, err := os.Open(src)
Expand All @@ -285,30 +296,22 @@ func copyFile(src, dst string) (err error) {
if err != nil {
return
}
defer func() {
if e := out.Close(); e != nil {
err = e
}
}()
defer out.Close()

_, err = io.Copy(out, in)
if err != nil {
if _, err = io.Copy(out, in); err != nil {
return
}

err = out.Sync()
if err != nil {
if err = out.Sync(); err != nil {
return
}

si, err := os.Stat(src)
if err != nil {
return
}

err = os.Chmod(dst, si.Mode())
if err != nil {
return
}

return
}
Expand All @@ -318,15 +321,10 @@ func copyFile(src, dst string) (err error) {
func cloneSymlink(sl, dst string) error {
resolved, err := os.Readlink(sl)
if err != nil {
return errors.Wrap(err, "failed to resolve symlink")
}

err = os.Symlink(resolved, dst)
if err != nil {
return errors.Wrapf(err, "failed to create symlink %s to %s", dst, resolved)
return err
}

return nil
return os.Symlink(resolved, dst)
}

// IsDir determines is the path given is a directory or not.
Expand Down
14 changes: 0 additions & 14 deletions internal/fs/fs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,13 +791,6 @@ func TestIsNonEmptyDir(t *testing.T) {
}

func TestIsSymlink(t *testing.T) {
if runtime.GOOS == "windows" {
// XXX: creating symlinks is not supported in Go on
// Microsoft Windows. Skipping this this until a solution
// for creating symlinks is is provided.
t.Skip("skipping on windows")
}

dir, err := ioutil.TempDir("", "dep")
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -852,13 +845,6 @@ func TestIsSymlink(t *testing.T) {
inaccessibleSymlink: {false, true},
}

if runtime.GOOS == "windows" {
// XXX: setting permissions works differently in Windows. Skipping
// these cases until a compatible implementation is provided.
delete(tests, inaccessibleFile)
delete(tests, inaccessibleSymlink)
}

for path, want := range tests {
got, err := IsSymlink(path)
if err != nil {
Expand Down

0 comments on commit 545881f

Please sign in to comment.