github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/keybase_service_base.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/keybase/client/go/kbfs/data"
    14  	"github.com/keybase/client/go/kbfs/favorites"
    15  	"github.com/keybase/client/go/kbfs/idutil"
    16  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    17  	"github.com/keybase/client/go/kbfs/kbfsmd"
    18  	"github.com/keybase/client/go/kbfs/tlf"
    19  	"github.com/keybase/client/go/kbfs/tlfhandle"
    20  	kbname "github.com/keybase/client/go/kbun"
    21  	"github.com/keybase/client/go/libkb"
    22  	"github.com/keybase/client/go/logger"
    23  	"github.com/keybase/client/go/protocol/keybase1"
    24  	"github.com/pkg/errors"
    25  	"golang.org/x/net/context"
    26  )
    27  
    28  const (
    29  	cacheNotWriterExpiration = 5 * time.Second
    30  )
    31  
    32  // KeybaseServiceBase implements most of KeybaseService from protocol
    33  // defined clients.
    34  type KeybaseServiceBase struct {
    35  	context         Context
    36  	identifyClient  keybase1.IdentifyInterface
    37  	userClient      keybase1.UserInterface
    38  	teamsClient     keybase1.TeamsInterface
    39  	merkleClient    keybase1.MerkleInterface
    40  	sessionClient   keybase1.SessionInterface
    41  	favoriteClient  keybase1.FavoriteInterface
    42  	kbfsClient      keybase1.KbfsInterface
    43  	kbfsMountClient keybase1.KbfsMountInterface
    44  	gitClient       keybase1.GitInterface
    45  	kvstoreClient   keybase1.KvstoreInterface
    46  	log             logger.Logger
    47  
    48  	config     Config
    49  	merkleRoot *EventuallyConsistentMerkleRoot
    50  
    51  	sessionCacheLock sync.RWMutex
    52  	// Set to the zero value when invalidated.
    53  	cachedCurrentSession idutil.SessionInfo
    54  	sessionInProgressCh  chan struct{}
    55  
    56  	userCacheLock sync.RWMutex
    57  	// Map entries are removed when invalidated.
    58  	userCache map[keybase1.UID]idutil.UserInfo
    59  
    60  	teamCacheLock sync.RWMutex
    61  	// Map entries are removed when invalidated.
    62  	teamCache      map[keybase1.TeamID]idutil.TeamInfo
    63  	notWriterCache map[keybase1.TeamID]map[keybase1.UID]time.Time
    64  }
    65  
    66  // Wrapper over `KeybaseServiceBase` implementing a `merkleRootGetter`
    67  // that gets the merkle root directly from the service, without using
    68  // the cache.
    69  type keybaseServiceMerkleGetter struct {
    70  	k *KeybaseServiceBase
    71  }
    72  
    73  var _ idutil.MerkleRootGetter = (*keybaseServiceMerkleGetter)(nil)
    74  
    75  func (k *keybaseServiceMerkleGetter) GetCurrentMerkleRoot(
    76  	ctx context.Context) (keybase1.MerkleRootV2, time.Time, error) {
    77  	return k.k.getCurrentMerkleRoot(ctx)
    78  }
    79  
    80  func (k *keybaseServiceMerkleGetter) VerifyMerkleRoot(
    81  	_ context.Context, _ keybase1.MerkleRootV2, _ keybase1.KBFSRoot) error {
    82  	panic("constMerkleRootGetter doesn't verify merkle roots")
    83  }
    84  
    85  // NewKeybaseServiceBase makes a new KeybaseService.
    86  func NewKeybaseServiceBase(config Config, kbCtx Context, log logger.Logger) *KeybaseServiceBase {
    87  	k := KeybaseServiceBase{
    88  		config:         config,
    89  		context:        kbCtx,
    90  		log:            log,
    91  		userCache:      make(map[keybase1.UID]idutil.UserInfo),
    92  		teamCache:      make(map[keybase1.TeamID]idutil.TeamInfo),
    93  		notWriterCache: make(map[keybase1.TeamID]map[keybase1.UID]time.Time),
    94  	}
    95  	if config != nil {
    96  		k.merkleRoot = NewEventuallyConsistentMerkleRoot(
    97  			config, &keybaseServiceMerkleGetter{&k})
    98  	}
    99  	return &k
   100  }
   101  
   102  // FillClients sets the client protocol implementations needed for a KeybaseService.
   103  func (k *KeybaseServiceBase) FillClients(
   104  	identifyClient keybase1.IdentifyInterface,
   105  	userClient keybase1.UserInterface, teamsClient keybase1.TeamsInterface,
   106  	merkleClient keybase1.MerkleInterface,
   107  	sessionClient keybase1.SessionInterface,
   108  	favoriteClient keybase1.FavoriteInterface,
   109  	kbfsClient keybase1.KbfsInterface,
   110  	kbfsMountClient keybase1.KbfsMountInterface,
   111  	gitClient keybase1.GitInterface, kvstoreClient keybase1.KvstoreClient) {
   112  	k.identifyClient = identifyClient
   113  	k.userClient = userClient
   114  	k.teamsClient = teamsClient
   115  	k.merkleClient = merkleClient
   116  	k.sessionClient = sessionClient
   117  	k.favoriteClient = favoriteClient
   118  	k.kbfsClient = kbfsClient
   119  	k.kbfsMountClient = kbfsMountClient
   120  	k.gitClient = gitClient
   121  	k.kvstoreClient = kvstoreClient
   122  }
   123  
   124  type addVerifyingKeyFunc func(kbfscrypto.VerifyingKey)
   125  type addCryptPublicKeyFunc func(kbfscrypto.CryptPublicKey)
   126  
   127  // processKey adds the given public key to the appropriate verifying
   128  // or crypt list (as return values), and also updates the given name
   129  // map and parent map in place.
   130  func processKey(publicKey keybase1.PublicKeyV2NaCl,
   131  	addVerifyingKey addVerifyingKeyFunc,
   132  	addCryptPublicKey addCryptPublicKeyFunc,
   133  	kidNames map[keybase1.KID]string,
   134  	parents map[keybase1.KID]keybase1.KID) error {
   135  	// Import the KID to validate it.
   136  	key, err := libkb.ImportKeypairFromKID(publicKey.Base.Kid)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	if publicKey.Base.IsSibkey {
   141  		addVerifyingKey(kbfscrypto.MakeVerifyingKey(key.GetKID()))
   142  	} else {
   143  		addCryptPublicKey(kbfscrypto.MakeCryptPublicKey(key.GetKID()))
   144  	}
   145  	if publicKey.DeviceDescription != "" {
   146  		kidNames[publicKey.Base.Kid] = publicKey.DeviceDescription
   147  	}
   148  
   149  	if publicKey.Parent != nil {
   150  		parents[publicKey.Base.Kid] = *publicKey.Parent
   151  	}
   152  	return nil
   153  }
   154  
   155  // updateKIDNamesFromParents sets the name of each KID without a name
   156  // that has a a parent with a name, to that parent's name.
   157  func updateKIDNamesFromParents(kidNames map[keybase1.KID]string,
   158  	parents map[keybase1.KID]keybase1.KID) {
   159  	for kid, parent := range parents {
   160  		if _, ok := kidNames[kid]; ok {
   161  			continue
   162  		}
   163  		if parentName, ok := kidNames[parent]; ok {
   164  			kidNames[kid] = parentName
   165  		}
   166  	}
   167  }
   168  
   169  func filterKeys(keys map[keybase1.KID]keybase1.PublicKeyV2NaCl) (
   170  	verifyingKeys []kbfscrypto.VerifyingKey,
   171  	cryptPublicKeys []kbfscrypto.CryptPublicKey,
   172  	kidNames map[keybase1.KID]string, err error) {
   173  	kidNames = make(map[keybase1.KID]string, len(keys))
   174  	parents := make(map[keybase1.KID]keybase1.KID, len(keys))
   175  
   176  	addVerifyingKey := func(key kbfscrypto.VerifyingKey) {
   177  		verifyingKeys = append(verifyingKeys, key)
   178  	}
   179  	addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) {
   180  		cryptPublicKeys = append(cryptPublicKeys, key)
   181  	}
   182  
   183  	for _, publicKey := range keys {
   184  		if publicKey.Base.Revocation != nil {
   185  			continue
   186  		}
   187  
   188  		err := processKey(publicKey, addVerifyingKey, addCryptPublicKey,
   189  			kidNames, parents)
   190  		if err != nil {
   191  			return nil, nil, nil, err
   192  		}
   193  	}
   194  	updateKIDNamesFromParents(kidNames, parents)
   195  	return verifyingKeys, cryptPublicKeys, kidNames, nil
   196  }
   197  
   198  func (k *KeybaseServiceBase) filterRevokedKeys(
   199  	ctx context.Context,
   200  	uid keybase1.UID,
   201  	keys map[keybase1.KID]keybase1.PublicKeyV2NaCl,
   202  	reset *keybase1.ResetSummary) (
   203  	map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo,
   204  	map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo,
   205  	map[keybase1.KID]string, error) {
   206  	verifyingKeys := make(map[kbfscrypto.VerifyingKey]idutil.RevokedKeyInfo)
   207  	cryptPublicKeys := make(
   208  		map[kbfscrypto.CryptPublicKey]idutil.RevokedKeyInfo)
   209  	var kidNames = map[keybase1.KID]string{}
   210  	var parents = map[keybase1.KID]keybase1.KID{}
   211  
   212  	for _, key := range keys {
   213  		var info idutil.RevokedKeyInfo
   214  		switch {
   215  		case key.Base.Revocation != nil:
   216  			info.Time = key.Base.Revocation.Time
   217  			info.MerkleRoot = key.Base.Revocation.PrevMerkleRootSigned
   218  			// If we don't have a prev seqno, then we already have the
   219  			// best merkle data we're going to get.
   220  			info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0)
   221  			info.SetSigChainLocation(key.Base.Revocation.SigChainLocation)
   222  		case reset != nil:
   223  			info.Time = keybase1.ToTime(keybase1.FromUnixTime(reset.Ctime))
   224  			info.MerkleRoot.Seqno = reset.MerkleRoot.Seqno
   225  			info.MerkleRoot.HashMeta = reset.MerkleRoot.HashMeta
   226  			// If we don't have a prev seqno, then we already have the
   227  			// best merkle data we're going to get.
   228  			info.SetFilledInMerkle(info.MerkleRoot.Seqno <= 0)
   229  			info.SetResetInfo(reset.ResetSeqno, true)
   230  		default:
   231  			// Not revoked.
   232  			continue
   233  		}
   234  
   235  		addVerifyingKey := func(key kbfscrypto.VerifyingKey) {
   236  			verifyingKeys[key] = info
   237  		}
   238  		addCryptPublicKey := func(key kbfscrypto.CryptPublicKey) {
   239  			cryptPublicKeys[key] = info
   240  		}
   241  		err := processKey(key, addVerifyingKey, addCryptPublicKey,
   242  			kidNames, parents)
   243  		if err != nil {
   244  			return nil, nil, nil, err
   245  		}
   246  	}
   247  	updateKIDNamesFromParents(kidNames, parents)
   248  	return verifyingKeys, cryptPublicKeys, kidNames, nil
   249  
   250  }
   251  
   252  func (k *KeybaseServiceBase) getCachedCurrentSession() idutil.SessionInfo {
   253  	k.sessionCacheLock.RLock()
   254  	defer k.sessionCacheLock.RUnlock()
   255  	return k.cachedCurrentSession
   256  }
   257  
   258  func (k *KeybaseServiceBase) setCachedCurrentSession(s idutil.SessionInfo) {
   259  	k.sessionCacheLock.Lock()
   260  	defer k.sessionCacheLock.Unlock()
   261  	k.cachedCurrentSession = s
   262  }
   263  
   264  func (k *KeybaseServiceBase) getCachedUserInfo(
   265  	uid keybase1.UID) idutil.UserInfo {
   266  	k.userCacheLock.RLock()
   267  	defer k.userCacheLock.RUnlock()
   268  	return k.userCache[uid]
   269  }
   270  
   271  func (k *KeybaseServiceBase) setCachedUserInfo(
   272  	uid keybase1.UID, info idutil.UserInfo) {
   273  	k.userCacheLock.Lock()
   274  	defer k.userCacheLock.Unlock()
   275  	if info.Name == kbname.NormalizedUsername("") {
   276  		delete(k.userCache, uid)
   277  	} else {
   278  		k.userCache[uid] = info
   279  	}
   280  }
   281  
   282  func (k *KeybaseServiceBase) getCachedTeamInfo(
   283  	tid keybase1.TeamID) idutil.TeamInfo {
   284  	k.teamCacheLock.RLock()
   285  	defer k.teamCacheLock.RUnlock()
   286  	return k.teamCache[tid]
   287  }
   288  
   289  func (k *KeybaseServiceBase) setCachedTeamInfo(
   290  	tid keybase1.TeamID, info idutil.TeamInfo) {
   291  	k.teamCacheLock.Lock()
   292  	defer k.teamCacheLock.Unlock()
   293  	if info.Name == kbname.NormalizedUsername("") {
   294  		delete(k.teamCache, tid)
   295  		delete(k.notWriterCache, tid)
   296  	} else {
   297  		k.teamCache[tid] = info
   298  	}
   299  }
   300  
   301  func (k *KeybaseServiceBase) getCachedNotWriter(
   302  	tid keybase1.TeamID, uid keybase1.UID) (notWriter bool) {
   303  	// Full write lock because of the delete-after-expiration code
   304  	// below.
   305  	k.teamCacheLock.Lock()
   306  	defer k.teamCacheLock.Unlock()
   307  	cachedTime, notWriter := k.notWriterCache[tid][uid]
   308  	if !notWriter {
   309  		return false
   310  	}
   311  
   312  	if k.config.Clock().Now().Sub(cachedTime) > cacheNotWriterExpiration {
   313  		delete(k.notWriterCache[tid], uid)
   314  		return false
   315  	}
   316  	return true
   317  }
   318  
   319  func (k *KeybaseServiceBase) setCachedNotWriter(
   320  	tid keybase1.TeamID, uid keybase1.UID) {
   321  	k.teamCacheLock.Lock()
   322  	defer k.teamCacheLock.Unlock()
   323  	teamMap := k.notWriterCache[tid]
   324  	if teamMap == nil {
   325  		teamMap = make(map[keybase1.UID]time.Time)
   326  		k.notWriterCache[tid] = teamMap
   327  	}
   328  	teamMap[uid] = k.config.Clock().Now()
   329  }
   330  
   331  // ClearCaches implements the KeybaseService interface for
   332  // KeybaseServiceBase.
   333  func (k *KeybaseServiceBase) ClearCaches(ctx context.Context) {
   334  	k.log.CDebugf(ctx, "Clearing KBFS-side user and team caches")
   335  
   336  	k.setCachedCurrentSession(idutil.SessionInfo{})
   337  	func() {
   338  		k.userCacheLock.Lock()
   339  		defer k.userCacheLock.Unlock()
   340  		k.userCache = make(map[keybase1.UID]idutil.UserInfo)
   341  	}()
   342  	k.teamCacheLock.Lock()
   343  	defer k.teamCacheLock.Unlock()
   344  	k.teamCache = make(map[keybase1.TeamID]idutil.TeamInfo)
   345  	k.notWriterCache = make(map[keybase1.TeamID]map[keybase1.UID]time.Time)
   346  }
   347  
   348  // LoggedIn implements keybase1.NotifySessionInterface.
   349  func (k *KeybaseServiceBase) LoggedIn(ctx context.Context, arg keybase1.LoggedInArg) error {
   350  	k.log.CDebugf(ctx, "Current session logged in: %s, signedUp: %t", arg.Username, arg.SignedUp)
   351  	// Since we don't have the whole session, just clear the cache and
   352  	// repopulate it.  The `CurrentSession` call executes the "logged
   353  	// in" flow.
   354  	k.setCachedCurrentSession(idutil.SessionInfo{})
   355  	const sessionID = 0
   356  	_, err := k.CurrentSession(ctx, sessionID)
   357  	if err != nil {
   358  		k.log.CDebugf(ctx, "Getting current session failed when %s is logged "+
   359  			"in, so pretending user has logged out: %v",
   360  			arg.Username, err)
   361  		if k.config != nil {
   362  			serviceLoggedOut(ctx, k.config)
   363  		}
   364  		return nil
   365  	}
   366  
   367  	return nil
   368  }
   369  
   370  // LoggedOut implements keybase1.NotifySessionInterface.
   371  func (k *KeybaseServiceBase) LoggedOut(ctx context.Context) error {
   372  	k.log.CDebugf(ctx, "Current session logged out")
   373  	k.setCachedCurrentSession(idutil.SessionInfo{})
   374  	if k.config != nil {
   375  		serviceLoggedOut(ctx, k.config)
   376  	}
   377  	return nil
   378  }
   379  
   380  // KeyfamilyChanged implements keybase1.NotifyKeyfamilyInterface.
   381  func (k *KeybaseServiceBase) KeyfamilyChanged(ctx context.Context,
   382  	uid keybase1.UID) error {
   383  	k.log.CDebugf(ctx, "Key family for user %s changed", uid)
   384  	k.setCachedUserInfo(uid, idutil.UserInfo{})
   385  
   386  	if k.getCachedCurrentSession().UID == uid {
   387  		mdServer := k.config.MDServer()
   388  		if mdServer != nil {
   389  			// Ignore any errors for now, we don't want to block this
   390  			// notification and it's not worth spawning a goroutine for.
   391  			mdServer.CheckForRekeys(context.Background())
   392  		}
   393  	}
   394  
   395  	return nil
   396  }
   397  
   398  // ReachabilityChanged implements keybase1.ReachabiltyInterface.
   399  func (k *KeybaseServiceBase) ReachabilityChanged(ctx context.Context,
   400  	reachability keybase1.Reachability) error {
   401  	k.log.CDebugf(ctx, "CheckReachability invoked: %v", reachability)
   402  	if reachability.Reachable == keybase1.Reachable_YES {
   403  		k.config.KBFSOps().PushConnectionStatusChange(GregorServiceName, nil)
   404  	} else {
   405  		k.config.KBFSOps().PushConnectionStatusChange(
   406  			GregorServiceName, errDisconnected{})
   407  	}
   408  	mdServer := k.config.MDServer()
   409  	if mdServer != nil {
   410  		mdServer.CheckReachability(ctx)
   411  	}
   412  	return nil
   413  }
   414  
   415  // StartReachability implements keybase1.ReachabilityInterface.
   416  func (k *KeybaseServiceBase) StartReachability(ctx context.Context) (res keybase1.Reachability, err error) {
   417  	return k.CheckReachability(ctx)
   418  }
   419  
   420  // CheckReachability implements keybase1.ReachabilityInterface.
   421  func (k *KeybaseServiceBase) CheckReachability(ctx context.Context) (res keybase1.Reachability, err error) {
   422  	res.Reachable = keybase1.Reachable_NO
   423  	mdServer := k.config.MDServer()
   424  	if mdServer != nil && mdServer.IsConnected() {
   425  		res.Reachable = keybase1.Reachable_YES
   426  	}
   427  	return res, nil
   428  }
   429  
   430  // PaperKeyCached implements keybase1.NotifyPaperKeyInterface.
   431  func (k *KeybaseServiceBase) PaperKeyCached(ctx context.Context,
   432  	arg keybase1.PaperKeyCachedArg) error {
   433  	k.log.CDebugf(ctx, "Paper key for %s cached", arg.Uid)
   434  
   435  	if k.getCachedCurrentSession().UID == arg.Uid {
   436  		err := k.config.KBFSOps().KickoffAllOutstandingRekeys()
   437  		if err != nil {
   438  			// Ignore and log errors here. For now the only way it could error
   439  			// is when the method is called on a folderBranchOps which is a
   440  			// developer mistake and not recoverable from code.
   441  			k.log.CDebugf(ctx,
   442  				"Calling KickoffAllOutstandingRekeys error: %s", err)
   443  		}
   444  		// Ignore any errors for now, we don't want to block this
   445  		// notification and it's not worth spawning a goroutine for.
   446  		mdServer := k.config.MDServer()
   447  		if mdServer != nil {
   448  			mdServer.CheckForRekeys(context.Background())
   449  		}
   450  	}
   451  
   452  	return nil
   453  }
   454  
   455  // ClientOutOfDate implements keybase1.NotifySessionInterface.
   456  func (k *KeybaseServiceBase) ClientOutOfDate(ctx context.Context,
   457  	arg keybase1.ClientOutOfDateArg) error {
   458  	k.log.CDebugf(ctx, "Client out of date: %v", arg)
   459  	return nil
   460  }
   461  
   462  // RootAuditError implements keybase1.NotifyAuditInterface.
   463  func (k *KeybaseServiceBase) RootAuditError(ctx context.Context,
   464  	arg keybase1.RootAuditErrorArg) error {
   465  	k.log.CDebugf(ctx, "Merkle tree audit error: %v", arg.Message)
   466  	return nil
   467  }
   468  
   469  // ConvertIdentifyError converts a errors during identify into KBFS errors
   470  func ConvertIdentifyError(assertion string, err error) error {
   471  	switch err.(type) {
   472  	case libkb.NotFoundError:
   473  		return idutil.NoSuchUserError{Input: assertion}
   474  	case libkb.ResolutionError:
   475  		return idutil.NoSuchUserError{Input: assertion}
   476  	}
   477  	return err
   478  }
   479  
   480  // Resolve implements the KeybaseService interface for KeybaseServiceBase.
   481  func (k *KeybaseServiceBase) Resolve(
   482  	ctx context.Context, assertion string,
   483  	offline keybase1.OfflineAvailability) (
   484  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   485  	res, err := k.identifyClient.Resolve3(
   486  		ctx, keybase1.Resolve3Arg{
   487  			Assertion: assertion,
   488  			Oa:        offline,
   489  		})
   490  	if err != nil {
   491  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""),
   492  			ConvertIdentifyError(assertion, err)
   493  	}
   494  	return kbname.NewNormalizedUsername(res.Name), res.Id, nil
   495  }
   496  
   497  // Identify implements the KeybaseService interface for KeybaseServiceBase.
   498  func (k *KeybaseServiceBase) Identify(
   499  	ctx context.Context, assertion, reason string,
   500  	offline keybase1.OfflineAvailability) (
   501  	kbname.NormalizedUsername, keybase1.UserOrTeamID, error) {
   502  	// setting UseDelegateUI to true here will cause daemon to use
   503  	// registered identify ui providers instead of terminal if any
   504  	// are available.  If not, then it will use the terminal UI.
   505  	arg := keybase1.IdentifyLiteArg{
   506  		Assertion:     assertion,
   507  		UseDelegateUI: true,
   508  		Reason:        keybase1.IdentifyReason{Reason: reason},
   509  		// No need to go back and forth with the UI until the service
   510  		// knows for sure there's a need for a dialogue.
   511  		CanSuppressUI: true,
   512  		Oa:            offline,
   513  	}
   514  
   515  	ei := tlfhandle.GetExtendedIdentify(ctx)
   516  	arg.IdentifyBehavior = ei.Behavior
   517  
   518  	res, err := k.identifyClient.IdentifyLite(ctx, arg)
   519  	// IdentifyLite still returns keybase1.UserPlusKeys data (sans
   520  	// keys), even if it gives a NoSigChainError or a UserDeletedError,
   521  	// and in KBFS it's fine if the user doesn't have a full sigchain
   522  	// (e.g., it's just like the sharing before signup case, except
   523  	// the user already has a UID).  Both types of users are based
   524  	// entirely on server trust anyway.
   525  	switch err.(type) {
   526  	case nil:
   527  	case libkb.NoSigChainError, libkb.UserDeletedError:
   528  		ei.OnError(ctx)
   529  		// But if the username is blame, just return it, since the
   530  		// returned username would be useless and confusing.
   531  		if res.Ul.Name == "" {
   532  			return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   533  		}
   534  		k.log.CDebugf(ctx,
   535  			"Ignoring error (%s) for user %s with no sigchain; "+
   536  				"error type=%T", err, res.Ul.Name, err)
   537  	default:
   538  		// If the caller is waiting for breaks, let them know we got an error.
   539  		ei.OnError(ctx)
   540  		return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""),
   541  			ConvertIdentifyError(assertion, err)
   542  	}
   543  
   544  	// This is required for every identify call. The userBreak
   545  	// function will take care of checking if res.TrackBreaks is nil
   546  	// or not.
   547  	name := kbname.NormalizedUsername(res.Ul.Name)
   548  	if res.Ul.Id.IsUser() {
   549  		asUser, err := res.Ul.Id.AsUser()
   550  		if err != nil {
   551  			return kbname.NormalizedUsername(""), keybase1.UserOrTeamID(""), err
   552  		}
   553  		ei.UserBreak(ctx, name, asUser, res.TrackBreaks)
   554  	} else if !res.Ul.Id.IsNil() {
   555  		ei.TeamBreak(ctx, res.Ul.Id.AsTeamOrBust(), res.TrackBreaks)
   556  	}
   557  
   558  	return name, res.Ul.Id, nil
   559  }
   560  
   561  // NormalizeSocialAssertion implements the KeybaseService interface for
   562  // KeybaseServiceBase.
   563  func (k *KeybaseServiceBase) NormalizeSocialAssertion(
   564  	ctx context.Context, assertion string) (keybase1.SocialAssertion, error) {
   565  	return k.identifyClient.NormalizeSocialAssertion(ctx, assertion)
   566  }
   567  
   568  // ResolveIdentifyImplicitTeam implements the KeybaseService interface
   569  // for KeybaseServiceBase.
   570  func (k *KeybaseServiceBase) ResolveIdentifyImplicitTeam(
   571  	ctx context.Context, assertions, suffix string, tlfType tlf.Type,
   572  	doIdentifies bool, reason string,
   573  	offline keybase1.OfflineAvailability) (idutil.ImplicitTeamInfo, error) {
   574  	if tlfType != tlf.Private && tlfType != tlf.Public {
   575  		return idutil.ImplicitTeamInfo{}, fmt.Errorf(
   576  			"Invalid implicit team TLF type: %s", tlfType)
   577  	}
   578  
   579  	arg := keybase1.ResolveIdentifyImplicitTeamArg{
   580  		Assertions:   assertions,
   581  		Suffix:       suffix,
   582  		DoIdentifies: doIdentifies,
   583  		Reason:       keybase1.IdentifyReason{Reason: reason},
   584  		Create:       true,
   585  		IsPublic:     tlfType == tlf.Public,
   586  		Oa:           offline,
   587  	}
   588  
   589  	ei := tlfhandle.GetExtendedIdentify(ctx)
   590  	arg.IdentifyBehavior = ei.Behavior
   591  
   592  	res, err := k.identifyClient.ResolveIdentifyImplicitTeam(ctx, arg)
   593  	if err != nil {
   594  		return idutil.ImplicitTeamInfo{}, ConvertIdentifyError(assertions, err)
   595  	}
   596  	if strings.Contains(res.DisplayName, "_implicit_team_") {
   597  		k.log.CWarningf(
   598  			ctx, "Got display name %s for assertions %s",
   599  			res.DisplayName, assertions)
   600  	}
   601  	name := kbname.NormalizedUsername(res.DisplayName)
   602  
   603  	// Exactly one break callback is required for every identify call.
   604  	if doIdentifies {
   605  		if len(res.TrackBreaks) > 0 {
   606  			// Iterate the map to get one entry, then break.
   607  			for userVer, breaks := range res.TrackBreaks {
   608  				// TODO: resolve the UID into a username so we don't have to
   609  				// pass in the full display name here?
   610  				ei.UserBreak(ctx, name, userVer.Uid, &breaks)
   611  				break
   612  			}
   613  		} else {
   614  			ei.TeamBreak(ctx, keybase1.TeamID(""), nil)
   615  		}
   616  	}
   617  
   618  	iteamInfo := idutil.ImplicitTeamInfo{
   619  		Name: name,
   620  		TID:  res.TeamID,
   621  	}
   622  	if res.FolderID != "" {
   623  		iteamInfo.TlfID, err = tlf.ParseID(res.FolderID.String())
   624  		if err != nil {
   625  			return idutil.ImplicitTeamInfo{}, err
   626  		}
   627  	}
   628  
   629  	return iteamInfo, nil
   630  }
   631  
   632  // ResolveImplicitTeamByID implements the KeybaseService interface for
   633  // KeybaseServiceBase.
   634  func (k *KeybaseServiceBase) ResolveImplicitTeamByID(
   635  	ctx context.Context, teamID keybase1.TeamID) (name string, err error) {
   636  	arg := keybase1.ResolveImplicitTeamArg{
   637  		Id: teamID,
   638  	}
   639  
   640  	res, err := k.identifyClient.ResolveImplicitTeam(ctx, arg)
   641  	if err != nil {
   642  		return "", err
   643  	}
   644  	return res.Name, nil
   645  }
   646  
   647  func (k *KeybaseServiceBase) checkForRevokedVerifyingKey(
   648  	ctx context.Context, currUserInfo idutil.UserInfo, kid keybase1.KID) (
   649  	newUserInfo idutil.UserInfo, exists bool, err error) {
   650  	newUserInfo = currUserInfo
   651  	for key, info := range currUserInfo.RevokedVerifyingKeys {
   652  		if !key.KID().Equal(kid) {
   653  			continue
   654  		}
   655  		exists = true
   656  		if info.FilledInMerkle() {
   657  			break
   658  		}
   659  
   660  		k.log.CDebugf(ctx, "Filling in merkle info for user %s, revoked key %s",
   661  			currUserInfo.UID, kid)
   662  
   663  		// If possible, ask the service to give us the first merkle
   664  		// root that covers this revoke. Some older device revokes
   665  		// didn't yet include a prev field, so we can't refine the
   666  		// merkle root in those cases, and will be relying only on
   667  		// server trust.
   668  		if info.MerkleRoot.Seqno > 0 {
   669  			var res keybase1.NextMerkleRootRes
   670  			resetSeqno, isReset := info.ResetInfo()
   671  			if isReset {
   672  				res, err = k.userClient.FindNextMerkleRootAfterReset(ctx,
   673  					keybase1.FindNextMerkleRootAfterResetArg{
   674  						Uid:        currUserInfo.UID,
   675  						ResetSeqno: resetSeqno,
   676  						Prev: keybase1.ResetMerkleRoot{
   677  							Seqno:    info.MerkleRoot.Seqno,
   678  							HashMeta: info.MerkleRoot.HashMeta,
   679  						},
   680  					})
   681  			} else {
   682  				res, err = k.userClient.FindNextMerkleRootAfterRevoke(ctx,
   683  					keybase1.FindNextMerkleRootAfterRevokeArg{
   684  						Uid:  currUserInfo.UID,
   685  						Kid:  kid,
   686  						Loc:  info.SigChainLocation(),
   687  						Prev: info.MerkleRoot,
   688  					})
   689  			}
   690  			if m, ok := err.(libkb.MerkleClientError); ok && m.IsOldTree() { // nolint
   691  				k.log.CDebugf(ctx, "Merkle root is too old for checking "+
   692  					"the revoked key: %+v", err)
   693  				info.MerkleRoot.Seqno = 0
   694  			} else if err != nil {
   695  				return idutil.UserInfo{}, false, err
   696  			} else if res.Res != nil {
   697  				info.MerkleRoot = *res.Res
   698  			}
   699  		}
   700  		info.SetFilledInMerkle(true)
   701  		newUserInfo = currUserInfo.DeepCopy()
   702  		newUserInfo.RevokedVerifyingKeys[key] = info
   703  		k.setCachedUserInfo(newUserInfo.UID, newUserInfo)
   704  		break
   705  	}
   706  
   707  	return newUserInfo, exists, nil
   708  }
   709  
   710  // LoadUserPlusKeys implements the KeybaseService interface for
   711  // KeybaseServiceBase.
   712  func (k *KeybaseServiceBase) LoadUserPlusKeys(
   713  	ctx context.Context, uid keybase1.UID, pollForKID keybase1.KID,
   714  	offline keybase1.OfflineAvailability) (idutil.UserInfo, error) {
   715  	cachedUserInfo := k.getCachedUserInfo(uid)
   716  	if cachedUserInfo.Name != kbname.NormalizedUsername("") {
   717  		if pollForKID == keybase1.KID("") {
   718  			return cachedUserInfo, nil
   719  		}
   720  		// Skip the cache if pollForKID isn't present in
   721  		// `VerifyingKeys` or one of the revoked verifying keys.
   722  		for _, key := range cachedUserInfo.VerifyingKeys {
   723  			if key.KID().Equal(pollForKID) {
   724  				return cachedUserInfo, nil
   725  			}
   726  		}
   727  
   728  		// Check if the key is revoked, and fill in the merkle info in
   729  		// that case.
   730  		cachedUserInfo, exists, err := k.checkForRevokedVerifyingKey(
   731  			ctx, cachedUserInfo, pollForKID)
   732  		if err != nil {
   733  			return idutil.UserInfo{}, err
   734  		}
   735  		if exists {
   736  			return cachedUserInfo, nil
   737  		}
   738  	}
   739  
   740  	arg := keybase1.LoadUserPlusKeysV2Arg{
   741  		Uid:        uid,
   742  		PollForKID: pollForKID,
   743  		Oa:         offline,
   744  	}
   745  	res, err := k.userClient.LoadUserPlusKeysV2(ctx, arg)
   746  	if err != nil {
   747  		return idutil.UserInfo{}, err
   748  	}
   749  
   750  	userInfo, err := k.processUserPlusKeys(ctx, res)
   751  	if err != nil {
   752  		return idutil.UserInfo{}, err
   753  	}
   754  
   755  	if pollForKID != keybase1.KID("") {
   756  		// Fill in merkle info if we were explicitly trying to load a
   757  		// revoked key.
   758  		userInfo, _, err = k.checkForRevokedVerifyingKey(
   759  			ctx, userInfo, pollForKID)
   760  		if err != nil {
   761  			return idutil.UserInfo{}, err
   762  		}
   763  	}
   764  	return userInfo, nil
   765  }
   766  
   767  func (k *KeybaseServiceBase) getLastWriterInfo(
   768  	ctx context.Context, teamInfo idutil.TeamInfo, tlfType tlf.Type,
   769  	user keybase1.UID, verifyingKey kbfscrypto.VerifyingKey) (
   770  	idutil.TeamInfo, error) {
   771  	if _, ok := teamInfo.LastWriters[verifyingKey]; ok {
   772  		// Already cached, nothing to do.
   773  		return teamInfo, nil
   774  	}
   775  
   776  	res, err := k.teamsClient.FindNextMerkleRootAfterTeamRemovalBySigningKey(
   777  		ctx, keybase1.FindNextMerkleRootAfterTeamRemovalBySigningKeyArg{
   778  			Uid:        user,
   779  			SigningKey: verifyingKey.KID(),
   780  			Team:       teamInfo.TID,
   781  			IsPublic:   tlfType == tlf.Public,
   782  		})
   783  	if err != nil {
   784  		return idutil.TeamInfo{}, err
   785  	}
   786  
   787  	// Copy any old data to avoid races.
   788  	newLastWriters := make(
   789  		map[kbfscrypto.VerifyingKey]keybase1.MerkleRootV2,
   790  		len(teamInfo.LastWriters)+1)
   791  	for k, v := range teamInfo.LastWriters {
   792  		newLastWriters[k] = v
   793  	}
   794  	newLastWriters[verifyingKey] = *res.Res
   795  	teamInfo.LastWriters = newLastWriters
   796  	return teamInfo, nil
   797  }
   798  
   799  var allowedLoadTeamRoles = map[keybase1.TeamRole]bool{
   800  	keybase1.TeamRole_NONE:   true,
   801  	keybase1.TeamRole_WRITER: true,
   802  	keybase1.TeamRole_READER: true,
   803  }
   804  
   805  // LoadTeamPlusKeys implements the KeybaseService interface for
   806  // KeybaseServiceBase.
   807  func (k *KeybaseServiceBase) LoadTeamPlusKeys(
   808  	ctx context.Context, tid keybase1.TeamID, tlfType tlf.Type,
   809  	desiredKeyGen kbfsmd.KeyGen, desiredUser keybase1.UserVersion,
   810  	desiredKey kbfscrypto.VerifyingKey, desiredRole keybase1.TeamRole,
   811  	offline keybase1.OfflineAvailability) (idutil.TeamInfo, error) {
   812  	if !allowedLoadTeamRoles[desiredRole] {
   813  		panic(fmt.Sprintf("Disallowed team role: %v", desiredRole))
   814  	}
   815  
   816  	cachedTeamInfo := k.getCachedTeamInfo(tid)
   817  	if cachedTeamInfo.Name != kbname.NormalizedUsername("") {
   818  		// If the cached team info doesn't satisfy our desires, don't
   819  		// use it.
   820  		satisfiesDesires := true
   821  		if desiredKeyGen >= kbfsmd.FirstValidKeyGen {
   822  			// If `desiredKeyGen` is at most as large as the keygen in
   823  			// the cached latest team info, then our cached info
   824  			// satisfies our desires.
   825  			satisfiesDesires = desiredKeyGen <= cachedTeamInfo.LatestKeyGen
   826  		}
   827  
   828  		if satisfiesDesires && desiredUser.Uid.Exists() {
   829  			// If the user is in the writer map, that satisfies none, reader
   830  			// or writer desires.
   831  			satisfiesDesires = cachedTeamInfo.Writers[desiredUser.Uid]
   832  			if !satisfiesDesires {
   833  				if desiredRole == keybase1.TeamRole_NONE ||
   834  					desiredRole == keybase1.TeamRole_READER {
   835  					// If the user isn't a writer, but the desired
   836  					// role is a reader, we need to check the reader
   837  					// map explicitly.
   838  					satisfiesDesires = cachedTeamInfo.Readers[desiredUser.Uid]
   839  				} else {
   840  					if !desiredKey.IsNil() {
   841  						// If the desired role was at least a writer, but
   842  						// the user isn't currently a writer, see if they
   843  						// ever were.
   844  						var err error
   845  						cachedTeamInfo, err = k.getLastWriterInfo(
   846  							ctx, cachedTeamInfo, tlfType, desiredUser.Uid,
   847  							desiredKey)
   848  						if err != nil {
   849  							return idutil.TeamInfo{}, err
   850  						}
   851  						k.setCachedTeamInfo(tid, cachedTeamInfo)
   852  					}
   853  
   854  					// If we have recently learned that the user is
   855  					// not a writer (e.g., of a public folder), we
   856  					// should rely on that cached info to avoid
   857  					// looking that up too often.
   858  					satisfiesDesires = k.getCachedNotWriter(
   859  						tid, desiredUser.Uid)
   860  				}
   861  			}
   862  		}
   863  
   864  		if satisfiesDesires {
   865  			return cachedTeamInfo, nil
   866  		}
   867  	}
   868  
   869  	arg := keybase1.LoadTeamPlusApplicationKeysArg{
   870  		Id:              tid,
   871  		Application:     keybase1.TeamApplication_KBFS,
   872  		IncludeKBFSKeys: true,
   873  		Oa:              offline,
   874  	}
   875  
   876  	if desiredKeyGen >= kbfsmd.FirstValidKeyGen {
   877  		arg.Refreshers.NeedApplicationsAtGenerationsWithKBFS =
   878  			map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{
   879  				keybase1.PerTeamKeyGeneration(desiredKeyGen): {
   880  					keybase1.TeamApplication_KBFS,
   881  				},
   882  			}
   883  	}
   884  
   885  	if desiredUser.Uid.Exists() && desiredKey.IsNil() {
   886  		arg.Refreshers.WantMembers = append(
   887  			arg.Refreshers.WantMembers, desiredUser)
   888  		arg.Refreshers.WantMembersRole = desiredRole
   889  	}
   890  
   891  	res, err := k.teamsClient.LoadTeamPlusApplicationKeys(ctx, arg)
   892  	if err != nil {
   893  		return idutil.TeamInfo{}, err
   894  	}
   895  
   896  	if tid != res.Id {
   897  		return idutil.TeamInfo{}, fmt.Errorf(
   898  			"TID doesn't match: %s vs %s", tid, res.Id)
   899  	}
   900  
   901  	info := idutil.TeamInfo{
   902  		Name:      kbname.NormalizedUsername(res.Name),
   903  		TID:       res.Id,
   904  		CryptKeys: make(map[kbfsmd.KeyGen]kbfscrypto.TLFCryptKey),
   905  		Writers:   make(map[keybase1.UID]bool),
   906  		Readers:   make(map[keybase1.UID]bool),
   907  	}
   908  	for _, key := range res.ApplicationKeys {
   909  		keyGen := kbfsmd.KeyGen(key.KeyGeneration)
   910  		info.CryptKeys[keyGen] =
   911  			kbfscrypto.MakeTLFCryptKey(key.Key)
   912  		if keyGen > info.LatestKeyGen {
   913  			info.LatestKeyGen = keyGen
   914  		}
   915  	}
   916  
   917  	for _, user := range res.Writers {
   918  		info.Writers[user.Uid] = true
   919  	}
   920  	for _, user := range res.OnlyReaders {
   921  		info.Readers[user.Uid] = true
   922  	}
   923  
   924  	// For subteams, get the root team ID.
   925  	if tid.IsSubTeam() {
   926  		rootID, err := k.teamsClient.GetTeamRootID(ctx, tid)
   927  		if err != nil {
   928  			return idutil.TeamInfo{}, err
   929  		}
   930  		info.RootID = rootID
   931  	}
   932  
   933  	// Fill in `LastWriters`, only if needed.
   934  	if desiredUser.Uid.Exists() && desiredRole == keybase1.TeamRole_WRITER &&
   935  		!info.Writers[desiredUser.Uid] && !desiredKey.IsNil() {
   936  		info, err = k.getLastWriterInfo(
   937  			ctx, info, tlfType, desiredUser.Uid, desiredKey)
   938  		if err != nil {
   939  			return idutil.TeamInfo{}, err
   940  		}
   941  	}
   942  
   943  	k.setCachedTeamInfo(tid, info)
   944  
   945  	if desiredUser.Uid.Exists() && !info.Writers[desiredUser.Uid] &&
   946  		!(desiredRole == keybase1.TeamRole_NONE ||
   947  			desiredRole == keybase1.TeamRole_READER) {
   948  		// Remember that this user was not a writer for a short
   949  		// amount of time, to avoid repeated lookups for writers
   950  		// in a public folder (for example).
   951  		k.setCachedNotWriter(tid, desiredUser.Uid)
   952  	}
   953  
   954  	return info, nil
   955  }
   956  
   957  // CreateTeamTLF implements the KeybaseService interface for
   958  // KeybaseServiceBase.
   959  func (k *KeybaseServiceBase) CreateTeamTLF(
   960  	ctx context.Context, teamID keybase1.TeamID, tlfID tlf.ID) (err error) {
   961  	return k.kbfsClient.CreateTLF(ctx, keybase1.CreateTLFArg{
   962  		TeamID: teamID,
   963  		TlfID:  keybase1.TLFID(tlfID.String()),
   964  	})
   965  }
   966  
   967  // GetTeamSettings implements the KeybaseService interface for
   968  // KeybaseServiceBase.
   969  func (k *KeybaseServiceBase) GetTeamSettings(
   970  	ctx context.Context, teamID keybase1.TeamID,
   971  	offline keybase1.OfflineAvailability) (
   972  	keybase1.KBFSTeamSettings, error) {
   973  	// TODO: get invalidations from the server and cache the settings?
   974  	return k.kbfsClient.GetKBFSTeamSettings(
   975  		ctx, keybase1.GetKBFSTeamSettingsArg{
   976  			TeamID: teamID,
   977  			Oa:     offline,
   978  		})
   979  }
   980  
   981  func (k *KeybaseServiceBase) getCurrentMerkleRoot(ctx context.Context) (
   982  	keybase1.MerkleRootV2, time.Time, error) {
   983  	const merkleFreshnessMs = int(time.Second * 60 / time.Millisecond)
   984  	res, err := k.merkleClient.GetCurrentMerkleRoot(ctx, merkleFreshnessMs)
   985  	if err != nil {
   986  		return keybase1.MerkleRootV2{}, time.Time{}, err
   987  	}
   988  
   989  	return res.Root, keybase1.FromTime(res.UpdateTime), nil
   990  }
   991  
   992  // GetCurrentMerkleRoot implements the KeybaseService interface for
   993  // KeybaseServiceBase.
   994  func (k *KeybaseServiceBase) GetCurrentMerkleRoot(ctx context.Context) (
   995  	keybase1.MerkleRootV2, time.Time, error) {
   996  	// Refresh the cached value in the background if the cached value
   997  	// is older than 30s; if our cached value is more than 60s old,
   998  	// block.
   999  	_, root, rootTime, err := k.merkleRoot.Get(
  1000  		ctx, 30*time.Second, 60*time.Second)
  1001  	return root, rootTime, err
  1002  }
  1003  
  1004  // VerifyMerkleRoot implements the KBPKI interface for KeybaseServiceBase.
  1005  func (k *KeybaseServiceBase) VerifyMerkleRoot(
  1006  	ctx context.Context, root keybase1.MerkleRootV2,
  1007  	kbfsRoot keybase1.KBFSRoot) error {
  1008  	return k.merkleClient.VerifyMerkleRootAndKBFS(ctx,
  1009  		keybase1.VerifyMerkleRootAndKBFSArg{
  1010  			Root:             root,
  1011  			ExpectedKBFSRoot: kbfsRoot,
  1012  		})
  1013  }
  1014  
  1015  func (k *KeybaseServiceBase) processUserPlusKeys(
  1016  	ctx context.Context, upk keybase1.UserPlusKeysV2AllIncarnations) (
  1017  	idutil.UserInfo, error) {
  1018  	verifyingKeys, cryptPublicKeys, kidNames, err := filterKeys(
  1019  		upk.Current.DeviceKeys)
  1020  	if err != nil {
  1021  		return idutil.UserInfo{}, err
  1022  	}
  1023  
  1024  	revokedVerifyingKeys, revokedCryptPublicKeys, revokedKidNames, err :=
  1025  		k.filterRevokedKeys(
  1026  			ctx, upk.Current.Uid, upk.Current.DeviceKeys, upk.Current.Reset)
  1027  	if err != nil {
  1028  		return idutil.UserInfo{}, err
  1029  	}
  1030  
  1031  	if len(revokedKidNames) > 0 {
  1032  		for k, v := range revokedKidNames {
  1033  			kidNames[k] = v
  1034  		}
  1035  	}
  1036  
  1037  	for _, incarnation := range upk.PastIncarnations {
  1038  		revokedVerifyingKeysPast, revokedCryptPublicKeysPast,
  1039  			revokedKidNames, err :=
  1040  			k.filterRevokedKeys(
  1041  				ctx, incarnation.Uid, incarnation.DeviceKeys, incarnation.Reset)
  1042  		if err != nil {
  1043  			return idutil.UserInfo{}, err
  1044  		}
  1045  
  1046  		if len(revokedKidNames) > 0 {
  1047  			for k, v := range revokedKidNames {
  1048  				kidNames[k] = v
  1049  			}
  1050  		}
  1051  
  1052  		for k, v := range revokedVerifyingKeysPast {
  1053  			revokedVerifyingKeys[k] = v
  1054  		}
  1055  		for k, v := range revokedCryptPublicKeysPast {
  1056  			revokedCryptPublicKeys[k] = v
  1057  		}
  1058  	}
  1059  
  1060  	u := idutil.UserInfo{
  1061  		Name: kbname.NewNormalizedUsername(
  1062  			upk.Current.Username),
  1063  		UID:                    upk.Current.Uid,
  1064  		VerifyingKeys:          verifyingKeys,
  1065  		CryptPublicKeys:        cryptPublicKeys,
  1066  		KIDNames:               kidNames,
  1067  		EldestSeqno:            upk.Current.EldestSeqno,
  1068  		RevokedVerifyingKeys:   revokedVerifyingKeys,
  1069  		RevokedCryptPublicKeys: revokedCryptPublicKeys,
  1070  	}
  1071  
  1072  	k.setCachedUserInfo(upk.Current.Uid, u)
  1073  	return u, nil
  1074  }
  1075  
  1076  func (k *KeybaseServiceBase) getCachedCurrentSessionOrInProgressCh() (
  1077  	cachedSession idutil.SessionInfo, inProgressCh chan struct{}, doRPC bool) {
  1078  	k.sessionCacheLock.Lock()
  1079  	defer k.sessionCacheLock.Unlock()
  1080  
  1081  	if k.cachedCurrentSession != (idutil.SessionInfo{}) {
  1082  		return k.cachedCurrentSession, nil, false
  1083  	}
  1084  
  1085  	// If someone already started the RPC, wait for them (and release
  1086  	// the lock).
  1087  	if k.sessionInProgressCh != nil {
  1088  		return idutil.SessionInfo{}, k.sessionInProgressCh, false
  1089  	}
  1090  
  1091  	k.sessionInProgressCh = make(chan struct{})
  1092  	return idutil.SessionInfo{}, k.sessionInProgressCh, true
  1093  }
  1094  
  1095  func (k *KeybaseServiceBase) getCurrentSession(
  1096  	ctx context.Context, sessionID int) (idutil.SessionInfo, bool, error) {
  1097  	var cachedCurrentSession idutil.SessionInfo
  1098  	var inProgressCh chan struct{}
  1099  	doRPC := false
  1100  	// Loop until either we have the session info, or until we are the
  1101  	// sole goroutine that needs to make the RPC.  Avoid holding the
  1102  	// session cache lock during the RPC, since that can result in a
  1103  	// deadlock if the RPC results in a call to `ClearCaches()`.
  1104  	for !doRPC {
  1105  		cachedCurrentSession, inProgressCh, doRPC =
  1106  			k.getCachedCurrentSessionOrInProgressCh()
  1107  		if cachedCurrentSession != (idutil.SessionInfo{}) {
  1108  			return cachedCurrentSession, false, nil
  1109  		}
  1110  
  1111  		if !doRPC {
  1112  			// Wait for another goroutine to finish the RPC.
  1113  			select {
  1114  			case <-inProgressCh:
  1115  			case <-ctx.Done():
  1116  				return idutil.SessionInfo{}, false, ctx.Err()
  1117  			}
  1118  		}
  1119  	}
  1120  
  1121  	var s idutil.SessionInfo
  1122  	// Close and clear the in-progress channel, even on an error.
  1123  	defer func() {
  1124  		k.sessionCacheLock.Lock()
  1125  		defer k.sessionCacheLock.Unlock()
  1126  		k.cachedCurrentSession = s
  1127  		close(k.sessionInProgressCh)
  1128  		k.sessionInProgressCh = nil
  1129  	}()
  1130  
  1131  	res, err := k.sessionClient.CurrentSession(ctx, sessionID)
  1132  	if err != nil {
  1133  		if _, ok := err.(libkb.NoSessionError); ok {
  1134  			// Use an error with a proper OS error code attached to it.
  1135  			err = idutil.NoCurrentSessionError{}
  1136  		}
  1137  		return idutil.SessionInfo{}, false, err
  1138  	}
  1139  	s, err = idutil.SessionInfoFromProtocol(res)
  1140  	if err != nil {
  1141  		return idutil.SessionInfo{}, false, err
  1142  	}
  1143  
  1144  	k.log.CDebugf(
  1145  		ctx, "new session with username %s, uid %s, crypt public key %s, and verifying key %s",
  1146  		s.Name, s.UID, s.CryptPublicKey, s.VerifyingKey)
  1147  	return s, true, nil
  1148  }
  1149  
  1150  // CurrentSession implements the KeybaseService interface for KeybaseServiceBase.
  1151  func (k *KeybaseServiceBase) CurrentSession(
  1152  	ctx context.Context, sessionID int) (
  1153  	idutil.SessionInfo, error) {
  1154  	ctx = CtxWithRandomIDReplayable(
  1155  		ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log)
  1156  
  1157  	s, newSession, err := k.getCurrentSession(ctx, sessionID)
  1158  	if err != nil {
  1159  		return idutil.SessionInfo{}, err
  1160  	}
  1161  
  1162  	if newSession && k.config != nil {
  1163  		// Don't hold the lock while calling `serviceLoggedIn`.
  1164  		_ = serviceLoggedIn(ctx, k.config, s, TLFJournalBackgroundWorkEnabled)
  1165  	}
  1166  
  1167  	return s, nil
  1168  }
  1169  
  1170  // FavoriteAdd implements the KeybaseService interface for KeybaseServiceBase.
  1171  func (k *KeybaseServiceBase) FavoriteAdd(ctx context.Context, folder keybase1.FolderHandle) error {
  1172  	return k.favoriteClient.FavoriteAdd(ctx, keybase1.FavoriteAddArg{Folder: folder})
  1173  }
  1174  
  1175  // FavoriteDelete implements the KeybaseService interface for KeybaseServiceBase.
  1176  func (k *KeybaseServiceBase) FavoriteDelete(ctx context.Context, folder keybase1.FolderHandle) error {
  1177  	return k.favoriteClient.FavoriteIgnore(ctx,
  1178  		keybase1.FavoriteIgnoreArg{Folder: folder})
  1179  }
  1180  
  1181  // FavoriteList implements the KeybaseService interface for KeybaseServiceBase.
  1182  func (k *KeybaseServiceBase) FavoriteList(ctx context.Context,
  1183  	sessionID int) (keybase1.FavoritesResult, error) {
  1184  	return k.favoriteClient.GetFavorites(ctx, sessionID)
  1185  }
  1186  
  1187  // EncryptFavorites encrypts cached favorites to store on disk.
  1188  func (k *KeybaseServiceBase) EncryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) {
  1189  	return k.kbfsClient.EncryptFavorites(ctx, dataToEncrypt)
  1190  }
  1191  
  1192  // DecryptFavorites decrypts cached favorites stored on disk.
  1193  func (k *KeybaseServiceBase) DecryptFavorites(ctx context.Context, dataToEncrypt []byte) (res []byte, err error) {
  1194  	return k.kbfsClient.DecryptFavorites(ctx, dataToEncrypt)
  1195  }
  1196  
  1197  // NotifyOnlineStatusChanged implements the KeybaseService interface for
  1198  // KeybaseServiceBase.
  1199  func (k *KeybaseServiceBase) NotifyOnlineStatusChanged(ctx context.Context,
  1200  	online bool) error {
  1201  	k.log.CDebugf(ctx, "Sending notification for onlineStatus: online=%v", online)
  1202  	return k.kbfsClient.FSOnlineStatusChangedEvent(ctx, online)
  1203  }
  1204  
  1205  // Notify implements the KeybaseService interface for KeybaseServiceBase.
  1206  func (k *KeybaseServiceBase) Notify(ctx context.Context, notification *keybase1.FSNotification) error {
  1207  	return k.kbfsClient.FSEvent(ctx, *notification)
  1208  }
  1209  
  1210  // NotifyPathUpdated implements the KeybaseService interface for
  1211  // KeybaseServiceBase.
  1212  func (k *KeybaseServiceBase) NotifyPathUpdated(
  1213  	ctx context.Context, path string) error {
  1214  	return k.kbfsClient.FSPathUpdate(ctx, path)
  1215  }
  1216  
  1217  // NotifySyncStatus implements the KeybaseService interface for
  1218  // KeybaseServiceBase.
  1219  func (k *KeybaseServiceBase) NotifySyncStatus(ctx context.Context,
  1220  	status *keybase1.FSPathSyncStatus) error {
  1221  	return k.kbfsClient.FSSyncEvent(ctx, *status)
  1222  }
  1223  
  1224  // NotifyOverallSyncStatus implements the KeybaseService interface for
  1225  // KeybaseServiceBase.
  1226  func (k *KeybaseServiceBase) NotifyOverallSyncStatus(
  1227  	ctx context.Context, status keybase1.FolderSyncStatus) error {
  1228  	return k.kbfsClient.FSOverallSyncEvent(ctx, status)
  1229  }
  1230  
  1231  // NotifyFavoritesChanged implements the KeybaseService interface for
  1232  // KeybaseServiceBase.
  1233  func (k *KeybaseServiceBase) NotifyFavoritesChanged(ctx context.Context) error {
  1234  	return k.kbfsClient.FSFavoritesChangedEvent(ctx)
  1235  }
  1236  
  1237  // OnPathChange implements the SubscriptionNotifier interface.
  1238  func (k *KeybaseServiceBase) OnPathChange(
  1239  	clientID SubscriptionManagerClientID,
  1240  	subscriptionIDs []SubscriptionID, path string,
  1241  	topics []keybase1.PathSubscriptionTopic) {
  1242  	subscriptionIDStrings := make([]string, 0, len(subscriptionIDs))
  1243  	for _, sid := range subscriptionIDs {
  1244  		subscriptionIDStrings = append(subscriptionIDStrings, string(sid))
  1245  	}
  1246  	err := k.kbfsClient.FSSubscriptionNotifyPathEvent(
  1247  		context.Background(), keybase1.FSSubscriptionNotifyPathEventArg{
  1248  			ClientID:        string(clientID),
  1249  			SubscriptionIDs: subscriptionIDStrings,
  1250  			Path:            path,
  1251  			Topics:          topics,
  1252  		})
  1253  	if err != nil {
  1254  		k.log.CDebugf(
  1255  			context.TODO(), "Couldn't send path change notification: %+v", err)
  1256  	}
  1257  }
  1258  
  1259  // OnNonPathChange implements the SubscriptionNotifier interface.
  1260  func (k *KeybaseServiceBase) OnNonPathChange(
  1261  	clientID SubscriptionManagerClientID,
  1262  	subscriptionIDs []SubscriptionID, topic keybase1.SubscriptionTopic) {
  1263  	subscriptionIDStrings := make([]string, 0, len(subscriptionIDs))
  1264  	for _, sid := range subscriptionIDs {
  1265  		subscriptionIDStrings = append(subscriptionIDStrings, string(sid))
  1266  	}
  1267  	err := k.kbfsClient.FSSubscriptionNotifyEvent(context.Background(),
  1268  		keybase1.FSSubscriptionNotifyEventArg{
  1269  			ClientID:        string(clientID),
  1270  			SubscriptionIDs: subscriptionIDStrings,
  1271  			Topic:           topic,
  1272  		})
  1273  	if err != nil {
  1274  		k.log.CDebugf(
  1275  			context.TODO(),
  1276  			"Couldn't send non-path change notification: %+v", err)
  1277  	}
  1278  }
  1279  
  1280  // FlushUserFromLocalCache implements the KeybaseService interface for
  1281  // KeybaseServiceBase.
  1282  func (k *KeybaseServiceBase) FlushUserFromLocalCache(ctx context.Context,
  1283  	uid keybase1.UID) {
  1284  	k.log.CDebugf(ctx, "Flushing cache for user %s", uid)
  1285  	k.setCachedUserInfo(uid, idutil.UserInfo{})
  1286  }
  1287  
  1288  // CtxKeybaseServiceTagKey is the type used for unique context tags
  1289  // used while servicing incoming keybase requests.
  1290  type CtxKeybaseServiceTagKey int
  1291  
  1292  const (
  1293  	// CtxKeybaseServiceIDKey is the type of the tag for unique
  1294  	// operation IDs used while servicing incoming keybase requests.
  1295  	CtxKeybaseServiceIDKey CtxKeybaseServiceTagKey = iota
  1296  )
  1297  
  1298  // CtxKeybaseServiceOpID is the display name for the unique operation
  1299  // enqueued rekey ID tag.
  1300  const CtxKeybaseServiceOpID = "KSID"
  1301  
  1302  // FSEditListRequest implements keybase1.NotifyFSRequestInterface for
  1303  // KeybaseServiceBase.
  1304  func (k *KeybaseServiceBase) FSEditListRequest(ctx context.Context,
  1305  	req keybase1.FSEditListRequest) (err error) {
  1306  	ctx = CtxWithRandomIDReplayable(ctx, CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID,
  1307  		k.log)
  1308  	k.log.CDebugf(ctx, "Edit list request for %s (public: %t)",
  1309  		req.Folder.Name, !req.Folder.Private)
  1310  	tlfHandle, err := getHandleFromFolderName(
  1311  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, req.Folder.Name,
  1312  		!req.Folder.Private)
  1313  	if err != nil {
  1314  		return err
  1315  	}
  1316  
  1317  	rootNode, _, err := k.config.KBFSOps().
  1318  		GetOrCreateRootNode(ctx, tlfHandle, data.MasterBranch)
  1319  	if err != nil {
  1320  		return err
  1321  	}
  1322  	history, err := k.config.KBFSOps().GetEditHistory(ctx,
  1323  		rootNode.GetFolderBranch())
  1324  	if err != nil {
  1325  		return err
  1326  	}
  1327  
  1328  	// TODO(KBFS-2996) Convert the edits to an RPC response.
  1329  	resp := keybase1.FSEditListArg{
  1330  		RequestID: req.RequestID,
  1331  		Edits:     history,
  1332  	}
  1333  
  1334  	k.log.CDebugf(ctx, "Sending edit history response with %d writer clusters",
  1335  		len(resp.Edits.History))
  1336  	return k.kbfsClient.FSEditList(ctx, resp)
  1337  }
  1338  
  1339  // FSSyncStatusRequest implements keybase1.NotifyFSRequestInterface for
  1340  // KeybaseServiceBase.
  1341  func (k *KeybaseServiceBase) FSSyncStatusRequest(ctx context.Context,
  1342  	req keybase1.FSSyncStatusRequest) (err error) {
  1343  	k.log.CDebugf(ctx, "Got sync status request: %v", req)
  1344  
  1345  	resp := keybase1.FSSyncStatusArg{RequestID: req.RequestID}
  1346  
  1347  	// For now, just return the number of syncing bytes.
  1348  	jManager, err := GetJournalManager(k.config)
  1349  	if err == nil {
  1350  		status, _ := jManager.Status(ctx)
  1351  		resp.Status.TotalSyncingBytes = status.UnflushedBytes
  1352  		k.log.CDebugf(ctx, "Sending sync status response with %d syncing bytes",
  1353  			status.UnflushedBytes)
  1354  	} else {
  1355  		k.log.CDebugf(ctx, "No journal server, sending empty response")
  1356  	}
  1357  
  1358  	return k.kbfsClient.FSSyncStatus(ctx, resp)
  1359  }
  1360  
  1361  // TeamChangedByID implements keybase1.NotifyTeamInterface for
  1362  // KeybaseServiceBase.
  1363  func (k *KeybaseServiceBase) TeamChangedByID(ctx context.Context,
  1364  	arg keybase1.TeamChangedByIDArg) error {
  1365  	k.log.CDebugf(ctx, "Flushing cache for team %s "+
  1366  		"(membershipChange=%t, keyRotated=%t, renamed=%t)",
  1367  		arg.TeamID, arg.Changes.MembershipChanged,
  1368  		arg.Changes.KeyRotated, arg.Changes.Renamed)
  1369  	k.setCachedTeamInfo(arg.TeamID, idutil.TeamInfo{})
  1370  
  1371  	if arg.Changes.Renamed {
  1372  		k.config.KBFSOps().TeamNameChanged(ctx, arg.TeamID)
  1373  	}
  1374  	return nil
  1375  }
  1376  
  1377  // TeamChangedByName implements keybase1.NotifyTeamInterface for
  1378  // KeybaseServiceBase.
  1379  func (k *KeybaseServiceBase) TeamChangedByName(ctx context.Context,
  1380  	arg keybase1.TeamChangedByNameArg) error {
  1381  	// ignore
  1382  	return nil
  1383  }
  1384  
  1385  // TeamDeleted implements keybase1.NotifyTeamInterface for
  1386  // KeybaseServiceBase.
  1387  func (k *KeybaseServiceBase) TeamDeleted(ctx context.Context,
  1388  	teamID keybase1.TeamID) error {
  1389  	return nil
  1390  }
  1391  
  1392  // TeamExit implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1393  func (k *KeybaseDaemonRPC) TeamExit(context.Context, keybase1.TeamID) error {
  1394  	return nil
  1395  }
  1396  
  1397  // TeamRoleMapChanged implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1398  func (k *KeybaseDaemonRPC) TeamRoleMapChanged(context.Context, keybase1.UserTeamVersion) error {
  1399  	return nil
  1400  }
  1401  
  1402  // NewlyAddedToTeam implements keybase1.NotifyTeamInterface for
  1403  // KeybaseServiceBase.
  1404  func (k *KeybaseDaemonRPC) NewlyAddedToTeam(context.Context, keybase1.TeamID) error {
  1405  	return nil
  1406  }
  1407  
  1408  // TeamMetadataUpdate implements keybase1.NotifyTeamInterface for
  1409  // KeybaseServiceBase.
  1410  func (k *KeybaseDaemonRPC) TeamMetadataUpdate(context.Context) error {
  1411  	return nil
  1412  }
  1413  
  1414  // TeamAbandoned implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1415  func (k *KeybaseDaemonRPC) TeamAbandoned(
  1416  	ctx context.Context, tid keybase1.TeamID) error {
  1417  	k.log.CDebugf(ctx, "Implicit team %s abandoned", tid)
  1418  	k.setCachedTeamInfo(tid, idutil.TeamInfo{})
  1419  	k.config.KBFSOps().TeamAbandoned(ctx, tid)
  1420  	return nil
  1421  }
  1422  
  1423  // AvatarUpdated implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1424  func (k *KeybaseDaemonRPC) AvatarUpdated(ctx context.Context,
  1425  	arg keybase1.AvatarUpdatedArg) error {
  1426  	return nil
  1427  }
  1428  
  1429  // TeamTreeMembershipsPartial implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1430  func (k *KeybaseDaemonRPC) TeamTreeMembershipsPartial(context.Context,
  1431  	keybase1.TeamTreeMembership) error {
  1432  	return nil
  1433  }
  1434  
  1435  // TeamTreeMembershipsDone implements keybase1.NotifyTeamInterface for KeybaseServiceBase.
  1436  func (k *KeybaseDaemonRPC) TeamTreeMembershipsDone(context.Context,
  1437  	keybase1.TeamTreeMembershipsDoneResult) error {
  1438  	return nil
  1439  }
  1440  
  1441  // StartMigration implements keybase1.ImplicitTeamMigrationInterface for
  1442  // KeybaseServiceBase.
  1443  func (k *KeybaseServiceBase) StartMigration(ctx context.Context,
  1444  	folder keybase1.Folder) (err error) {
  1445  	mdServer := k.config.MDServer()
  1446  	if mdServer == nil {
  1447  		return errors.New("no mdserver")
  1448  	}
  1449  	// Making a favorite here to reuse the code that converts from
  1450  	// `keybase1.FolderType` into `tlf.Type`.
  1451  	fav := favorites.NewFolderFromProtocol(folder)
  1452  	handle, err := GetHandleFromFolderNameAndType(
  1453  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type)
  1454  	if err != nil {
  1455  		return err
  1456  	}
  1457  	// Before taking the lock, first make sure this device can handle
  1458  	// the migration.
  1459  	tlfID := handle.TlfID()
  1460  	err = k.config.KBFSOps().CheckMigrationPerms(ctx, tlfID)
  1461  	if err != nil {
  1462  		k.log.CDebugf(ctx, "This device cannot migrate %s: %+v", tlfID, err)
  1463  		return err
  1464  	}
  1465  	return k.config.MDServer().StartImplicitTeamMigration(ctx, tlfID)
  1466  }
  1467  
  1468  // FinalizeMigration implements keybase1.ImplicitTeamMigrationInterface for
  1469  // KeybaseServiceBase.
  1470  func (k *KeybaseServiceBase) FinalizeMigration(ctx context.Context,
  1471  	folder keybase1.Folder) (err error) {
  1472  	fav := favorites.NewFolderFromProtocol(folder)
  1473  	handle, err := GetHandleFromFolderNameAndType(
  1474  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, fav.Name, fav.Type)
  1475  	if err != nil {
  1476  		return err
  1477  	}
  1478  	if handle.TypeForKeying() == tlf.TeamKeying {
  1479  		// Clear the cache for this implicit team, to ensure we get
  1480  		// all the latest key generations for the team info during the
  1481  		// migration.
  1482  		id := handle.FirstResolvedWriter()
  1483  		if id.IsTeamOrSubteam() {
  1484  			tid, err := id.AsTeam()
  1485  			if err != nil {
  1486  				return err
  1487  			}
  1488  			k.log.CDebugf(ctx, "Clearing team info for tid=%s, handle=%s",
  1489  				tid, handle.GetCanonicalPath())
  1490  			k.setCachedTeamInfo(tid, idutil.TeamInfo{})
  1491  		}
  1492  	}
  1493  	return k.config.KBFSOps().MigrateToImplicitTeam(ctx, handle.TlfID())
  1494  }
  1495  
  1496  // GetTLFCryptKeys implements the TlfKeysInterface interface for
  1497  // KeybaseServiceBase.
  1498  func (k *KeybaseServiceBase) GetTLFCryptKeys(ctx context.Context,
  1499  	query keybase1.TLFQuery) (res keybase1.GetTLFCryptKeysRes, err error) {
  1500  	if ctx, err = tlfhandle.MakeExtendedIdentify(
  1501  		CtxWithRandomIDReplayable(ctx,
  1502  			CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log),
  1503  		query.IdentifyBehavior,
  1504  	); err != nil {
  1505  		return keybase1.GetTLFCryptKeysRes{}, err
  1506  	}
  1507  
  1508  	tlfHandle, err := getHandleFromFolderName(
  1509  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName, false)
  1510  	if err != nil {
  1511  		return res, err
  1512  	}
  1513  
  1514  	res.NameIDBreaks.CanonicalName = keybase1.CanonicalTlfName(
  1515  		tlfHandle.GetCanonicalName())
  1516  
  1517  	keys, id, err := k.config.KBFSOps().GetTLFCryptKeys(ctx, tlfHandle)
  1518  	if err != nil {
  1519  		return res, err
  1520  	}
  1521  	res.NameIDBreaks.TlfID = keybase1.TLFID(id.String())
  1522  
  1523  	for i, key := range keys {
  1524  		res.CryptKeys = append(res.CryptKeys, keybase1.CryptKey{
  1525  			KeyGeneration: int(kbfsmd.FirstValidKeyGen) + i,
  1526  			Key:           keybase1.Bytes32(key.Data()),
  1527  		})
  1528  	}
  1529  
  1530  	if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
  1531  		res.NameIDBreaks.Breaks = tlfhandle.GetExtendedIdentify(ctx).
  1532  			GetTlfBreakAndClose()
  1533  	}
  1534  
  1535  	return res, nil
  1536  }
  1537  
  1538  // GetPublicCanonicalTLFNameAndID implements the TlfKeysInterface interface for
  1539  // KeybaseServiceBase.
  1540  func (k *KeybaseServiceBase) GetPublicCanonicalTLFNameAndID(
  1541  	ctx context.Context, query keybase1.TLFQuery) (
  1542  	res keybase1.CanonicalTLFNameAndIDWithBreaks, err error) {
  1543  	if ctx, err = tlfhandle.MakeExtendedIdentify(
  1544  		CtxWithRandomIDReplayable(ctx,
  1545  			CtxKeybaseServiceIDKey, CtxKeybaseServiceOpID, k.log),
  1546  		query.IdentifyBehavior,
  1547  	); err != nil {
  1548  		return keybase1.CanonicalTLFNameAndIDWithBreaks{}, err
  1549  	}
  1550  
  1551  	tlfHandle, err := getHandleFromFolderName(
  1552  		ctx, k.config.KBPKI(), k.config.MDOps(), k.config, query.TlfName,
  1553  		true /* public */)
  1554  	if err != nil {
  1555  		return res, err
  1556  	}
  1557  
  1558  	res.CanonicalName = keybase1.CanonicalTlfName(
  1559  		tlfHandle.GetCanonicalName())
  1560  
  1561  	id, err := k.config.KBFSOps().GetTLFID(ctx, tlfHandle)
  1562  	if err != nil {
  1563  		return res, err
  1564  	}
  1565  	res.TlfID = keybase1.TLFID(id.String())
  1566  
  1567  	if query.IdentifyBehavior.WarningInsteadOfErrorOnBrokenTracks() {
  1568  		res.Breaks = tlfhandle.GetExtendedIdentify(ctx).GetTlfBreakAndClose()
  1569  	}
  1570  
  1571  	return res, nil
  1572  }
  1573  
  1574  // EstablishMountDir asks the service for the current mount path
  1575  func (k *KeybaseServiceBase) EstablishMountDir(ctx context.Context) (
  1576  	string, error) {
  1577  	dir, err := k.kbfsMountClient.GetCurrentMountDir(ctx)
  1578  	if err != nil {
  1579  		k.log.CInfof(ctx, "GetCurrentMountDir fails - ", err)
  1580  		return "", err
  1581  	}
  1582  	if dir == "" {
  1583  		dirs, err := k.kbfsMountClient.GetAllAvailableMountDirs(ctx)
  1584  		if err != nil {
  1585  			k.log.CInfof(ctx, "GetAllAvailableMountDirs fails - ", err)
  1586  			return "", err
  1587  		}
  1588  		dir, err = chooseDefaultMount(ctx, dirs, k.log)
  1589  		if err != nil {
  1590  			k.log.CInfof(ctx, "chooseDefaultMount fails - ", err)
  1591  			return "", err
  1592  		}
  1593  		err2 := k.kbfsMountClient.SetCurrentMountDir(ctx, dir)
  1594  		if err2 != nil {
  1595  			k.log.CInfof(ctx, "SetCurrentMountDir fails - ", err2)
  1596  		}
  1597  		// Continue mounting even if we can't save the mount
  1598  		k.log.CDebugf(ctx, "Choosing mountdir %s from %v", dir, dirs)
  1599  	}
  1600  	return dir, err
  1601  }
  1602  
  1603  // PutGitMetadata implements the KeybaseService interface for
  1604  // KeybaseServiceBase.
  1605  func (k *KeybaseServiceBase) PutGitMetadata(
  1606  	ctx context.Context, folder keybase1.FolderHandle, repoID keybase1.RepoID,
  1607  	metadata keybase1.GitLocalMetadata) error {
  1608  	return k.gitClient.PutGitMetadata(ctx, keybase1.PutGitMetadataArg{
  1609  		Folder:   folder,
  1610  		RepoID:   repoID,
  1611  		Metadata: metadata,
  1612  	})
  1613  }
  1614  
  1615  // GetKVStoreClient implements the KeybaseService interface for
  1616  // KeybaseServiceBase.
  1617  func (k *KeybaseServiceBase) GetKVStoreClient() keybase1.KvstoreInterface {
  1618  	return k.kvstoreClient
  1619  }