78 lines
2.3 KiB
Go
78 lines
2.3 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package ipnlocal
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"os/user"
|
|
"path/filepath"
|
|
|
|
"tailscale.com/atomicfile"
|
|
"tailscale.com/ipn"
|
|
"tailscale.com/util/winutil/policy"
|
|
)
|
|
|
|
const (
|
|
legacyPrefsFile = "prefs"
|
|
legacyPrefsMigrationSentinelFile = "_migrated-to-profiles"
|
|
legacyPrefsExt = ".conf"
|
|
)
|
|
|
|
func legacyPrefsDir(uid ipn.WindowsUserID) (string, error) {
|
|
// TODO(aaron): Ideally we'd have the impersonation token for the pipe's
|
|
// client and use it to call SHGetKnownFolderPath, thus yielding the correct
|
|
// path without having to make gross assumptions about directory names.
|
|
usr, err := user.LookupId(string(uid))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if usr.HomeDir == "" {
|
|
return "", fmt.Errorf("user %q does not have a home directory", uid)
|
|
}
|
|
userLegacyPrefsDir := filepath.Join(usr.HomeDir, "AppData", "Local", "Tailscale")
|
|
return userLegacyPrefsDir, nil
|
|
}
|
|
|
|
func (pm *profileManager) loadLegacyPrefs() (string, ipn.PrefsView, error) {
|
|
userLegacyPrefsDir, err := legacyPrefsDir(pm.currentUserID)
|
|
if err != nil {
|
|
pm.dlogf("no legacy preferences directory for %q: %v", pm.currentUserID, err)
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
migrationSentinel := filepath.Join(userLegacyPrefsDir, legacyPrefsMigrationSentinelFile+legacyPrefsExt)
|
|
// verify that migration sentinel is not present
|
|
_, err = os.Stat(migrationSentinel)
|
|
if err == nil {
|
|
pm.dlogf("migration sentinel %q already exists", migrationSentinel)
|
|
return "", ipn.PrefsView{}, errAlreadyMigrated
|
|
}
|
|
if !os.IsNotExist(err) {
|
|
pm.dlogf("os.Stat(%q) = %v", migrationSentinel, err)
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
prefsPath := filepath.Join(userLegacyPrefsDir, legacyPrefsFile+legacyPrefsExt)
|
|
prefs, err := ipn.LoadPrefs(prefsPath)
|
|
pm.dlogf("ipn.LoadPrefs(%q) = %v, %v", prefsPath, prefs, err)
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
return "", ipn.PrefsView{}, errAlreadyMigrated
|
|
}
|
|
if err != nil {
|
|
return "", ipn.PrefsView{}, err
|
|
}
|
|
|
|
prefs.ControlURL = policy.SelectControlURL(defaultPrefs.ControlURL(), prefs.ControlURL)
|
|
|
|
pm.logf("migrating Windows profile to new format")
|
|
return migrationSentinel, prefs.View(), nil
|
|
}
|
|
|
|
func (pm *profileManager) completeMigration(migrationSentinel string) {
|
|
atomicfile.WriteFile(migrationSentinel, []byte{}, 0600)
|
|
}
|