@ -35,9 +35,9 @@ type profileManager struct {
health * health . Tracker
currentUserID ipn . WindowsUserID
knownProfiles map [ ipn . ProfileID ] * ipn . LoginProfile // always non-nil
currentProfile * ipn . LoginProfile // always non-nil
prefs ipn . PrefsView // always Valid.
knownProfiles map [ ipn . ProfileID ] ipn . LoginProfile View // always non-nil
currentProfile ipn . LoginProfileView // always Valid.
prefs ipn . PrefsView // always Valid.
}
func ( pm * profileManager ) dlogf ( format string , args ... any ) {
@ -89,7 +89,7 @@ func (pm *profileManager) DefaultUserProfileID(uid ipn.WindowsUserID) ipn.Profil
pm . dlogf ( "DefaultUserProfileID: windows: migrating from legacy preferences" )
profile , err := pm . migrateFromLegacyPrefs ( uid , false )
if err == nil {
return profile . ID
return profile . ID ( )
}
pm . logf ( "failed to migrate from legacy preferences: %v" , err )
}
@ -98,17 +98,17 @@ func (pm *profileManager) DefaultUserProfileID(uid ipn.WindowsUserID) ipn.Profil
pk := ipn . StateKey ( string ( b ) )
prof := pm . findProfileByKey ( pk )
if prof == nil {
if ! prof . Valid ( ) {
pm . dlogf ( "DefaultUserProfileID: no profile found for key: %q" , pk )
return ""
}
return prof . ID
return prof . ID ( )
}
// checkProfileAccess returns an [errProfileAccessDenied] if the current user
// does not have access to the specified profile.
func ( pm * profileManager ) checkProfileAccess ( profile * ipn . LoginProfile ) error {
if pm . currentUserID != "" && profile . LocalUserID != pm . currentUserID {
func ( pm * profileManager ) checkProfileAccess ( profile ipn . LoginProfile View ) error {
if pm . currentUserID != "" && profile . LocalUserID ( ) != pm . currentUserID {
return errProfileAccessDenied
}
return nil
@ -116,21 +116,21 @@ func (pm *profileManager) checkProfileAccess(profile *ipn.LoginProfile) error {
// allProfiles returns all profiles accessible to the current user.
// The returned profiles are sorted by Name.
func ( pm * profileManager ) allProfiles ( ) ( out [ ] * ipn . LoginProfile ) {
func ( pm * profileManager ) allProfiles ( ) ( out [ ] ipn . LoginProfile View ) {
for _ , p := range pm . knownProfiles {
if pm . checkProfileAccess ( p ) == nil {
out = append ( out , p )
}
}
slices . SortFunc ( out , func ( a , b * ipn . LoginProfile ) int {
return cmp . Compare ( a . Name , b . Name )
slices . SortFunc ( out , func ( a , b ipn . LoginProfile View ) int {
return cmp . Compare ( a . Name ( ) , b . Name ( ) )
} )
return out
}
// matchingProfiles is like [profileManager.allProfiles], but returns only profiles
// matching the given predicate.
func ( pm * profileManager ) matchingProfiles ( f func ( * ipn . LoginProfile ) bool ) ( out [ ] * ipn . LoginProfile ) {
func ( pm * profileManager ) matchingProfiles ( f func ( ipn . LoginProfile View ) bool ) ( out [ ] ipn . LoginProfile View ) {
all := pm . allProfiles ( )
out = all [ : 0 ]
for _ , p := range all {
@ -144,11 +144,11 @@ func (pm *profileManager) matchingProfiles(f func(*ipn.LoginProfile) bool) (out
// findMatchingProfiles returns all profiles accessible to the current user
// that represent the same node/user as prefs.
// The returned profiles are sorted by Name.
func ( pm * profileManager ) findMatchingProfiles ( prefs ipn . PrefsView ) [ ] * ipn . LoginProfile {
return pm . matchingProfiles ( func ( p * ipn . LoginProfile ) bool {
return p . ControlURL == prefs . ControlURL ( ) &&
( p . UserProfile . ID == prefs . Persist ( ) . UserProfile ( ) . ID ||
p . NodeID == prefs . Persist ( ) . NodeID ( ) )
func ( pm * profileManager ) findMatchingProfiles ( prefs ipn . PrefsView ) [ ] ipn . LoginProfile View {
return pm . matchingProfiles ( func ( p ipn . LoginProfile View ) bool {
return p . ControlURL ( ) == prefs . ControlURL ( ) &&
( p . UserProfile ( ) . ID == prefs . Persist ( ) . UserProfile ( ) . ID ||
p . NodeID ( ) == prefs . Persist ( ) . NodeID ( ) )
} )
}
@ -157,18 +157,18 @@ func (pm *profileManager) findMatchingProfiles(prefs ipn.PrefsView) []*ipn.Login
// accessible to the current user.
func ( pm * profileManager ) ProfileIDForName ( name string ) ipn . ProfileID {
p := pm . findProfileByName ( name )
if p == nil {
if ! p . Valid ( ) {
return ""
}
return p . ID
return p . ID ( )
}
func ( pm * profileManager ) findProfileByName ( name string ) * ipn . LoginProfile {
out := pm . matchingProfiles ( func ( p * ipn . LoginProfile ) bool {
return p . Name == name
func ( pm * profileManager ) findProfileByName ( name string ) ipn . LoginProfile View {
out := pm . matchingProfiles ( func ( p ipn . LoginProfile View ) bool {
return p . Name ( ) == name
} )
if len ( out ) == 0 {
return nil
return ipn . LoginProfileView { }
}
if len ( out ) > 1 {
pm . logf ( "[unexpected] multiple profiles with the same name" )
@ -176,12 +176,12 @@ func (pm *profileManager) findProfileByName(name string) *ipn.LoginProfile {
return out [ 0 ]
}
func ( pm * profileManager ) findProfileByKey ( key ipn . StateKey ) * ipn . LoginProfile {
out := pm . matchingProfiles ( func ( p * ipn . LoginProfile ) bool {
return p . Key == key
func ( pm * profileManager ) findProfileByKey ( key ipn . StateKey ) ipn . LoginProfile View {
out := pm . matchingProfiles ( func ( p ipn . LoginProfile View ) bool {
return p . Key ( ) == key
} )
if len ( out ) == 0 {
return nil
return ipn . LoginProfileView { }
}
if len ( out ) > 1 {
pm . logf ( "[unexpected] multiple profiles with the same key" )
@ -194,8 +194,8 @@ func (pm *profileManager) setUnattendedModeAsConfigured() error {
return nil
}
if pm . currentProfile . Key != "" && pm . prefs . ForceDaemon ( ) {
return pm . WriteState ( ipn . ServerModeStartKey , [ ] byte ( pm . currentProfile . Key ) )
if pm . currentProfile . Key ( ) != "" && pm . prefs . ForceDaemon ( ) {
return pm . WriteState ( ipn . ServerModeStartKey , [ ] byte ( pm . currentProfile . Key ( ) ) )
} else {
return pm . WriteState ( ipn . ServerModeStartKey , nil )
}
@ -229,29 +229,36 @@ func (pm *profileManager) SetPrefs(prefsIn ipn.PrefsView, np ipn.NetworkProfile)
existing = existing [ 1 : ]
for _ , p := range existing {
// Clear the state.
if err := pm . store . WriteState ( p . Key , nil ) ; err != nil {
if err := pm . store . WriteState ( p . Key ( ) , nil ) ; err != nil {
// We couldn't delete the state, so keep the profile around.
continue
}
// Remove the profile, knownProfiles will be persisted
// in [profileManager.setProfilePrefs] below.
delete ( pm . knownProfiles , p . ID )
delete ( pm . knownProfiles , p . ID ( ) )
}
}
pm . currentProfile = cp
if err := pm . SetProfilePrefs ( cp , prefsIn , np ) ; err != nil {
cp , err := pm . setProfilePrefs ( nil , prefsIn , np )
if err != nil {
return err
}
return pm . setProfileAsUserDefault ( cp )
}
// SetProfilePrefs is like [profileManager.SetPrefs], but sets prefs for the specified [ipn.LoginProfile]
// which is not necessarily the [profileManager.CurrentProfile]. It returns an [errProfileAccessDenied]
// if the specified profile is not accessible by the current user.
func ( pm * profileManager ) SetProfilePrefs ( lp * ipn . LoginProfile , prefsIn ipn . PrefsView , np ipn . NetworkProfile ) error {
if err := pm . checkProfileAccess ( lp ) ; err != nil {
return err
// setProfilePrefs is like [profileManager.SetPrefs], but sets prefs for the specified [ipn.LoginProfile],
// returning a read-only view of the updated profile on success. If the specified profile is nil,
// it defaults to the current profile. If the profile is not accessible by the current user,
// the method returns an [errProfileAccessDenied].
func ( pm * profileManager ) setProfilePrefs ( lp * ipn . LoginProfile , prefsIn ipn . PrefsView , np ipn . NetworkProfile ) ( ipn . LoginProfileView , error ) {
isCurrentProfile := lp == nil || ( lp . ID != "" && lp . ID == pm . currentProfile . ID ( ) )
if isCurrentProfile {
lp = pm . CurrentProfile ( ) . AsStruct ( )
}
if err := pm . checkProfileAccess ( lp . View ( ) ) ; err != nil {
return ipn . LoginProfileView { } , err
}
// An empty profile.ID indicates that the profile is new, the node info wasn't available,
@ -291,23 +298,29 @@ func (pm *profileManager) SetProfilePrefs(lp *ipn.LoginProfile, prefsIn ipn.Pref
lp . UserProfile = up
lp . NetworkProfile = np
// Update the current profile view to reflect the changes
// if the specified profile is the current profile.
if isCurrentProfile {
pm . currentProfile = lp . View ( )
}
// An empty profile.ID indicates that the node info is not available yet,
// and the profile doesn't need to be saved on disk.
if lp . ID != "" {
pm . knownProfiles [ lp . ID ] = lp
pm . knownProfiles [ lp . ID ] = lp . View ( )
if err := pm . writeKnownProfiles ( ) ; err != nil {
return err
return ipn. LoginProfileView { } , err
}
// Clone prefsIn and create a read-only view as a safety measure to
// prevent accidental preference mutations, both externally and internally.
if err := pm . setProfilePrefsNoPermCheck ( lp , prefsIn . AsStruct ( ) . View ( ) ) ; err != nil {
return err
if err := pm . setProfilePrefsNoPermCheck ( lp .View ( ) , prefsIn . AsStruct ( ) . View ( ) ) ; err != nil {
return ipn. LoginProfileView { } , err
}
}
return nil
return lp . View ( ) , nil
}
func newUnusedID ( knownProfiles map [ ipn . ProfileID ] * ipn . LoginProfile ) ( ipn . ProfileID , ipn . StateKey ) {
func newUnusedID ( knownProfiles map [ ipn . ProfileID ] ipn . LoginProfile View ) ( ipn . ProfileID , ipn . StateKey ) {
var idb [ 2 ] byte
for {
rand . Read ( idb [ : ] )
@ -326,14 +339,14 @@ func newUnusedID(knownProfiles map[ipn.ProfileID]*ipn.LoginProfile) (ipn.Profile
// The method does not perform any additional checks on the specified
// profile, such as verifying the caller's access rights or checking
// if another profile for the same node already exists.
func ( pm * profileManager ) setProfilePrefsNoPermCheck ( profile * ipn . LoginProfile , clonedPrefs ipn . PrefsView ) error {
func ( pm * profileManager ) setProfilePrefsNoPermCheck ( profile ipn . LoginProfile View , clonedPrefs ipn . PrefsView ) error {
isCurrentProfile := pm . currentProfile == profile
if isCurrentProfile {
pm . prefs = clonedPrefs
pm . updateHealth ( )
}
if profile . Key != "" {
if err := pm . writePrefsToStore ( profile . Key , clonedPrefs ) ; err != nil {
if profile . Key ( ) != "" {
if err := pm . writePrefsToStore ( profile . Key ( ) , clonedPrefs ) ; err != nil {
return err
}
} else if ! isCurrentProfile {
@ -362,11 +375,11 @@ func (pm *profileManager) writePrefsToStore(key ipn.StateKey, prefs ipn.PrefsVie
}
// Profiles returns the list of known profiles accessible to the current user.
func ( pm * profileManager ) Profiles ( ) [ ] ipn . LoginProfile {
func ( pm * profileManager ) Profiles ( ) [ ] ipn . LoginProfile View {
allProfiles := pm . allProfiles ( )
out := make ( [ ] ipn . LoginProfile , len ( allProfiles ) )
out := make ( [ ] ipn . LoginProfile View , len ( allProfiles ) )
for i , p := range allProfiles {
out [ i ] = * p
out [ i ] = p
}
return out
}
@ -374,26 +387,26 @@ func (pm *profileManager) Profiles() []ipn.LoginProfile {
// ProfileByID returns a profile with the given id, if it is accessible to the current user.
// If the profile exists but is not accessible to the current user, it returns an [errProfileAccessDenied].
// If the profile does not exist, it returns an [errProfileNotFound].
func ( pm * profileManager ) ProfileByID ( id ipn . ProfileID ) ( ipn . LoginProfile , error ) {
func ( pm * profileManager ) ProfileByID ( id ipn . ProfileID ) ( ipn . LoginProfile View , error ) {
kp , err := pm . profileByIDNoPermCheck ( id )
if err != nil {
return ipn . LoginProfile { } , err
return ipn . LoginProfile View { } , err
}
if err := pm . checkProfileAccess ( kp ) ; err != nil {
return ipn . LoginProfile { } , err
return ipn . LoginProfile View { } , err
}
return * kp , nil
return kp , nil
}
// profileByIDNoPermCheck is like [profileManager.ProfileByID], but it doesn't
// check user's access rights to the profile.
func ( pm * profileManager ) profileByIDNoPermCheck ( id ipn . ProfileID ) ( * ipn . LoginProfile , error ) {
if id == pm . currentProfile . ID {
func ( pm * profileManager ) profileByIDNoPermCheck ( id ipn . ProfileID ) ( ipn . LoginProfile View , error ) {
if id == pm . currentProfile . ID ( ) {
return pm . currentProfile , nil
}
kp , ok := pm . knownProfiles [ id ]
if ! ok {
return nil , errProfileNotFound
return ipn . LoginProfileView { } , errProfileNotFound
}
return kp , nil
}
@ -412,11 +425,11 @@ func (pm *profileManager) ProfilePrefs(id ipn.ProfileID) (ipn.PrefsView, error)
return pm . profilePrefs ( kp )
}
func ( pm * profileManager ) profilePrefs ( p * ipn . LoginProfile ) ( ipn . PrefsView , error ) {
if p . ID == pm . currentProfile . ID {
func ( pm * profileManager ) profilePrefs ( p ipn . LoginProfile View ) ( ipn . PrefsView , error ) {
if p . ID ( ) == pm . currentProfile . ID ( ) {
return pm . prefs , nil
}
return pm . loadSavedPrefs ( p . Key )
return pm . loadSavedPrefs ( p . Key ( ) )
}
// SwitchProfile switches to the profile with the given id.
@ -429,14 +442,14 @@ func (pm *profileManager) SwitchProfile(id ipn.ProfileID) error {
if ! ok {
return errProfileNotFound
}
if pm . currentProfile != nil && kp . ID == pm . currentProfile . ID && pm . prefs . Valid ( ) {
if pm . currentProfile . Valid ( ) && kp . ID ( ) == pm . currentProfile . ID ( ) && pm . prefs . Valid ( ) {
return nil
}
if err := pm . checkProfileAccess ( kp ) ; err != nil {
return fmt . Errorf ( "%w: profile %q is not accessible to the current user" , err , id )
}
prefs , err := pm . loadSavedPrefs ( kp . Key )
prefs , err := pm . loadSavedPrefs ( kp . Key ( ) )
if err != nil {
return err
}
@ -459,8 +472,8 @@ func (pm *profileManager) SwitchToDefaultProfile() error {
// setProfileAsUserDefault sets the specified profile as the default for the current user.
// It returns an [errProfileAccessDenied] if the specified profile is not accessible to the current user.
func ( pm * profileManager ) setProfileAsUserDefault ( profile * ipn . LoginProfile ) error {
if profile . Key == "" {
func ( pm * profileManager ) setProfileAsUserDefault ( profile ipn . LoginProfile View ) error {
if profile . Key ( ) == "" {
// The profile has not been persisted yet; ignore it for now.
return nil
}
@ -468,7 +481,7 @@ func (pm *profileManager) setProfileAsUserDefault(profile *ipn.LoginProfile) err
return errProfileAccessDenied
}
k := ipn . CurrentProfileKey ( string ( pm . currentUserID ) )
return pm . WriteState ( k , [ ] byte ( profile . Key ) )
return pm . WriteState ( k , [ ] byte ( profile . Key ( ) ) )
}
func ( pm * profileManager ) loadSavedPrefs ( key ipn . StateKey ) ( ipn . PrefsView , error ) {
@ -507,10 +520,10 @@ func (pm *profileManager) loadSavedPrefs(key ipn.StateKey) (ipn.PrefsView, error
return savedPrefs . View ( ) , nil
}
// CurrentProfile returns the current LoginP rofile.
// CurrentProfile returns a read-only [ipn.LoginProfileView] of the current p rofile.
// The value may be zero if the profile is not persisted.
func ( pm * profileManager ) CurrentProfile ( ) ipn . LoginProfile {
return * pm . currentProfile
func ( pm * profileManager ) CurrentProfile ( ) ipn . LoginProfile View {
return pm . currentProfile
}
// errProfileNotFound is returned by methods that accept a ProfileID
@ -533,7 +546,7 @@ var errProfileAccessDenied = errors.New("profile access denied")
// recommended to call [profileManager.SwitchProfile] first.
func ( pm * profileManager ) DeleteProfile ( id ipn . ProfileID ) error {
metricDeleteProfile . Add ( 1 )
if id == pm . currentProfile . ID {
if id == pm . currentProfile . ID ( ) {
return pm . deleteCurrentProfile ( )
}
kp , ok := pm . knownProfiles [ id ]
@ -550,7 +563,7 @@ func (pm *profileManager) deleteCurrentProfile() error {
if err := pm . checkProfileAccess ( pm . currentProfile ) ; err != nil {
return err
}
if pm . currentProfile . ID == "" {
if pm . currentProfile . ID ( ) == "" {
// Deleting the in-memory only new profile, just create a new one.
pm . NewProfile ( )
return nil
@ -560,14 +573,14 @@ func (pm *profileManager) deleteCurrentProfile() error {
// deleteProfileNoPermCheck is like [profileManager.DeleteProfile],
// but it doesn't check user's access rights to the profile.
func ( pm * profileManager ) deleteProfileNoPermCheck ( profile * ipn . LoginProfile ) error {
if profile . ID == pm . currentProfile . ID {
func ( pm * profileManager ) deleteProfileNoPermCheck ( profile ipn . LoginProfile View ) error {
if profile . ID ( ) == pm . currentProfile . ID ( ) {
pm . NewProfile ( )
}
if err := pm . WriteState ( profile . Key , nil ) ; err != nil {
if err := pm . WriteState ( profile . Key ( ) , nil ) ; err != nil {
return err
}
delete ( pm . knownProfiles , profile . ID )
delete ( pm . knownProfiles , profile . ID ( ) )
return pm . writeKnownProfiles ( )
}
@ -578,7 +591,7 @@ func (pm *profileManager) DeleteAllProfilesForUser() error {
currentProfileDeleted := false
writeKnownProfiles := func ( ) error {
if currentProfileDeleted || pm . currentProfile . ID == "" {
if currentProfileDeleted || pm . currentProfile . ID ( ) == "" {
pm . NewProfile ( )
}
return pm . writeKnownProfiles ( )
@ -589,14 +602,14 @@ func (pm *profileManager) DeleteAllProfilesForUser() error {
// Skip profiles we don't have access to.
continue
}
if err := pm . WriteState ( kp . Key , nil ) ; err != nil {
if err := pm . WriteState ( kp . Key ( ) , nil ) ; err != nil {
// Write to remove references to profiles we've already deleted, but
// return the original error.
writeKnownProfiles ( )
return err
}
delete ( pm . knownProfiles , kp . ID )
if kp . ID == pm . currentProfile . ID {
delete ( pm . knownProfiles , kp . ID ( ) )
if kp . ID ( ) == pm . currentProfile . ID ( ) {
currentProfileDeleted = true
}
}
@ -633,26 +646,27 @@ func (pm *profileManager) NewProfileForUser(uid ipn.WindowsUserID) {
pm . prefs = defaultPrefs
pm . updateHealth ( )
pm . currentProfile = & ipn . LoginProfile { LocalUserID : uid }
newProfile := & ipn . LoginProfile { LocalUserID : uid }
pm . currentProfile = newProfile . View ( )
}
// newProfileWithPrefs creates a new profile with the specified prefs and assigns
// the specified uid as the profile owner. If switchNow is true, it switches to the
// newly created profile immediately. It returns the newly created profile on success,
// or an error on failure.
func ( pm * profileManager ) newProfileWithPrefs ( uid ipn . WindowsUserID , prefs ipn . PrefsView , switchNow bool ) ( * ipn . LoginProfile , error ) {
func ( pm * profileManager ) newProfileWithPrefs ( uid ipn . WindowsUserID , prefs ipn . PrefsView , switchNow bool ) ( ipn . LoginProfile View , error ) {
metricNewProfile . Add ( 1 )
profile := & ipn . LoginProfile { LocalUserID : uid }
if err := pm . SetProfilePrefs ( profile , prefs , ipn . NetworkProfile { } ) ; err != nil {
return nil , err
profile , err := pm . setProfilePrefs ( & ipn . LoginProfile { LocalUserID : uid } , prefs , ipn . NetworkProfile { } )
if err != nil {
return ipn . LoginProfileView { } , err
}
if switchNow {
pm . currentProfile = profile
pm . prefs = prefs . AsStruct ( ) . View ( )
pm . updateHealth ( )
if err := pm . setProfileAsUserDefault ( profile ) ; err != nil {
return nil , err
return ipn . LoginProfileView { } , err
}
}
return profile , nil
@ -711,8 +725,8 @@ func readAutoStartKey(store ipn.StateStore, goos string) (ipn.StateKey, error) {
return ipn . StateKey ( autoStartKey ) , nil
}
func readKnownProfiles ( store ipn . StateStore ) ( map [ ipn . ProfileID ] * ipn . LoginProfile , error ) {
var knownProfiles map [ ipn . ProfileID ] * ipn . LoginProfile
func readKnownProfiles ( store ipn . StateStore ) ( map [ ipn . ProfileID ] ipn . LoginProfile View , error ) {
var knownProfiles map [ ipn . ProfileID ] ipn . LoginProfile View
prfB , err := store . ReadState ( ipn . KnownProfilesStateKey )
switch err {
case nil :
@ -720,7 +734,7 @@ func readKnownProfiles(store ipn.StateStore) (map[ipn.ProfileID]*ipn.LoginProfil
return nil , fmt . Errorf ( "unmarshaling known profiles: %w" , err )
}
case ipn . ErrStateNotExist :
knownProfiles = make ( map [ ipn . ProfileID ] * ipn . LoginProfile )
knownProfiles = make ( map [ ipn . ProfileID ] ipn . LoginProfile View )
default :
return nil , fmt . Errorf ( "calling ReadState on state store: %w" , err )
}
@ -749,17 +763,17 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
if stateKey != "" {
for _ , v := range knownProfiles {
if v . Key == stateKey {
if v . Key ( ) == stateKey {
pm . currentProfile = v
}
}
if pm . currentProfile == nil {
if ! pm . currentProfile . Valid ( ) {
if suf , ok := strings . CutPrefix ( string ( stateKey ) , "user-" ) ; ok {
pm . currentUserID = ipn . WindowsUserID ( suf )
}
pm . NewProfile ( )
} else {
pm . currentUserID = pm . currentProfile . LocalUserID
pm . currentUserID = pm . currentProfile . LocalUserID ( )
}
prefs , err := pm . loadSavedPrefs ( stateKey )
if err != nil {
@ -788,18 +802,18 @@ func newProfileManagerWithGOOS(store ipn.StateStore, logf logger.Logf, ht *healt
return pm , nil
}
func ( pm * profileManager ) migrateFromLegacyPrefs ( uid ipn . WindowsUserID , switchNow bool ) ( * ipn . LoginProfile , error ) {
func ( pm * profileManager ) migrateFromLegacyPrefs ( uid ipn . WindowsUserID , switchNow bool ) ( ipn . LoginProfile View , error ) {
metricMigration . Add ( 1 )
sentinel , prefs , err := pm . loadLegacyPrefs ( uid )
if err != nil {
metricMigrationError . Add ( 1 )
return nil , fmt . Errorf ( "load legacy prefs: %w" , err )
return ipn . LoginProfileView { } , fmt . Errorf ( "load legacy prefs: %w" , err )
}
pm . dlogf ( "loaded legacy preferences; sentinel=%q" , sentinel )
profile , err := pm . newProfileWithPrefs ( uid , prefs , switchNow )
if err != nil {
metricMigrationError . Add ( 1 )
return nil , fmt . Errorf ( "migrating _daemon profile: %w" , err )
return ipn . LoginProfileView { } , fmt . Errorf ( "migrating _daemon profile: %w" , err )
}
pm . completeMigration ( sentinel )
pm . dlogf ( "completed legacy preferences migration with sentinel=%q" , sentinel )
@ -809,8 +823,8 @@ func (pm *profileManager) migrateFromLegacyPrefs(uid ipn.WindowsUserID, switchNo
func ( pm * profileManager ) requiresBackfill ( ) bool {
return pm != nil &&
pm . currentProfile != nil &&
pm . currentProfile . NetworkProfile . RequiresBackfill ( )
pm . currentProfile . Valid ( ) &&
pm . currentProfile . NetworkProfile ( ) . RequiresBackfill ( )
}
var (