clientupdate, util/osshare, util/winutil, version: improve Windows GUI filename resolution and WinUI build awareness

On Windows arm64 we are going to need to ship two different GUI builds;
one for Win10 (GOARCH=386) and one for Win11 (GOARCH=amd64, tags +=
winui). Due to quirks in MSI packaging, they cannot both share the
same filename. This requires some fixes in places where we have
hardcoded "tailscale-ipn" as the GUI filename.

We also do some cleanup in clientupdate to ensure that autoupdates
will continue to work correctly with the temporary "-winui" package
variant.

Fixes #17480
Updates https://github.com/tailscale/corp/issues/29940

Signed-off-by: Aaron Klotz <aaron@tailscale.com>
This commit is contained in:
Aaron Klotz
2025-09-29 11:44:23 -06:00
parent e45557afc0
commit 7c49cab1a6
7 changed files with 146 additions and 33 deletions

View File

@@ -9,30 +9,31 @@ import (
"fmt"
"os"
"path/filepath"
"sync"
"runtime"
"golang.org/x/sys/windows/registry"
"tailscale.com/types/lazy"
"tailscale.com/types/logger"
"tailscale.com/util/winutil"
)
const (
sendFileShellKey = `*\shell\tailscale`
)
var ipnExePath struct {
sync.Mutex
cache string // absolute path of tailscale-ipn.exe, populated lazily on first use
}
var ipnExePath lazy.SyncValue[string] // absolute path of the GUI executable
func getIpnExePath(logf logger.Logf) string {
ipnExePath.Lock()
defer ipnExePath.Unlock()
if ipnExePath.cache != "" {
return ipnExePath.cache
exe, err := winutil.GUIPathFromReg()
if err == nil {
return exe
}
// Find the absolute path of tailscale-ipn.exe assuming that it's in the same
return findGUIInSameDirAsThisExe(logf)
}
func findGUIInSameDirAsThisExe(logf logger.Logf) string {
// Find the absolute path of the GUI, assuming that it's in the same
// directory as this executable (tailscaled.exe).
p, err := os.Executable()
if err != nil {
@@ -43,14 +44,23 @@ func getIpnExePath(logf logger.Logf) string {
logf("filepath.EvalSymlinks error: %v", err)
return ""
}
p = filepath.Join(filepath.Dir(p), "tailscale-ipn.exe")
if p, err = filepath.Abs(p); err != nil {
logf("filepath.Abs error: %v", err)
return ""
}
ipnExePath.cache = p
return p
d := filepath.Dir(p)
candidates := []string{"tailscale-ipn.exe"}
if runtime.GOARCH == "arm64" {
// This name may be used on Windows 10 ARM64.
candidates = append(candidates, "tailscale-gui-386.exe")
}
for _, c := range candidates {
testPath := filepath.Join(d, c)
if _, err := os.Stat(testPath); err == nil {
return testPath
}
}
return ""
}
// SetFileSharingEnabled adds/removes "Send with Tailscale" from the Windows shell menu.
@@ -64,7 +74,9 @@ func SetFileSharingEnabled(enabled bool, logf logger.Logf) {
}
func enableFileSharing(logf logger.Logf) {
path := getIpnExePath(logf)
path := ipnExePath.Get(func() string {
return getIpnExePath(logf)
})
if path == "" {
return
}
@@ -79,7 +91,7 @@ func enableFileSharing(logf logger.Logf) {
logf("k.SetStringValue error: %v", err)
return
}
if err := k.SetStringValue("Icon", path+",0"); err != nil {
if err := k.SetStringValue("Icon", path+",1"); err != nil {
logf("k.SetStringValue error: %v", err)
return
}