diff --git a/go.mod b/go.mod index 9cb9d35..a26fca9 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/xanzy/go-gitlab v0.115.0 golang.org/x/crypto v0.32.0 golang.org/x/oauth2 v0.25.0 + golang.org/x/sys v0.29.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -24,6 +25,5 @@ require ( github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.29.0 // indirect golang.org/x/time v0.9.0 // indirect ) diff --git a/internal/path.go b/internal/path.go index e33f8bb..9b56ec4 100644 --- a/internal/path.go +++ b/internal/path.go @@ -2,7 +2,6 @@ package internal import ( "os" - "path/filepath" ) // GetExecutablePath returns the path of the executable file with all symlinks resolved. @@ -12,7 +11,7 @@ func GetExecutablePath() (string, error) { return "", err } - exe, err = filepath.EvalSymlinks(exe) + exe, err = ResolvePath(exe) if err != nil { return "", err } diff --git a/internal/resolve_path_unix.go b/internal/resolve_path_unix.go new file mode 100644 index 0000000..0a8ee79 --- /dev/null +++ b/internal/resolve_path_unix.go @@ -0,0 +1,17 @@ +//go:build !windows + +package internal + +import ( + "path/filepath" +) + +// ResolvePath returns the path of a given filename with all symlinks resolved. +func ResolvePath(filename string) (string, error) { + finalPath, err := filepath.EvalSymlinks(filename) + if err != nil { + return "", err + } + + return finalPath, nil +} diff --git a/internal/resolve_path_windows.go b/internal/resolve_path_windows.go new file mode 100644 index 0000000..68016a0 --- /dev/null +++ b/internal/resolve_path_windows.go @@ -0,0 +1,42 @@ +//go:build windows + +package internal + +import ( + "golang.org/x/sys/windows" + "os" + "strings" + "syscall" +) + +// ResolvePath returns the path of a given filename with all symlinks resolved. +func ResolvePath(filename string) (string, error) { + f, err := os.Open(filename) + if err != nil { + return "", err + } + defer f.Close() + + // Get the Windows handle + handle := windows.Handle(f.Fd()) + + // Probe call to determine the needed buffer size + bufSize, err := windows.GetFinalPathNameByHandle(handle, nil, 0, 0) + if err != nil { + return "", err + } + + buf := make([]uint16, bufSize) + n, err := windows.GetFinalPathNameByHandle(handle, &buf[0], uint32(len(buf)), 0) + if err != nil { + return "", err + } + + // Convert the buffer to a string + final := syscall.UTF16ToString(buf[:n]) + + // Strip possible "\\?\" prefix + final = strings.TrimPrefix(final, `\\?\`) + + return final, nil +} diff --git a/update.go b/update.go index 1b5e5e4..2f1ce05 100644 --- a/update.go +++ b/update.go @@ -54,7 +54,7 @@ func (up *Updater) UpdateCommand(ctx context.Context, cmdPath string, current st return nil, fmt.Errorf("failed to stat '%s'. file may not exist: %s", cmdPath, err) } if stat.Mode()&os.ModeSymlink != 0 { - p, err := filepath.EvalSymlinks(cmdPath) + p, err := internal.ResolvePath(cmdPath) if err != nil { return nil, fmt.Errorf("failed to resolve symlink '%s' for executable: %s", cmdPath, err) }