diff --git a/internal/update/update.go b/internal/update/update.go index bb92930..f4c27ca 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -67,13 +67,19 @@ func update(ctx context.Context, p *core.Printer, timeout time.Duration, silent func updateInner(ctx context.Context, p *core.Printer, silent bool) error { c := client.NewClient(client.ClientConfig{}) - // Get the current version by calling `fetch --version` so that if the - // executable was updated while we were waiting for the update lock, - // we have the most up-to-date local version. + // Get the current executable path and verify that we have write + // permission in order to replace the file. exePath, err := getExecutablePath() if err != nil { return err } + if !canReplaceFile(exePath) { + return errNoWritePermission(exePath) + } + + // Get the current version by calling `fetch --version` so that if the + // executable was updated while we were waiting for the update lock, + // we have the most up-to-date local version. version, err := getExeVersion(ctx, exePath) if err != nil { return err @@ -459,3 +465,17 @@ func (err errNoReleaseArtifact) PrintTo(p *core.Printer) { p.Reset() p.WriteString("'") } + +type errNoWritePermission string + +func (err errNoWritePermission) Error() string { + return fmt.Sprintf("the current process does not have write permission to '%s'", string(err)) +} + +func (err errNoWritePermission) PrintTo(p *core.Printer) { + p.WriteString("the current process does not have write permission to '") + p.Set(core.Dim) + p.WriteString(string(err)) + p.Reset() + p.WriteString("'") +} diff --git a/internal/update/update_unix.go b/internal/update/update_unix.go index 1abd637..5339573 100644 --- a/internal/update/update_unix.go +++ b/internal/update/update_unix.go @@ -97,3 +97,9 @@ func tryLockFile(f *os.File) (bool, error) { func unlockFile(f *os.File) error { return unix.Flock(int(f.Fd()), unix.LOCK_UN) } + +// canReplaceFile returns true if this process can replace the file at the +// provided location. +func canReplaceFile(path string) bool { + return unix.Access(path, unix.W_OK) == nil +} diff --git a/internal/update/update_windows.go b/internal/update/update_windows.go index d5be7d9..62652d6 100644 --- a/internal/update/update_windows.go +++ b/internal/update/update_windows.go @@ -245,3 +245,8 @@ func unlockFile(f *os.File) error { var ol windows.Overlapped return windows.UnlockFileEx(windows.Handle(f.Fd()), 0, allBytes, allBytes, &ol) } + +// canReplaceFile always returns true on windows. +func canReplaceFile(path string) bool { + return true +}