From b3030cdadc9e98ab223b331a32bc72efa3a17534 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 04:37:41 +0100 Subject: [PATCH 1/8] Added support for non-symlink checkouts on Windows when elevated rights are missing This implementation mimicks the behavior of Git on Windows --- worktree.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/worktree.go b/worktree.go index a23397edf..67db0e583 100644 --- a/worktree.go +++ b/worktree.go @@ -540,6 +540,18 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { return } +func isSymlinkWindowsNonAdmin(err error) bool { + if err != nil { + if x, ok := err.(*os.LinkError); ok { + if xx, ok := x.Err.(syscall.Errno); ok { + return xx == syscall.ERROR_PRIVILEGE_NOT_HELD + } + } + } + + return false +} + func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { from, err := f.Reader() if err != nil { @@ -554,6 +566,20 @@ func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { } err = w.Filesystem.Symlink(string(bytes), f.Name) + + // On windows, this might fail. + // Follow Git on Windows behavior by writing the link as it is. + if err != nil && isSymlinkWindowsNonAdmin(err) { + to, err := w.Filesystem.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm()) + if err != nil { + return + } + + defer ioutil.CheckClose(to, &err) + + _, err = to.Write(bytes) + } + return } From 50e722a0539c56e2f776fae3a351e1766ff52295 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 04:50:21 +0100 Subject: [PATCH 2/8] Fixed missing import --- worktree.go | 1 + 1 file changed, 1 insertion(+) diff --git a/worktree.go b/worktree.go index 67db0e583..9ff2c3c76 100644 --- a/worktree.go +++ b/worktree.go @@ -9,6 +9,7 @@ import ( "os" "path/filepath" "strings" + "syscall" "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" From 0a3af05dce976c6de8072c7ea4a4e929a184f32e Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 04:56:14 +0100 Subject: [PATCH 3/8] Fixed missing constant --- worktree.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/worktree.go b/worktree.go index 9ff2c3c76..e9ef2311d 100644 --- a/worktree.go +++ b/worktree.go @@ -542,17 +542,19 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { } func isSymlinkWindowsNonAdmin(err error) bool { + const ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314 + if err != nil { if x, ok := err.(*os.LinkError); ok { if xx, ok := x.Err.(syscall.Errno); ok { - return xx == syscall.ERROR_PRIVILEGE_NOT_HELD + return xx == ERROR_PRIVILEGE_NOT_HELD } } } - return false -} - + return false +} + func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { from, err := f.Reader() if err != nil { @@ -571,11 +573,13 @@ func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { // On windows, this might fail. // Follow Git on Windows behavior by writing the link as it is. if err != nil && isSymlinkWindowsNonAdmin(err) { + mode, _ := f.Mode.ToOSFileMode() + to, err := w.Filesystem.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm()) if err != nil { return } - + defer ioutil.CheckClose(to, &err) _, err = to.Write(bytes) From 79c3fbdb14c5ca14cb2872e843177ab26201d69b Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Fri, 9 Mar 2018 04:59:25 +0100 Subject: [PATCH 4/8] Fixed shadowing of error --- worktree.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worktree.go b/worktree.go index e9ef2311d..01c01aaed 100644 --- a/worktree.go +++ b/worktree.go @@ -577,12 +577,13 @@ func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { to, err := w.Filesystem.OpenFile(f.Name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode.Perm()) if err != nil { - return + return err } defer ioutil.CheckClose(to, &err) _, err = to.Write(bytes) + return err } return From 47ac8aea4ed3b577865cede06983ab0e1d6c427c Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Mon, 12 Mar 2018 14:30:53 +0100 Subject: [PATCH 5/8] Improved code based on review feedback --- worktree.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/worktree.go b/worktree.go index 9a1e82571..d8b06a232 100644 --- a/worktree.go +++ b/worktree.go @@ -545,9 +545,9 @@ func isSymlinkWindowsNonAdmin(err error) bool { const ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314 if err != nil { - if x, ok := err.(*os.LinkError); ok { - if xx, ok := x.Err.(syscall.Errno); ok { - return xx == ERROR_PRIVILEGE_NOT_HELD + if errLink, ok := err.(*os.LinkError); ok { + if errNo, ok := errLink.Err.(syscall.Errno); ok { + return errNo == ERROR_PRIVILEGE_NOT_HELD } } } From 80e419ec005bd47ebe1bca29d2af3c9dbaf891ee Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Mon, 12 Mar 2018 14:31:14 +0100 Subject: [PATCH 6/8] Limiting ERROR_PRIVILEGE_NOT_HELD check to windows runtime --- worktree.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/worktree.go b/worktree.go index d8b06a232..83136d12b 100644 --- a/worktree.go +++ b/worktree.go @@ -8,6 +8,7 @@ import ( stdioutil "io/ioutil" "os" "path/filepath" + "runtime" "strings" "syscall" @@ -544,6 +545,10 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { func isSymlinkWindowsNonAdmin(err error) bool { const ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314 + if runtime.GOOS != "windows" { + return false + } + if err != nil { if errLink, ok := err.(*os.LinkError); ok { if errNo, ok := errLink.Err.(syscall.Errno); ok { From a880ed0abeb5b3ed38323bc18a1d68fb7d975a22 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Mon, 12 Mar 2018 14:33:23 +0100 Subject: [PATCH 7/8] Sign-off, Signed-off-by: Felix Kollmann From 59953cbc850a8b3f8672d89b1a71ac04be504f30 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Mon, 12 Mar 2018 14:41:50 +0100 Subject: [PATCH 8/8] Moved isSymlinkWindowsNonAdmin() to platform specific files Signed-off-by: Felix Kollmann --- worktree.go | 20 -------------------- worktree_darwin.go | 4 ++++ worktree_linux.go | 4 ++++ worktree_windows.go | 15 +++++++++++++++ 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/worktree.go b/worktree.go index 83136d12b..24df1ab68 100644 --- a/worktree.go +++ b/worktree.go @@ -8,9 +8,7 @@ import ( stdioutil "io/ioutil" "os" "path/filepath" - "runtime" "strings" - "syscall" "gopkg.in/src-d/go-git.v4/config" "gopkg.in/src-d/go-git.v4/plumbing" @@ -542,24 +540,6 @@ func (w *Worktree) checkoutFile(f *object.File) (err error) { return } -func isSymlinkWindowsNonAdmin(err error) bool { - const ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314 - - if runtime.GOOS != "windows" { - return false - } - - if err != nil { - if errLink, ok := err.(*os.LinkError); ok { - if errNo, ok := errLink.Err.(syscall.Errno); ok { - return errNo == ERROR_PRIVILEGE_NOT_HELD - } - } - } - - return false -} - func (w *Worktree) checkoutFileSymlink(f *object.File) (err error) { from, err := f.Reader() if err != nil { diff --git a/worktree_darwin.go b/worktree_darwin.go index 8eaffde8a..3b374c77b 100644 --- a/worktree_darwin.go +++ b/worktree_darwin.go @@ -20,3 +20,7 @@ func init() { } } } + +func isSymlinkWindowsNonAdmin(err error) bool { + return false +} diff --git a/worktree_linux.go b/worktree_linux.go index a33cd2fb9..891cb1cf3 100644 --- a/worktree_linux.go +++ b/worktree_linux.go @@ -20,3 +20,7 @@ func init() { } } } + +func isSymlinkWindowsNonAdmin(err error) bool { + return false +} diff --git a/worktree_windows.go b/worktree_windows.go index d59448ef8..1bef6f759 100644 --- a/worktree_windows.go +++ b/worktree_windows.go @@ -3,6 +3,7 @@ package git import ( + "os" "syscall" "time" @@ -18,3 +19,17 @@ func init() { } } } + +func isSymlinkWindowsNonAdmin(err error) bool { + const ERROR_PRIVILEGE_NOT_HELD syscall.Errno = 1314 + + if err != nil { + if errLink, ok := err.(*os.LinkError); ok { + if errNo, ok := errLink.Err.(syscall.Errno); ok { + return errNo == ERROR_PRIVILEGE_NOT_HELD + } + } + } + + return false +}